1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Python】7DaysToDieをDiscordから管理したい! 2/3

Last updated at Posted at 2020-07-12

#概要
【Python】7DaysToDieをDiscordから管理したい! 1/3 (環境構築)
【Python】7DaysToDieをDiscordから管理したい! 2/3 (Discordから管理できるBOTの作成)
【Python】7DaysToDieをDiscordから管理したい! 3/3 (動作チェック)

GitHubに保管しました。
https://github.com/Trusted-Dream/7DaysToDie_Launcher
##Discordから管理できるBOTの作成
前回に続き今度はメインとなるDiscordから管理できるプログラムを作成していきます。

###sdtd_run.pyの作成

$ cd
$ mkdir -p $HOME/python/discord/Sdtd
$ cd $HOME/python/discord/
$ vim sdtd_run.py

続いてsdtd_run.pyは確認箇所が2つあります。

  • API
  • ADMIN
sdtd_run.py
#!/bin/env python
import discord
import threading
import os
import re
import time
import subprocess as prc
from Sdtd import command

API = "ここにディスコードAPIを記入してください。"
ADMIN = "ここにディスコードのサーバ管理者の名前を記入します。例:Dream"
#SubADMIN ="必要であれば副官の名前"

client = discord.Client()
@client.event
async def on_message(message):
    #サーバ管理者を指定します。副官がいる場合は以下のようにorで繋げてください。
    #if message.author.name == (ADMIN) or message.author.name == ("SubADMIN"):
    if message.author.name == (ADMIN):
        cmd = command.SDTD()
        if message.content.startswith('/help'):
            cmdhelp = cmd.command_help()
            await message.channel.send(cmdhelp)

        elif message.content.startswith('/member'):
            await message.channel.send(cmd.player_joined_check())

        elif message.content.startswith('/server-start'):
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            await message.channel.send(timemsg + "--- サーバを起動します。(起動には5分程掛かかる場合があります。)")
            thread = threading.Thread(target=cmd.start)
            thread.start()

        elif message.content.startswith('/server-stop'):
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            await message.channel.send(timemsg + "--- サーバを停止します。")
            thread = threading.Thread(target=cmd.stop)
            thread.start()

        elif message.content.startswith('/server-restart'):
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            await message.channel.send(timemsg + "--- サーバを再起動します。")
            thread = threading.Thread(target=cmd.stop)
            thread.start()
            time.sleep(15)
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            await message.channel.send(timemsg + "--- サーバを起動します。(起動には5分程掛かかる場合があります。)")
            thread = threading.Thread(target=cmd.start)
            thread.start()

        elif message.content.startswith('/server-status'):
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            await message.channel.send(timemsg + "--- サーバの状況を表示します。")
            await message.channel.send(cmd.status())

client.run(API)

続いてcommand.pyを作成します。

$ cd Sdtd
$ vim command.py

続いてcommand.pyは確認箇所が3つあります。

  • webhooks APIの記入
  • ゲームディレクトリの場所の確認。(バニラならデフォでOK)
  • Screenの場所の確認(恐らくデフォでOK)
command.py
#!/bin/env python
import os
import re
import sys
import requests
import subprocess as prc
import time
import glob as g
from telnetlib import Telnet

class SDTD(object):

    def __init__(self):
        #ディスコードのWebhooks APIを埋める。
        self.discord = "Discord_Webhooks_API"
        #screen生成ディレクトリの確認。
        self.screendir = "/var/run/screen/S-" + os.environ['USER']
        #今回は/sdtdで作成しているので以下で設定。mod鯖にするときは適時変更。
        self.gamedir = os.environ['HOME'] + "/steamcmd/sdtd"

    def port_check(self):
        p1 = prc.Popen(["lsof"], stdout=prc.PIPE)
        p2 = prc.Popen(["grep", "7Days"], stdin=p1.stdout, stdout=prc.PIPE)
        p3 = prc.Popen(["grep","-E","TCP..:"], stdin=p2.stdout, stdout=prc.PIPE)
        p4 = prc.Popen(["awk", "-F:", "NR==1 {print $2}"], stdin=p3.stdout, stdout=prc.PIPE)
        p5 = prc.Popen(["awk", "{print $1}"], stdin=p4.stdout, stdout=prc.PIPE)
        p1.stdout.close()
        p2.stdout.close()
        p3.stdout.close()
        p4.stdout.close()
        output = p5.communicate()[0].decode('utf-8')
        return output[:-1]

    def proc_check(self):
        p1 = prc.Popen(['ps', 'x'], stdout=prc.PIPE)
        p2 = prc.Popen(["grep", "-v","grep"], stdin=p1.stdout, stdout=prc.PIPE)
        p3 = prc.Popen(["grep", "7DaysToDieServer.x86_64"], stdin=p2.stdout, stdout=prc.PIPE)
        p4 = prc.Popen(["awk","{print $1}"], stdin=p3.stdout,stdout=prc.PIPE)
        p1.stdout.close()
        p2.stdout.close()
        p3.stdout.close()
        output = p4.communicate()[0].decode('utf-8')
        return output[:-1]

    def screen_check(self):
        p1 = prc.Popen(['ps', 'x'], stdout=prc.PIPE)
        p2 = prc.Popen(["grep", "-v","grep"], stdin=p1.stdout, stdout=prc.PIPE)
        p3 = prc.Popen(["grep", "SCREEN"], stdin=p2.stdout, stdout=prc.PIPE)
        p4 = prc.Popen(["awk","{print $1}"], stdin=p3.stdout, stdout=prc.PIPE)
        p1.stdout.close()
        p2.stdout.close()
        p3.stdout.close()
        output = p4.communicate()[0].decode('utf-8')
        return output[:-1]

    def command_help(self):
        lists = (
        "```/help                        この内容を表示します。\n"
        "/server-stop                 7Days to Die サーバを停止します。\n"
        "/server-start                7Days to Die サーバを起動します。\n"
        "/server-restart              7Days to Die サーバを再起動します。\n"
        "/server-status               7Days to Die サーバの状態を表示します。\n"
        "/member                      現在接続しているユーザを確認します。\n```"
        )
        return lists

    def server_status(self):
        init = 0
        port = self.port_check()
        if port != "":
            port_status_msg = "ポート [" + port + "] で解放されています。"
            init += 1
        else:
            port_status_msg = "ポートは解放されていません。"
            init -= 1

        process = self.proc_check()
        if process != "":
            proc_status_msg = "GAME PID [" + process[:-1] + "] で稼働しています。"
            init += 1
        else:
            proc_status_msg = "プロセスは稼働していません。"
            init -= 1

        screen = self.screen_check()
        if screen != "":
            screen_status_msg = "SCREEN PID [" + screen[:-1] + "] で稼働しています。"
            init += 1
        else:
            screen_status_msg = "スクリーンは稼働していません。"
            init -= 1

        message = port_status_msg + "\n" + proc_status_msg + "\n" + screen_status_msg + "\n"

        return message,init

    def status(self):
        message = self.server_status()[0]
        return message

    def player_joined_check(self):
        status = self.server_status()[1]
        sts_msg = self.server_status()[0]
        if status <= 0:
            msg = "サーバが正常に起動していません。\n" + sts_msg
            return msg
        login_status = []
        member = ""
        with Telnet('localhost',8081) as tn:
            tn.write(b'lp\n')
            time.sleep(1)
            tn.write(b'exit\n')
            login_mem = tn.read_all().decode().split("\n")[16:-1]

            for i in range(len(login_mem)):
                login_status += [login_mem[i].replace('\r','\n')]
                member += login_status[i]

        return member[:-1]

    def start_check(self):
        for i in range(420):
            status = self.server_status()[1]
            if status == 3:
                timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
                payload = {
                    "content" : timemsg + "--- サーバの起動が完了しました。"
                }
                requests.post(self.discord, data=payload)
                sys.exit()

            time.sleep(1)

        else:
            status = self.server_status()[0]
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            payload = {
                "content" : timemsg + "--- サーバの起動に失敗しました。状況を確認してください。\n" + status
            }
            requests.post(self.discord, data=payload)
            sys.exit()

    def stop_check(self):
        for i in range(30):
            status = self.server_status()[1]
            if status == -3:
                timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
                payload = {
                    "content" : timemsg + "--- サーバの停止が完了しました。"
                }
                requests.post(self.discord, data=payload)
                sys.exit()

            time.sleep(1)

        else:
            status = self.server_status()[0]
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            payload = {
                "content" : timemsg + "--- サーバの停止に失敗しました。状況を確認してください。\n" + status
            }
            requests.post(self.discord, data=payload)
            sys.exit()

    def start(self):
        status = self.server_status()[1]
        if status == 3:
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            payload = {
                "content" : timemsg + "--- サーバは既に起動しています。"
            }
            requests.post(self.discord, data=payload)
            sys.exit()

        os.chdir(self.gamedir)
        com1 = prc.run(["screen", "-dmS", "sdtd"], stdout=prc.PIPE)
        time.sleep(2)
        com2 = prc.run(['screen','-S','sdtd','-p','0','-X','exec','/bin/bash','startserver.sh'], stdout=prc.PIPE)
        sys.stdout.buffer.write(com1.stdout)
        time.sleep(2)
        sys.stdout.buffer.write(com2.stdout)

        self.start_check()

    def stop(self):
        prc_chk = self.proc_check()
        scn_chk = self.screen_check()
        status = self.server_status()[1]
        if status == 3:
            message = "say 10秒後サーバを停止します。\n\n".encode('utf-8')
            with Telnet('localhost',8081) as tn:
                tn.write(message)
                time.sleep(10)
                tn.write(b'shutdown\n')
                try:
                    tn.interact()
                except: pass

        if status == -3:
            timemsg = time.strftime("%Y/%m/%d %H:%M:%S ", time.strptime(time.ctime()))
            payload = {
                "content" : timemsg + "--- サーバは既に停止しています。"
            }
            requests.post(self.discord, data=payload)
            sys.exit()

        if prc_chk != "":
            prc.Popen(['kill', '-9',prc_chk], stdout=prc.PIPE)

        if scn_chk != "":
            prc.Popen(['kill', '-9',scn_chk], stdout=prc.PIPE)
            os.chdir(self.screendir)
            for remove in g.glob("*"):
                os.remove(remove)

        self.stop_check()

※port_checkの以下の部分について(どちらでもOK)
p3 = prc.Popen(["grep","-E","TCP..:"], stdin=p2.stdout, stdout=prc.PIPE)
本来はp3 = prc.Popen(["grep","TCP \*:"], stdin=p2.stdout, stdout=prc.PIPE)
と記述していましたが、掲載する時にPythonの文字色が崩れたので仕方なく変更してます。

###sdtd_start.shの作成

$ cd $HOME/python/discord
$ vim sdtd_start.sh

###sdtd_start.shの内容
次に起動用のシェルスクリプトを書きます。

sdtd_start.sh
#!/bin/sh
DIR=$(cd $(dirname $0); pwd)
PID=$(ps x |grep sdtd_run.py |grep -v grep |awk '{print $1}')

case "$1" in
  "start" )
        if [[ $PID == "" ]]; then
            env python $DIR/sdtd_run.py > /dev/null &
            sleep 3
            PID=$(ps x |grep sdtd_run.py |grep -v grep |awk '{print $1}')
            date +"%Y-%m-%d %H:%M:%S --- [$PID] start"
        else
            echo "pid already exists.[$PID]"
        fi
        ;;

  "restart" )
        if [[ $PID != "" ]]; then
                kill -9 $PID
                sleep 2
                date +"%Y-%m-%d %H:%M:%S --- [$PID] kill ok"
        fi
        env python $DIR/sdtd_run.py > /dev/null &
        sleep 3
        PID=$(ps x |grep sdtd_run.py |grep -v grep |awk '{print $1}')
        date +"%Y-%m-%d %H:%M:%S ---[$PID] start"
        ;;

  "stop" )
        if [[ $PID != "" ]]; then
                kill -9 $PID
                sleep 2
                date +"%Y-%m-%d %H:%M:%S --- [$PID] kill ok"
        else
                echo "pid does not exist."
        fi
        ;;

  "status" )
        if [[ $PID != "" ]]; then
                echo "running PID:"$PID
        else
                echo "not running."
        fi
        ;;

  * )
        if [[ $PID != "" ]]; then
            kill -9 $PID
            sleep 2
            date +"%Y-%m-%d %H:%M:%S --- [$PID] kill ok"
        fi
        env python $DIR/sdtd_run.py > /dev/null &
        sleep 3
        PID=$(ps x |grep sdtd_run.py |grep -v grep |awk '{print $1}')
        date +"%Y-%m-%d %H:%M:%S --- [$PID] start"
esac

sdtd_start.shの権限変更

$ chmod +x sdtd_start.sh

お疲れさまでした。では最後に実行して確認してみましょう。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?