3
4

More than 1 year has passed since last update.

【discord bot】マイクラサーバーの起動・停止をdiscordからできるようにしてみた

Posted at

はじめに

マイクラのサーバーをdiscordから誰でも起動できるようにしたくてdiscord botからEC2・マイクラサーバーの起動・停止を行えるようにしてみました。
EC2は従量課金制なので、起動していればその分だけお金がかかってしまいます。誰も使っていないのに起動しているのは無駄なので、だれでもdiscordから自由に起動と停止ができるようにしてみました。

この記事の対象者

  • Pythonの基本的な構文が理解できている方
  • EC2の制御をdiscord botから行ってみたい方

構成図

Minecraft Bot.png

  • t2.micro:discord bot用
  • t2.medium:minecraft server用

discord bot用サーバーは常時動いており、minecraft用サーバーを必要なときに起動するような構成となっています。

使用ライブラリなど

  • Python 3.7.10
  • paramiko 2.8.1
  • discord.py 1.7.3

構築手順

1.discord botのトークン発行
2.discord bot用サーバーの構築
3.minecraft用サーバーの構築
4.discord bot用サーバー→minecraft用サーバーのノンパス設定
5.discord bot起動
の流れになっています。

discordのbotトークン発行

こちらを参考にdiscordのbot用トークンを発行します。
発行したトークンは後で使うので控えておきます。

discord bot用サーバーの構築

discord bot用サーバーの構築を行います。

サーバーをたてる

EC2でdiscord bot用サーバーを作成します。
以下構成内容
インスタンス:t2.micro
OS:Amazon Linux 2
※そのほかはデフォルト設定

  • 必要なライブラリのインストール (AmazonLinux2ではPython3があらかじめインストールされているため、インストール不要)
python3 -m pip install -U discord.py
pip3 install paramiko 

discord bot用のコードを書く

処理内容としては
1.minecraft用サーバーを起動する
2.minecraft用サーバーにSSH接続する
3.minecraftサーバーを起動する
となっています。

discordbot.py
import discord
import subprocess
import paramiko
import time

# 自分のBotのアクセストークンに置き換えてください
TOKEN = '発行したトークンをここに貼り付けてください'
# game-serverのインスタンスidを指定してください
INSTANCEID = '後で作成するminecraft用サーバーのインスタンスidをここに入力してください'
# regionを指定
REGION = "minecraft用サーバーを作ったリージョンを書いてください"

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

# ***************************
# ***    処理関数
# ***************************


class DiscordBOT:
    def __init__(self, client):
        self.SSHClient = None

    async def main(self, discord_event):
        get_text = discord_event.content
        send_text = ""

        if "$start minecraft" in get_text:
            await discord_event.channel.send("サーバーを起動します。(起動には1分ほど時間がかかります。)")
            # インスタンスの起動
            subprocess.call(
                "aws ec2 start-instances --instance-ids {} --region {}".format(INSTANCEID, REGION), shell=True)
            time.sleep(1)

            # インスタンスが起動するまで待機
            subprocess.call(
                "aws ec2 wait instance-running --instance-ids {}  --region {}".format(INSTANCEID, REGION), shell=True)
            time.sleep(1)

            # インスタンスのIPアドレスを取得
            proc = subprocess.run(["aws ec2 describe-instances --instance-ids {} --region {} --query 'Reservations[*].Instances[*].PublicIpAddress' --output text".format(
                INSTANCEID, REGION)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
            ip_add = proc.stdout.decode("utf-8")
            ip_add = ip_add.replace("\n", "")
            self.server_ip = ip_add
            print("server_ip=", self.server_ip)

            time.sleep(15)

            # SSH接続クライアント作成
            self.SSHClient = paramiko.SSHClient()
            self.SSHClient.set_missing_host_key_policy(
                paramiko.WarningPolicy())
            self.SSHClient.connect(
                self.server_ip, username="ec2-user", timeout=1.0)
            time.sleep(2)

            # SSHでminecraftサーバー起動
            stdin, stdout, stderr = self.SSHClient.exec_command(
                "cd /home/ec2-user/minecraft ; source startminecraft.sh")
            for x in stdout:
                print(x)
            for x in stderr:
                print("error:", x)
            time.sleep(10)

            # 接続用のIPアドレスをdiscordに送信
            send_text = "インスタンスの起動とminecraftサーバーへの接続に成功しました。\n サーバー起動後は {} で接続できます".format(
                ip_add.replace("-", "."))

        elif "$stop minecraft" in get_text:
            await discord_event.channel.send("サーバーを停止します。(この処理は1分ほど時間がかかります。)")
            # サーバーの停止
            self.SSHClient.connect(
                self.server_ip, username="ec2-user", timeout=1.0)
            time.sleep(2)

            stdin, stdout, stderr = self.SSHClient.exec_command(
                "cd /home/ec2-user/minecraft ; source stopminecraft.sh")
            self.SSHClient.close()
            time.sleep(60)

            # インスタンスの停止
            subprocess.call(
                "aws ec2 stop-instances --instance-ids {} --region {}".format(INSTANCEID, REGION), shell=True)
            send_text = "サーバーの停止が完了しました。"

        if send_text:
            await discord_event.channel.send(send_text)


discordbot = DiscordBOT(client)


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

# on get message


@client.event
async def on_message(message):
    if message.author.bot:
        return

    await discordbot.main(message)

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

minecraft用サーバーの構築

minecraftサーバーを建てた後に、起動と停止を制御するシェルスクリプトを書きます。

minecraftサーバーを建てる

基本的な構築方法はこちら

シェルスクリプトを作成する

minecraftサーバーの起動、停止用のスクリプトを作成します。~/minecraft/に配置しています。

  • 開始時の処理ではssh接続が切れても処理を継続させたい&追加でコマンドを入力したかったので、screenコマンドで
startminecraft.sh
#! /bin/bash

# screenの名前
readonly SCREEN_NAME='minecraft'

screen -AmdS $SCREEN_NAME java -Xmx1024M -Xms1024M -Dlog4j2.formatMsgNoLookups=true -jar server.jar nogui pause
  • 終了時は念のため以下のプロセスを踏んでいます。

1.マイクラ内にアナウンス
2.マイクラサーバーを停止
3.停止コマンドを実行したら30秒待機(マイクラサーバーが停止するまで待機)

stopminecraft.sh
#! /bin/bash

# screen name
readonly SCREEN_NAME='minecraft'

if screen -ls | grep -o ${SCREEN_NAME}; then

    # 停止開始
    echo [date '+%F %T'] 'server stop script start'

    # サーバー内にアナウンス
    screen -S $SCREEN_NAME -X stuff 'say 30秒後にサーバーを停止します\n'
    sleep 30s

    # saveコマンド発行
    screen -S $SCREEN_NAME -X stuff 'save-all\n'
    sleep 5s

    # stopコマンド発行
    screen -S $SCREEN_NAME -X stuff 'stop\n'

    # 停止実行待機
    sleep 30s

else
    echo [date '+%F %T'] 'server is not runnning'
fi

discord bot用サーバー→minecraft用サーバーのノンパス設定を行う

開始シェルや終了シェルを実行したいので、ノンパス設定を行います。
書くと記事が長くなってしまうので、こちらを参考にしてください。

IAMロールの設定

discord bot用サーバーからminecraft用サーバーの起動・停止を制御するために行います。
1.IAMロールを作成する
2.IAMロールには以下の権限を付ける

  • 全部のリソースに対して
    • DescribeInstances
    • DescribeInstanceStatus
  • ARN指定
    • StartInstances
    • StopInstances

3.IAMロールをdiscord bot用サーバーに付与する

discord botを起動

ssh接続が切れても処理が継続されるように、以下のコマンドで実行する

nohup python3 main.py &

動作確認

minecraftのサーバーが立ち上がることを確認できました。
スクリーンショット 2021-12-19 164018.png

まとめ

今回はdiscordからminecraftサーバーの起動・停止を制御できるようにしてみました。
今後はこちらのツールをterraformを用いて管理したり、discordのスラッシュコマンド機能を利用したものに移行することなどを行っていきたいと思っています。

参考文献

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4