class MemoryMonitor(threading.Thread): """Checks when COH1 game has started/ended.""" def __init__( self, pollInterval=10, ircClient: IRC_Client = None, settings=None ): threading.Thread.__init__(self) try: logging.info("Memory Monitor Started!") self.running = True self.settings = settings if not settings: self.settings = Settings() self.pm = None self.baseAddress = None self.gameInProgress = False self.ircClient = ircClient self.pollInterval = int(pollInterval) self.event = threading.Event() self.gameData = None self.winLostTimer = None except Exception as e: logging.error("In FileMonitor __init__") logging.error(str(e)) logging.exception("Exception : ") def run(self): try: while self.running: self.get_gamedata() if self.gameInProgress: if self.gameData.gameInProgress != self.gameInProgress: # coh was running and now its not (game over) self.game_over() else: if self.gameData.gameInProgress != self.gameInProgress: # coh wasn't running and now it is (game started) self.game_started() # set local gameInProgress flag to it can be compared with # any changes to it in the next loop self.gameInProgress = self.gameData.gameInProgress self.event.wait(self.pollInterval) except Exception as e: logging.error("In FileMonitor run") logging.error(str(e)) logging.exception("Exception : ") def get_gamedata(self): try: self.gameData = GameData( ircClient=self.ircClient, settings=self.settings) self.gameData.get_data_from_game() except Exception as e: logging.error("In getGameData") logging.info(str(e)) logging.exception("Exception : ") def game_started(self): try: self.gameData.output_opponent_data() # legacy posting of data to opponent bot channel self.post_steam_number() # enable to output to the opponent bot channel self.post_data() # enable to output to the opponent bot channel self.start_bets() # enable to set odds self.set_odds() except Exception as e: logging.info("Problem in GameStarted") logging.error(str(e)) logging.exception("Exception : ") def post_steam_number(self): try: channel = str(self.settings.data.get('channel')) steamNumber = str(self.settings.data.get('steamNumber')) message = f"!setsteam,{channel},{steamNumber}" self.ircClient.send_message_to_opponentbot_channel(message) except Exception as e: logging.error("Problem in PostSteamNumber") logging.exception("Exception : ") logging.error(str(e)) def post_data(self): # Sending to cohopponentbot channel about game, # this requires parsing mapName First try: message = self.gameData.get_game_description_string() self.ircClient.send_message_to_opponentbot_channel(message) except Exception as e: logging.error("Problem in PostData") logging.error(str(e)) logging.exception("Exception : ") def game_over(self): try: # Get Win/Lose from server after 50 seconds # legacy can only be turned on by toggle in file # removed from GUI because server win / lose inconsistant # occasionally reports incorrectly therefore not worth using. # by default writeIWonLostInChat now set to False in file. if self.settings.data.get('writeIWonLostInChat'): self.winLostTimer = threading.Timer(50.0, self.get_win_lose) self.winLostTimer.start() # Clear the overlay if (self.settings.data.get('clearOverlayAfterGameOver')): self.ircClient.queue.put("CLEAROVERLAY") except Exception as e: logging.info("Problem in GameOver") logging.error(str(e)) logging.exception("Exception : ") def get_win_lose(self): try: # legacy method for info only # match history doesn't always get updated immediately. # unknown delay or faulty server means can report incorrectly. # therefore not used, code kept for reference only. statnumber = self.settings.data.get('steamNumber') statRequest = StatsRequest(settings=self.settings) statRequest.get_match_history_from_server(statnumber) mostRecentWin = statRequest.get_player_win_last_match(statnumber) if mostRecentWin: self.ircClient.send_private_message_to_IRC("!I won") else: self.ircClient.send_private_message_to_IRC("!I lost") except Exception as e: logging.info("Problem in GetWinLose") logging.error(str(e)) logging.exception("Exception : ") def start_bets(self): info = ( f"Size of self.gameData.playerList " f"in StartBets {len(self.gameData.playerList)}" ) logging.info(info) if (bool(self.settings.data.get('writePlaceYourBetsInChat'))): ps = "" outputList = [] if self.gameData: if self.gameData.playerList: if len(self.gameData.playerList) == 2: # if two player make sure the streamer is put first for player in self.gameData.playerList: output = player.name output += " " output += player.faction.name outputList.append(output) # player list does not have steam numbers. # Need to aquire these from warning.log ps = f"{outputList[1]} Vs. {outputList[0]}" pls = self.gameData.playerList[0].stats if pls: sn = str(self.settings.data.get('steamNumber')) psn = str(pls.steamNumber) if sn == psn: ps = f"{outputList[0]} Vs. {outputList[1]}" message = "!startbets {}".format(ps) self.ircClient.send_private_message_to_IRC(message) def set_odds(self): if (bool(self.settings.data.get('automaticSetBettingOdds'))): if self.gameData: if self.gameData.playerList: odds = 0.5 contenderTeamRatio = 0 opponentTeamRatio = 0 axisTeamList = [] alliesTeamList = [] for item in self.gameData.playerList: if ( str(item.faction) == str(Faction.US) or str(item.faction) == str(Faction.CW) ): if item.name != "": alliesTeamList.append(item) if ( str(item.faction) == str(Faction.WM) or str(item.faction) == str(Faction.PE) ): if item.name != "": axisTeamList.append(item) team1List = [] team2List = [] # by default player team is allies unless # the player is steam number is present in # the axisTeamList team1List = alliesTeamList team2List = axisTeamList for item in axisTeamList: if item.stats: steamNumber = str( self.settings.data.get('steamNumber')) if steamNumber == str( item.stats.steamNumber): # logging.info ("Player team is AXIS") team1List = axisTeamList team2List = alliesTeamList # The contender is now in team1List for player in team1List: for value in player.stats.leaderboardData: matchType = str( player.stats.leaderboardData[value].matchType) if matchType == str(self.gameData.matchType): faction = str( player.stats.leaderboardData[ value].faction) if faction == str(player.faction): contenderTeamRatio += ( player.stats.leaderboardData[ value].winLossRatio) for player in team2List: for value in player.stats.leaderboardData: matchType = str( player.stats.leaderboardData[value].matchType) if matchType == str(self.gameData.matchType): faction = str( player.stats.leaderboardData[ value].faction) if faction == str(player.faction): opponentTeamRatio += ( player.stats.leaderboardData[ value].winLossRatio) try: if opponentTeamRatio: odds = (contenderTeamRatio / ( contenderTeamRatio + opponentTeamRatio)) else: odds = 0.5 except Exception as e: odds = 0.5 logging.error(str(e)) logging.exception("Exception : ") message = f"!setodds {str(round(odds, 2))}" self.ircClient.send_private_message_to_IRC(message) def close(self): logging.info("Memory Monitor Closing!") self.running = False # break out of loops if waiting if self.event: self.event.set() # if timer is running and program is closed then cancel the timer # and call getwinlose early. legacy timer. if self.winLostTimer: self.winLostTimer.cancel() # self.GetWinLose() def find_between(self, s, first, last): try: start = s.index(first) + len(first) end = s.index(last, start) return s[start:end] except ValueError: return ""
class IRC_Channel(threading.Thread): """Implements an IRC Channel Connection. Checks User Commands.""" def __init__( self, ircClient, ircSocket: socket, queue, channel, settings=None ): Thread.__init__(self) self.ircClient = ircClient self.running = True self.ircSocket = ircSocket self.queue = queue self.channel = channel self.settings = settings if not settings: self.settings = Settings() self.gameData = GameData(self.ircClient, settings=self.settings) def run(self): self.ircSocket.send(('JOIN ' + self.channel + '\r\n').encode("utf8")) while self.running: line = self.queue.get() line = str.rstrip(line) line = str.split(line) if (line[0] == "EXITTHREAD"): self.close() if (line[0] == "OPPONENT"): self.check_for_user_command("self", "opp") if (line[0] == "TEST"): self.test_output() if (line[0] == "IWON"): self.ircClient.send_private_message_to_IRC("!i won") if (line[0] == "ILOST"): self.ircClient.send_private_message_to_IRC("!i lost") if (line[0] == "CLEAROVERLAY"): GameData.clear_overlay_HTML() if ( len(line) >= 4 and "PRIVMSG" == line[2] and "jtv" not in line[0] ): # call function to handle user message self.user_message(line) def user_message(self, line): """Processes IRC returned raw string data.""" # Dissect out the useful parts of the raw data line # into username and message and remove certain characters msgFirst = line[1] msgUserName = msgFirst[1:] msgUserName = msgUserName.split("!")[0] # msgType = line [1]; # msgChannel = line [3] msgMessage = " ".join(line[4:]) msgMessage = msgMessage[1:] messageString = str(msgUserName) + " : " + str(msgMessage) logging.info(str(messageString).encode('utf8')) # Check for UserCommands self.check_for_user_command(msgUserName, msgMessage) if ( msgMessage == "exit" and msgUserName == self.ircClient.adminUserName ): self.ircClient.send_private_message_to_IRC("Exiting") self.close() def check_for_user_command(self, userName, message): """Performs string comparisons for hardcoded commands.""" logging.info("Checking For User Comamnd") try: if ( bool(re.match(r"^(!)?opponent(\?)?$", message.lower())) or bool(re.match(r"^(!)?place your bets$", message.lower())) or bool(re.match(r"^(!)?opp(\?)?$", message.lower())) ): self.gameData = GameData( ircClient=self.ircClient, settings=self.settings ) if self.gameData.get_data_from_game(): self.gameData.output_opponent_data() else: self.ircClient.send_private_message_to_IRC( "Can't find the opponent right now." ) user = str(userName).lower() admin = str(self.settings.privatedata.get('adminUserName')) channel = str(self.settings.data.get('channel')) if ( message.lower() == "test" and ( user == admin.lower() or user == channel.lower()) ): self.ircClient.send_private_message_to_IRC( "I'm here! Pls give me mod to prevent twitch" " from autobanning me for spam if I have to send" " a few messages quickly." ) self.ircClient.output.insert( END, f"Oh hi again, I heard you in the {self.channel[1:]}" " channel.\n" ) if (bool(re.match(r"^(!)?gameinfo(\?)?$", message.lower()))): self.game_info() if (bool(re.match(r"^(!)?story(\?)?$", message.lower()))): self.story() if (bool(re.match(r"^(!)?debug(\?)?$", message.lower()))): self.print_info_to_debug() except Exception as e: logging.error("Problem in CheckForUserCommand") logging.error(str(e)) logging.exception("Exception : ") def print_info_to_debug(self): """Outputs gameData to the log file.""" try: self.gameData = GameData( self.ircClient, settings=self.settings ) self.gameData.get_data_from_game() self.gameData.get_mapDescriptionFull_from_UCS_file() self.gameData.get_mapNameFull_from_UCS_file() logging.info(self.gameData) self.ircClient.send_private_message_to_IRC( "GameData saved to log file." ) except Exception as e: logging.error("Problem in PrintInfoToDebug") logging.error(str(e)) logging.exception("Exception : ") def game_info(self): """Outputs a summary of the current game to IRC.""" self.gameData = GameData(self.ircClient, settings=self.settings) if self.gameData.get_data_from_game(): self.ircClient.send_private_message_to_IRC( f"Map : {self.gameData.mapNameFull}," f" High Resources : {self.gameData.highResources}," f" Automatch : {self.gameData.automatch}," f" Slots : {self.gameData.slots}," f" Players : {self.gameData.numberOfPlayers}." ) def story(self): """Outputs the game description to IRC.""" self.gameData = GameData(self.ircClient, settings=self.settings) logging.info(str(self.gameData)) if self.gameData.get_data_from_game(): logging.info(str(self.gameData)) # Requires parsing the map description from # the UCS file this takes time so must be done first self.gameData.get_mapDescriptionFull_from_UCS_file() self.ircClient.send_private_message_to_IRC( "{}.".format(self.gameData.mapDescriptionFull)) def test_output(self): """Outputs information general on pressing test button.""" if not self.gameData: self.gameData = GameData( self.ircClient, settings=self.settings ) self.gameData.get_data_from_game() self.gameData.test_output() def close(self): """Closes the IRC channel.""" self.running = False logging.info("Closing Channel " + str(self.channel) + " thread.")