はじめに
この記事は、discord.py v2.0より利用できるようになったBot UIキットについての解説を行う。
Bot UIキットは、Botにボタンやセレクトメニュー、テキストインプットなどのv2.0で追加された部品を扱う際に利用する。
v2.0で追加されたBOT UIキットに関連するアップデートは非常に豊富であるため、当記事ではよく利用するパターンと要素を抜粋して紹介する。
クイックスタート
はじめにBOT UIキットの基本となるdiscord.ui.Viewの最小構成を紹介する。
import discord
from discord.ext import commands
bot = commands.Bot(command_prefix="?", intents=discord.Intents.all())
class SampleView(discord.ui.View): # UIキットを利用するためにdiscord.ui.Viewを継承する
def __init__(self, timeout=180): # Viewにはtimeoutがあり、初期値は180(s)である
super().__init__(timeout=timeout)
@bot.command()
async def test(ctx):
view = SampleView(timeout=None)
await ctx.send(view=view)
bot.run("YOUR BOT TOKEN")
上記のプログラムは?test
というコマンドを入力すると、そのチャンネルにViewを返すというものである。ここで言うViewとはdiscord.ui.Viewを継承し、作成したSampleViewクラスである。このプログラムでは、Viewに部品を設定していないため、見かけ上BOTからは何も返ってこない。これ以降、継承したクラスにボタンやセレクトメニューなどの部品を配置していくことになる。
補足として、上記プログラムのコメントでも明記しているようにViewにはタイムアウトが存在し、タイムアウトするとViewの要素(ボタンなど)のイベントを取得出来なくなってしまう。デフォルトでは180(s)だが、Noneにすることによってタイムアウトを無効化することができる。
ボタンの追加
これ以降のサンプルでは、簡略化のためdiscord.ui.Viewを継承したSampleViewクラスのみを提示する。前後のコード等は前章のクイックスタートを参照のこと。
class SampleView(discord.ui.View):
def __init__(self, timeout=180):
super().__init__(timeout=timeout)
@discord.ui.button(label="OK", style=discord.ButtonStyle.success)
async def ok(self, button: discord.ui.Button, interaction: discord.Interaction):
await interaction.response.send_message(f"{interaction.user.mention} OK!")
@discord.ui.button(label="NG", style=discord.ButtonStyle.gray)
async def ng(self, button: discord.ui.Button, interaction: discord.Interaction):
await interaction.response.send_message(f"{interaction.user.mention} NG")
上記のプログラムでは、「OK」「NG」の2種類のボタンを作成し、ボタンを押したユーザーに対してそれぞれメンション付きで「OK!」と「NG」を返信するものである。
@discord.ui.button
のlabelには、ボタンの名前を記述し、styleにはボタンの色などのスタイルを指定する。スタイルの種類に関しては公式ドキュメントを参照。
引数interactionには、押されたボタンに関する情報が格納されており、例のようにinteraction.userなどとすることでボタンを押したユーザーの情報を取得することができる。また、discordではinteractionが発行されてから3秒以内にinteractionのresponseを返すように推奨しており、これを満たさない場合は、ボタンの下部に「インタラクションに失敗しました」というメッセージが表示される。あくまでも推奨されているだけなので、返さないと処理が無効化されるということはない。今回は、responseとしてsend_message関数を利用して、ユーザーにメンションを返している。
「インタラクションに失敗しました」対策
前の章で述べたようにinteractionは3秒以内にresponseを発行しないと「インタラクションに失敗しました」というメッセージを表示してしまう問題に対する対策は空メッセージを送ることである。
await interaction.response.send_message("")
上記の例のように空メッセージを送ると、responseはされたものとして判定され「インタラクションに失敗しました」というメッセージを表示させることを防止する。これはボタンの入力により、メッセージを返したくない場合に非常に有効である。
セレクトメニューの追加
class SampleView(discord.ui.View):
def __init__(self, timeout=180):
super().__init__(timeout=timeout)
@discord.ui.select(
cls=discord.ui.Select,
placeholder="What is your favorite fruit?",
options=[
discord.SelectOption(label="Banana"),
discord.SelectOption(label="Apple"),
discord.SelectOption(label="Pineapple"),
discord.SelectOption(label="Grapefruit"),
discord.SelectOption(label="Orange"),
]
)
async def select(self, interaction: discord.Interaction, select: discord.ui.Select):
await interaction.response.send_message(f"{interaction.user.mention} {select.values[0]}")
上記のプログラムでは、5種類の選択肢を持つセレクトメニューを追加し、ボタンを押したユーザーに対してそれぞれメンション付きで選択した選択肢を返信する。
@discord.ui.select
のclsには、セレクトメニューの種類を表すクラスを指定する。今回、指定しているdiscord.ui.Selectは一番基本的なクラスで、ユーザーが任意に選択肢を設定することができる。選択肢はdiscord.ui.Selectの場合は、optionsにdiscord.SelectOptionのリストとしてそれぞれlabelを設定することで実現できる。選択肢のクラスは他にも、サーバー内のロールやチャンネルを選択できるもの等、複数種類存在する。詳細は公式ドキュメントを参照。
おわりに
当記事で紹介した要素以外にもv2.0で追加されたUI関連のアップデートでは、TextInputなどをModal(Modelではない)を利用して設置することができる。また、こちらのほうも機会があれば紹介しようと思う。最後に改めて、詳細な情報を得られたい方は公式ドキュメントを参照されたい。