def ban_if_blacklisted(rcon, steam_id_64, name): with enter_session() as sess: player = get_player(sess, steam_id_64) if not player: logger.error("Can't check blacklist, player not found %s", steam_id_64) return if player.blacklist and player.blacklist.is_blacklisted: logger.info("Player %s was banned due blacklist, reason: %s", str(player), player.blacklist.reason) rcon.do_perma_ban(name, player.blacklist.reason) # TODO save author of blacklist safe_save_player_action(rcon=rcon, player_name=name, action_type="PERMABAN", reason=player.blacklist.reason, by='BLACKLIST', steam_id_64=steam_id_64) try: send_to_discord_audit( f"`BLACKLIST` -> {dict_to_discord(dict(player=name, reason=player.blacklist.reason))}", "BLACKLIST") except: logger.error("Unable to send blacklist to audit log")
def ban_if_blacklisted(rcon: RecordedRcon, steam_id_64, name): with enter_session() as sess: player = get_player(sess, steam_id_64) if not player: logger.error("Can't check blacklist, player not found %s", steam_id_64) return if player.blacklist and player.blacklist.is_blacklisted: try: logger.info("Player %s was banned due blacklist, reason: %s", str(name), player.blacklist.reason) rcon.do_perma_ban(player=name, reason=player.blacklist.reason, by=f"BLACKLIST: {player.blacklist.by}") safe_save_player_action(rcon=rcon, player_name=name, action_type="PERMABAN", reason=player.blacklist.reason, by=f"BLACKLIST: {player.blacklist.by}", steam_id_64=steam_id_64) try: send_to_discord_audit( f"`BLACKLIST` -> {dict_to_discord(dict(player=name, reason=player.blacklist.reason))}", "BLACKLIST") except: logger.error("Unable to send blacklist to audit log") except: send_to_discord_audit( "Failed to apply ban on blacklisted players, please check the logs and report the error", "ERROR")
def add_player_to_blacklist(steam_id_64, reason, name=None, by=None): # TODO save author of blacklist with enter_session() as sess: player = _get_set_player(sess, steam_id_64=steam_id_64, player_name=name) if not player: raise CommandFailedError(f"Player with steam id {steam_id_64} not found") if player.blacklist: if player.blacklist.is_blacklisted: logger.warning( "Player %s was already blacklisted with %s, updating reason to %s", str(player), player.blacklist.reason, reason, ) player.blacklist.is_blacklisted = True player.blacklist.reason = reason player.blacklist.by = by else: logger.info("Player %s blacklisted for %s", str(player), reason) sess.add( BlacklistedPlayer( steamid=player, is_blacklisted=True, reason=reason, by=by ) ) sess.commit()
def get_player_profile(steam_id_64, nb_sessions): with enter_session() as sess: player = sess.query(PlayerSteamID).filter( PlayerSteamID.steam_id_64 == steam_id_64).one_or_none() if player is None: return return player.to_dict(limit_sessions=nb_sessions)
def get_scoreboard_maps(request): data = _get_data(request) page_size = min(int(data.get("limit", 100)), 1000) page = max(1, int(data.get("page", 1))) server_number = data.get("server_number", os.getenv("SERVER_NUMBER")) with enter_session() as sess: query = (sess.query(Maps).filter( Maps.server_number == server_number).order_by(Maps.start.desc())) total = query.count() res = query.limit(page_size).offset((page - 1) * page_size).all() return api_response( result={ "page": page, "page_size": page_size, "total": total, "maps": [ dict( just_name=map_name(r.map_name), long_name=LONG_HUMAN_MAP_NAMES.get( r.map_name, r.map_name), **r.to_dict(), ) for r in res ], }, failed=False, command="get_scoreboard_maps", )
def get_player_profile_by_id(id, nb_sessions): with enter_session() as sess: player = sess.query(PlayerSteamID).filter( PlayerSteamID.id == id).one_or_none() if player is None: return return player.to_dict(limit_sessions=nb_sessions)
def get_players_stats_at_time(self, from_, until, server_number=None): server_numer = server_number or os.getenv('SERVER_NUMBER') with enter_session() as sess: rows = get_historical_logs_records( sess, from_=from_, till=until, time_sort="asc", server_filter=server_numer, limit=99999999 ) indexed_logs = {} players = [] players_times = {} ids = set() for r in rows: log = r.compatible_dict() if player := log.get("player"): self._set_start_end_times(player, players_times, log, from_) indexed_logs.setdefault(player, []).append(log) ids.add(log.get("player1_id")) if steamid := log.get("steam_id_64_1"): players.append(dict(name=player, steam_id_64=steamid)) if player2 := log.get("player2"): self._set_start_end_times(player2, players_times, log, from_) indexed_logs.setdefault(player2, []).append(log) ids.add(log.get("player2_id")) if steamid2 := log.get("steam_id_64_2"): players.append(dict(name=player2, steam_id_64=steamid2))
def get_current_players_stats(self): players = self.rcon.get_players() if not players: logger.debug("No players") return {} players = [p for p in players if p.get("steam_id_64")] with enter_session() as sess: profiles_by_id = { profile.steam_id_64: profile for profile in _get_profiles( sess, [p["steam_id_64"] for p in players], nb_sessions=1) } logger.info("%s players, %s profiles loaded", len(players), len(profiles_by_id)) oldest_session_seconds = self._get_player_session_time( max(players, key=self._get_player_session_time)) logger.debug("Oldest session: %s", oldest_session_seconds) now = datetime.datetime.now() min_timestamp = (now - datetime.timedelta( seconds=oldest_session_seconds)).timestamp() logger.debug("Min timestamp: %s", min_timestamp) logs = get_recent_logs(min_timestamp=min_timestamp) logger.info("%s log lines to process", len(logs["logs"])) indexed_players = {p["name"]: p for p in players} indexed_logs = self._get_indexed_logs_by_player_for_session( now, indexed_players, reversed(logs["logs"])) return self.get_stats_by_player(indexed_logs, players, profiles_by_id)
def save_end_player_session(steam_id_64, timestamp): with enter_session() as sess: player = get_player(sess, steam_id_64) if not player: logger.error( "Can't record player session for %s, player not found", steam_id_64 ) return last_session = ( sess.query(PlayerSession) .filter(PlayerSession.steamid == player) .order_by(PlayerSession.created.desc()) .first() ) if last_session.end: logger.warning( "Last session was already ended for %s. Creating a new one instead", steam_id_64, ) last_session = PlayerSession( steamid=player, ) last_session.end = datetime.datetime.fromtimestamp(timestamp) logger.info( "Recorded player %s session end at %s", steam_id_64, last_session.end ) sess.commit()
def get_user_config(key, default=None): logger.debug("Getting user config for %s", key) with enter_session() as sess: res = _get_conf(sess, key) res = res.value if res else default logger.debug("User config for %s is %s", key, res) return res
def seed_default_config(): with enter_session() as sess: AutoBroadcasts().seed_db(sess) StandardMessages().seed_db(sess) CameraConfig().seed_db(sess) AutoVoteKickConfig().seed_db(sess) sess.commit()
def enrich_db_users(chunk_size=100, update_from_days_old=30): from rcon.models import enter_session, SteamInfo, PlayerSteamID from sqlalchemy import or_ max_age = datetime.datetime.utcnow() - datetime.timedelta( days=update_from_days_old) with enter_session() as sess: query = sess.query(PlayerSteamID).outerjoin(SteamInfo).filter( or_(PlayerSteamID.steaminfo == None, SteamInfo.updated <= max_age)) pages = math.ceil(query.count() / chunk_size) for page in range(pages): by_ids = {p.steam_id_64: p for p in query.limit(chunk_size).all()} profiles = get_steam_profiles(list(by_ids.keys())) logger.info( "Updating steam profiles page %s of %s - result count (query) %s - result count (steam) %s", page + 1, pages, len(by_ids), len(profiles)) if not by_ids: logger.warning("Empty query results") continue for p in profiles: player = by_ids.get(p['steamid']) if not player: logger.warning("Unable to find player %s", p["steamid"]) continue #logger.debug("Saving info for %s: %s", player.steam_id_64, p) update_db_player_info(player=player, steam_profile=p) sess.commit() time.sleep(5)
def get_map_scoreboard(request): data = _get_data(request) error = None failed = False game = None try: map_id = int(data.get("map_id", None)) with enter_session() as sess: game = sess.query(Maps).filter(Maps.id == map_id).one_or_none() #import ipdb; ipdb.set_trace() if not game: error = "No map for this ID" failed = True else: game = game.to_dict(with_stats=True) except Exception as e: game = None error = repr(e) failed = True return api_response(result=game, error=error, failed=failed, command="get_map_scoreboard")
def get_all_hook_types(as_dict=False): hooks = [] with enter_session() as sess: for name in DiscordHookConfig.expected_hook_types: hooks.append( asdict(DiscordHookConfig(for_type=name).get_hooks())) return hooks
def set_user_config(key, object_): with enter_session() as sess: conf = _get_conf(sess, key) if conf is None: _add_conf(sess, key, object_) else: conf.value = object_ sess.commit()
def ban_if_has_vac_bans(rcon: RecordedRcon, steam_id_64, name): try: max_days_since_ban = int(MAX_DAYS_SINCE_BAN) max_game_bans = ( float("inf") if int(MAX_GAME_BAN_THRESHOLD) <= 0 else int(MAX_GAME_BAN_THRESHOLD) ) except ValueError: # No proper value is given logger.error( "Invalid value given for environment variable BAN_ON_VAC_HISTORY_DAYS or MAX_GAME_BAN_THRESHOLD" ) return if max_days_since_ban <= 0: return # Feature is disabled with enter_session() as sess: player = get_player(sess, steam_id_64) if not player: logger.error("Can't check VAC history, player not found %s", steam_id_64) return bans = get_player_bans(steam_id_64) if not bans or not isinstance(bans, dict): logger.warning( "Can't fetch Bans for player %s, received %s", steam_id_64, bans ) # Player couldn't be fetched properly (logged by get_player_bans) return if should_ban(bans, max_game_bans, max_days_since_ban): reason = AUTO_BAN_REASON.format( DAYS_SINCE_LAST_BAN=bans.get("DaysSinceLastBan"), MAX_DAYS_SINCE_BAN=str(max_days_since_ban), ) logger.info( "Player %s was banned due VAC history, last ban: %s days ago", str(player), bans.get("DaysSinceLastBan"), ) rcon.do_perma_ban(player=name, reason=reason, by="VAC BOT") try: audit_params = dict( player=name, steam_id_64=player.steam_id_64, reason=reason, days_since_last_ban=bans.get("DaysSinceLastBan"), vac_banned=bans.get("VACBanned"), number_of_game_bans=bans.get("NumberOfGameBans"), ) send_to_discord_audit( f"`VAC/GAME BAN` -> {dict_to_discord(audit_params)}", "AUTOBAN" ) except: logger.exception("Unable to send vac ban to audit log")
def set_user_config(key, object_): logger.debug("Setting user config for %s with %s", key, object_) with enter_session() as sess: conf = _get_conf(sess, key) if conf is None: _add_conf(sess, key, object_) else: conf.value = object_ sess.commit()
def get_profiles(steam_ids, nb_sessions=0): with enter_session() as sess: players = ( sess.query(PlayerSteamID) .filter(PlayerSteamID.steam_id_64.in_(steam_ids)) .all() ) return [p.to_dict(limit_sessions=nb_sessions) for p in players]
def save_player_action(rcon, action_type, player_name, by, reason=''): with enter_session() as sess: steam_id_64 = rcon.get_player_info(player_name)['steam_id_64'] player = _get_set_player(sess, player_name, steam_id_64) sess.add( PlayersAction(action_type=action_type.upper(), steamid=player, reason=reason, by=by))
def unwatch(self): with enter_session() as sess: player = get_player(sess, self.steam_id_64) if not player: raise CommandFailedError( "Can't remove player to wathlist, player not found") if player.watchlist: player.watchlist.is_watched = False return True
def _record_stats(map_info): stats = TimeWindowStats() start = datetime.datetime.utcfromtimestamp(map_info.get('start')) end = datetime.datetime.utcfromtimestamp(map_info.get('end')) if not start or not end: logger.error("Can't record stats, no time info for %s", map_info) return with enter_session() as sess: map = get_or_create_map(sess=sess, start=start, end=end, server_number=os.getenv("SERVER_NUMBER"), map_name=map_info["name"]) player_stats = stats.get_players_stats_at_time(from_=start, until=end) for player, stats in player_stats.items(): if steam_id_64 := stats.get("steam_id_64"): player_record = get_player(sess, steam_id_64=steam_id_64) if not player_record: logger.error("Can't find DB record for %s", steam_id_64) continue player_stats = dict( playersteamid_id=player_record.id, map_id=map.id, name=stats.get("player"), kills=stats.get("kills"), kills_streak=stats.get("kills_streak"), deaths=stats.get("deaths"), deaths_without_kill_streak=stats.get( "deaths_without_kill_streak"), teamkills=stats.get("teamkills"), teamkills_streak=stats.get("teamkills_streak"), deaths_by_tk=stats.get("deaths_by_tk"), deaths_by_tk_streak=stats.get("deaths_by_tk_streak"), nb_vote_started=stats.get("nb_vote_started"), nb_voted_yes=stats.get("nb_voted_yes"), nb_voted_no=stats.get("nb_voted_no"), time_seconds=stats.get("time_seconds"), kills_per_minute=stats.get("kills_per_minute"), deaths_per_minute=stats.get("deaths_per_minute"), kill_death_ratio=stats.get("kill_death_ratio"), longest_life_secs=stats.get("longest_life_secs"), shortest_life_secs=stats.get("shortest_life_secs"), weapons=stats.get("weapons"), most_killed=stats.get("most_killed"), death_by=stats.get("death_by")) logger.debug(f"Saving stats %s", player_stats) player_stat_record = PlayerStats(**player_stats) sess.add(player_stat_record) else: logger.error("Stat object does not contain a steam id: %s", stats)
def remove_player_from_blacklist(steam_id_64): with enter_session() as sess: player = get_player(sess, steam_id_64) if not player: raise CommandFailedError(f"Player with steam id {steam_id_64} not found") if not player.blacklist: raise CommandFailedError(f"Player {player} was not blacklisted") player.blacklist.is_blacklisted = False sess.commit()
def save_player_action( rcon, action_type, player_name, by, reason="", steam_id_64=None, timestamp=None ): with enter_session() as sess: _steam_id_64 = steam_id_64 or rcon.get_player_info(player_name)["steam_id_64"] player = _get_set_player(sess, player_name, _steam_id_64, timestamp=timestamp) sess.add( PlayersAction( action_type=action_type.upper(), steamid=player, reason=reason, by=by ) )
def remove_flag(flag_id): with enter_session() as sess: exits = sess.query(PlayerFlag).filter( PlayerFlag.id == int(flag_id)).one_or_none() if not exits: logger.warning("Flag does not exists") raise CommandFailedError("Flag does not exists") player = exits.steamid.to_dict() flag = exits.to_dict() sess.delete(exits) sess.commit() return player, flag
def seed_default_config(): logger.info('Seeding DB') try: with enter_session() as sess: AutoBroadcasts().seed_db(sess) StandardMessages().seed_db(sess) CameraConfig().seed_db(sess) AutoVoteKickConfig().seed_db(sess) VoteMapConfig().seed_db(sess) RealVipConfig().seed_db(sess) AutoSettingsConfig().seed_db(sess) sess.commit() except Exception as e: logger.exception('Failed to seed DB')
def get_players_by_appearance(page=1, page_size=500, last_seen_from: datetime.datetime = None, last_seen_till: datetime.datetime = None, player_name = None, blacklisted = None, steam_id_64 = None): if page <= 0: raise ValueError('page needs to be >= 1') if page_size <= 0: raise ValueError('page_size needs to be >= 1') with enter_session() as sess: sub = sess.query( PlayerSession.playersteamid_id, func.min(func.coalesce(PlayerSession.start, PlayerSession.created)).label('first'), func.max(func.coalesce(PlayerSession.end, PlayerSession.created)).label('last') ).group_by(PlayerSession.playersteamid_id).subquery() query = sess.query(PlayerSteamID, sub.c.first, sub.c.last).outerjoin( sub, sub.c.playersteamid_id == PlayerSteamID.id) if steam_id_64: query = query.filter(PlayerSteamID.steam_id_64.ilike("%{}%".format(steam_id_64))) if player_name: query = query.join(PlayerSteamID.names).filter(PlayerName.name.ilike("%{}%".format(player_name))).options(contains_eager(PlayerSteamID.names)) if blacklisted is True: query = query.join(PlayerSteamID.blacklist).filter(BlacklistedPlayer.is_blacklisted == True).options(contains_eager(PlayerSteamID.blacklist)) if last_seen_from: query = query.filter(sub.c.last >= last_seen_from) if last_seen_till: query = query.filter(sub.c.last <= last_seen_till) total = query.count() page = min(max(math.ceil(total / page_size), 1), page) players = query.order_by(sub.c.last.desc()).limit( page_size).offset((page - 1) * page_size).all() return { 'total': total, 'players': [ { 'steam_id_64': p[0].steam_id_64, 'names': [n.name for n in p[0].names], 'first_seen_timestamp_ms': int(p[1].timestamp() * 1000) if p[1] else None, 'last_seen_timestamp_ms': int(p[2].timestamp() * 1000) if p[1] else None, 'penalty_count': p[0].get_penalty_count(), 'blacklisted': p[0].blacklist.is_blacklisted if p[0].blacklist else False } for p in players ], 'page': page, 'page_size': page_size }
def save_start_player_session(steam_id_64, timestamp): with enter_session() as sess: player = get_player(sess, steam_id_64) if not player: logger.error( "Can't record player session for %s, player not found", steam_id_64) return sess.add( PlayerSession(steamid=player, start=datetime.datetime.fromtimestamp(timestamp))) logger.info("Recorded player %s session start at %s", steam_id_64, datetime.datetime.fromtimestamp(timestamp)) sess.commit()
def watch(self, reason, comment, player_name=""): with enter_session() as sess: player = _get_set_player( sess, player_name=player_name, steam_id_64=self.steam_id_64 ) if player.watchlist: player.watchlist.is_watched = True else: watch = WatchList( steamid=player, comment=comment, reason=reason, is_watched=True ) sess.add(watch) sess.commit() return True
def get_players_stats_at_time(self, from_, until, server_number=None): server_number = server_number or os.getenv("SERVER_NUMBER") with enter_session() as sess: # Get the logs from the database for the given time range rows = get_historical_logs_records( sess, from_=from_, till=until, time_sort="asc", server_filter=server_number, limit=99999999, ) return self._get_players_stats_for_logs( [row.compatible_dict() for row in rows], from_, until)
def add_flag_to_player(steam_id_64, flag, comment=None, player_name=None): with enter_session() as sess: player = _get_set_player(sess, player_name=player_name, steam_id_64=steam_id_64) exits = ( sess.query(PlayerFlag) .filter(PlayerFlag.playersteamid_id == player.id, PlayerFlag.flag == flag) .all() ) if exits: logger.warning("Flag already exists") raise CommandFailedError("Flag already exists") new = PlayerFlag(flag=flag, comment=comment, steamid=player) sess.add(new) sess.commit() res = player.to_dict() return res, new.to_dict()