From 60ad27126bbd5d9cff6bfcea0678df9ba6e99bf0 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Fri, 6 Dec 2024 12:23:15 +0000 Subject: [PATCH] console reader and player join alert --- bot.py | 2 ++ cogs/console.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 3 files changed, 96 insertions(+) create mode 100644 cogs/console.py diff --git a/bot.py b/bot.py index a373103..464050c 100644 --- a/bot.py +++ b/bot.py @@ -27,10 +27,12 @@ class DiscordBot(commands.Bot): Represents a Discord bot. Contains controls to interact with the bot via the Discord API. """ + in_game_channel_id: int rcon_details: dict def __init__(self): super().__init__(command_prefix="-", intents=Intents.all()) + self.in_game_channel_id = int(getenv("SPIFFO__DISCORD_CHANNEL_ID")) self.rcon_details = { "host": getenv("SPIFFO__RCON_HOST"), "port": getenv("SPIFFO__RCON_PORT"), diff --git a/cogs/console.py b/cogs/console.py new file mode 100644 index 0000000..0f8546e --- /dev/null +++ b/cogs/console.py @@ -0,0 +1,93 @@ +""" +Reads and handles updates in the server console file. +""" + +import re +import logging +from pathlib import Path + +import aiofiles +from discord.ext import commands, tasks + +CONSOLE_FILE_PATH = Path(__file__).parent.parent / "data" / "server-console.txt" +log = logging.getLogger(__name__) + + +class ConsoleCog(commands.Cog): + """ + Reads and handles the server-console.txt file. + """ + _last_line_number = 0 + + def __init__(self, bot: commands.Bot): + self.bot = bot + self.monitor_console.start() + + @tasks.loop(seconds=1) + async def monitor_console(self): + """ + Check the latest version of the console log file. + """ + + if not CONSOLE_FILE_PATH.exists(): + self.monitor_console.cancel() + raise FileNotFoundError("Server console file doesn't exist, task cancelled.") + + async with aiofiles.open(CONSOLE_FILE_PATH, "r", encoding="utf-8") as file: + await file.seek(self._last_line_number) + lines = await file.readlines() + if not lines: + log.debug("no new lines to read") + return + + for line in lines: + await self.process_console_line(line.strip()) + + self._last_line_number = await file.tell() + + async def process_console_line(self, line: str): + if "CheckModsNeedUpdate" in line: + await self.handle_mod_needs_update(line) + + elif "ConnectionManager: [fully-connected]" in line: + await self.handle_player_joined(line) + + elif "ConnectionManager: [disconnect]" in line: + await self.handle_player_left(line) + + async def handle_mod_needs_update(self, line: str): + log.debug("mod update instruction: %s", line) + + async def handle_player_joined(self, line: str): + # example + # ConnectionManager: [fully-connected] "" connection: guid=1733487070761473 ip=192.168.1.23 steam-id=76561198202697217 access=admin username="corbz" connection-type="UDPRakNet" + re_pattern = r"guid=(\d+) ip=([\d\.]+) steam-id=(\d+) access=(\w+) username=\"([^\"]+)\" connection-type=\"([^\"]+)\"" + re_match = re.search(re_pattern, line) + if not re_match: + log.warning("failed to parse player data: %s", line) + return + + data = { + "guid": re_match.group(1), + "ip": re_match.group(2), + "steam_id": re_match.group(3), + "access": re_match.group(4), + "username": re_match.group(5), + "connection_type": re_match.group(6), + } + + channel = self.bot.get_channel(self.bot.in_game_channel_id) + channel = await self.bot.fetch_channel(self.bot.in_game_channel_id) if not channel else channel + + await channel.send(content=f"Player Joined: **{data['username']}**") + + async def handle_player_left(self, line: str): + # example + # ConnectionManager: [disconnect] "receive-disconnect" connection: guid=1733487070761473 ip=192.168.1.23 steam-id=76561198202697217 access=admin username="corbz" connection-type="Disconnected" + pass + + +async def setup(bot: commands.Bot): + cog = ConsoleCog(bot) + await bot.add_cog(cog) + log.info("Added %s cog", cog.__class__.__name__) diff --git a/requirements.txt b/requirements.txt index 5e50184..5391963 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +aiofiles==24.1.0 aiohappyeyeballs==2.4.4 aiohttp==3.11.9 aiosignal==1.3.1