DiscordチャットボットをPythonでつくる (4) - チャットボットでメッセージを受信する

今回は、ようやくPythonを使ったチャットボット作成を始めていきます。

前回までの作業により、作業ディレクトリ discord-chatbot 内には少なくとも以下の2つが存在してることを確認してください。

  1. discord-chatbotディレクトリに移動し、仮想環境を有効化します。

    cd discord-chatbot
    source .venv/bin/activate
    
  2. discord.py がインストールされていることも確認しておきましょう。

    pip list
    

最初は単純に、Discordに接続するだけのチャットボットを作成します。
chatbot.py という名前のファイルを作成し、その中に以下のコードを書いてください。

import discord


with open('.discord_token') as f:
    token = f.read().strip()

intents = discord.Intents.default()
client = discord.Client(intents=intents)
client.run(token)

作成したスクリプトを実行してみましょう。

python chatbot.py

するとコンソールに以下のようなログが出力されるはずです。
“… has connected to Gateway” と書いてあります。ちゃんとDiscordに接続することが出来たようです。

2023-08-18 23:39:36 INFO     discord.client logging in using static token
2023-08-18 23:39:37 INFO     discord.gateway Shard ID None has connected to Gateway (Session ID: xxx).

停止するには Ctrl+C を入力します。

スクリプトの内容を一つずつ見ていきましょう。

import discord

discord.py を読み込んでいます。

with open('.discord_token') as f:
    token = f.read().strip()

これは.discord_tokenに記載されているトークンを読み込み、 token 変数に代入しています。
末尾の改行を除去するために strip を適用しています。

intents = discord.Intents.default()

discord.Intentsはチャットボットにどのようなイベントの受信を許すかを定義するためのクラスです。
default() となっておりデフォルト値を使用しています。とりあえず追求はせず、次に行きます。

client = discord.Client(intents=intents)

discord.Client(...) となっており、discordのクライアントを生成しています。
これがチャットボットの正体です。

client.run(token)

トークンを使用してクライアントを起動しています。
これによりチャットボットが稼働を開始します。

作成したチャットボットに機能を追加していきましょう。

discord.pyでは様々な機能追加方法が用意されていますが、
このシリーズでは discord.Client クラスを継承した独自のクラスを作成し、それに変更を加えていきます。

先ほど作成した chatbot.py を以下のように変更しましょう。

import discord


class MyClient(discord.Client):
    async def on_ready(self):
        """Discord接続時に実行される関数."""
        print(f'{self.user}として接続しました。')

    async def on_message(self, message: discord.Message):
        """メッセージ受信時に実行される関数."""
        print(f'{message.author}よりメッセージを受信しました: {message.content}')


with open('.discord_token') as f:
    token = f.read().strip()

intents = discord.Intents.default()
client = MyClient(intents=intents)
client.run(token)
  1. MyClientという名前のdiscord.Clientを継承したクラスを定義し、
    on_ready, on_message という2つの関数をオーバーライドしています。
  2. それぞれ以下の用途で使用されます:
    • on_readyメソッド: Discord接続時に実行される関数
    • on_messageメソッド: メッセージ受信時に実行される関数
  3. いずれの関数にも定義の先頭に async という文字列があります。
    これらはコルーチン関数と呼ばれる非同期処理を行うための関数です。詳細は後日解説します。
  4. self.user にはボットのユーザ名が入っています。
  5. on_messagemessage 引数には、discord.Message型のデータが入力されます。
    message.authorはメッセージの送信者、message.contentはメッセージの内容が入っています。
文字列の先頭についている f の意味は何?

f'{self.user}として接続しました。' のように、先頭に f が付く文字列が出てきます。
これはフォーマット済み文字列リテラルまたはf-stringと呼ばれるもので、
Python 3.6 より追加されました。(参照: フォーマット済み文字列リテラル)

文字列内に {self.user} のように書くことで self.user の値が表示されます。
formatメソッドよりも簡潔に書けて便利なので、このシリーズでは頻繁に使用していきます。

`message`変数の右側にあるコロン(:)は何?

これは型ヒントと呼ばれるもので、コロンの右側に変数に入力される型の情報を記述します (Python 3.5 で追加)。
Pythonでは型の指定がなくても動作しますが、型ヒントを書いておくと様々なメリットがあります。

例えばVSCodeでコードを書く際に型ヒントがあると、message.と打つだけでmessage.content等の候補を表示してくれるためとても便利です。

  1. それでは実行してみましょう。

    python chatbot.py
    
  2. すると、接続時にxxxとして接続が完了しました。といったメッセージがコンソール上に表示されます。

  3. またチャットにメッセージを記入すると、xxxよりメッセージを受信しました:といったメッセージがコンソール上に表示されます。しかし、メッセージの内容が表示されていない!

なぜかメッセージの内容が表示されないので、ドキュメントを見てみましょう。
すると、以下のような記述がありました。

Intents.message_content が有効化されていない場合は、ボットがメンションされている場合とDMである場合を除き、これは常に空文字列となります。

はい。先程スルーしていた discord.Intents の設定が足りていませんでした。
メッセージの内容を受信するためには、intents.message_contentの値をTrueに設定する必要があります。

intents.message_content = True

これを追加して再度スクリプトを起動し直してみましょう。
するとxxxよりメッセージを受信しました: yyyのようにメッセージ内容が出力されるようになりました。

今回は、チャットボットをDiscordに接続してユーザが入力したメッセージを受信するところまで行いました。
次回は受信したメッセージに対してチャット上で返信を行う設定を追加します。

import discord


class MyClient(discord.Client):
    async def on_ready(self):
        """Discord接続時に実行される関数."""
        print(f'{self.user}として接続しました。')

    async def on_message(self, message: discord.Message):
        """メッセージ受信時に実行される関数."""
        print(f'{message.author}よりメッセージを受信しました: {message.content}')


with open('.discord_token') as f:
    token = f.read().strip()

intents = discord.Intents.default()
intents.message_content = True
client = MyClient(intents=intents)
client.run(token)

関連記事