def update_achievement(self, player, ach): """Update achievement given achievement string and player object""" if not player: say("[ERROR] failed achievement: invalid player.") sys.exit(1) return False timestamp = str(datetime.datetime.now().year) + \ "-" + str(datetime.datetime.now().month) + \ "-" + str(datetime.datetime.now().day) if ach == "haxx0r": if not player.a_haxx0r == "": return False player.a_haxx0r = a_best(timestamp, player.a_haxx0r) elif ach == "blazeit": if not player.a_blazeit == "": return False player.a_blazeit = a_best(timestamp, player.a_blazeit) elif ach == "satan": if not player.a_satan == "": return False player.a_satan = a_best(timestamp, player.a_satan) elif ach == "virgin": if not player.a_virgin == "": return False player.a_virgin = a_best(timestamp, player.a_virgin) else: say("[WARNING] unknown achievement '" + str(ach) + "'") return False return True
def handle_flag_cap(self, data): """0.7 flag cap""" if self.settings.get("tw_version")[0:3] != "0.7": return # old 0.7 # flag_capture player='0:ChillerDragon' team=0 # new 0.7 # flag_capture player='0:ChillerDragon' team=0 time=6.32 # flag_capture player='0:ChillerDragon' team=0 time=0.72 # flag_capture player='0:ChillerDragon' team=0 time=1.30 # flag_capture player='0:ChillerDragon' team=0 time=113.94 data = data[:-1] match = re.match( r"^\[game\]: flag_capture player='(?P<id>-?\d{1,2}):(?P<name>.*)' " r"team=(?P<team>-?\d{1,2}) time=(?P<time>\d+\.\d{2})$", data) if not match: if self.settings.get("debug"): say("[WARNING] flag time not found. Please update to newer version of teeworlds." ) return if self.settings.get("debug"): say("[DEBUG] flag cap in '" + match.group("time") + "' seconds.") player = self.players_controller.get_player_by_id(match.group("id")) flag_color = "red" if player.team == "red": flag_color = "blue" self.__handle_flag_cap(player, match.group("time"), flag_color)
def get_rank_player(self, msg, rank_cmd): """Parse command and return player object""" if self.settings.get("stats_mode") != "sql": say("not supported in file stats mode") return None, None msg_normal = msg msg = msg.lower() id_str = self.get_chat_id(msg_normal) rankname_start = -1 if msg.find(rank_cmd + " ") != -1: cmd_end = msg.rfind(rank_cmd) rankname_start = msg.find(rank_cmd + " ", cmd_end) + len(rank_cmd + " ") rankname_end = len(msg) - 1 # cut off newline rankname = msg_normal[rankname_start:rankname_end] if not rankname or rankname == "" or rankname_start == -1: return self.players_controller.get_player_by_id(id_str), id_str argplayer = self.players_controller.get_player_by_name(rankname) if not argplayer: # try to find id prefix in argument name pattern = r'(\d{1,2}):(.*)' if self.settings.get("tw_version") == "ddnet": # F-DDrace 128 slots pattern = r'(\d{1,3}):(.*)' match = re.match(pattern, rankname) if match: r_id = match.group(1) r_name = match.group(2) r_player = self.players_controller.get_player_by_id(r_id) if r_player and r_player.name == r_name: argplayer = r_player return argplayer, rankname
def update_player_deaths(self, player, killer, deaths): """bundle all the logic that happens on a death""" if not player: return False player.deaths += deaths # only activate killingsprees on 8+ players if self.count_players() > self.settings.get("spree_players"): if player.killingspree > 9 and self.settings.get("show_sprees") == 1: broadcast( "'" + player.name + "'s killing spree with " + str(player.killingspree) + " kills was ended by '" + killer + "'" ) if player.killingspree > player.best_spree and self.settings.get("show_sprees") == 1: if player.killingspree > 9: say( "'" + player.name + "' new killingspree record! Old: " + str(player.best_spree) + " New: " + str(player.killingspree) ) player.best_spree = player.killingspree save_stats_partially(player) player.killingspree = 0 return True
def process_multi_kills(self, player, weapon): """Check if a kill is a multikill""" now = base.generic.get_timestamp() diff = now - player.last_kill if diff > 300000000: return now if player.last_kill == player.last_multi_kill: player.current_multi += 1 if player.current_multi > 32: player.current_multi = 2 # after Duotriguple start from double agian else: player.current_multi = 2 player.is_combo_multi = False if player.last_kill_weapon != weapon: player.is_combo_multi = True weapon_str = self.game_controller.WEAPONS[weapon] if player.is_combo_multi: weapon_str = "combo" if self.settings.get("show_combos") == 1: say( "'" + player.name + \ "' did a " + weapon_str + " " + \ self.game_controller.MULTIS[player.current_multi] + " kill!" ) player.double_kills[weapon] += 1 player.last_multi_kill = now return now
def load_stats_file(name: str): """Return player object given a name""" if not hash_stats(name): return None try: with open(stats_path(name), "r", encoding='UTF-8') as stats_file: player = Player(name) player.kills = int(stats_file.readline()) player.deaths = int(stats_file.readline()) player.flag_grabs = int(stats_file.readline()) player.flag_caps_red = int(stats_file.readline()) player.flag_caps_blue = int(stats_file.readline()) player.flag_time = float(stats_file.readline()) player.flagger_kills = int(stats_file.readline()) player.best_spree = int(stats_file.readline()) player.wins = int(stats_file.readline()) player.looses = int(stats_file.readline()) player.a_haxx0r = str(stats_file.readline()) player.a_blazeit = str(stats_file.readline()) player.a_satan = str(stats_file.readline()) player.a_virgin = str(stats_file.readline()) stats_file.close() return player except OSError: say("[ERROR] (load) failed to loaded stats for name='" + name + "' filename='" + stats_path(name) + "'") sys.exit(1) return None
def spam_protection(self, msg): """Takes a message and mutes the author if it is spam""" player = self.get_spam_player(msg) if not player: if self.settings.get("hotplug") == 1: return False say("[ERROR] spam_protection() failed! please contact an admin") sys.exit(1) now = datetime.datetime.now() diff = now - player.last_chat player.last_chat = now #say("chat diff seconds: " + str(diff.seconds) + " last_chat: " + str(player.last_chat)) seconds = diff.seconds if seconds < 15: player.mute_score += 1 if player.mute_score > 5: if not player.is_muted: player.is_muted = True say("'" + str(player.name) + "' is banned from the command system (spam)") if seconds > 120: player.is_muted = False player.mute_score = 0 if player.is_muted: return True return False
def create_player(self, name, cid=-1, ip_addr="", team="", show_stats=True, spree=0): """Get player object from init_player and append it to the player list""" global CONNECTED_PLAYERS player = self.init_player(name, cid, ip_addr, team, show_stats, spree) if not player: say("[ERROR] CreatePlayer init_player=None name='" + str(name) + "' id=" + str(cid)) sys.exit(1) CONNECTED_PLAYERS.append(player)
def admin_contact_msg(self): """Display the admin contact message in chat""" if str(self.settings.get("admin_contact")) == "": return say( "[INFO] Contact the admin " + \ str(self.settings.get("admin_contact")) + \ " to report players." )
def handle_kills_07_and_ddnet(self, data): """Handle kill messages in ddnet or 0.7 format""" if self.settings.get("tw_version") == "ddnet": # [game]: kill killer='5:chiller' victim='5:chiller' # weapon=-3 special=0 killer_team:0 victim_team:0 # [game]: kill killer='6:chiller.*' victim='5:chiller' # weapon=-2 special=0 killer_team:0 victim_team:0 # F-DDrace has 128 and thus ids can be up to 3 digits match = re.match( r"^\[game\]: kill killer='(?P<k_id>-?\d{1,3}):(?P<k_name>.*)' " r"victim='(?P<v_id>-?\d{1,3}):(?P<v_name>.+?)' " r"weapon=(?P<weapon>-?\d) " r"special=(\d) killer_team:(?P<k_team>-?\d{1,3}) " r"victim_team:(?P<v_team>-?\d{1,3})$", data) else: # teeworlds 0.7 # id:team id:team # [game]: kill killer='0:0:nameless tee' victim='0:0:nameless tee' weapon=-1 special=0 # [game]: kill killer='-2:1:' victim='0:0:ChillerDragon' weapon=3 special=0 # use .* for killer but .+? for victim # because killer can be empty if it left the server before the projectile hit match = re.match( r"^\[game\]: kill killer='(?P<k_id>-?\d{1,2}):" r"(?P<k_team>-?\d{1,2}):(?P<k_name>.*)' " r"victim='(?P<v_id>-?\d{1,2}):(?P<v_team>-?\d{1,2}):(?P<v_name>.+?)' " r"weapon=(?P<weapon>-?\d) special=(\d)$", data) if match: if self.settings.get("debug"): say("KILLER ID=<%s> TEAM=<%s> NAME=<%s> " "VICTIM ID=<%s> TEAM=<%s> NAME=<%s> weapon=<%s>" % (match.group("k_id"), match.group("k_team"), match.group("k_name"), match.group("v_id"), match.group("v_team"), match.group("v_name"), match.group("weapon"))) if match.group("k_name") == "" or int(match.group("k_id")) < 0: killer_name = "a left player" killer_id = match.group("k_id") killer = None else: killer_name = match.group("k_name") killer_id = match.group("k_id") killer = self.players_controller.get_player_by_id(killer_id) victim_name = match.group("v_name") victim_id = match.group("v_id") victim = self.players_controller.get_player_by_id(victim_id) weapon = match.group("weapon") else: say("[ERROR] failed parsing kill msg: " + str(data)) sys.exit(1) return Kill(Player(killer, killer_name, killer_id), Player(victim, victim_name, victim_id), weapon)
def team_won(self, team): """Update wins and loses for all players""" global CONNECTED_PLAYERS if not team == "red" and not team == "blue": say("[WARNING] invalid team won " + str(team)) for player in CONNECTED_PLAYERS: if player.team == team: player.wins += 1 elif not player.team == "" and not player.team == "spectator": player.looses += 1
def handle_player_team(self, data): """Parse 'team_join' message""" global CONNECTED_PLAYERS id_start = data.find("'") + 1 id_end = base.generic.cfind(data, ":", 2) id_str = data[id_start:id_end] player = self.get_player_by_id(id_str) if player is None: if self.settings.get("hotplug") == 1: return say("[ERROR] teamchange failed id=" + str(id_str) + " data=" + str(data)) self.debug_player_list() sys.exit(1) team = "invalid" data_end = data[-5:] change = data_end.rfind(">") if change != -1: team = data_end[change + 1:] else: team = str(data[data.rfind("=") + 1:]) if team == "0": player.team = "red" elif team == "1": player.team = "blue" elif team == "-1": player.team = "spectator" else: say("[ERROR] invalid team=" + str(team)) sys.exit(1) name_start = base.generic.cfind(data, ":", 2) + 1 name_end = data.rfind("'") name = data[name_start:name_end] if player.name is None: # player just joined and still has to be loaded self.delete_player(player.cid) # delete invalid tmp player self.create_player(name, player.cid, player.ip_addr, player.team) locked = locked_names.get_instance() if not locked.check(name, player.ip_addr): rcon_exec("kick " + str(player.cid) + " please change name") elif player.name != name: # https://github.com/chillavanilla/TeeworldsEconMod/issues/49 # it is very rare but possible that one joins without name # the during join the placeholder (connecting) is shown in the logs # but later the actual name is used if player.name == "(connecting)": say("[WARNING] untracked namechange from '" + player.name + "' to '" + name + "'") player.name = name else: say("[ERROR] untracked namechange from '" + player.name + "' to '" + name + "'") say('[ERROR] data=' + data) sys.exit(1)
def is_muted(self, msg): """Take a message and return of the message author is muted or not""" player = self.get_spam_player(msg) if not player: if self.settings.get("hotplug") == 1: return False say("[WARNING] is_muted() failed! please contact an admin") return False if player.is_muted: return True return False
def print_stats_all(self, debug=False): """Print stats of all players in chat""" global CONNECTED_PLAYERS if debug: say("Kills/Deaths/Spree Grabs/RedCaps/BlueCaps/CapTime/FlaggerKills") for player in CONNECTED_PLAYERS: say( "'" + player.name + "' k/d/s: " + str(player.kills) + "/" + str(player.deaths) + "/" + str(player.best_spree) + " flag g" + str(player.flag_grabs) + "/r" + str(player.flag_caps_red) + "/b" + str(player.flag_caps_blue) + "/t" + str(player.flag_time) + "/k" + str(player.flagger_kills)) #say("debug is_flagger: " + str(player.is_flagger)) else: say("=== stats for all players ===") for player in CONNECTED_PLAYERS: say( "'" + player.name + "' k/d: " + str(player.kills) + "/" + str(player.deaths) + " spree: " + str(player.best_spree) + " flags: " + str(player.flag_caps_red + player.flag_caps_blue) + " fastest cap: " + str(player.flag_time))
def update_player_flag_caps(self, player, color, caps): """Update flag cap stats""" if not player: say("[ERROR] failed player.update_player_flag_caps: invalid player.") sys.exit(1) return False if self.count_players() <= self.settings.get("flag_players"): return False if color == "blue": player.flag_caps_blue += caps elif color == "red": player.flag_caps_red += caps else: say("savage '" + player.name + "' captured the pink flag") return False return True
def set_flagger(self, player, is_flag, timestamp=""): """Update flagger attribute of player object""" if not player: if is_flag: if self.settings.get("hotplug") == 1: return say("[ERROR] set flagger failed: invalid player.") sys.exit(1) return False if is_flag and timestamp == "": say("[ERROR] set flagger failed: empty timestamp.") sys.exit(1) return False player.is_flagger = is_flag player.grab_timestamp = timestamp return True
def get_rank_name(self, msg, rank_cmd): """Parse message and return playername""" if self.settings.get("stats_mode") != "sql": say("not supported in file stats mode") return None msg_normal = msg msg = msg.lower() name_start = base.generic.cfind(msg, ":", 3) + 1 name_end = msg.find(rank_cmd, name_start) name_end = msg.rfind(": ", name_end) name = msg_normal[name_start:name_end] rankname_start = -1 if msg.find(rank_cmd + " ") != -1: rankname_start = msg.find(rank_cmd + " ", name_end) + len(rank_cmd + " ") rankname_end = len(msg) - 1 # cut off newline rankname = msg_normal[rankname_start:rankname_end] if not rankname or rankname == "" or rankname_start == -1: return name return rankname
def __handle_flag_cap(self, player_obj, time, flag_color): """called by 0.6 and 0.7 parser""" if not player_obj: if self.settings.get("hotplug") == 1: return say("[ERROR] flag capture error: player is invalid.") sys.exit(1) if flag_color == player_obj.team: say("[ERROR] flag capture error: flag color matches team color.") say(" team=" + player_obj.team) say(" flag=" + flag_color) sys.exit(1) name = player_obj.name self.achievements_controller.check_flag(player_obj, time) self.players_controller.update_player_flag_time(player_obj, time) self.players_controller.set_flagger(player_obj, False) self.players_controller.update_player_flag_caps( player_obj, flag_color, 1) if self.settings.get("debug"): say("[DEBUG] flag cap '" + name + "' in '" + str(time) + "' secs color: '" + flag_color + "'")
def handle_name_change(self, data): """Parse 'changed name to' chat message""" old_start = data.find("'") + 1 old_end = data.find("' changed name to '") old = data[old_start:old_end] new_start = old_end + len("' changed name to '") new_end = data.rfind("'") new = data[new_start:new_end] team = "" player = self.get_player_by_name(old) if not player: if self.settings.get("hotplug") == 1: return say("[ERROR] name_change player not found name=" + str(old)) sys.exit(1) team = player.team self.save_and_delete_player_by_name(old) self.create_player(new, player.cid, player.ip_addr, team=team) locked = locked_names.get_instance() if not locked.check(new, player.ip_addr): rcon_exec("kick " + str(player.cid) + " please change name")
def handle_call_vote(self, data): """Recive vote log line and parse it""" # TODO: use regex here to avoid false positives if data.find("' voted option '") != -1: if self.settings.get("debug"): say("[VOTES] skip force on option vote") return if self.settings.get("votes_force") != 0: if self.settings.get("votes_blocked_reasons") is None: rcon_exec("vote no") else: match = re.match( r'.*server\]: \'.+\' voted (spectate|kick) \'.+\' ' r'reason=\'(.+)\' cmd=\'.*', data) if match: reason = match.group(2) if self.is_blocked_reason(reason): rcon_exec("vote no") say("[ANTI-FUNVOTE] please provide a better reason.") else: say("[WARNING] Vote parsing error. Please contact a admin.") if self.settings.get("votes_discord") != 0: self.chat_controller.admin_contact_msg() if self.settings.get("votes_discord") == 2: send_discord("vote called " + str(self.settings.get("mod_discord")) + "!\n" + str(data[:data.find(" cmd='ban")]))
def check_flag(self, player_obj, time): """Check if the flag time is an achievement""" if not player_obj: if self.settings.get("hotplug") == 1: return say("[ERROR] check flag failed: invalid player.") sys.exit(1) name = player_obj.name if str(time) == "13.37": if self.players_controller.update_achievement( player_obj, "haxx0r"): say("[achievement] '" + str(name) + "' unlocked: haxx0r") elif str(time) == "4.20": if self.players_controller.update_achievement( player_obj, "blazeit"): say("[achievement] '" + str(name) + "' unlocked: blaze it") elif str(time) == "6.66": if self.players_controller.update_achievement(player_obj, "satan"): say("[achievement] '" + str(name) + "' unlocked: satan") elif str(time) == "6.90": if self.players_controller.update_achievement( player_obj, "virgin"): say("[achievement] '" + str(name) + "' unlocked: virgin")
def handle_game(self, timestamp, data): """Parse game messages""" if data.find("kill killer") != -1: self.kills_controller.handle_kills(timestamp, data) elif data.startswith("[game]: start round type='"): global CAPS_RED global CAPS_BLUE # [game]: start round type='CTF' teamplay='1' say("[SERVER] ChillerDragon wishes you all hf & gl c:") self.players_controller.refresh_all_players() if data.startswith("[game]: start round type='CTF'"): if CAPS_RED == 0 and CAPS_BLUE == 0: # already catched by 10 flags auto detection return if CAPS_RED > CAPS_BLUE: self.update_wins(True) elif CAPS_RED < CAPS_BLUE: self.update_wins(False) else: say("draw lul") elif data.startswith("[game]: flag_grab player='"): self.flags_controller.handle_flag_grab(timestamp, data) # [2019-10-15 11:41:04][game]: flag_capture player='0:ChillerDragon' team=0 elif data.startswith("[game]: flag_capture player='"): self.flags_controller.handle_flag_cap(data)
def update_player_flag_time(self, player, time): """Update flag time stats""" if not player: say("[ERROR] failed player.update_player_flag_time: invalid player." ) sys.exit(1) time = float(time) try: # TypeError: unorderable types: float() < str() if time < float(player.flag_time): diff = player.flag_time - time diff = float("{0:.2f}".format(diff)) say("[FastCap] '" + player.name + "' " + str(diff) + " seconds faster") player.flag_time = time elif int(player.flag_time) == 0: player.flag_time = time return True except TypeError as error: say("[ERROR] error calculating flag time (" + str(time) + ") and (" + str(player.flag_time) + ")") say(error) sys.exit(1)
def save_stats_partially_file(player: Player) -> bool: """Save only killingspree""" if not player: say("[stats] (partially) failed to load player.") return False name = player.name if hash_stats(name): #say("[stats] found stats --> loading and appending") load_player = load_stats_file(name) if not load_player: say( "[stats] (partially) error loading stats for player='" + \ name + \ "' filename='" + \ stats_path(name) + "'" ) sys.exit(1) player = player + load_player try: with open(stats_path(name), "w", encoding='UTF-8') as stats_file: stats_file.write("0" + "\n") stats_file.write("0" + "\n") stats_file.write("0" + "\n") stats_file.write("0" + "\n") stats_file.write("0" + "\n") stats_file.write("0.0" + "\n") stats_file.write("0" + "\n") stats_file.write(str(player.best_spree) + "\n") stats_file.write("0" + "\n") stats_file.write("0" + "\n") stats_file.write("" + "\n") stats_file.write("" + "\n") stats_file.write("" + "\n") stats_file.write("" + "\n") stats_file.close() return True except OSError: say( "[stats] (partially) error saving stats for player='" + \ name + "' filename='" + stats_path(name) + "'") sys.exit(1)
def save_stats_file(player: Player): """Save stats to file given a player object""" if not player: say("[stats] failed to save player.") return False name = player.name if hash_stats(name): #say("[stats] found stats --> loading and appending") load_player = load_stats_file(name) if not load_player: say( "[stats] (save) error loading stats for player='" + \ name + "' filename='" + \ stats_path(name) + "'" ) sys.exit(1) player = player + load_player try: with open(stats_path(name), "w", encoding='UTF-8') as stats_file: stats_file.write(str(player.kills) + "\n") stats_file.write(str(player.deaths) + "\n") stats_file.write(str(player.flag_grabs) + "\n") stats_file.write(str(player.flag_caps_red) + "\n") stats_file.write(str(player.flag_caps_blue) + "\n") stats_file.write(str(player.flag_time) + "\n") stats_file.write(str(player.flagger_kills) + "\n") stats_file.write(str(player.best_spree) + "\n") stats_file.write(str(player.wins) + "\n") stats_file.write(str(player.looses) + "\n") stats_file.write(str(player.a_haxx0r) + "\n") stats_file.write(str(player.a_blazeit) + "\n") stats_file.write(str(player.a_satan) + "\n") stats_file.write(str(player.a_virgin) + "\n") stats_file.close() return True except OSError: say( "[stats] (save) error saving stats for player='" + \ name + "' filename='" + stats_path(name) + "'") sys.exit(1)
def handle_flag_grab(self, timestamp, data): """parse flag grab message""" id_start = data.find("'", 10) + 1 id_end = base.generic.cfind(data, ":", 2) id_str = data[id_start:id_end] player = self.players_controller.get_player_by_id(id_str) if not player: if self.settings.get("hotplug") == 1: return say("[ERROR] flag_grab player not found ID=" + str(id_str)) self.players_controller.debug_player_list() sys.exit(1) name_start = data.find(":", 10) + 1 # first ' name_end = data.rfind("'") # last ' name = data[name_start:name_end] if player.name != name: say("[ERROR] name missmatch p.name='" + str(player.name) + "' name='" + str(name) + "'") sys.exit(1) self.players_controller.update_player_flag_grabs(player, 1) self.players_controller.set_flagger(player, True, timestamp) if self.settings.get("debug"): say("[DEBUG] '" + str(name) + "' grabbed the flag ts=" + str(timestamp))
def debug_player_list(self): """Print player list in chat""" global CONNECTED_PLAYERS for p in CONNECTED_PLAYERS: say(" id=" + str(p.cid) + " name='" + str(p.name) + "'")
def handle_chat_message(self, msg): """ Main method of this module takes a message and parses it for chat commands """ if self.is_muted(msg): return prefix = self.settings.get("chat_command_prefix") is_cmd = True msg_normal = msg msg = msg.lower() # the first possible occurence of a chat command (to filter chat command names) chat_cmd_start = base.generic.cfind(msg, ":", 4) cmd = msg[chat_cmd_start:-1] # cut newline at end if cmd.endswith(": " + prefix + "help") or \ cmd.endswith(": " + prefix + "info") or \ cmd.endswith(": " + prefix + "cmdlist"): say("==== Teeworlds Econ Mod (TEM) ====") say("developed by ChillerDragon version: " + str(version.VERSION)) say("https://github.com/ChillaVanilla/TeeworldsEconMod") say("'" + prefix + "help' to show this help") say("'" + prefix + "stats' to show round stats") say("'" + prefix + "achievements' to show achievements") if self.settings.get("stats_mode") == "sql": say("'" + prefix + "top5' for all time stats commands") say("'" + prefix + "rank' for all rank commands") elif cmd.endswith(": " + prefix + "top5"): if self.settings.get("stats_mode") == "sql": say("'" + prefix + "top_kills' to see top5 killers of all time") if self.settings.get("stats_mode") == "sql": say("'" + prefix + "top_flags' to see top5 flag cap times of all time") if self.settings.get("stats_mode") == "sql": say("'" + prefix + "top_caps' to see top5 flag amount of all time") if self.settings.get("stats_mode") == "sql": say("'" + prefix + "top_sprees' to see top5 killing sprees of all time") else: say("not supported in file stats mode") #elif cmd.endswith(": " + prefix + "stats_all"): #player.print_stats_all(True) elif cmd.find(": " + prefix + "stats") != -1: if self.settings.get("stats_mode") != "sql": say("not supported in file stats mode") return player, name = self.get_rank_player(msg_normal, ": " + prefix + "stats") if not player: say("[stats] player '" + str(name) + "' is not online.") return player.show_stats_round() #player.print_stats_all() elif cmd.endswith(": " + prefix + "top_caps"): if self.settings.get("stats_mode") == "sql": sql_stats.best_flag_caps() else: say("not supported in file stats mode") elif cmd.endswith(": " + prefix + "top_flags"): if self.settings.get("stats_mode") == "sql": sql_stats.best_times() else: say("not supported in file stats mode") elif cmd.endswith(": " + prefix + "top_kills"): if self.settings.get("stats_mode") == "sql": sql_stats.best_killers() else: say("not supported in file stats mode") elif cmd.endswith(": " + prefix + "top_sprees"): if self.settings.get("stats_mode") == "sql": sql_stats.best_spree() else: say("not supported in file stats mode") elif cmd.find("" + prefix + "rank_kills") != -1: sql_stats.rank_kills( self.get_rank_name(msg_normal, ": " + prefix + "rank_kills")) elif msg.find("" + prefix + "rank_flags") != -1: sql_stats.rank_flag_time( self.get_rank_name(msg_normal, ": " + prefix + "rank_flags")) elif msg.find("" + prefix + "rank_caps") != -1: sql_stats.rank_flag_caps( self.get_rank_name(msg_normal, ": " + prefix + "rank_caps")) elif cmd.find("" + prefix + "rank_sprees") != -1: sql_stats.rank_spree( self.get_rank_name(msg_normal, ": " + prefix + "rank_sprees")) elif cmd.find("" + prefix + "rank_all") != -1: name = self.get_rank_name(msg_normal, ": " + prefix + "rank_all") if not name: return say("=== '" + str(name) + "'s stats ===") sql_stats.rank_kills(str(name)) sql_stats.rank_flag_time(str(name)) sql_stats.rank_flag_caps(str(name)) sql_stats.rank_spree(str(name)) elif cmd.find("" + prefix + "rank") != -1: if self.settings.get("stats_mode") != "sql": say("not supported in file stats mode") return say("'" + prefix + "rank_kills' to show global kills rank") say("'" + prefix + "rank_sprees' to show global spree rank") say("'" + prefix + "rank_flags' to show global flag time rank") say("'" + prefix + "rank_caps' to show global flag capture rank") elif cmd.find("" + prefix + "achievements") != -1: name = self.get_rank_name(msg_normal, ": " + prefix + "achievements") self.achievements_controller.show_achievements(name) elif cmd.endswith(": " + prefix + "test"): player, name = self.get_rank_player(msg_normal, ": " + prefix + "test") if not player: if self.settings.get("hotplug") == 1: return say("error") sys.exit(1) say("got player: " + str(name)) # say("current spree: " + str(p.killingspree)) # handle this like a chat command (so it has spam prot) elif self.is_ban_reason_in_str(cmd): self.admin_contact_msg() # players containing : will be cutted in discord message but this is fine for now name = self.get_rank_name(msg_normal, ": ") if self.settings.get("filter_discord") == 1: send_discord( "chat trigger " + \ str(self.settings.get("mod_discord")) + \ "!\n" + str(msg) ) else: is_cmd = False if is_cmd: self.spam_protection(msg_normal)
def handle_kills(self, timestamp, data): """Parse kill message. Track kills, deaths and flags""" kill = None if self.settings.get("tw_version")[0:3] == "0.6" or \ self.settings.get("tw_version") is None: # default 6 kill = self.handle_kills_06(data) else: # teeworlds 0.7 or ddnet kill = self.handle_kills_07_and_ddnet(data[:-1]) # don't count suicide as kill or when killer left already if not kill.killer.obj == kill.victim.obj and kill.killer.obj: if not self.players_controller.update_player_kills( kill.killer.obj, 1, int(kill.weapon)): if self.settings.get("hotplug") == 1: return say("[ERROR] failed adding kill:") say(" ID=" + str(kill.killer.cid)) say(" NAME=" + str(kill.killer.name)) say(" OBJ=" + str(kill.killer.obj)) say(" DATA=" + data) sys.exit(1) if str(kill.weapon ) != "-3": # don't count disconnect or teamswitch as death if not self.players_controller.update_player_deaths( kill.victim.obj, kill.killer.name, 1): if self.settings.get("hotplug") == 1: return say("[ERROR] failed adding death:") say(" ID=" + str(kill.victim.cid)) say(" NAME=" + str(kill.victim.name)) say(" OBJ=" + str(kill.victim.obj)) say(" DATA=" + data) sys.exit(1) # say("[KILL] killer=" + killer_name + " victim=" + victim_name + " weapon=" + str(weapon)) self.players_controller.check_flagger_kill(kill.victim.name, kill.killer.name) self.players_controller.set_flagger(kill.victim.obj, False, timestamp)
def show_achievements(self, name): """Print achievements of player in chat""" global CONNECTED_PLAYERS _player = None for player in CONNECTED_PLAYERS: if player.name == name: _player = player break # player not online -> load from database if not _player: _player = save_stats.load_stats(name) if not _player: say("'" + str(name) + "' is unranked on this server") return False if str(_player.a_haxx0r) == "" and str( _player.a_blazeit) == "" and str( _player.a_satan) == "" and str(_player.a_virgin) == "": say("'" + str(name) + "' didn't unlock any achievements yet") return True say("=== '" + str(name) + "'s achievements ===") if str(_player.a_haxx0r) != "": say("[" + str(_player.a_haxx0r) + "] haxx0r") if str(_player.a_blazeit) != "": say("[" + str(_player.a_blazeit) + "] blaze it") if str(_player.a_satan) != "": say("[" + str(_player.a_satan) + "] satan") if str(_player.a_virgin) != "": say("[" + str(_player.a_virgin) + "] virgin") return True