今からDiscordのBotをPythonで作るなら dislash.py でスラッシュコマンドに対応させる

**DiscordのBotをPythonでいじってみるか、と思い立ったわずか数時間前に主要なライブラリである `discord.py` の開発終了が発表されていた**ので泣いています。なんというタイミングなのでしょうか。

この件に関しての説明は、この記事が詳しいです。

https://note.com/grapecolor/n/nb16071ae81bf

とりあえず、即座に `discord.py` を使った開発ができなくなるというわけではないものの、これを使い続けることにはセキュリティ的なことを始めとして様々な懸念があることは、想像に難くありません。

## スラッシュコマンドへの対応が必須

その懸念の中でも、この記事で話題にするのが**スラッシュコマンド**です。

上記記事を読むと、

>Discordは2022年4月以降、メッセージの内容を読み取る権限を、基本的にはBOTから奪う方針を打ち出しており、以降はスラッシュコマンドを介したコマンド操作のみで対応する必要があります。

とある通り、今後Botを新しく作成するのであれば**スラッシュコマンドをユーザーから受け取る**ことで動作させることを前提にする必要があります。

### スラッシュコマンドとは？
Bot側で事前に定義しておけば、ユーザーがDiscordのメッセージ送信欄に入力することで受け取れるようになるコマンドです。
![2021-08-30_03h04_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/dba6ff41-b866-5a70-0dfe-7b2c4eb95f16.png)
上記画像のような、テキスト欄に `/` を打っただけでコマンド一覧や説明、設定できる引数が出てくるっていう便利機能があるため、これだけを利用せよっていうのは一理あると思います。

画像はDiscordビルトインのコマンドなので、どのサーバーでも出せますよ。

## 具体的に今後の開発はどうするか

今後、Botの作成方法の風潮がどうなるかは `discord.py` からフォークされるなどした後継プロジェクトの開発がどうなるかとかに関わってくるため、現時点ではBot開発者側がどう対応していくべきかは謎です。

ただ、前述の通り**スラッシュコマンドへの対応だけは2022年4月までにする必要がある**ことは確定しています。

よって、この記事ではとりあえず `discord.py` を使うところは目をつぶって、スラッシュコマンドへの対応を行うようにします（とりあえず今できることを使って必須なことを学んでおかないと、後で何も分からなくなるので）。あとは今後の状況を見るしかない。

# dislash.py なら簡単！

`dislash.py` は `discord.py` を前提としつつも、非常に簡単にスラッシュコマンドを実装することができる拡張ライブラリです。

新しいライブラリらしく、日本語で紹介してるところが現状ありませんでしたが、見つけてから一瞬で使えるくらいには単純明快で優秀だったので、とりあえずこれを使っておくことにしました。

https://github.com/EQUENOS/dislash.py

とりあえず、上のドキュメントを読むだけで導入から使用まで非常に簡単だと感じましたが、一応説明します。

## 前提
これらの前提に関しては、ググれば詳しい記述が簡単に見つかるため省略します。

- Python 3.6～3.9。
- discord.py をインストール済みであること。
- [Discordのデベロッパーツール](https://discord.com/developers/applications)でBotとして使用するアプリケーションを作っておく。

## Botをサーバーに招待
テスト用のDiscordサーバーを作って、そこにBotを招待する必要がありますが、その前に与える権限を正しく設定します。
![2021-08-30_04h22_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/e752627e-ad39-6e21-a448-fe1766093f11.png)

- リダイレクトURLを設定しないと招待リンクが生成できないため、適当に`http://localhost/`を設定しています。
- OAuth2 タブを開いて、SCOPES のうち **bot** と **applications.commands** の2つにチェックします。特に後者はスラッシュコマンドを扱うために必須というわけです。

![2021-08-30_04h22_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/376c3300-aff7-b080-6349-eb62eee29daf.png)

- さらに下にある、BOT PERMISSIONS のうち **Send Messages** にもチェックを入れることを忘れずに。当然ですが、Bot側からメッセージを返せなければ対話できませんからね。
- コマンドによってもっといろいろ操作したい（ロール変更とかいろいろ）って場合は追加で必要な権限を設定します。ただし**Administratorはマジで危険**なので面倒でも絶対に設定しないように。うっかりミスでサーバー内のあらゆるものを破壊する可能性があります。

![2021-08-30_04h23_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/61ae71b0-6a30-2ca0-06c8-963d5d35df6a.png)
ここまでできたら、先ほど設定した SCOPES の中にあるURLをブラウザで開き、テスト用に作ったサーバーに招待すれば準備ヨシ。ちゃんと「サーバーでスラッシュコマンドを作る」権限が表示されていますね。


## dislash.py のインストール

ここまででお膳立てはできましたので、`dislash.py`をインストールしてスラッシュコマンドを作っていきましょう。

私はとりあえずWindowsでやってるので以下のようになりますが、環境によっては異なります。

```
py -m pip install dislash.py
```

# とりあえず書いていく

``` bot.py
from discord.ext import commands
from dislash import slash_commands, Option, OptionType

# Botのトークンを入れる
TOKEN = 'dummy'

client = commands.Bot(command_prefix = '/')
slash = slash_commands.SlashClient(client)

# コマンドが使えるようにするサーバーのIDを列挙
test_guilds = [123456789012345678]


# 引数なしのコマンド例
@slash.command(
    name = 'hello',
    description = '挨拶するよ！',
    guild_ids = test_guilds # この引数を省略すれば、このBotを招待したサーバー全てで使えるコマンドになる
)
async def hello(inter):
    await inter.reply('やぁ！')


# 引数ありのコマンド例
@slash.command(
    name = 'echo',
    description = 'あなたが言ったことをそのまま返すよ！',
    options = [
        Option('text', '好きなことを書いてね', OptionType.STRING),
    ],
    guild_ids = test_guilds
)
async def echo(inter, text=None):
    if text is not None:
        await inter.reply(text)
    else:
        await inter.reply('なんかいえや！！')


@client.event
async def on_ready():
    print('Botでログインしました')


client.run(TOKEN)

```

## 気を付けるところ
### Botのトークン

``` python
TOKEN = 'dummy'
```

ここではBotにアクセスするためのトークンを設定します。
![2021-08-30_03h22_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/76283384-215a-b52a-2457-72b749d99e64.png)
ここの「TOKEN」の下の「Click to Reveal Token」をクリックして表示されたやつをコピペします。当然、他人に知られるとBotをいじられ放題なので注意してください（万が一漏れたら「Regenerate」を押して再生成する）。


### サーバー（Guild）IDの指定

``` python
test_guilds = [123456789012345678] 
```

コマンドが使えるようにするサーバーのID（通常18桁）をここで設定しておきます。当然、配列なので複数設定することも可能です。

![2021-08-30_04h45_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/10f852bd-2a37-6629-0af2-b5efda2afb65.png)
サーバーのIDは、Discordの **`ユーザー設定 > 詳細設定 > 開発者モード` をオンにした状態**で、サーバー名を右クリックすると出現する「IDをコピー」でクリップボードにコピーすることができます。

``` python
@slash.command(
    name = 'hello',
    description = '挨拶するよ！',
    guild_ids = test_guilds # この引数を省略すれば、このBotを招待したサーバー全てで使えるコマンドになる
)
```

それを各コマンドを作成する際に `guild_ids` という引数に渡すことで、設定したサーバーでのみ使用できるコマンドとなります。

なおコメントでも入れましたが、**`guild_ids` を省略することにより、Botを招待したサーバーであればどこでも使えるコマンドにする**（グローバル）こともできます。ただし、Discordの仕様上、

- サーバーを指定した場合は**即座に**コマンドが使用可能になります。
- 指定しない場合は、コマンドが使用できるようになるまで**1時間程度**かかります。

ということなので、テスト中は常にテスト用サーバーでのみ実行可能としておけばよいでしょう。


# テスト
上記 `bot.py` を実行します（コマンドは環境により異なる）。

```
>py bot.py
Botでログインしました
```

![2021-08-30_05h03_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/8492bd51-ffa9-9576-28a1-a31a5765ee95.png)
Botが、招待したサーバーでオンラインになったことを確認してください。

![2021-08-30_05h04_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/49c521da-06d9-1567-af50-a2fe698d8fbe.png)
入力欄に `/` を入れると、ちゃんと登録したコマンドが表示されています。

![2021-08-30_05h06_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213093/e2719d6a-9b7c-2a11-0166-4d91f8f32c56.png)
それぞれ、

- `/hello`
- `/echo やっほーーーーー！！！`
- `/echo`

の実行結果です。

引数もちゃんと受け取れていることが分かります。実際は何かしらのデータベースにアクセスしてユーザーの情報を返すなどすれば、さらに高度なことができるでしょう。

## もっと色んなことをする
https://dislashpy.readthedocs.io/en/latest/index.html
こちらに十分すぎるほどの例が掲載されています。


# 終わりに
現状では先行きが見えないため、この記事はこの程度にしておきますが、今後の`dislash.py`の対応次第ではこの方法がBotとの対話方法としてスタンダードになるかもしれません（例えば`discord.py`以外を前提にしてセキュリティ的な問題を払拭する等）。

そうなった場合はこの記事を更新し、さらにコマンド作成例をさらに増やすなどして、より内容を充実させます。