Discord interactions
Discord Developer Portal に文献があります。
APIバージョンはhttps://discord.com/api/v8です。
前提
すっ飛ばして、ボタンの表示方法知りたい人はボタンの表示を参照。
Botの登録
Discord Dev App へアクセスし、Botを作成してください。そしてTOKENを取得しておきましょう。
※方法はネット上に転がっているので省略します。
Python3.8 の構築
本記事ではPython3.8の環境で行っています。
また、あらかじめライブラリをインストールしておきましょう。
$ python3 -m pip install discord.py
一応、requirements.txtは以下の通りです。
discord~=1.0.1
requests~=2.25.1
aiohttp~=3.7.4.post0
基本的なBotプログラム
import discord
import requests
from discord.ext import commands
from pprint import pprint
import aiohttp
TOKEN = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
AuthB = "Bot " + TOKEN
headers = {
    "Authorization": AuthB
}
def returnNormalUrl(channelId):
    return "https://discordapp.com/api/channels/" + str(channelId) + "/messages"
async def notify_callback(id, token):
    url = "https://discord.com/api/v8/interactions/{0}/{1}/callback".format(id, token)
    json = {
        "type": 6
    }
    async with aiohttp.ClientSession() as s:
        async with s.post(url, json=json) as r:
            if 200 <= r.status < 300:
                return
async def on_socket_response(msg):
    if msg["t"] != "INTERACTION_CREATE":
        return
    pprint(msg)
class MyBot(commands.Bot):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.add_listener(on_socket_response)
bot = MyBot(command_prefix='$', description='discord bot')
@bot.event
async def on_message(msg):
    if msg.content == "hello":
        normal_url = returnNormalUrl(msg.channel.id)
        json = {
            "content": "Hello World"
        }
        r = requests.post(normal_url, headers=headers, json=json)
bot.run(oTOKEN)
これをコピペしてTOKEN書き変えて実行すれば多分動きます。(ライブラリ不足のエラー等があればその都度pipでインストールしてください。Pycharm等のIDEで編集すれば自動的にインストールしてくれるかもしれません。)
アクティビティの表示
@bot.event
async def on_ready():
    print("Boot")
    await bot.change_presence(activity=discord.Game("Python")) #Pythonをプレイ中
をon_message関数の前に書けば、
- プログラム起動時にコンソールにBootと表示
- Bot起動中にBotプロフィールに「Pythonをプレイ中」と表示
ボタンメッセージの表示
        normal_url = "https://discordapp.com/api/channels/" + str(message.channel.id) + "/messages"
        json = {
            "content": "Hello World",
            "components": [
                {
                    "type": 1,
                    "components": [
                        {
                            "type": 2,
                            "label": "1st",
                            "style": 1,
                            "custom_id": "click_one",
                        },
                        {
                            "type": 2,
                            "label": "2nd",
                            "style": 3,
                            "custom_id": "click_two",
                        },
                    ]
                }
            ]
        }
        r = requests.post(normal_url, headers=headers, json=json)
をon_message()関数内のif文内に記述等すればボタンが付いたメッセージを表示できます。

"style"要素では、この文献に書いてあるVALUEを指定すればよいです。ボタンの色やデザインが変わります。
"custom_id"の説明はボタン押された時の処理で説明します。
ボタン押された時の処理
on_socket_response関数の引数となっているmsgのmsg["d"]["data"]["custom_id"]にボタンが押された際のcustom_idが格納されているので、これの値をもとに処理を走らせればよいわけです。
例えばon_socket_response関数を以下のように変えてみましょう。
async def on_socket_response(msg):
    if msg["t"] != "INTERACTION_CREATE":
        return
    pprint(msg)
    custom_id = msg["d"]["data"]["custom_id"]
    if custom_id == "click_one":
        normal_url = returnNormalUrl(msg["d"]["channel_id"]) #returnNormalUrl関数の定義はこの記事のどこかにあるよ
        json = {
            "content": "Push button_1"
        }
        r = requests.post(normal_url, headers=headers, json=json)
        await notify_callback(msg["d"]["id"], msg["d"]["token"]) #notify_callback関数は後で説明するよ
こうすると、ボタン1stが押された際に「Push Button_1」というメッセージが同じチャンネルに投稿されます。
notify_callback関数の定義
通常、slash commandと同じように、ボタンが押されると「インタラクション」がサーバーに送信されます。このインタラクションに対しサーバーは200を含んだデータを返さなければなりません。(200はHTTPステータスコードの一種で、Successを意味します。)ここで普通にメッセージをインタラクションの成功返信として返してもよいのですが、ボタンが押されるたびにこの返信メッセージが帰ってくるので煩わしいこともあるでしょう。そのため、notify_callback関数を
async def notify_callback(id, token):
    url = "https://discord.com/api/v8/interactions/{0}/{1}/callback".format(id, token)
    json = {
        "type": 6
    }
    async with aiohttp.ClientSession() as s:
        async with s.post(url, json=json) as r:
            if 200 <= r.status < 300:
                return
と定義しました。json変数の"type"で6を指定しているのはこの文献でもあるようにとりあえず成功返信をするためです。
ボタン押すたびにメッセージを更新
ボタンを押すたびに、ボタンを押せなくしたり、メッセージの番号を+1していく、などしたい時があります。
その際には、更新後のメッセージ内容を格納したjson変数を用意し、
r2 = requests.patch(normal_url2, headers=headers, json=json2)
でPATCHとして送信します。
サンプルコード
左右のボタンを押すたびにメッセージがRightとLeftで切り替わるコードです。ボタンを押すたびに片方のボタンが押せなくなります。
普通にコピペすれば動くはずです....
最後に
間違ってたり、冗長だったらコメントを残していただけると幸いです。
- [2021/5/30 1:21] APIバージョンを追記(コメントありがとうございます。)
