見出し画像

discord botとminecraftサーバーを連携させていろいろやった話

マイクラサーバーのステータスと/startをdiscordbotに連携させる話


必要なもの
python
Minecraft Server
あとChatGPT

マイクラサーバーのステータスをdiscordbotのステータスに表示

こんなかんじ

code(ChatGPT産)

import discord
from discord.ext import commands, tasks 
from discord import Activity, ActivityType
from mcstatus import JavaServer

# Discord Botの設定
TOKEN = "とーくん" #DiscordBotのトークン

# server設定
server_ip = "サーバーip" #ステータスを表示させたいサーバーのIPを入れてね、自分のサーバーならlocalhostと入力すればいける。
server_port = 25565

intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)

@tasks.loop(seconds=30)  # 10分おきに実行
async def update_mcstatus():
    channel = bot.get_channel(1306972521446506496)
    try:
        # Minecraftサーバーの状態を取得
        server = JavaServer.lookup(f"{server_ip}:{server_port}")
        status = server.status()

        # サーバー情報を取得
        online_players = status.players.online
        version = status.version.name
        latency = status.latency

#サーバー情報をステータスに表示

        await bot.change_presence(activity=Activity(type=ActivityType.custom, name='LINDA Server', state="サーバー稼働中")) 

        # チャンネルにステータスを送信

#        await channel.send(
#            f"**Minecraft Server Status**\n"
#            f"IP: {server_ip}\n"
#            f"Version: {version}\n"
#            f"Players Online: {online_players}\n"
#            f"Latency: {latency}ms"
#        )
    except Exception as e:
        # エラーが発生した場合
#        await channel.send(f"Error: Unable to reach the server. Details: {e}")

        await bot.change_presence(activity=Activity(type=ActivityType.custom, name='LINDA Server', state="サーバー停止中"))

# Botが準備完了した際にタスクをスタート
@bot.event
async def on_ready():
    if not update_mcstatus.is_running():
        update_mcstatus.start()
    print(f'Logged in as {bot.user}')

    # ボットの名前を変更
#    await bot.user.edit(username="ServerStatusBot")  # 新しい名前に変更
#    print(f'Changed bot name to {bot.user.name}')

# Botの実行
bot.run(TOKEN)

これでサーバーのステータスをdiscordbotのステータスに表示されます。
このぐらいでいいんだよ、シンプルさがいいんだよ。

サーバーが稼働してると「サーバー稼働中」と表示される



次はマイクラサーバーを/startさせるやつ

code(https://qiita.com/insane_cattさん産をChatGPTでいじったやつ)

import discord
from discord import app_commands, Activity, ActivityType
import asyncio
import psutil

# 環境設定
TOKEN = "とーくん"  # Discord botのトークン(必ずご自身のトークンに差し替えてください)
SERVER_PATH = r"ふぉるだ"  # Minecraft実行ファイルのあるフォルダ
JAR_FILE = ".jarふぁいる"  # 使用するMinecraftサーバー実行ファイル名
MAX_RAM = 6  # 最大メモリ(整数に変更)
MIN_RAM = 2  # 最小メモリ(整数に変更)
JAVA_PROCESS_NAME = "java.exe"  # Javaプロセス名
SERVER_NAME = "Minecraftをプレイ中" #ここに入力した名前がBotのステータスになります。

intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)

# hello コマンド
@tree.command(name="hello", description="Hello, world!")
async def hello(interaction: discord.Interaction):
    await interaction.response.send_message(f'Hello, {interaction.user.mention}!')

# サーバー起動コマンド
@tree.command(name="start", description="サーバーを起動する")
async def start(interaction: discord.Interaction):
    if is_server_running():
        await interaction.response.send_message('サーバーは既に起動しています')
    else:
        # 非同期でサーバーを起動する
        await interaction.response.send_message('サーバーの起動を開始しました。しばらくお待ちください...')

        try:
            # 非同期でサーバーを起動する
            result = await start_server()  # start_server関数を非同期で呼び出し
            await interaction.followup.send(f'サーバーが正常に起動しました。{result}')
        except Exception as e:
            await interaction.followup.send(f"サーバー起動中にエラーが発生しました: {str(e)}")
            print(f"Error during server start: {str(e)}")
            traceback.print_exc()
# サーバーを非同期で起動する
async def start_server():
    try:
        # 非同期でMinecraftサーバーを起動
        process = await asyncio.create_subprocess_exec(
            r"C:\Program Files\Java\jdk-17.0.5\bin\java.exe", 
            "-Xmx" + str(MAX_RAM) + "G",  # 数値を文字列に変換
            "-Xms" + str(MIN_RAM) + "G",  # 数値を文字列に変換
            "-jar", JAR_FILE, 
            "nogui", 
            cwd=SERVER_PATH,
            stdout=asyncio.subprocess.PIPE, 
            stderr=asyncio.subprocess.PIPE
        )

        # 非同期で出力を待機
        stdout, stderr = await process.communicate()

        # エラーがあれば出力
        if process.returncode != 0:
            print(f"サーバー起動エラー: {stderr.decode()}")
            return f"サーバーの起動に失敗しました: {stderr.decode()}"
        print(f"サーバーのプロセス {process.pid} を起動しました。")
        return "Minecraftサーバーの起動を開始しました。"
    except asyncio.TimeoutError:
        return "サーバー起動に時間がかかりすぎています。タイムアウトしました。"
    except Exception as e:
        return f"サーバーの起動に失敗しました: {e}"

# サーバー停止コマンド
@tree.command(name="stop", description="サーバーを停止する")
async def stop(interaction: discord.Interaction):
    if not is_server_running():
        await interaction.response.send_message('サーバーは起動していません。')
    else:
        stop_server()
        await interaction.response.send_message('サーバーを停止しました。')

# botログアウトコマンド
@tree.command(name="logout", description="このbotをログアウトさせる")
@app_commands.default_permissions(administrator=True)
async def exitbot(interaction: discord.Interaction):
    await interaction.response.send_message('ログアウトを実行します')
    await client.close()  # ボットを正常にログアウト

# サーバーが実行中かどうかを確認
def is_server_running():
    try:
        # psutilを使ってプロセスを調べる
        for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
            if JAVA_PROCESS_NAME in proc.info['name'] and JAR_FILE in ' '.join(proc.info['cmdline']):
                return True
        return False
    except Exception as e:
        print(f"Error checking if server is running: {e}")
        return False

# サーバーを停止する
def stop_server():
    try:
        # サーバーのプロセスIDを検索して停止
        for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
            if JAVA_PROCESS_NAME in proc.info['name'] and JAR_FILE in ' '.join(proc.info['cmdline']):
                proc.terminate()  # プロセスを停止
                print(f"サーバーのプロセス {proc.info['pid']} を停止しました。")
                break
    except Exception as e:
        print(f"Error stopping server: {e}")

# botがオンラインになったときの処理
@client.event
async def on_ready():
    await client.change_presence(activity=Activity(type=ActivityType.custom, name='LINDA Server', state=SERVER_NAME))
    await tree.sync()
    print(f'Logged in as {client.user}')
    # ボットの名前を変更
#    await client.user.edit(username="ServerStartBot")  # 新しい名前に変更
#    print(f'Changed bot name to {client.user.name}')


# botを実行
client.run(TOKEN)

えーまず、まともでない猫さんありがとうございます。GPTで汚してすみません。

/startをすれば任意のサーバーを開ける

で、何をいじったのかというとWindowsだけで動くようになってます。
Linuxのscreenって機能?持ってないが?なんとかならんの?GPTさん???
っておねがいしたらこうなりました。

よくわかんね
でも稼働はします。
/stop?


それもしらないな。なにそれ。


これらのcodeを私は二つのBotに割り当てて動かしてます。
このコードは自分用に変更することで初めて動くので、変更する場所を言っていきます。

ステータスBOT
server_ip = "ここ"
TOKEN = "ここ"

あとお好みでBOTの名前を変更したい場合(ニックネームだけど)
await bot.user.edit(username="ここ")


スタートBOT
TOKEN = "ここ"
SERVER_PATH = r"ここ"
JAR_FILE = "ここ"

BOTのステータスをいじりたい場合は
SERVER_NAME = "ここ"

BOTの名前をいじりたい場合は
await client.user.edit(username="ここ")


これで機能するはず、まあ他にも不具合はあるんで、暇なときに直す。


いいなと思ったら応援しよう!