From 603d44ba003047b50f135dca8e2f3a824fa4361e Mon Sep 17 00:00:00 2001 From: altNightHawk <84723615+altNightHawk@users.noreply.github.com> Date: Fri, 2 Jan 2026 03:25:45 +0000 Subject: [PATCH 1/2] Add BattleMetrics fallback for ASA protocol when EOS fails When EOS registration is broken (due to game updates), the ASA protocol now falls back to querying BattleMetrics API to find the server by IP:port. This allows monitoring to continue working even when Epic Online Services is not returning server data. The protocol tries EOS first, and if that fails, automatically searches BattleMetrics for an online server matching the configured IP:port. --- discordgsm/protocols/asa.py | 92 ++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 22 deletions(-) diff --git a/discordgsm/protocols/asa.py b/discordgsm/protocols/asa.py index 1415cc7..f7061b8 100644 --- a/discordgsm/protocols/asa.py +++ b/discordgsm/protocols/asa.py @@ -1,6 +1,7 @@ import time from typing import TYPE_CHECKING +import aiohttp import opengsq from discordgsm.protocols.protocol import Protocol @@ -36,29 +37,76 @@ async def query(self): await self.pre_query() host, port = str(self.kv["host"]), int(str(self.kv["port"])) - eos = opengsq.EOS( - host, port, self._deployment_id, ASA._access_token, self.timeout - ) start = time.time() - info = await eos.get_info() - ping = int((time.time() - start) * 1000) + + # Try EOS first + try: + eos = opengsq.EOS( + host, port, self._deployment_id, ASA._access_token, self.timeout + ) + info = await eos.get_info() + ping = int((time.time() - start) * 1000) + + # Credits: @dkoz https://github.com/DiscordGSM/GameServerMonitor/pull/54/files + attributes = dict(info.get("attributes", {})) + settings = dict(info.get("settings", {})) - # Credits: @dkoz https://github.com/DiscordGSM/GameServerMonitor/pull/54/files - attributes = dict(info.get("attributes", {})) - settings = dict(info.get("settings", {})) + result: GamedigResult = { + "name": attributes.get("CUSTOMSERVERNAME_s", ""), + "map": attributes.get("MAPNAME_s", ""), + "password": attributes.get("SERVERPASSWORD_b", False), + "numplayers": info.get("totalPlayers", 0), + "numbots": 0, + "maxplayers": settings.get("maxPublicPlayers", 0), + "players": None, + "bots": None, + "connect": f"{host}:{port}", + "ping": ping, + "raw": info, + } - result: GamedigResult = { - "name": attributes.get("CUSTOMSERVERNAME_s", ""), - "map": attributes.get("MAPNAME_s", ""), - "password": attributes.get("SERVERPASSWORD_b", False), - "numplayers": info.get("totalPlayers", 0), - "numbots": 0, - "maxplayers": settings.get("maxPublicPlayers", 0), - "players": None, - "bots": None, - "connect": f"{host}:{port}", - "ping": ping, - "raw": info, - } + return result + except Exception: + # EOS failed, fallback to BattleMetrics + pass + + # Fallback: Query BattleMetrics API by IP:port + async with aiohttp.ClientSession() as session: + url = f"https://api.battlemetrics.com/servers?filter[game]=arksa&filter[search]={host}:{port}" + async with session.get(url, timeout=aiohttp.ClientTimeout(total=self.timeout)) as response: + if response.status != 200: + raise Exception(f"BattleMetrics API returned {response.status}") + + data = await response.json() + servers = data.get("data", []) + + # Find the online server matching our IP:port + server_info = None + for server in servers: + attrs = server.get("attributes", {}) + if attrs.get("ip") == host and attrs.get("port") == port and attrs.get("status") == "online": + server_info = server + break + + if not server_info: + raise Exception(f"No online server found on BattleMetrics for {host}:{port}") + + ping = int((time.time() - start) * 1000) + attrs = server_info.get("attributes", {}) + details = attrs.get("details", {}) + + result: GamedigResult = { + "name": attrs.get("name", ""), + "map": details.get("map", ""), + "password": details.get("password", False), + "numplayers": attrs.get("players", 0), + "numbots": 0, + "maxplayers": attrs.get("maxPlayers", 0), + "players": None, + "bots": None, + "connect": f"{host}:{port}", + "ping": ping, + "raw": attrs, + } - return result + return result From b64fbb8b018169cef74d560043210ba3b9a8a374 Mon Sep 17 00:00:00 2001 From: altNightHawk <84723615+altNightHawk@users.noreply.github.com> Date: Fri, 2 Jan 2026 03:31:29 +0000 Subject: [PATCH 2/2] Fix ping timer reset in ASA BattleMetrics fallback --- discordgsm/protocols/asa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discordgsm/protocols/asa.py b/discordgsm/protocols/asa.py index f7061b8..a5d6fae 100644 --- a/discordgsm/protocols/asa.py +++ b/discordgsm/protocols/asa.py @@ -68,7 +68,7 @@ async def query(self): return result except Exception: # EOS failed, fallback to BattleMetrics - pass + start = time.time() # Restart timer for BattleMetrics query # Fallback: Query BattleMetrics API by IP:port async with aiohttp.ClientSession() as session: