Discordのbotを作ってみる

#はじめに

これは、[SLP KBIT Advent Calendar 2020 ](https://adventar.org/calendars/5402)の16日目の記事です。  
今年も残すところあと半月となりましたね！
ということで、今回はDiscordのbotを作ってみました。
言語はPythonを使用し、DiscordのAPIラッパーであるdiscord.pyを利用しています。

##目次
> - [開発環境](#開発環境)
> - [botアカウントの作成と招待](#botアカウントの作成と招待)
> - [事前準備](#事前準備)
> - [botの作成](#botの作成)
>   - [とりあえず動かしてみる](#とりあえず動かしてみる)
>   - [Minecraftサーバーを操作する](https://qiita.com/Gomatamago_/items/e38f50d764cfc2dc20b6#minecraft%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%82%92%E6%93%8D%E4%BD%9C%E3%81%99%E3%82%8B)
> - [おわりに](#おわりに)
> - [参考](#参考)

#開発環境

- Windows10
- Python3.7.9

# botアカウントの作成と招待

まずはディスコ側でbotアカウントを設定し、サーバーに招待してあげます。
下の記事に簡単な導入方法が解説されています。
アクセストークンも控えておきましょう。
https://note.com/bami55/n/ncc3a68652697

# 事前準備

今回はPythonを使用するのでdiscord.pyというモジュールをインストールします。
cmdを開いて以下のコマンドを実行します。

```shell:discord.pyをインストール
py -3 -m pip install -U discord.py[voice]
```

ちなみにpython3.9ではインストールしようとしてもエラーを吐いて使えないみたいです。

# botの作成

## とりあえず動かしてみる

[こちら](https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f)の記事を参考にプログラムを動かしてみます。

```py:discordbot.py
# インストールした discord.py を読み込む
import discord

TOKEN = 'ここに自分のbotのアクセストークンを入力'

# 接続に必要なオブジェクトを生成
client = discord.Client()

# 起動時に動作する処理
@client.event
async def on_ready():
    # 起動したらターミナルにログイン通知が表示される
    print('ログインしました')

# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は無視する
    if message.author.bot:
        return
    # 「/neko」と発言したら「にゃーん」が返る処理
    if message.content == '/neko':
        await message.channel.send('にゃーん')

# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)
```

このプログラムを起動すると、
![bot返信例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925552/3c2bbceb-7574-69d6-5662-3cb04f08de37.png)
こんな感じでレスポンスを返してくれます。
[こちら](https://discordpy.readthedocs.io/en/stable/api.html#event-reference)でイベントリストとその詳細について解説されています。

## Minecraftサーバーを操作する

最近身内でMinecraftを遊んでいて、僕がサーバーを立てているのですが、PCから離れていると操作できないんですよね。
そんな不満を解消してくれたら便利だなあと思って作りました。
先程のプログラムを基に、Discordを通じてMinecraftマルチサーバーの起動及び終了ができるように色々付け足したプログラムです。

```py:MCserverbot.py
import subprocess
# インストールした discord.py を読み込む
import discord

# TOKENとサーバーの情報
TOKEN = 'ここに自分のbotのアクセストークンを入力'
f_name = 'サーバー実行ファイル名.jar'
maxMemory = '8G'  # 任意の最大メモリ割り当てサイズ
minMemory = '8G'  # 任意の最小メモリ割り当てサイズ

# 接続に必要なオブジェクトを生成
client = discord.Client()

# サーバー操作用
class server_process:

    def __init__(self, f_name, maxMemory,minMemory):
        self.server = None
        self.command = ["java", "-server",f'-Xms{minMemory}', f'-Xmx{maxMemory}', "-jar", f_name, "nogui"]

    def start(self):
        self.server = subprocess.Popen(self.command, stdin=subprocess.PIPE)

    def stop(self):
        input_string = "stop"
        self.server.communicate(input_string.encode())

server = server_process(f_name, maxMemory, minMemory)

# 起動時に動作する処理
@client.event
async def on_ready():
    # 起動したらターミナルにログイン通知が表示される
    print('ログインしました')

# メッセージ受信時に動作する処理
@client.event
async def on_message(message):
    # メッセージ送信者がBotだった場合は無視する
    if message.author.bot:
        return

    if message.content == '/mcstart':
        await message.channel.send('サーバーを起動します')
        server.start()

    elif message.content == '/mcstop':
        await message.channel.send('サーバーを終了します')
        server.stop()

# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)
```

これと以下の記述をしたバッチファイルを作成して、マイクラサーバーの.jarファイルと同じ場所に置きます。

```bat
python MCserverbot.py
```

あとはバッチファイルを開くことでbotが起動します。
Discordで`/mcstart`を送信することでサーバーを起動、`/mcstop`で停止させることができます。

Discord側で見るとこんな感じ。
![MCserverbot実行例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925552/c3c1d1b7-5739-7e4c-3b10-cefe5815e3f6.png)

サーバーもちゃんと起動しています。
<img width="1000" alt="server_shell" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/925552/dcbebc48-cd1d-8227-ee93-92972b9340c8.png">


# おわりに

この記事では、DiscordのbotでMinecraftサーバーの起動と終了をできるようにしました。
本当はもっと色々な機能 (Rich Presence機能を使ってステータスを表示させるみたいな) を持たせたかったのですが、途中で力尽きました、、、
botには他にも様々な動作をさせることができるみたいなので、時間があれば挑戦してみたいですね！

# 参考

- Pythonで実用Discord Bot(discordpy解説)  
https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f