2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

マイクラ鯖をDiscordから起動する

Last updated at Posted at 2025-01-13

目的

Discordボットを使ってMinecraftサーバを操作できるようにする

準備

以下の導入が済んでいることを前提として話を進めていきます。

  • Discordボット
  • Minecraftサーバ
  • tmux
  • Java
  • Python3
    ※この記事では上記すべてのやり方を解説しません。

実行環境

OS

Ubuntu Server 24.04.1 LTS

Minecraftサーバ

CurseForge for Minecraft 1.16.5 - 36.2.34

Java

OpenJDK 11.0.25

(2025/01/13 時点)

結論

最新版のコードはGithubに公開しています
https://github.com/MotchiyTuti/oss-discordbot

以下のコードを実行することで操作ができます。

discordbot.py
import discord
import subprocess
import time

# Initialize intents
intents = discord.Intents.default()
intents.message_content = True

client = discord.Client(intents=intents)

def execute(shell_script):
    subprocess.run(shell_script, shell=True)

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

@client.event
async def on_message(message):
    # メッセージがBOTからのものでないことを確認
    if message.author.bot:
        return
    elif message.content.strip():
        try:
            await message.channel.send('wait...')
            command = message.content.split()
            await message.channel.send(f'now {command[0]}ing...')
            
            match command[0]:
                case 'open':
                    with open(f'server/{command[1]}/status.txt', encoding='utf-8') as s:
                        if s.read() == 'running':
                            await message.channel.send(f'{command[1]} is still running!')
                        else:
                            execute('tmux new -s '+command[1]+' -d')
                            execute(f'tmux send-keys -t {command[1]} "cd server/{command[1]}" ENTER')
                            execute(f'tmux send-keys -t {command[1]} "java -jar *.jar" ENTER')
                            with open(f'server/{command[1]}/status.txt', mode='w', encoding='utf-8') as s:
                                s.write('running')
                    await message.channel.send('finished!')

                case 'close':
                    with open(f'server/{command[1]}/status.txt', encoding='utf-8') as s:
                        if s.read() == 'waiting':
                            await message.channel.send(f'{command[1]} is still waiting!')
                        else:
                            if '-p' in command == True:
                                close_command = 'end'
                            else:
                                close_command = 'stop'
                            execute(f'tmux send-keys -t {command[1]} "'+close_command+'" ENTER')
                            execute(f'tmux send-keys -t {command[1]} "exit" ENTER')
                            with open(f'server/{command[1]}/status.txt', mode='w', encoding='utf-8') as s:
                                s.write('waiting')
                    await message.channel.send('finished!')

                case 'status':
                    with open(f'server/{command[1]}/status.txt', encoding='utf-8') as s:
                        await message.channel.send(f'{command[1]} is {s.read()}')
                    await message.channel.send('finished!')

        except Exception as e:
            # エラー発生時の処理
            await message.channel.send(f'error: {e}')

with open('token.txt', encoding='utf-8') as f:
    client.run(f.read())

使い方

1. ディレクトリ構成をそろえる

上記のdiscordbot.pyと同じディレクトリに token.txtserver ディレクトリを作成し、
1段階深いディレクトリにサーバのjarファイルがくるようにする。

ディレクトリ構成(一部省略)
┝ discordbot.py
┝ token.txt
└ server
    └ 250113_test
        ┝ config
        ┝ eula.txt
        ┝ ***.jar

2. token.txtにDiscordボットのトークンをペーストする

token.txt
s3dfD3ydf...

※余計なことは書かない!

3. discordbot.pyを実行

シェルから以下のコマンドを実行する。

python3 discordbot.py

解説

import discord

ディスコードボットを動かすために必要なモジュール

import subprocess

Pythonからシェルコマンドを実行するために必要

intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

ディスコードボットの設定

command = []

送られたメッセージリスト型にする先をつくる

def execute(shell_script):
    subprocess.run(shell_script, shell=True

シェルでのコマンドを使いやすくする

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

適切にディスコードへとログインできたかの確認

@client.event
async def on_message(message):  # メッセージへの対応
    if message.author.bot:  # 送信者がBOTのとき
        return

ボット同士で会話しないようにする

elif message.content.strip():  # メッセージが空白でないとき
    try:
        await message.channel.send('wait...')
    except Exception as e:  # エラーが発生したとき
        await message.channel.send(f'error: {e}')
else:
    return

何かしらの読み取れるメッセージが送られたとき、wait... と返信する

command = message.content.split()

メッセージを区切ってリスト型に変換

match command[0]:
    case 'open':    # 先頭がopenのとき
    case 'close':   # 先頭closeのとき

メッセージの先頭がopen のときとclose のときの分岐

await message.channel.send('now '+command[0]+'ing...')

ディスコードにnow opening...now closeing... と送信
closeingのスペルがおかしいのは無視

execute('tmux new -s '+command[1]+' -d')

tmuxで新しいセッションを作る
(subprocessをそのまま使うと鯖にstopコマンドが使えなくなってしまうため)

execute(f'tmux send-keys -t {command[1]} "cd server/{command[1]}" ENTER')

サーバーディレクトリに移動

execute(f'tmux send-keys -t {command[1]} "java -jar *.jar" ENTER')

マイクラサーバーを起動

execute('tmux send-keys -t '+command[1]+' "stop" ENTER')

マイクラサーバーを停止

execute('tmux send-keys -t '+command[1]+' "exit" ENTER')

tmuxのセッションを停止

with open('token.txt', encoding='utf-8') as f:
    client.run(f.read())   # Discordにログイン

カレントディレクトリのtoken.txtからトークンを読み取り、Discordにログイン

おわりに

最後まで読んでいただき、ありがとうございます。
何か不具合や要望がありましたら、気軽に連絡してください。

参考記事

プログラミング初心者のためのQiita記事投稿テンプレート

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?