25
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Discord bot で ボタンを実装

Last updated at Posted at 2021-05-29

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は以下の通りです。

requirements.txt
discord~=1.0.1
requests~=2.25.1
aiohttp~=3.7.4.post0

基本的なBotプログラム

discordbot.py

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関数の引数となっているmsgmsg["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を含んだデータを返さなければなりません。(200HTTPステータスコードの一種で、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バージョンを追記(コメントありがとうございます。)
25
24
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
24

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?