Discord Botであそぼ

# はじめに
この記事は東京高専プロコンゼミ Advent Calendar①1日目の記事です。
https://adventar.org/calendars/5509


# [Discord](https://discord.com/)とは？
> ゲーマー向けの無料ボイスチャットアプリ。SlackやTeamsと同じように1つのサーバーと複数のチャンネルを使ったコミュニケーションツールです。

[Discord](https://discord.com/)のいいところを適当に箇条書きで挙げておきます。

* フレンドが今やっているゲームがわかる(マルチプレイに誘いやすい)
* 役職(ロール)という概念があり、かなり細かく権限の設定ができる(ネッ友とリア友が混ざってるサーバーだとすごく便利です)
* ユーザーが多い
* **無料で**音声通話が25人まで、ビデオ通話が10人までできる
* **Bot開発者にやさしい**(Botについては次の章で詳しく説明します)

みんな[Discord](https://discord.com/)、使おう！！(宣伝)


# Discord Botとは？
Discordは色々なAPIを無料で提供しています。これを使ってbotを作ることで、音声通話で音楽を流したり、おみくじを作ったり、色々な機能を実現することができます。

APIの公式ドキュメントを1から見て自分でAPIを叩くのは面倒ですが、素晴らしいことに有志のユーザーがAPIのWrapperを作ってくれているので、botを作って動かすのはそう難しいことではありません。(プログラミングの基礎を知っていれば簡単に作れると思います)

現在メジャーなWrapperには、
　・JavaScriptで実装されている [discord.js](https://discord.js.org/)
　・Pythonで実装されている [discord.py](https://discordpy.readthedocs.io/ja/latest/index.html)
の2つがあります。
この記事では [discord.py](https://discordpy.readthedocs.io/ja/latest/index.html) を使って簡単なbotを実装し動かすところまでを説明していきたいと思います！


# 簡単なbotを作ってみよう！！
というわけで、ようやく本題に入ります。今回は、じゃんけんができるbotを1から作っていきます。


## 準備
まずは、環境構築を済ませましょう。
今回は入れると長くなってしまうのでpythonとpipの環境構築は省いて、API Tokenの取得とサーバーへのbotの導入，discord.pyのダウンロードを説明します。


### tokenの取得
Discordにログインした状態で https://discord.com/developers/applications にアクセスします。すると、このような画面が出てくるので右上の"New Application"ボタンを押します。
![Inked68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3534373433322f62646239363639642d623563612d613533302d616663392d3034643137353431343463622e706e67_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/0618ef58-14db-641c-b275-f65753ee6081.jpeg)
出てきたポップアップのNAME欄にbotの名前を入力します．今回は"jyankenbot"と入力しました．これは読者の方が自由に変更しても大丈夫ですのでお好きな名前をつけてあげてください！
名前を入力したら"Create"ボタンを押します．
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/cc43fdb1-53c5-cb69-dbd7-6ad6350ab3d3.png)
作成が終わると，この画面に遷移するので左のメニューにある"Bot"の部分をクリックしてください．
![Inked1111_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/b2e9b6d3-caea-17d7-0b77-c77a727ac250.jpeg)
右上にある"Add Bot"ボタンを押してbotを追加します．
![スクリーンショット (219)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/68717255-10d9-1f76-e207-3e1d97a0d5f9.jpeg)
このようなポップアップが出てきますが，"Yes, do it!"を押してください．
![スクリーンショット (220)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/ac2f81ac-4fa1-2782-75bc-5a65feb13a6f.jpeg)
作成に成功するとこのような表示がでてくるので，青い"Click to Reveal Token"をクリックしてください．Tokenが表示されます．
次の章ではプログラムにこのTokenを使うので，コピーするなりしてとっておいてください．(何度でも確認できるので個別に保存等はしなくて大丈夫です)
![スクリーンショット (221)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/ddc2ef85-40e2-f4ad-1784-edbafc9b4a7b.jpeg)


### サーバーへのbotの導入
まずは新しくテスト用のサーバーを作りましょう！
![スクリーンショット (230)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/31eb0da2-e06c-77e8-dfee-829ae9f04f61.jpeg)
![スクリーンショット (231)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/906b9381-f6a7-2309-7947-e42989cc6da8.jpeg)
サーバー名は適当で大丈夫ですし，アイコン画像もアップロードしなくてもサーバーを作ることができます．新規作成を押したらサーバーが作成されます！
![スクリーンショット (232).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/598c4154-94d0-9dbe-c738-8ee208c9c521.png)
次にbotの招待リンクを取得しましょう！
https://discord.com/developers/applications にアクセスして，先ほど作成したjyankenbotを選択したのち遷移されるページで，左のメニューの"OAuth2"の部分をクリックしてください．
クリックするとこのページに移るので，真ん中の"bot"のチェックボックスを選択してください．
![スクリーンショット (233)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/5631d238-b14b-3181-51dc-b3bb28089e6e.jpeg)
少し下にスクロールすると"BOT PERMISSIONS"という欄があるので，"Administrator"を選択して下さい．これを選ぶとbotがサーバーに対してすべての行為を行えてしまうので，本当は良くないのですが今回はお試しで作るだけなので見逃してください．(ちゃんとしたbotを作るときはちゃんと権限を設定するようにしてください！！！！)
選択できたら画面中央にあるURLをコピーして，アクセスしてください．
![スクリーンショット (234)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/b5bc62f6-36d0-fc16-b33c-926c9c17ff4b.jpeg)
アクセスするとbotをサーバーに追加する管理画面が表示されるので，先ほど作ったサーバーを選択して"はい"を押します．
![スクリーンショット (235).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/c7fc4d2a-63d5-e80e-1c6b-c43de9e2b823.png)
認証ボタンを押す前にしっかり"管理者"にチェックがついていることを確認してください．
![スクリーンショット (236)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/9135fad2-2244-ede0-9f18-4a752bf7f4f8.jpeg)
先ほど作ったサーバーのページに戻り，jyankenbotが追加されていれば成功です．お疲れさまでした．
![スクリーンショット (237)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/762ecbeb-c021-329c-31e8-5a2ac3e04966.jpeg)




### discord.pyのダウンロード

ターミナルで以下のコマンドを実行するだけです、簡単！

```shell
$ pip install discord.py
```

## プログラムを書く
環境構築が終わったら、さっそくコードを書いていきましょう！
まずはbot.pyファイルを作って，botのベースを書き込んでいきます。

```python:bot.py
from discord.ext import commands


TOKEN = "ここにさっき取得したトークンを入れる"

# botの初期化(command_prefixはコマンドの先頭につく文字を示している)
bot = commands.Bot(command_prefix="/")


# "/test"とメッセージが送られて来たらこの関数を実行する
@bot.command()
async def test(ctx):  # 関数名がそのままコマンドになる
    # メッセージが送られたチャンネルに"hello world"と送る
    await ctx.send("hello world!!")  # ctx.send()はctx.channel.send()のショートカット


if __name__ == "__main__":
    # botを起動
    bot.run(TOKEN)

```

これは，チャットに"/test"と送られると"hello world!!"と返すbotです．
また，このコードではdiscord.pyのコマンドフレームワークというものを使っており，botのコマンド関連のコードをスマートに書くことができています．
次のようなコードでも同じ機能を実装できますが、明らかに前述のコードの方がコードの量が短くきれいですし、コマンドが10個、20個と増えたときもスパゲッティコードにならずに済む実装です。

```python:bot.py
import discord


TOKEN = "ここにさっき取得したトークンを入れる"

bot = discord.Client()


# メッセージが送られて来たらこの関数を実行する
@bot.event
async def on_message(message):
    # 文字列が"/"から始まっているかどうかを判定
    if message.content.startswith("/"):
        # 文字列の1文字目を切り捨てる
        command = message.content[1:]
        if command == "test":
            # メッセージが送られたチャンネルに"hello world"と送る
            await message.channel.send("hello world!!")


if __name__ == "__main__":
    # botを起動
    bot.run(TOKEN)

```

では実際にこのbotを動かしてテストしてみましょう！terminalでこのコマンドを実行してください．

```shell
$ python bot.py
```

このように"/test"と入力して"hello world!!"と返ってきたら成功です！
![14.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/104e5768-2c9c-019a-64a2-9882195c8bba.gif)


これでbotの基本的な部分が完成しました！
次はじゃんけんコマンドを作ります！！


```python:bot.py
from discord.ext import commands
from random import randint


TOKEN = "ここにさっき取得したトークンを入れる"

# botの初期化(command_prefixはコマンドの先頭につく文字を示している)
bot = commands.Bot(command_prefix="/")


# "/test"とメッセージが送られて来たらこの関数を実行する
@bot.command()
async def test(ctx):  # 関数名がそのままコマンドになる
    # メッセージが送られたチャンネルに"hello world"と送る
    await ctx.send("hello world!!")  # ctx.send()はctx.channel.send()のショートカット


def hand_to_int(hand):
    """
    グー: 0, チョキ: 1, パー: 2 とする
    handはカタカナ，ひらがな表記，数字に対応する
    """
    hand_int = None
    if hand in ["グー", "ぐー", "0"]:
        hand_int = 0
    elif hand in ["チョキ", "ちょき", "1"]:
        hand_int = 1
    elif hand in ["パー", "ぱー", "2"]:
        hand_int = 2

    return hand_int


def get_player_result(player_hand, bot_hand):
    """
    勝利: 1, 敗北: 0, ひきわけ: 2 とする
    result_table[player_hand][bot_hand]で結果がわかるようにする
    """
    result_table = [
        [2, 1, 0],
        [0, 2, 1],
        [1, 0, 2]
    ]
    return result_table[player_hand][bot_hand]


@bot.command()
async def jyanken(ctx, hand):
    hand_emoji_list = [":fist:", ":v:", ":hand_splayed:"]

    player_hand = hand_to_int(hand)
    if player_hand is None:
        await ctx.send("不正な手です！もう一度やり直してください！！")
        return

    bot_hand = randint(0, 2)

    await ctx.send(
        f"あなた: {hand_emoji_list[player_hand]}\n"
        f"Bot: {hand_emoji_list[bot_hand]}"
    )

    result = get_player_result(player_hand, bot_hand)
    if result == 0:
        await ctx.send("残念，あなたの負けです！！")
    elif result == 1:
        await ctx.send("おめでとうございます！！，あなたの勝ちです:tada:")
    else:
        await ctx.send("あいこ！")


if __name__ == "__main__":
    # botを起動
    bot.run(TOKEN)
```

これでbotとじゃんけんができるようになりました！
これから上記のコードの解説をしていきます．

まずこの部分ですが，コマンドフレームワークの恩恵でコマンド関数に引数を追加するだけで簡単に
`/jyanken ぱー` のようなコマンドの後ろの値を取得することができます！

```python
@bot.command()
async def jyanken(ctx, hand):
```

上の部分でとった手を表すテキストをプログラムで扱いやすいように，この関数を使って変換します．
`hand`が手を表すテキストであれば対応する数字を返します．

```python
def hand_to_int(hand):
    """
    グー: 0, チョキ: 1, パー: 2 とする
    handはカタカナ，ひらがな表記，数字に対応する
    """
    hand_int = None
    if hand in ["グー", "ぐー", "0"]:
        hand_int = 0
    elif hand in ["チョキ", "ちょき", "1"]:
        hand_int = 1
    elif hand in ["パー", "ぱー", "2"]:
        hand_int = 2

    return hand_int
```

先ほどのように手を数字化したので，事前に勝ち負けを計算した表(2次元配列)を利用して，数字化されたじゃんけん結果を簡単に得ることができます．

```python
def get_player_result(player_hand, bot_hand):
    """
    勝利: 1, 敗北: 0, ひきわけ: 2 とする
    result_table[player_hand][bot_hand]で結果がわかるようにする
    """
    result_table = [
        [2, 1, 0],
        [0, 2, 1],
        [1, 0, 2]
    ]
    return result_table[player_hand][bot_hand]
```

最後に結果ごとにメッセージを送ります．

```python
result = get_player_result(player_hand, bot_hand)
    if result == 0:
        await ctx.send("残念，あなたの負けです！！")
    elif result == 1:
        await ctx.send("おめでとうございます！！，あなたの勝ちです:tada:")
    else:
        await ctx.send("あいこ！")
```

コードが書けたら実行して試してみましょう！！
先ほどと同じようにターミナルで以下のコマンドを実行するだけです！

```shell
$ python bot.py
```

このようにじゃんけんができれば成功です！！(僕は勝ててませんが，みなさんは勝てましたか？)
![15.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547432/a24afaf0-6938-6e4b-0b85-ceecfa24d2bf.gif)


# 最後に
こんな感じでDiscord Botは簡単に作ることができます！今回はじゃんけんbotを作りましたが，Discord Botは本当に色々なことができるので是非皆さんもオリジナルbotを作って遊んでみてください！！

本当はもっとコードについて詳しく解説したり，botをクラウドにデプロイするところまで書きたかったのですが，さすがに~~めんどくさい~~記事が長くなってしまうのでこのぐらいにしておきます．(デプロイに関してはカレンダーの担当日がもう一日あるので，もしかしたらそこで書くかも？)

最後になりますが，拙い文章にここまでお付き合いいただきありがとうございました！！