はじめに
AsettoServer にてオンラインサーバーを運用している時、 誰がコネクトしているのか知りたい。誰かが走っているなら、追走したい。 という欲求から AssettoServerへコネクト(Connect) / ディスコネクト(Disconnect)している情報を Discordへ通知出来れば便利だよねって事で実現を目指す。
オンライン参加者が Chat を行うと、内容を Discord へ通知する仕立ては 導入済みだけど、一人で参加している時にわざわざchatは打たない。
自動で Connect / Disconnect が分かると皆うれしい(はず
※個人練習している時はバレたくないが....
Discord Webhook
AssettoServerのプラグインにて"DiscordAuditPlugin"がある。 オンラインにてChatした内容がDiscordへ送られるプラグイン。 これがあるなら...Connect/DisconetもDiscordへ送ってくれればよいのに...
Discord側にて Webhook を事前に準備する必要あり。
Webhookの準備が出来れば、あとは送るだけ。 AssettoServerに限らず、Webhookのurlが分かっていれば どこからでもDiscordに対してメッセージ送信が可能となる。
AssettoServerが出力するログをトリガーにてwebhookする方法も考えたが、 AssettoServerが吐くログファイルはタイムスタンプが付加されているので 最新のファイルを検索処理も作る必要がある。
だが、そこまでして通知を実装する気なし!!
どうする?
git hub を漁ったところ... iiferedon さんが作ってた!!
一先ず、ありがたくダウンロードさせて頂き導入してみた。
Assetto Corsa Server Listener - Discord Webhook -
設定は簡単に終了。以下の2点だけで完了 * Discord webhook URL 設定 * AssettoServerのURL、port番号設定
Pythonで書かれているので、Pythonで実行するだけ...
AssettoServer Onlineへconnectしてみると...
おぉぉ。connect情報がDiscordへ送信されている!!!
ぢゃぁ、Disconnect!!
ん?? Errorが出て止まってる??
ちょいと調査が必要
仕立て
そもそもAssettoServerを動作させると、Content Managerなどオンライン情報表示を行うため、 jsonにて現在のサーバー接続状況を取得可能となっている。 ※ウェブブラウザを使用すると簡単に見る事が可能。
- " Assetto Corsa Server Listener - Discord Webhook -"では最初の起動時にサーバーからjsonデータを取得
- 取得したjsonデータをローカルのファイルへ書き出す。
- 一定間隔にてサーバーからjsonデータを取得
- ローカル側のファイルと比較し、差異があった場合に Connect / Disconnect を判断
- 結果をDiscordへwebhook
という流れぽい。
"Disconnect"の情報までは検知しているが、Webhookするためのデータ作成処理においてエラーが発生。
エラーの状況から"Null"参照しているっぽいけど... どのタイミングで発生しているのかわかりづらい... "Python"ってデバッグしづらい... (Pythonに限ったことではないが...)
※"pdb"というpythonデバッガが存在している事は後で知る事になる。
※"pdb"は便利!! これが無いと python デバッグ無理!!
デバッグすれば済む事なのだが、以下の点が気になる。 * jsonデータをローカルファイルへ保存 * AssettoServerに誰かがログインしている状態にて python を動かすとエラー出る。
だったら勉強がてら自分で組むことにする
ChatGPT
Python , jsonともに素人の私がググって調べると時間がかかるので、 "ChatGPT"へお伺いする。
※ChatGPTの回答内容は自粛
Pythonを使用しWebページからjsonデータを取得しjsonデータの内容に変化があるか確認を行うためのサンプルコードを教えてほしい
ChatGPT : 再帰的なアプローチを使用してJSONデータを探索し、差分を検出する方法を採用しています。
で、サンプルコードが吐かれる... ふむふむ。 さすがPython!! コード量が少なくて見やすいwww
追加質問
get_json_dataにて取得したjsonデータにてどの部分が変更になったか確認したい
あぁ、なるほどねぇwww
雰囲気はわかったのであとは実装!! (ほんとうはわかってない
ぷろぐらみんぐ 方向性
- Python起動時、AsettoServerから接続情報(json)取得
- 一定時間にて最新jsonを取得
- 前回 / 最新 json にて差異があるか確認
- Connect / Disconnect 情報のみが必要なので、json内の"DriverName"に変化があるか確認
- 変化があった場合、以下の条件を確認しDiscordへwebhookする
- None -> user名 : Connect
- user名 -> None : Disconet
※ iiferedonさんのPythonではユーザー名、使用車両、スキン名などもWebhookされるが Connect情報が重要なので、他の項目は捨てる。 webhookしている部分は参考にする。(ありがたい)
ついでにwebhook関係をググってみる www
※最重要な事は 動けば良い をコンセプトとする。
AssettoServerからjson取得
get_jsonする事で簡単にjsonデータ取得が行える。便利だねぇ。
current_data= get_json_data(url)
新旧データ
新旧jsonデータを保持するために2つバッファを用意 * current_data * previous_data
起動時に"previous_data"へ一発jsonを読ませる。 最新データを"current_data"へ読み込み 二つのデータに差異があるか確認すれば全てOK
比較
ChatGPTさんに教えてもらったサンプルコードをcopy して2つのjsonデータ比較を行う。
def get_json_data(url): try: response = requests.get(url) if response.status_code == 200: return response.json() else: print(f"Failed to fetch data from {url}. Status code: {response.status_code}") return None except requests.exceptions.RequestException as e: print(f"An error occurred while fetching data: {e}") return None def compare_json_data(data1, data2, path=""): if isinstance(data1, dict) and isinstance(data2, dict): for key in data2.keys(): if key not in data1: print(f"Key '{key}' added at path: {path}/{key}") else: compare_json_data(data1[key], data2[key], f"{path}/{key}") for key in data1.keys(): if key not in data2: print(f"Key '{key}' removed from path: {path}/{key}") elif isinstance(data1, list) and isinstance(data2, list): for i in range(min(len(data1), len(data2))): compare_json_data(data1[i], data2[i], f"{path}/{i}") if len(data1) > len(data2): for i in range(len(data2), len(data1)): print(f"Item '{data1[i]}' removed from path: {path}/{i}") elif len(data1) < len(data2): for i in range(len(data1), len(data2)): print(f"Item '{data2[i]}' added at path: {path}/{i}") (続くけど...)
上記の場合 "data1"と"data2"を使用しているので
if data1 != data2:
とするだけで比較完了!! なんて簡単な!!
Webhook
後は条件をダラダラを書いて、Webhookを書けば終了。
ログインしているユーザーは"DriverName"というキー名にて管理されている。 結果が保持されている"path"中に "DriverName"が含まれているか確認
- "path"中に"DriverName"が含まれているか確認
- 新旧 "DriverName"の状態確認
- None -> user名 : Connect
- user名 --> None : Disconnect
- Webhook データ生成
- Connectしたよん / Disconnect したよん
- Webhook 実行
- Discordに表示される
Code
出来上がったコードは以下の通り。
import requests import json from urllib.request import urlopen, Request import time import os import sys import colorama from colorama import Fore from discord_webhook import DiscordWebhook, DiscordEmbed #Change these values webhook = DiscordWebhook(url="DiscordWebHookURL") server_address = "localhost:8081" def get_json_data(url): try: response = requests.get(url) if response.status_code == 200: return response.json() else: print(f"Failed to fetch data from {url}. Status code: {response.status_code}") return None except requests.exceptions.RequestException as e: print(f"An error occurred while fetching data: {e}") return None def send_webhook(title, player, colour): embed = DiscordEmbed(title=title, color=colour) embed.set_timestamp() embed.add_embed_field(name='User Name :', value=player) webhook.add_embed(embed) response = webhook.execute() return None def compare_json_data(data1, data2, path=""): if isinstance(data1, dict) and isinstance(data2, dict): for key in data2.keys(): if key not in data1: print(f"Key '{key}' added at path: {path}/{key}") else: compare_json_data(data1[key], data2[key], f"{path}/{key}") for key in data1.keys(): if key not in data2: print(f"Key '{key}' removed from path: {path}/{key}") elif isinstance(data1, list) and isinstance(data2, list): for i in range(min(len(data1), len(data2))): compare_json_data(data1[i], data2[i], f"{path}/{i}") if len(data1) > len(data2): for i in range(len(data2), len(data1)): print(f"Item '{data1[i]}' removed from path: {path}/{i}") elif len(data1) < len(data2): for i in range(len(data1), len(data2)): print(f"Item '{data2[i]}' added at path: {path}/{i}") else: if data1 != data2: if path.find('DriverName') > 0: #Send Discord Messages if data1 == None: title = "SRP Connected" color=5763719 usr = data2 else: title = "SRP DisConnected" color=15548997 usr = data1 print(f"Path: {title}") print(f"{usr}") send_webhook(title,usr,color) if __name__ == "__main__": # Specify the URL of the web page you want to check for changes url = "http://"+server_address+"/JSON%7C" # first fetch previous_data = get_json_data(url) if not previous_data: exit() while True: # Get data every 10 seconds and check the change time.sleep(10) current_data = get_json_data(url) if current_data: compare_json_data(previous_data, current_data) previous_data = current_data else: print("Failed to Get Data")
準備
- 自分のDiscord Webhook アドレスへ書き換える
- AssettoServerのIPアドレスおよびポート番号へ書き換える
実行
webからjson情報を取得し実行するため、AssettoServer動作中のPC以外からも実行可能である。 まずはAssettoServerが動作している端末にてpythonを実行し通知を行う事とする。
- 新規コンソールを開く
- "tmux"などを使用し別スレッドでもOK
- AssettoServerが実行中である事を確認
- サーバーが停止状態の場合、jsonデータ取得が行えないのでエラーとなる。
- python実行
- python3 xxxx.py <リターン>
- 何事もなければ、画面はウンスン状態 (変化なし)
- Error発生時はエラー内容を表示し停止するので、エラー内容に応じて対処する。
Connect / Disconnectを行い、以下のような通知が来ればOK.