🐍

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

2023/03/12に公開

方法は複数あると思いますので方法の一つです。
discord.pyでスラッシュコマンドを実現するためにはバージョンを2.x系にアップデートする必要があります。
1.x系からのアップデートには破壊的変更があり、まずはその対応が必要です。

ファイル構成

.
├── cogs/
│   ├── ping.py
│   ├── sample1.py
│   └── sample2.py
└── main.py

本体 (main.py)

自分のサーバーで使うような小規模のBotを想定しています。
ギルドコマンドとグローバルコマンドというのがありますが、
グローバルコマンドはBotを立ち上げてからスラッシュコマンドが反映されるまでに時間がかかったり、同期しすぎると怒られたり(?) するようです。

今回はGuild IDを指定してそのギルドだけで使えるギルドコマンドです。
ギルドコマンドはBotを立ち上げると即時反映されます。

import os

import discord
from discord.ext import commands

DISCROD_TOKEN = os.environ["DISCORD_TOKEN"]
GUILD_ID = os.environ["GUILD_ID"]

class MyBot(commands.Bot):
    def __init__(self):
        super().__init__(
            intents=discord.Intents.all(),
            help_command=None
        )

        # 読み込むcogsのリストを作る
        self.initial_extensions = [
            "cogs.ping",
	    "cogs.sample1.py",
	    "cogs.sample2.py"
        ]
    async def setup_hook(self):

        await self.load_extension(extension)

        # インタラクションをシンクする。ギルドコマンドなので即時反映。
        await bot.tree.sync(guild=discord.Object(id=GUILD_ID))

    # おまじない。
    async def close(self):
        await super().close()
        await self.session.close()

    async def on_ready(self):
        print("Connected!")
        await bot.change_presence(activity=discord.Game(name="~をプレイ中のコメント", type=1))
        return

bot = MyBot()
bot.run(TOKEN)

Cogs

サンプルとしてBotの応答速度を返すシンプルなcogsです。
from discord import app_commandsを使います。

ping.py

import discord
from discord import app_commands
from discord.ext import commands


class Ping(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot

    @app_commands.command(
        name="ping", #/pingコマンドになる
        description="Catch the ping of the bot!" #コマンドの説明文
    )
    async def ping(
        self,
        interaction: discord.Interaction
    ):
        # interactionは3秒以内にレスポンスしないといけないとエラーになるのでこの処理を入れる。
        await interaction.response.defer()

        latency: float = self.bot.latency
        latency_ms: int = round(latency * 1000)

        await interaction.followup.send(f'🏓Pong! ({latency_ms}ms)')

async def setup(bot: commands.Bot):
    await bot.add_cog(
        Ping(bot),
        guilds = [discord.Object(id=xxxxxxxxxxxxxx)]
    )

ポイントいろいろ

interactionsへのレスポンスは3秒以内に返す必要がある

処理に時間がかかる場合は注意です。3秒以内に返せない可能性がある場合は上のping.pyのような処理が必要です。

await interaction.response.defer()

処理

await interaction.followup.send("contents")

こうすることで「考え中」となって、3秒以上でもエラーになりません。
3秒以内に返せる通常の書き方はこうです。

処理

await interaction.response.send_message("contents")

interactionはNoneを返してくる

スラッシュコマンドへのBotレスポンス自体にリアクションをつけたりすることはできない。(おそらくです。)

コマンドに引数(オプション)を追加し、オプションの説明をつけたい場合

@app_commands.command(
    name="hello",
    description="Botの応答速度を返します。"
)
@app_commands.describe(user="ユーザーを指定します。※これが説明部分")
async def hello(
    self,
    interaction: discord.Interaction,
    user: discord.Member # こうすることでユーザー指定に限定できる
):

オプションのタイプ一覧

OptionType discord.pyでの型
String(文字列) str
Integer(整数) int
bool(True,False) bool
Channel discord.TextChanneldiscord.VoiceChannel
Member discord.Member
Role discord.Role
Attachment(添付ファイル) discord.Attachment

オプションを必須項目でなくする

@app_commands.command(
    name="hello",
    description="ユーザーにhelloを返します。"
)
@app_commands.describe(user="ユーザーを指定します。※これが説明部分")
async def hello(
    self,
    interaction: discord.Interaction,
    user: discord.Member = None
):

こうすることuserのデフォルト値をNoneとすることができる。

コマンドの権限を管理者のみにする

@app_commands.command(
    name="admin",
    description="admin専用コマンド"
)
@app_commands.default_permissions(administrator=True)
async def admin(
    self,
    interaction: discord.Interaction
):

サーバー管理者以外には表示されることもありません。
パーミッションの一覧はここ

オプションを選択肢型にしたい

@app_commands.command(
    name= "apexrank",
    description= "Apex Legendsのランクを取得します。"
)
@app_commands.describe(
    platform="プラットフォームを選択してください",
    user_id="ユーザーIDを入力してください"
)
@app_commands.choices(
    platform=[
        discord.app_commands.Choice(name="Origin & Steam(PC)",value="origin"),
        discord.app_commands.Choice(name="Xbox",value="xbl"),
        discord.app_commands.Choice(name="Play Station",value="psn")
    ]
)
async def apexrank(
    self,
    interaction: discord.Interaction,
    platform: str,
    user_id: str
):

Discussion