LoginSignup
26

More than 1 year has passed since last update.

discord.pyでスラッシュコマンドを実装する。

Last updated at Posted at 2022-08-07

こんにちは。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との接続ができてから実行しましょう。

2022/12/16追記。

Discordのコマンド同期の申請制限に引っかかる恐れがあるので、テストなどの際はサーバー限定コマンドとして登録するようにして、on_ready内部に入れるなどむやみにsyncをしないようにしましょう。

インタラクション

インタラクションは、ユーザーに返信をする必要のあるアクションが行われた際に発生します。現在の例はスラッシュコマンドとコンポーネントです。

(Discord.py インタラクションAPIリファレンスより)

インタラクションはスラッシュコマンドの最初の引数として渡されます。
Botはこれに対して返答などをすることになります。

この節で書くctxdiscord.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_commandsdescribeデコレーターを使います。

@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_commandsrenameデコレーターを使います。

@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_commandschoicesデコレーターを使います。

@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_commandsguildsデコレーターを使います。
引数にはサーバーIDを渡します。

コマンドをサーバーでのみ使えるようにする

ここで解説するのはDMなどでコマンドが実行できないようにする方法です。
ギルドコマンドとして登録したい場合は前の項で解説しています。

discord.app_commandsguild_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() を使用してみてください。

discord.py インタラクションAPIリファレンスより

@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の形式で指定します。権限はここのパラメータから指定します。

いかがだったでしょうか?
正式リリースは近いのでよかったらみんなもやってみてください!

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
26