【Discord bot】DiscordからUbuntu上のARKサーバを起動・停止できるようにした

## 概要
```
Server: Ubuntu20.04.4 LTS@自宅PCサーバ
Python: Python3.8.10
Poetry: Poetry1.1.13

Discordでコマンド入力 -> Discord botが反応 -> シェルスクリプトを実行
```

## 目次
1. 導入のきっかけ
2. Discord bot用のコード
3. ARKサーバの導入
4. Discord botの導入

## 導入のきっかけ
ARK: Survival Evolvedのマルチプレイ用サーバを立てたのですが, ARKではプレイヤーが操作していない間でもゲーム内時間が進んでしまう仕様のため, 適宜サーバを停止してやる必要があります。また, サーバー管理者(ここでは私)が常にコンソールにアクセスできるわけではないので, 管理者以外のプレイヤーも任意にサーバの起動・停止を行える必要があります。そこで, Discord botを導入してサーバ上に常駐させることで自動化することにしました。

## Discord bot用のコード
今回はPythonを使ってDiscord botを動かしています。お作法としてあまり良いコードではありませんがご容赦ください。
```bot.py
import discord
import os
import subprocess

flag_ark = 0
flag_minecraft = 0

client = discord.Client()
TOKEN = 'ディスコードbotのトークン'

@client.event
async def on_ready():
    print(f'We have logged in as {client.user}')

@client.event
async def on_message(message):
    global flag_ark
    global flag_minecraft

    if message.author == client.user:
        return

    if message.content == '$start ark server':
        if flag_ark == 1:
                await message.channel.send('既に起動中')
        elif flag_ark == 0:
                flag_ark = 1
                subprocess.run(['./startARK.sh'])
                await message.channel.send('起動します')

    if message.content == '$start minecraft server':
        if flag_minecraft == 1:
                await message.channel.send('既に起動中')
        elif flag_minecraft == 0:
                flag_minecraft = 1
                subprocess.run(['./startMinecraft.sh'])
                await message.channel.send('起動します')

    if message.content == '$stop ark server':
        if flag_ark == 0:
                await message.channel.send('既に停止中')
        elif flag_ark == 1:
                flag_ark = 2
                await message.channel.send('このコマンドではセーブデータの保存を行いません。セーブデータは15分おきに自動セーブしているので, 現在の進行度を確実に保存するには15分待機してください。サーバを停止する場合はもう一度コマンドを入力してください')
        elif flag_ark == 2:
                flag_ark = 0
                subprocess.run(['./stopARK.sh'])
                await message.channel.send('ARKサーバを停止します')

    if message.content == '$stop minecraft server':
        if flag_minecraft == 0:
                await message.channel.send('既に停止中')
        elif flag_minecraft == 1:
                flag_minecraft = 0
                subprocess.run(['./stopMinecraft.sh'])
                await message.channel.send('マインクラフトサーバを停止します')

    if message.content == '$status ark server':
        stream = os.popen('systemctl status ark-dedicated.service')
        output = stream.read()
        await message.channel.send(output)

    if message.content == '$status minecraft server':
        stream = os.popen('systemctl status minecraft.service')
        output = stream.read()
        await message.channel.send(output)

    if message.content == '$status server':
        stream = os.popen('free -m')
        output = stream.read()
        await message.channel.send(output)

client.run(TOKEN)

```

タイトルで"ARKサーバを"と書いておきながらどうせだからとMinecraftサーバも同じbotで管理しているので長くなっています。また, 呼び出しているシェルスクリプトは次のようになっています。
```startARK.sh
#!/bin/sh

echo パスワード | sudo -S systemctl start ark-dedicated.service
```
```stopARK.sh
#!/bin/sh

echo パスワード | sudo -S systemctl stop ark-dedicated.service
```
echoの後に実行ユーザのパスワードを入力してsudo後の入力を自動化しています。このため, 実行ユーザはsudoできる権限が必要です。
また, シェルスクリプトにも忘れずに実行権限を与えておきましょう。
```
$chmod 755 startARK.sh
$chmod 755 stopARK.sh
```

## ARKサーバの導入
ARKサーバの導入方法については長くなるので他の方が書かれた記事を参考にしてください。一応私が主に参考にしたものを記載しておきます。

https://teityura.com/engineer/ark_server

https://ark.fandom.com/ja/wiki/Dedicated_Server_Setup#.E5.86.8D.E8.B5.B7.E5.8B.95.E6.99.82.E3.81.AB.E3.82.B5.E3.83.BC.E3.83.90.E3.83.BC.E3.81.8C.E3.82.AD.E3.83.A3.E3.83.A9.E3.82.AF.E3.82.BF.E3.83.BC.E3.81.BE.E3.81.9F.E3.81.AF.E3.83.AF.E3.83.BC.E3.83.AB.E3.83.89.E3.83.87.E3.83.BC.E3.82.BF.E3.82.92.E5.A4.B1.E3.81.86

私が詰まったところを記載しておくと, 
1. SteamCMDをインストール
2. SteamCMDを使ってARKサーバをインストール
3. デーモン化

という流れのうち, まず最初のSteamCMDのインストールで詰まりました。
```
E: Unable to locate package steamcmd
```
となってしまい, [公式Wiki](https://developer.valvesoftware.com/wiki/SteamCMD#Linux)によると, 
>Note: 	If you are using a 64-bit machine, you will need to add multiverse:
sudo add-apt-repository multiverse
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install lib32gcc-s1 steamcmd 

とのことなので, 言われたとおりにしたところ解決しました。
また, 最後のデーモン化でも問題が発生し, いざ起動しようとすると
```
Error! App '376030' state is 0x206 after update job.
```
というエラーが出ました。よく見てみると実行ユーザを間違えていました。シェルの実行ユーザを書き直して無事解決しました。

## Discord botの導入
先ほどと同様に導入方法は長いので他の方の記事を参考にしてください。

https://qiita.com/1ntegrale9/items/9d570ef8175cf178468f

https://dev.classmethod.jp/articles/discord-bot-by-discordpy/

Discord botは非常に使いやすく整備されており特別詰まることはありませんでしたが, botを起動したままにするには間違ってセッションを切らないようにする必要があります。tmuxを使ってセッションを維持するとよいと思います。