""" Handles tasks related to in-game players, such as connect/disconnect alerts. """ import re import logging from os import getenv from pathlib import Path from datetime import datetime from discord import Colour from discord.ext import commands, tasks from utils.reader import LogFileReader from utils.models import Player ZOMBOID_FOLDER_PATH = Path(getenv("SPIFFO__ZOMBOID_FOLDER_PATH")) LOGS_FOLDER_PATH = ZOMBOID_FOLDER_PATH / "Logs" USER_LOG_FILE_PATH = None for path in LOGS_FOLDER_PATH.iterdir(): if path.name.endswith("_user.txt"): USER_LOG_FILE_PATH = path break log = logging.getLogger(__name__) class PlayersCog(commands.Cog): """ Handles tasks related to in-game players. """ file_handler: LogFileReader def __init__(self, bot: commands.Bot): self.bot = bot self.file_handler = LogFileReader(USER_LOG_FILE_PATH) self.listen_for_changes.start() @tasks.loop(seconds=3) async def listen_for_changes(self): log.debug("listening for changes") for line in await self.file_handler.read(): await self.process_log_line(line) async def process_log_line(self, line: str): log.debug("processing log line") if "died" in line: await self.process_player_death(line) elif "fully connected" in line: await self.process_connected_player(line) elif "disconnected player" in line: await self.process_disconnected_player(line) async def process_player_death(self, line: str): log.debug("processing player death") re_pattern = r"\[(?P[\d\- :\.]+)\] user (?P.+?) died at \((?P\d+),(?P\d+),(?P\d+)\) \((?P.+?)\)" re_match = re.search(re_pattern, line) if not re_match: log.warning("failed to parse player death log: %s", line) return username = re_match.group("username") player = await Player.get_or_none(username=username) if not player: log.warning("Player returned none, cannot add death: %s", username) return await player.add_death( coord_x=re_match.group("x"), coord_y=re_match.group("y"), coord_z=re_match.group("z"), cause=re_match.group("cause"), timestamp=datetime.strptime( re_match.group("timestamp"), "%m-%d-%y %H:%M:%S.%f" ) ) await player.save() channel = self.bot.get_channel(self.bot.in_game_channel_id) channel = channel or await self.bot.fetch_channel(self.bot.in_game_channel_id) embed = await player.get_embed() embed.title = "Player Has Died" embed.colour = Colour.dark_orange() await channel.send(embed=embed) log.debug("successfully registered player death to %s", re_match.group("username")) async def process_connected_player(self, line: str): """ """ log.debug("processing connected player") re_pattern = r'\[(?P.*?)\] (?P\d+) "(?P.*?)" fully connected \((?P\d+,\d+,\d+)\)' re_match = re.search(re_pattern, line) if not re_match: log.warning("Failed to parse player data: %s", line) return player, created = await Player.get_or_create(username=re_match.group("username")) player.last_connection = datetime.strptime( re_match.group("timestamp"), "%m-%d-%y %H:%M:%S.%f" ) await player.update_steam_summary(re_match.group("steam_id"), self.bot.steam_api_key) await player.save() # This connection method is called when the player respawns if player.is_dead: player.is_dead = False # player must be alive if fully connected await player.save() return channel = self.bot.get_channel(self.bot.in_game_channel_id) channel = channel or await self.bot.fetch_channel(self.bot.in_game_channel_id) embed = await player.get_embed() embed.title = "Player Has Connected" embed.colour = Colour.brand_green() await channel.send(embed=embed) async def process_disconnected_player(self, line: str): """ """ log.debug("processing disconnected player") re_pattern = r'\[(?P.*?)\] (?P\d+) "(?P.*?)" disconnected player \((?P\d+,\d+,\d+)\)' re_match = re.search(re_pattern, line) if not re_match: log.warning("Failed to parse player data: %s", line) return player, created = await Player.get_or_create(username=re_match.group("username")) player.last_disconnection = datetime.strptime( re_match.group("timestamp"), "%m-%d-%y %H:%M:%S.%f" ) await player.update_steam_summary(re_match.group("steam_id"), self.bot.steam_api_key) await player.save() if player.is_dead: return channel = self.bot.get_channel(self.bot.in_game_channel_id) channel = channel or await self.bot.fetch_channel(self.bot.in_game_channel_id) embed = await player.get_embed() embed.title = "Player Has Disconnected" embed.colour = Colour.brand_red() await channel.send(embed=embed) async def setup(bot: commands.Bot): cog = PlayersCog(bot) await bot.add_cog(cog) log.info("Added %s cog", cog.__class__.__name__)