【ARK】個人サーバーのゲームログをDiscordへ通知する

ARK: Survival Evolvedは個人で遊ぶよりも、フレンドとワイワイ協力し合いながらプレイする方が圧倒的に楽しく、何よりもゲーム効率が良い。

しかし大人になると、なかなかプレイ時間が合わせられず、せめてプレイしている様子だけでもフレンドに伝える術はないか思い立った。

ARKサーバーは起動時に -servergamelog のフラグオプションを追加することで、プレイヤーのログイン・ログアウト、テイム、キルログを代表とするゲームログをリアルタイムでテキストファイルに出力するようになる。

このテキストファイルを定期的にPythonスクリプトで解析することで、最新のゲームログを任意のSNS(今回はDiscord)へ自動通知するようにしてみた。

この通知機能はフレンドからも好評だったので、通知方法を紹介する。

Sponsored Link

動作環境

ARKサーバー
Linux版でもWindows版、どちらのARKサーバーでも動作できる。ここではLinux環境下の紹介となるので、Windowsで動作させたい場合は適宜読み替えて欲しい。

Python
Python 3.4.3 で動作するスクリプトを紹介している。必要な外部ライブラリは「discordwebhook」のみとなるよう工夫した。

ARKサーバーのゲームログ出力

サーバー起動時のオプションに -servergamelog フラグを追加するだけ。

さらっと書いているが、フラグオプションの記述方法を理解することが少々難しかった。

サーバー起動時のフラグオプションを付加する
  1. /ark ディレクトリ内に存在するファイル arkmanager.cfg を開く
  2. 24行目くらいに # ARK server flags – use arkflag_<optionname>=trueというフラグに関するコメントがある。その下に以下の1行を追記する。
    arkflag_servergamelog=true
  3. ファイルを保存して、サーバーを再起動する
コンフィグファイルに arkflag_servergamelog=true を挿入

これで、ゲームログ出力オプション”-servergamelog” が有効な状態でサーバーが起動するはずだ。

ゲームログファイルの確認

ゲームログ出力オプション”-servergamelog” が有効な状態でサーバーが起動すると、/ark/server/ShooterGame/Saved/Logs のディレクトリ内にServerGame.〇〇〇〇〇.log というファイルが出力される。

このログファイルが作成されているか確認しよう。少しゲームをプレイした状態でログを開いてみると、このように記録されている。

[2020.06.14-05.08.28:341][885]Log file open, 06/14/20 05:08:28
[2020.06.14-05.08.28:341][885]2020.06.14_05.08.28: player1 joined this ARK!
[2020.06.14-05.29.44:201][561]2020.06.14_05.29.44: ヒト1 of Tribe Tribe of トライブ名 Tamed a Doedicurus - Lvl 29 (Doedicurus)!
[2020.06.14-06.02.02:902][465]2020.06.14_06.02.02: player1 left this ARK!
[2020.06.14-06.48.27:511][376]2020.06.14_06.48.27: player2 joined this ARK!
[2020.06.14-07.03.57:390][893]2020.06.14_07.03.57: ヒト2 of Tribe Tribe of トライブ名 Tamed a Pteranodon - Lvl 25 (Pteranodon)!
[2020.06.14-07.39.36:584][211]2020.06.14_07.39.36: palyer2 left this ARK!

1行目にはサーバーを起動した時刻が記録され、その下に実際のゲームログが保存される。

なお、ログファイルに記録される時刻はUTC(協定世界時)になっている。JST(日本標準時)に変換するには、UTCの時刻に+9時間を足せばよい。

このログファイルの記述形式を意識してPythonスクリプトを作っていく。

ログファイルを解析するPythonスクリプト

ARKサーバーの環境下に以下のPythonスクリプトを作成する。User Settingで指定したログファイルが保存されるディレクトリパスは環境に合わせて変更して欲しい。

import re
from pathlib import Path
from datetime import datetime, timedelta, timezone

## User Setting ##
log_dir  = '/ark/server/ShooterGame/Saved/Logs/'
##################

UTC = timezone.utc
JST = timezone(timedelta(hours=+9), 'JST')
dt_now = datetime.now(UTC)

# 指定ディレクトリ内で'ServerGame'で始まるログファイル一覧を取得
p = Path(log_dir)
log_arr = list (p.glob('ServerGame*'))
log_dct = {}

for item in log_arr:
    log_dct[item] = item.stat().st_mtime
log_arr = []

# タイムスタンプをキーにログファイル一覧を降順ソート
for k,v in sorted(log_dct.items(), key=lambda x:x[1], reverse=True):
    log_arr.append(str(k))

# 最新のログファイルをオープン
with open(log_arr[0]) as f:
    l_strip = [s.strip() for s in f.readlines()]

# 正規表現にマッチするテキストを抜き出す
for item in l_strip:
    item_ext = re.sub(r'\[.+\]','',item)    
    m = re.match(r'^[0-9|\.|_]+', item_ext)

    if m:
        item_time = m.group()
        item_text = re.sub(r'^[0-9|\.|_]+: ','',item_ext)
        
        dt = datetime.strptime(item_time, '%Y.%m.%d_%H.%M.%S')
        dt_utc = dt.replace(tzinfo=UTC)
        dt_jst = dt_utc.astimezone(JST)

        delta_dt = dt_now - dt_utc
        
        #プログラム実行時から60秒以内のログを出力
        if(delta_dt.total_seconds() <= 60):      
            print(dt_jst.strftime('%Y-%m-%d %H:%M'))
            print(item_text)

現在時刻から60秒以内のログをprint出力するスクリプトになっている。

テストのために当分のログを出力したいのであれば、この部分の時間を変更する。以下は60秒以内の例:

        #プログラム実行時から60秒以内のログを出力
        if(delta_dt.total_seconds() <= 60):    

Discordへの通知

このスクリプトでログテキストが抽出できたので、あとは、時間とテキストをDiscordへ通知するだけだ。

DiscordはWebhookを使って任意のチャンネルにメッセージを通知可能なAPIを公開しているので、これを活用する。

Webhook URLの取得

こちらのWebサイトを参考にDiscordへの通知に必要なWebhook URLを取得する。

PythonスクリプトへDiscord通知文を挿入

Discordへのメッセージ通知は比較的簡単だ。まず必要なライブラリをpipでインストールする。

$ pip install discordwebhook

あとは以下のPythonスクリプトで、任意のチャンネルにメッセージを通知できる。

from discordwebhook import Discord

discord = Discord(url="<ウェブフックurl>")
discord.post(content="Hello, world.")

この仕組みを愚直にゲームログを抽出するPythonスクリプトに実装すると、指定秒数(ここでは60秒)以内にゲームログが複数発生したときに、ログ回数分の通知が行われる。さすがにこれは鬱陶しい。

そこで、単なるメッセージポストではなく、「embeds」という埋め込みメッセージを利用する。

このポスト形式を指定することで、最大10個までのメッセージを一度に送信可能だ。

具体的には、先ほどのログ抽出スクリプトに以下の文を追加する

from discordwebhook import Discord

discord = Discord(url="<ウェブフックurl>")
discord_contents = {}
discord_array = []
・・・省略・・・

        #プログラム実行時から60秒以内のログを配列に格納
        if(delta_dt.total_seconds() <= 60):            
            #print(dt_jst.strftime('%Y-%m-%d %H:%M'))
            #print(item_text)
            discord_contents={'name':dt_jst.strftime('%Y-%m-%d %H:%M'),'value':item_text}
            discord_array.append(discord_contents)

#60秒以内にログがあればembeds形式でdiscordに送信
if discord_array:
    discord_dic = {'fields':discord_array}
    discord.post(embeds=[discord_dic],)

Pythonスクリプトの全体

最後に、出来上がったスクリプトを掲載しておく。こちらのスクリプトをcronで60秒間隔で実行すれば、ほぼリアルタイムでDiscordにゲームログが通知できる。

import re
from pathlib import Path
from datetime import datetime, timedelta, timezone
from discordwebhook import Discord

## User Setting ##
discord = Discord(url="<ウェブフックurl>")
log_dir  = '/ark/server/ShooterGame/Saved/Logs/'
##################

UTC = timezone.utc
JST = timezone(timedelta(hours=+9), 'JST')
dt_now = datetime.now(UTC)

discord_contents = {}
discord_array = []

# 指定ディレクトリ内で'ServerGame'で始まるログファイル一覧を取得
p = Path(log_dir)
log_arr = list (p.glob('ServerGame*'))

log_dct = {}

for item in log_arr:
    log_dct[item] = item.stat().st_mtime

log_arr = []

# タイムスタンプをキーにログファイル一覧を降順ソート
for k,v in sorted(log_dct.items(), key=lambda x:x[1], reverse=True):
    log_arr.append(str(k))

# 最新のログファイルをオープン
with open(log_arr[0]) as f:
    l_strip = [s.strip() for s in f.readlines()]

# 正規表現にマッチするテキストを抜き出す
for item in l_strip:
    item_ext = re.sub(r'\[.+\]','',item)    
    m = re.match(r'^[0-9|\.|_]+', item_ext)

    if m:
        item_time = m.group()
        item_text = re.sub(r'^[0-9|\.|_]+: ','',item_ext)
        
        dt = datetime.strptime(item_time, '%Y.%m.%d_%H.%M.%S')
        dt_utc = dt.replace(tzinfo=UTC)
        dt_jst = dt_utc.astimezone(JST)

        delta_dt = dt_now - dt_utc
        
        #プログラム実行時から60秒以内のログを配列に格納
        if(delta_dt.total_seconds() <= 60):
            discord_contents={'name':dt_jst.strftime('%Y-%m-%d %H:%M'),'value':item_text}
            discord_array.append(discord_contents)

#60秒以内にログがあればembeds形式でdiscordに送信
if discord_array:
    discord_dic = {'fields':discord_array}
    discord.post(embeds=[discord_dic],)
Discordへの通知例

謝辞:本記事アイキャッチのアイコンはhttps://icons8.jp/を利用しました

コメント