【discord.py】Button 入門〜削除ボタンを作る〜

# はじめに

discord.py または Pycord で以下のように bot のレスポンスに押したら消せるボタンを付けます．（スラッシュコマンドでも可）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625302/0a01b52c-77b5-b369-421f-ba38c15cb5d6.png)
説明は少なめでコードを見て雰囲気を掴んでもらうスタイルでいきます．

## 前提知識

- Python の基本的な文法（クラスなど）
- 超基本的な Discord bot の作り方（トークンの発行の仕方やインテントなど）

## 筆者の環境

- MX Linux 21.3
- Python 3.10.9
- discord.py 2.3.0（py-cord 2.4.1 でも動作確認済み）

# Discord におけるボタンとは

Button は 2021 年 5 月 26 日に Discord に追加された UI Component で，他には Select Menu や Modal があります．また，スラッシュコマンドと同様に Interaction というオブジェクトでユーザと bot の対話を実現します．ボタンなどの UI Component は `callback` メソッドを定義することで，インタラクションを受け取った時（ボタンなら押された時）の処理を記述できます．

# 一旦 ping コマンド

今回はテキストのコマンドでやります．スラッシュコマンドでも同様にできます．
ベースのコードは [discord.py の README](https://github.com/Rapptz/discord.py) から拝借しました．

```py
import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='>', intents=intents)

@bot.command()
async def ping(ctx):
    await ctx.send('pong')

bot.run('token')
```

# 誰でも消せる削除ボタン

`send` （スラッシュコマンドなら `send_message` など）の引数に View というものを与えることができます．
この View に Button などを追加していきます．

```py
import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='>', intents=intents)

# discord.ui.Button を継承し，callback 関数をオーバーライド
class DeleteButton(discord.ui.Button):
    async def callback(self, interaction: discord.Interaction):
        await interaction.message.delete()

@bot.command()
async def ping(ctx: commangds.Context):
    view = discord.ui.View()
    delete_button = DeleteButton(label='Delete', style=discord.ButtonStyle.red)
    view.add_item(delete_button)  # 作ったボタンを view に追加
    await ctx.send('pong', view=view)  # view と一緒にメッセージを送信

bot.run('token')
```

これではじめに貼ったようなボタンができて，押すとメッセージが消えると思います．

# コマンドを呼んだ人しか消せないようにする

先程のコードでは誰でもボタンを押せば消せたと思いますが，コマンドを呼んだ人しか消せないようにしたい場合も多いと思います．そこで `DeleteButton` を以下のように変更します．

```py
class DeleteButton(discord.ui.Button):
    # コンストラクタの引数に user を追加
    def __init__(self, user: discord.User, style=discord.ButtonStyle.red, label='Delete', **kwargs):
        self.user_id = user.id  # クラス変数にユーザ ID を保存
        super().__init__(style=style, label=label, **kwargs)

    async def callback(self, interaction: discord.Interaction):
        # 保存したユーザとボタンを押したユーザが同じかどうか
        if self.user_id == interaction.user.id:
            await interaction.message.delete()
```

さらに `DeleteButton` のインスタンスを作る時，
```py
DeleteButton(ctx.author)
```
のようにユーザを渡します．完成形のコードがこちら．

```py
import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='>', intents=intents)

# discord.ui.Button を継承
class DeleteButton(discord.ui.Button):
    # コンストラクタの引数に user を追加
    def __init__(self, user: discord.User, style=discord.ButtonStyle.red, label='Delete', **kwargs):
        self.user_id = user.id  # クラス変数にユーザ ID を保存
        super().__init__(style=style, label=label, **kwargs)

    async def callback(self, interaction: discord.Interaction):
        # 保存したユーザとボタンを押したユーザが同じかどうか
        if self.user_id == interaction.user.id:
            await interaction.message.delete()

@bot.command()
async def ping(ctx: commands.Context):
    view = discord.ui.View()
    delete_button = DeleteButton(ctx.author)  # コマンドを呼んだユーザを渡す
    view.add_item(delete_button) # 作ったボタンを view に追加
    await ctx.send('pong', view=view) # view と一緒にメッセージを送信


bot.run('token')
```

これでコマンドを呼んだ人しか消せないボタンが作れました．

# 参考文献

- [Rapptz/discord.py: An API wrapper for Discord written in Python.](https://github.com/Rapptz/discord.py)
- [Buttons | Pycord Guide](https://guide.pycord.dev/interactions/ui-components/buttons)

# この記事のライセンス
[![クリエイティブ・コモンズ・ライセンス](https://i.creativecommons.org/l/by/4.0/88x31.png)](http://creativecommons.org/licenses/by/4.0/deed.ja)
この記事は[CC BY 4.0（クリエイティブ・コモンズ 表示 4.0 国際 ライセンス）](http://creativecommons.org/licenses/by/4.0/deed.ja)の元で公開します．