Advent Calendar 12日目の記事です。
初めての参加となります。よろしくお願いします。
現地時間11月16日より、Discordで返信機能が実装されました。
さぞかしBOTに「返信機能を付けてみたい!」と思う方もいるかと思います。
今回はそれについて書いていきます。
返信機能は特別なものではない
Discordの公式APIリファレンスを見ると、新しくmessage_referenceという項目ができているのが分かります。
ここにメッセージIDを指定してやればあっという間に返信が実現できます。
ライブラリを使わずにどうやるか?
私達は普段直接APIを触ることなく、それぞれの言語に用意されたフレームワーク(Discord.pyやDiscord.jsなど)を使いプログラミングしています。しかし、実際はAPIを扱いやすくしたに過ぎないため、素の状態(APIを直接触ること)でやり取りすることもできます。
この記事を書いた時点ではまだ返信機能をサポートしていないライブラリが大半です。ここでは、直接APIとやり取りしてメッセージを送信する方法を解説します。
エンドポイント
まず、メッセージを送信するためのエンドポイントは
https://discord.com/api/channels/[チャンネルID]/messages
となっているので、そんなに難しくありません。メッセージオブジェクトのチャンネルIDを当てはめればURLは完成です。
ヘッダーとデータ
APIとはJSONを使ってやり取りします。
そのため、ヘッダのContent-TypeはContent-Type: application/json
とする必要があります。
APIを使うにあたって、ヘッダ部分に認証情報を記載する必要があります。
「え、これはどうすれば…」と思った方。もちろんBOTのトークンが使えるので大丈夫です。
言語別に記載しませんが、リクエストヘッダに書くときは
Authorization: Bot [G6WtkBk9nHW4AlhB3wbCBSzx.nDUuhSMp02k9Okjs5DwRn4QRt9SvFo36m5]
となります。トークンは適宜ご自身のものに変えてください。[ ]で囲んだ部分にトークンを入れます。
次に送信するデータです。
基本形は
{
"content": "Hello world"
}
です。contentにメッセージを入れます。
Webhookみたいにとてもシンプルですね。
ここに、message_reference
プロパティを追加して「返信だよ!」という情報を追加します。
公式リファレンスには
FIELD | DESCRIPTION (和訳) |
---|---|
message_id | 元のメッセージのID |
channel_id? | 元のメッセージのチャンネルID |
guild_id? | 元のメッセージのサーバーID |
と書かれています。FIELDの最後についている**?**は「パラメータは必須ではない、省略可」という意になっているので、message_idだけ渡せばよいということが分かります。
message_idには、メッセージオブジェクトのメッセージIDを渡せばいいのです。
ここまでのをまとめると、送信するJSONはこうなります。
{
"content": "Hello world!",
"message_reference": {
"message_id": 1234567890123456
}
}
となります。
これらをヘッダと組み合わせて送信してやれば、返信機能が実現できます!
試しに実験サーバーで何かメッセージを送信してみて、チャンネルIDをURLにセットして、message_idのところを送信したメッセージIDに書き換えて
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bot YourToken.PUT.Here" -d "{\"content\":\"Reply\" , \"message_reference\":{\"message_id\":YOUR_MESSAGE_ID}}" https://discord.com/api/channels/YOUR_CHANNEL_ID/messages
を端末で実行して
というふうになったことを確認できれば、完了です。
メンションOFFにする
ただ、このままだと相手にメンションが飛んでしまいます。
メンションするか、しないかは通常だとアプリの設定で変えられます。
APIも同様で「メンションしないで」という情報を送ればよいのです。
先程のJSONに付け足して
{
"content": "Hello world!",
"message_reference": {
"message_id": 1234567890123456
},
"allowed_mentions": {
"replied_user": false
}
}
というふうになります。
詳しくはAllowed mentions objectを参照してください。
これのデータを先程のcurlで送るとメンションなしで返信できるのを確認できると思います。
関数化させよう
ここまでcurlを使いましたが、プログラムで使うのにはとても不都合です。
関数化させて、比較的便利に使えるようにしましょう。
Python用にあらかじめ用意した関数を貼っておきます。
使いやすいようにアレンジしてお使いください。
async def reply(basemsg, message, send_mention, **kwargs):
url = f'https://discord.com/api/channels/{basemsg.channel.id}/messages'
heads = {"Content-Type":"application/json", "Authorization":f"Bot {TOKEN}"}
data = {"content":message, "message_reference": {"message_id":basemsg.id}, "allowed_mentions": {"replied_user":send_mention}}
if kwargs.get('embed'):
data['embed']=kwargs.get('embed').to_dict()
async with aiohttp.ClientSession() as session:
async with session.post(url, data=json.dumps(data), headers=heads) as req:
if req.status!=200:
raise Exception(f'Gateway returned an error')
return False
return True
使い方は
await reply(message, '返信した!', False)
となります。左から1番目にはメッセージオブジェクトを、2番目には送信したいテキスト、3番目は相手にメンションするかどうかをTrue or Falseで指定します。
また、任意でembed
というキーワード付き引数にdiscord.Embedのオブジェクトを渡すと埋め込みも送信できます。
em = discord.Embed(title='埋め込みもできるよ')
await reply(message, '返信した!', False, embed=em)
なお、この関数はrequests、json、aiohttpモジュールを使用します。事前にインポートしてください。
まとめ
返信をするときはmessage_reference
のmessage_id
プロパティに返信先メッセージIDを指定する
@メンションをしないときはallowed_mentions
のreplied_user
をFalseにする
本当であればライブラリのアップデートを待つのが一番だし、一番簡単です。
あくまでも参考として、早く使ってみたい方向けです。
その点についてご理解ください。