From 4faa8deee760082de6548566d68030c593843a19 Mon Sep 17 00:00:00 2001 From: Milkeyyy <59532514+Milkeyyy@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:48:46 +0900 Subject: [PATCH] =?UTF-8?q?=E3=82=AE=E3=83=AB=E3=83=89=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=81=AE=E5=BD=A2=E5=BC=8F=E3=82=92=E5=A4=89=E6=9B=B4?= =?UTF-8?q?(=E5=A4=89=E6=9B=B4=E5=87=A6=E7=90=86=E3=82=82=E5=AE=9F?= =?UTF-8?q?=E8=A3=85)=20/=20=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=83=86=E3=83=BC=E3=82=BF=E3=82=B9=E3=82=92=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=83=81?= =?UTF-8?q?=E3=83=A3=E3=83=B3=E3=83=8D=E3=83=AB=E3=81=AE=E5=90=8D=E5=89=8D?= =?UTF-8?q?=E3=81=AB=E3=82=A4=E3=83=B3=E3=82=B8=E3=82=B1=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + src/main.py | 123 ++++++++++++++++++++++++++++++++++------- src/serverstatus.py | 21 +++++++ src/statusindicator.py | 6 ++ 4 files changed, 131 insertions(+), 20 deletions(-) create mode 100644 src/statusindicator.py diff --git a/.gitignore b/.gitignore index c4c90f0..100ff16 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vscode/launch.json guild.json +guilds.json token.txt cronitor_keys.txt diff --git a/src/main.py b/src/main.py index 47e89ef..6a2706e 100644 --- a/src/main.py +++ b/src/main.py @@ -1,6 +1,7 @@ import argparse import json import logging +import os import sys import traceback @@ -12,6 +13,7 @@ import localizations import serverstatus import statusicon +import statusindicator logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.WARNING) @@ -19,11 +21,21 @@ # Botの名前 bot_name = "R6SSS" # Botのバージョン -bot_version = "1.3.13" +bot_version = "1.4.0" default_embed = discord.Embed -default_guilddata_item = {"server_status_message": [0, 0, "en-GB"]} # チャンネルID, メッセージID +default_guilddata_item = {"server_status_message": [0, 0, "en-GB"]} # チャンネルID, メッセージID] + +default_guilddata_item = { + "server_status_message": { + "channel_id": 0, + "message_id": 0, + "language": "en-GB", + "status_indicator": True + } +} + db = {} # 引数ぱーさー @@ -56,6 +68,9 @@ async def on_ready(): # ハートビートのキーを読み込み heartbeat.loadKeys() + # 旧ギルドデータの変換処理を試行 + await convertGuildData() + # ギルドデータの確認を開始 await loadGuildData() await checkGuildData() @@ -71,7 +86,7 @@ async def saveGuildData(): global db # 書き込み用にファイルを開く - file = open("guild.json", "w", encoding="utf-8") + file = open("guilds.json", "w", encoding="utf-8") # 辞書をファイルへ保存 file.write(json.dumps(db, indent=2, sort_keys=True)) file.close() @@ -86,17 +101,17 @@ async def loadGuildData(): try: # ファイルが存在しない場合 # ファイルを作成して初期データを書き込む - file = open("guild.json", "x", encoding="utf-8") + file = open("guilds.json", "x", encoding="utf-8") file.write(json.dumps(db, indent=2, sort_keys=True)) file.close() # ファイルから読み込む - file = open("guild.json", "r", encoding="utf-8") + file = open("guilds.json", "r", encoding="utf-8") db = json.load(file) file.close() except FileExistsError: # ファイルが存在する場合 # ファイルから読み込む - file = open("guild.json", "r", encoding="utf-8") + file = open("guilds.json", "r", encoding="utf-8") db = json.load(file) file.close() @@ -123,6 +138,37 @@ async def checkGuildData(guild = None): logging.info("ギルドデータの確認完了") +# 旧ギルドデータの変換 +async def convertGuildData(): + global default_guilddata_item + + try: + # 旧ギルドデータが存在する場合は変換処理を実行する + if os.path.exists("guild.json"): + # ファイルの読み込み + file = open("guild.json", "r", encoding="utf-8") + old_gd = json.load(file) + new_gd = {} + file.close() + + for guild_id in old_gd.keys(): + new_gd[guild_id] = {"server_status_message": {}} + new_gd[guild_id]["server_status_message"]["channel_id"] = old_gd[guild_id]["server_status_message"][0] + new_gd[guild_id]["server_status_message"]["message_id"] = old_gd[guild_id]["server_status_message"][1] + new_gd[guild_id]["server_status_message"]["language"] = old_gd[guild_id]["server_status_message"][2] + new_gd[guild_id]["server_status_message"]["status_indicator"] = True + + # 書き込み用にファイルを開く + file = open("guilds.json", "w", encoding="utf-8") + # 辞書をファイルへ保存 + file.write(json.dumps(new_gd, indent=2, sort_keys=True)) + file.close() + await loadGuildData() + + except Exception as e: + logging.warning("ギルドデータの変換処理に失敗しました: " + str(e)) + + # 1分毎にサーバーステータスを更新する serverstatus_loop_isrunning = False @@ -152,22 +198,23 @@ async def updateserverstatus(): for guild in client.guilds: #logging.info(f"ギルド: {guild.name}") try: - ch_id = int(db[str(guild.id)]["server_status_message"][0]) - msg_id = int(db[str(guild.id)]["server_status_message"][1]) - loc = db[str(guild.id)]["server_status_message"][2] + ch_id = int(db[str(guild.id)]["server_status_message"]["channel_id"]) + msg_id = int(db[str(guild.id)]["server_status_message"]["message_id"]) + loc = db[str(guild.id)]["server_status_message"]["language"] except Exception as e: logging.warning(f"ギルドデータ({guild.name}) の読み込み失敗") tb = sys.exc_info() logging.error(str(traceback.format_tb(tb))) db[str(guild.id)] = default_guilddata_item - ch_id = db[str(guild.id)]["server_status_message"][0] - msg_id = db[str(guild.id)]["server_status_message"][1] - loc = db[str(guild.id)]["server_status_message"][2] + ch_id = db[str(guild.id)]["server_status_message"]["channel_id"] + msg_id = db[str(guild.id)]["server_status_message"]["message_id"] + loc = db[str(guild.id)]["server_status_message"]["language"] try: if ch_id != 0 and msg_id != 0 and loc != None: # IDからテキストチャンネルを取得する ch = client.get_channel(ch_id) + ch_name = ch.name e = "" try: @@ -182,11 +229,15 @@ async def updateserverstatus(): logging.warning(str(e)) db[str(guild.id)] = default_guilddata_item else: + # テキストチャンネルの名前にステータスインジケーターを設定 + if ch_name[0] in statusindicator.List: ch_name = ch_name[1:] + if db[str(guild.id)]["server_status_message"]["status_indicator"] == True: await msg.channel.edit(name=serverstatus.indicator + ch_name) + await msg.edit(embeds=await generateserverstatusembed(loc)) except Exception as e: tb = sys.exc_info() logging.error(f"ギルド {guild.name} のサーバーステータスメッセージ({str(msg_id)})の更新に失敗") - logging.error(str(traceback.format_tb(tb))) + logging.error(traceback.format_exc()) except Exception as e: logging.error(traceback.format_exc()) heartbeat.monitor.ping(state="fail", message="サーバーステータスの更新エラー: " + str(e)) @@ -293,25 +344,54 @@ async def setlanguage(ctx, ): global db + logging.info(f"コマンド実行: setlanguage / 実行者: {ctx.user}") + await ctx.defer(ephemeral=True) # ギルドデータをチェック await checkGuildData(ctx.guild) if locale in localizations.data["Locales"].values(): - db[str(ctx.guild.id)]["server_status_message"][2] = [k for k, v in localizations.data["Locales"].items() if v == locale][0] + db[str(ctx.guild.id)]["server_status_message"]["language"] = [k for k, v in localizations.data["Locales"].items() if v == locale][0] else: - db[str(ctx.guild.id)]["server_status_message"][2] = "en-GB" + db[str(ctx.guild.id)]["server_status_message"]["language"] = "en-GB" + + # ギルドデータを保存 + await saveGuildData() await ctx.send_followup(content="サーバーステータスメッセージの言語を `" + locale + "` に設定しました。") +@client.slash_command(description="サーバーステータスインジケーターの表示を設定します。") +async def setindicator(ctx, + enable: Option( + bool, + name="enable", + permission=discord.Permissions.administrator + ) +): + global db + + logging.info(f"コマンド実行: setindicator / 実行者: {ctx.user}") + + await ctx.defer(ephemeral=True) + + # ギルドデータをチェック + await checkGuildData(ctx.guild) + + db[str(ctx.guild.id)]["server_status_message"]["status_indicator"] = enable + + # ギルドデータを保存 + await saveGuildData() + + await ctx.send_followup(content="サーバーステータスインジケーターの表示を `" + str(enable) + "` に設定しました。") + @client.slash_command(description="現在のサーバーステータスを送信します。このコマンドで送信されたサーバーステータスは自動更新されません。") async def status(ctx): logging.info(f"コマンド実行: status / 実行者: {ctx.user}") await ctx.defer(ephemeral=True) try: - await ctx.send_followup(embeds=await generateserverstatusembed(db[str(ctx.guild_id)]["server_status_message"][2])) + await ctx.send_followup(embeds=await generateserverstatusembed(db[str(ctx.guild_id)]["server_status_message"]["language"])) except Exception as e: logging.error(traceback.format_exc()) await ctx.send_followup(content="サーバーステータスメッセージの送信時にエラーが発生しました: `" + str(e) + "`") @@ -335,7 +415,7 @@ async def create(ctx, await checkGuildData(ctx.guild) additional_msg = "" - if db[str(ctx.guild_id)]["server_status_message"][1] != 0: + if db[str(ctx.guild_id)]["server_status_message"]["message_id"] != 0: additional_msg = "\n(以前送信した古いメッセージは更新されなくなります。)" if channel is None: @@ -346,7 +426,7 @@ async def create(ctx, # サーバーステータス埋め込みメッセージを送信 try: - msg = await ch.send(embeds=await generateserverstatusembed(db[str(ctx.guild_id)]["server_status_message"][2])) + msg = await ch.send(embeds=await generateserverstatusembed(db[str(ctx.guild_id)]["server_status_message"]["language"])) except Exception as e: if type(e) == discord.errors.ApplicationCommandInvokeError and str(e).endswith("Missing Permissions"): await ctx.send_followup(content="テキストチャンネル " + ch.mention + " へメッセージを送信する権限がありません!") @@ -356,8 +436,11 @@ async def create(ctx, return # 送信したチャンネルとメッセージのIDをギルドデータへ保存する - db[str(ctx.guild_id)]["server_status_message"][0] = ch_id - db[str(ctx.guild_id)]["server_status_message"][1] = msg.id + db[str(ctx.guild_id)]["server_status_message"]["channel_id"] = ch_id + db[str(ctx.guild_id)]["server_status_message"]["message_id"] = msg.id + + # ギルドデータを保存 + await saveGuildData() await ctx.send_followup(content="テキストチャンネル " + ch.mention + " へサーバーステータスメッセージを送信しました。\n以後このメッセージは自動的に更新されます。" + additional_msg) except Exception as e: diff --git a/src/serverstatus.py b/src/serverstatus.py index aae8022..695583f 100644 --- a/src/serverstatus.py +++ b/src/serverstatus.py @@ -3,14 +3,21 @@ import logging import urllib +import statusindicator + # サーバーステータスAPIのURL api_url = "https://api.r6sss.milkeyyy.com" # サーバーステータス辞書 data = {} +# テキストチャンネルの名前に表示するステータスインジケーター(絵文字) +indicator = statusindicator.Unknown + # サーバーステータスを取得して整えて返す async def get(): + global indicator + # サーバーステータスを取得する res = urllib.request.urlopen(urllib.request.Request(api_url)) #logging.info(str(res.read())) @@ -18,9 +25,23 @@ async def get(): # ステータスコードが200以外の場合はUnknownなデータを返す if res.status != 200: status = {"Unknown": {"Status": {"Connectivity": "Unknown", "Authentication": "Unknown", "Leaderboard": "Unknown", "Matchmaking": "Unknown", "Purchase": "Unknown"}, "Maintenance": None, "ImpactedFeatures": None}, "_update_date": datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9)))} + indicator = statusindicator.Unknown return status status = json.loads(res.read()) status["_update_date"] = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))) + # ステータスインジケーターを設定 + for k, v in status.items(): + if k == "_update_date": continue + st = v["Status"]["Connectivity"] + if st == "Operational" and indicator != statusindicator.Interrupted and indicator != statusindicator.Degraded and indicator != statusindicator.Maintenance: + indicator = statusindicator.Operational + if st == "Interrupted": + indicator = statusindicator.Interrupted + if st == "Degraded": + indicator = statusindicator.Degraded + if st == "Maintenance": + indicator = statusindicator.Maintenance + return status diff --git a/src/statusindicator.py b/src/statusindicator.py new file mode 100644 index 0000000..0b04ad5 --- /dev/null +++ b/src/statusindicator.py @@ -0,0 +1,6 @@ +Operational = "🟢" +Interrupted = "🟡" +Degraded = "🔴" +Maintenance = "🔧" +Unknown = "⬜" +List = [Operational, Interrupted, Degraded, Maintenance, Unknown] \ No newline at end of file