こんにちは。beatbox4108です。
今日は、Discord.py 2.0.0で実装予定の (実装されました) スラッシュコマンドを実際に触ってみて説明したいと思います。
今の所まだ自分がコマンドグループについて理解していないのでそれについての説明はしません。
Discord.pyのインストール
リリースされたので、次のような通常のpipコマンドでインストールできます。
$ pip install discord
ただ、古いバージョンが入っていると更新されない場合があるので、その場合は次のようにしてください。
$ pip install discord==2.0.0
リリースされた内容については、discordでの告知の翻訳版があるので、それを参考にしてください。
古い内容
今の所まだPyPIにはリリースされていないのでgit経由でインストールします。
$ pip install git+https://github.com/Rapptz/discord.py.git
早速触ってみる
スラッシュコマンドの実装方法は数種類ありますが、今回はCommandTree
を使った方法で実装します。
import discord
import discord.app_commands
token = "" #Your TOKEN
client = discord.Client()
tree = discord.app_commands.CommandTree(client) #←ココ
@client.event
async def on_ready():
print("ready")
client.run(token)
これが基本の形です。
大事なのはこれです。
tree = discord.app_commands.CommandTree(client)
この部分でCommandTree
というインスタンスを作成しています。
これは、スラッシュコマンドの登録に使います。
コマンドを登録する
コマンドの登録にはCommandTree.command
デコレーターを使います。
import discord
import discord.app_commands
token = "" # Your TOKEN
client = discord.Client()
tree = discord.app_commands.CommandTree(client)
@tree.command(
name="hello",#コマンド名
description="Send Hello world."#コマンドの説明
)
async def hoge(ctx:discord.Interaction):
await ctx.response.send_message("Hello world")
@client.event
async def on_ready():
print("ready")
client.run(token)
ですが、これだけだとDiscord側でコマンドを確認しても登録されていません。
Discord側とコマンドの同期を行うには、CommandTree.sync
関数を実行します。
await tree.sync()
on_ready
の中などDiscordとの接続ができてから実行しましょう。
インタラクション
インタラクションは、ユーザーに返信をする必要のあるアクションが行われた際に発生します。現在の例はスラッシュコマンドとコンポーネントです。
(Discord.py インタラクションAPIリファレンスより)
インタラクションはスラッシュコマンドの最初の引数として渡されます。
Botはこれに対して返答などをすることになります。
この節で書くctx
はdiscord.Interaction
オブジェクトです。
response
を使った返答
インタラクションにはresponse
で返答できます。
responseでの返答は一度しかできません。
2度目からは、webhookで返答することになります。
メッセージで返答する
await ctx.response.send_message("メッセージ")
基本的にsend
と変わりません。
引数としてephemeral=True
を渡すと、コマンドを実行したユーザーにのみ返答が表示されます。
返答の遅延
重い処理や時間がかかる処理などですぐに返答できない場合は、次の関数を実行します。
await ctx.response.defer()
これも返答に数えられるので注意してください。
ほかにもいくつかの返答方法があるので リファレンス を読んでみてください。
followup
での返答
2回目以降の返答はWebhookを通じて行います。
ctx.followup
に格納されます。
メッセージの返答
await ctx.followup.send()
普通のsend
ですが、引数としてephemeral=True
を渡せます。
引数をとる
Discordのコマンドは引数をとれます。
基本的に関数に引数を追加するときは型アノテーションをつけたほうがいいです。
discord.User
など、自動補完されるようになったり、
自動で型変換してくれたりします。
説明をつける
discord.app_commands
のdescribe
デコレーターを使います。
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello." # 引数名=説明
)
async def hoge(ctx:discord.Interaction,text:str):
await ctx.response.send_message(f"Hello, {text}!")
上の例のように引数名=説明
の形式で指定します。
コマンドを定義しているデコレーターよりも前には記述しないでください。
引数を改名する
discord.app_commands
のrename
デコレーターを使います。
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello."
)
@discord.app_commands.rename(
text="name" # 引数名=名前
)
async def hoge(ctx:discord.Interaction,text:str):
await ctx.response.send_message(f"Hello, {text}!")
上の例のように引数名=名前
の形式で指定します。
describe
などで名前を指定するときは、元々の名前を使ってください。
変換候補を出す
いくつか方法があります。
デコレーター
discord.app_commands
のchoices
デコレーターを使います。
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello."
)
@discord.app_commands.rename(
text="name"
)
@discord.app_commands.choices(
text=[
discord.app_commands.Choice(name="Discord",value="Discord"),
discord.app_commands.Choice(name="World",value="World"),
discord.app_commands.Choice(name="Asia",value="Asia"),
discord.app_commands.Choice(name="Japan",value="Japan")
]
)
async def hoge(ctx:discord.Interaction,text:str):
await ctx.response.send_message(f"Hello, {text}!")
上のように、引数名=Choiceのリスト
形式で設定します。
enum.Enum
を使う方法
class Names(enum.Enum):
Discord = "Discord"
World = "World"
Asia = "Asia"
Japan = "Japan"
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello."
)
@discord.app_commands.rename(
text="name"
)
async def hoge(ctx:discord.Interaction,text:Names):
await ctx.response.send_message(f"Hello, {text}!")
引数の型アノテーションにEnum
を指定します。
typing.Literal
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello."
)
@discord.app_commands.rename(
text="name"
)
async def hoge(ctx:discord.Interaction,text:typing.Literal["Discord","World","Asia","Japan"]):
await ctx.response.send_message(f"Hello, {text}!")
Enum
と同じように、型アノテーションで指定します。
サーバー限定コマンド(ギルドコマンド)にする
ここで解説するのはコマンドをグローバルに登録しない方法です。
DMなどで使用できないようにしたい場合は次で解説します。
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello."
)
@discord.app_commands.rename(
text="name"
)
@discord.app_commands.guilds(
#サーバーID
)
async def hoge(ctx:discord.Interaction,text:str):
await ctx.response.send_message(f"Hello, {text}!")
discord.app_commands
のguilds
デコレーターを使います。
引数にはサーバーIDを渡します。
コマンドをサーバーでのみ使えるようにする
ここで解説するのはDMなどでコマンドが実行できないようにする方法です。
ギルドコマンドとして登録したい場合は前の項で解説しています。
discord.app_commands
のguild_only
デコレーターを使います。
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello."
)
@discord.app_commands.rename(
text="name"
)
@discord.app_commands.guild_only()
async def hoge(ctx:discord.Interaction,text:str):
await ctx.response.send_message(f"Hello, {text}!")
引数はありません。
デコレーターに括弧をつけていますが、次のように省略できます。
@discord.app_commands.guild_only
権限があるかを確認する
これは ヒント として機能し、メンバーはこれらのコマンドを実際に実行するのに権限を持つ必要は ありません 。メンバーが必要な権限を有することを確かめたい場合は、代わりに has_permissions() を使用してみてください。
@tree.command(
name="hello",
description="Send Hello world."
)
@discord.app_commands.describe(
text="Text to say hello."
)
@discord.app_commands.rename(
text="name"
)
@discord.app_commands.default_permissions(
administrator=True
)
async def hoge(ctx:discord.Interaction,text:str):
await ctx.response.send_message(f"Hello, {text}!")
引数は権限名=True/False
の形式で指定します。権限はここのパラメータから指定します。
いかがだったでしょうか?
正式リリースは近いのでよかったらみんなもやってみてください!