PythonにおいてのDiscord Slash Commandの実装方法

Discordには、最近「スラッシュコマンド」と呼ばれる新しい機能が実装されました。
公式リファレンス見てても英語ばかり、翻訳してみてもいまいち理解できないですよね。

そこで、ライブラリを使ってかんたんに実装しつつ仕組みを理解していくことにします。

※なお、Webhook型に関してはここで扱いません

# スラッシュコマンドの標準的な仕様
![無題3_20210105114325.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196404/61b97532-63e6-d6f3-5d3b-d2f49989106e.png)

言葉で長々と説明するのも分かりづらいと思いましたので図を用意しました。

クライアントがスラッシュコマンドを使うとゲートウェイ経由でボットに「〇〇が□□というコマンドを使った、引数はA, B, ...」などという情報とともに通知が行きます。

それに従ってボットは応答処理をします。

しかしこの方法はWebSocket経由であるため、送られてきた生の状態のデータを扱うのは難易度が高いです。

# discord-interactionsを使う
最も簡単でおすすめできるのは有志のモジュールを使うことです。
これを使えば、コマンドの実装だけに集中できて他にとらわれることもないと思います。

```
python3 -m pip install discord-py-interactions
```

でパッケージをインストールします。

# その前に

今のボットの設定では、スラッシュコマンドが使えません。

[Discord Developer Portal](https://discord.com/developers/)でボットに新しく権限を与えてサーバーに入れ直す必要があります。
※…と言われていますが、ボットを追い出さなくても入れるときと同じように認証を進めれば権限が付与されるようです。

Developer Portalで必要な設定を行います。
設定したいボットを選んで下の図に従って行ってください。

![無題4_20210106130237.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196404/7e56b516-56e4-b7a0-be7d-9c9a649f54b5.png)

SCOPESの下に認証用のURLができます。

![無題7_20210106131134.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196404/534c15f1-882f-d1e5-49a4-a4b4109461d8.png)


これからはこのURLを経由して導入してもらうことになります。

# コードを書く

```py
import discord
from discord.ext import commands
from discord_slash import SlashCommand, SlashContext

bot = discord.Client(intents=discord.Intents.all())
# または:
# bot = commands.Bot(command_prefix='@', intents=discord.Intents.all())

slash_client = SlashCommand(bot)

@slash_client.slash(name="hello")
async def _slash_hello(ctx: SlashContext):
    await ctx.send(content="Hello!")

@bot.event
async def on_ready():
  print('bot ready.')

bot.run("discord_token")
```

スラッシュコマンドを使ったボットのコードはこんな感じになります。
※ボットの基本的な部分は解説しません

```py
slash_client = SlashCommand(bot)
```

これは、スラッシュコマンド専用のオブジェクトです。

```py
@slash_client.slash(name="hello")
async def _slash_hello(ctx: SlashContext):
    await ctx.send(content="Hello!")
```

このコードでスラッシュコマンドを作ります。
基本的に関数に渡されるコンテキストはCogの時と変わりません。

ただ、違うのは`send`メソッドの引数です。
`hidden=True`という引数を追加してあげると、コマンドを実行した人にだけメッセージが表示されるようになります。

これだけです。

最後にひと工夫します。
`SlashCommand`オブジェクトを作るとき

```py
slash_client = SlashCommand(bot, sync_commands=True)
```

と、`sync_commands=True`という引数を追加してあげると、モジュールが自動でスラッシュコマンドを登録してくれます。

# どのサーバーでも使えるコマンドとそうでないもの

プログラム書いて、いざ起動してみて、テストチャンネルで「/」と入力しても自分のボットは出てこないと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196404/92f06add-1abd-2326-61ad-b99fa9a92dc2.png)

これは仕様で、今登録したコマンドは、どのサーバーでも使える「Global Command」です。
登録してから使えるようになるまで**最大1時間**かかります。

一方、あるサーバー限定でしか使えない「Guild Command」は、**即時反映**となっています。

また、登録数にも上限があります。簡単に表にまとめました。

|種類|反映にかかる時間|最大個数|
|----|--------------|--------|
|Global Command|最大1時間|100|
|Guild Command|即時反映|100|

※2021/07/30 更新

いかがでしょうか。スラッシュコマンドには説明が最初からあって、丁寧に引数まで教えてくれます。
今後に期待しましょう。

# 注意点もある！応答メッセージの仕様
ボットが応答するメッセージは、Webhookに似た仕様となっています。

## everyoneメンションとhereメンションは権限問わず可能
**これはとても重要です。予めボット側でメンションの設定を行わないと予期せぬメンションをしてしまうかもしれません。**
`send`メソッドに、普通のチャンネルに送信する`Message.send()`と同様`allowed_mentions=None`という引数を渡すことで未然に防止できます。

## Markdownのリンク表記が使える
通常のメッセージでは`[リンク](URL)`という表記は使えませんが、スラッシュコマンドの応答メッセージでは使えます。

## 埋め込みを最大10個まで送信可能
複数の埋め込みを一度に送ることができます。

# おことわり
discord-interactionsは、Discord.py公式のライブラリではなく、有志によって開発されたものです。
現在開発途中のパブリックベータ版であり、破壊的な仕様変更によって突然動作しなくなったり、ボットの動作に影響を及ぼす場合があります。
以上の点を理解したうえで、モジュールを利用するようにお願いします。

# 参考

discord-py-slash-command - PyPI
https://pypi.org/project/discord-py-slash-command/

discord-interactions 公式リファレンス
https://discord-interactions.readthedocs.io/en/latest/

Discord Developer Portalドキュメンテーション Interactions: Slash Commands
https://discord.com/developers/docs/interactions/slash-commands