示例#1
0
def flag_player(request):
    data = _get_data(request)
    res = None
    try:
        player, flag = add_flag_to_player(
            steam_id_64=data["steam_id_64"],
            flag=data["flag"],
            comment=data.get("comment"),
        )
        res = flag
        send_to_discord_audit(
            "`flag`: steam_id_64: `{}` player: `{}` flag: `{}`comment:`{}`".format(
                data["steam_id_64"],
                " | ".join(n["name"] for n in player["names"]),
                flag["flag"],
                data.get("comment", ""),
            ),
            request.user.username,
        )
    except KeyError:
        logger.warning("Missing parameters")
        # TODO return 400
    except CommandFailedError:
        logger.exception("Failed to flag")
    return JsonResponse(
        {"result": res, "command": "flag_player", "arguments": data, "failed": not res}
    )
示例#2
0
def auto_kick(_, log):
    try:
        config = get_config().get('NAME_KICKS')
    except KeyError:
        logger.error("Invalid configuration file, NAME_KICKS key is missing")
        return

    for r in config['regexps']:
        name = log["player"]
        info = recorded_rcon.get_player_info(name)
        try:
            profile = get_player_profile(info["steam_id_64"], 0)
            for f in config.get("whitelist_flags", []):
                if player_has_flag(profile, f):
                    logger.debug(
                        "Not checking nickname validity for whitelisted player %s (%s)",
                        name, info["steam_id_64"])
                    return
        except:
            logger.exception("Unable to check player profile")

        if re.match(r, name):
            logger.info("%s matched player %s", r, name)
            recorded_rcon.do_kick(player=name,
                                  reason=config["reason"],
                                  by="NAME_KICK")
            try:
                send_to_discord_audit(
                    f"`{name}` kicked from regexp `{r}`",
                    by="NAME_KICK",
                    webhookurl=config.get("discord_webhook_url"))
            except Exception:
                logger.error("Unable to send to audit_log")
            return
示例#3
0
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")
示例#4
0
def set_real_vip_config(request):
    error = None
    data = _get_data(request)
    try:
        config = RealVipConfig()
        real_vip_config = {
            "enabled": (bool, config.set_enabled),
            "desired_total_number_vips":
            (int, config.set_desired_total_number_vips),
            "minimum_number_vip_slot":
            (int, config.set_minimum_number_vip_slot),
        }
        for k, v in data.items():
            if k in real_vip_config:
                cast, setter = real_vip_config[k]
                send_to_discord_audit(f"RealVIP set {dict_to_discord({k: v})}",
                                      request.user.username)
                setter(cast(v))
    except Exception as e:
        logger.exception("Failed to set realvip config")
        error = repr(e)
    return api_response(
        result=_get_real_vip_config(),
        failed=bool(error),
        error=error,
        command="get_real_vip_config",
    )
示例#5
0
    def detect_map_change(self):
        try:
            current_map = self.rcon.get_map()
        except CommandFailedError:
            logger.info("Faied to get current map. Skipping")
            return
        logger.debug("Checking for map change current: %s prev: %s",
                     current_map, self.prev_map)
        if self.prev_map != current_map:
            if self.prev_map in ALL_MAPS:
                self.maps_history.save_map_end(self.prev_map)
            if current_map in ALL_MAPS:
                self.maps_history.save_new_map(current_map)

            logger.info(
                "Map change detected updating state. Prev map %s New Map %s",
                self.prev_map,
                current_map,
            )
            send_to_discord_audit(
                f"map change detected {dict_to_discord(dict(previous=self.prev_map, new=current_map))}",
                by="MAP_RECORDER",
                silent=False)
            self.prev_map = current_map
            self.last_map_change_time = datetime.now()
            return True

        return False
示例#6
0
    def detect_map_change(self):
        try:
            current_map = self.rcon.get_map()
        except Exception:
            logger.info("Faied to get current map. Skipping")
            return

        logger.debug("Checking for map change current: %s prev: %s",
                     current_map, self.prev_map)
        if self.prev_map != current_map:
            if (self.prev_map
                    and self.prev_map.replace("_RESTART", "") in ALL_MAPS
                    and current_map
                    and current_map.replace("_RESTART", "") in ALL_MAPS):
                self.maps_history.save_map_end(self.prev_map)
            if current_map and current_map.replace("_RESTART", "") in ALL_MAPS:
                self.maps_history.save_new_map(current_map)
                logger.info(
                    "Map change detected updating state. Prev map %s New Map %s",
                    self.prev_map,
                    current_map,
                )
                if not os.getenv("SILENT_MAP_RECORDER", None):
                    send_to_discord_audit(
                        f"map change detected {dict_to_discord(dict(previous=self.prev_map, new=current_map))}",
                        by="MAP_RECORDER",
                        silent=False,
                    )
                on_map_change(self.prev_map, current_map)
                self.prev_map = current_map

            return True

        return False
示例#7
0
def do_service(request):
    data = _get_data(request)
    client = get_supervisor_client()
    error = None
    res = None

    actions = {
        'START': client.supervisor.startProcess,
        'STOP': client.supervisor.stopProcess
    }
    action = data.get('action')
    service_name = data.get('service_name')

    if not action or action.upper() not in actions:
        return api_response(error="action must be START or STOP",
                            status_code=400)
    if not service_name:
        return api_response(error="process_name must be set", status_code=400)

    try:
        res = actions[action.upper()](service_name)
        send_to_discord_audit(f"do_service {service_name} {action}",
                              request.user.username)
    except Fault as e:
        error = repr(e)

    return api_response(result=res, failed=bool(error), error=error)
示例#8
0
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")
示例#9
0
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")
示例#10
0
def async_upload_vips(request):
    errors = []
    send_to_discord_audit("upload_vips", request.user.username)
    # Handle file upload
    vips = []
    if request.method == "POST":
        for name, data in request.FILES.items():
            for l in data:
                try:
                    l = l.decode()
                    if not l:
                        continue
                    try:
                        steam_id, name = l.split(" ", 1)
                    except ValueError:
                        steam_id, name = l.split("\t", 1)

                    if len(steam_id) != 17:
                        errors.append(
                            f"{l} has an invalid steam id, expecter length of 17"
                        )
                        continue
                    if not name:
                        errors.append(
                            f"{l} doesn't have a name attached to the steamid")
                        continue
                    vips.append((name, steam_id))
                except UnicodeDecodeError:
                    errors.append(
                        "File encoding is not supported. Must use UTF8")
                    break
                except Exception as e:
                    errors.append(f"Error on line {l} {repr(2)}")
    else:
        return api_response(error="Bad method", status_code=400)

    if vips:
        worker_bulk_vip(vips,
                        job_key=f"upload_vip_{os.getenv('SERVER_NUMBER')}",
                        mode="override")
    else:
        errors.append("No vips submitted")

    # Render list page with the documents and the form
    return api_response(
        result="Job submitted, will take several minutes",
        failed=bool(errors),
        error="\n".join(errors),
        command="async_upload_vips",
    )
示例#11
0
def audit(func_name, request, arguments):
    dont_audit = ["get_"]

    try:
        if any(func_name.startswith(s) for s in dont_audit):
            return
        args = dict(**arguments)
        try:
            del args["by"]
        except KeyError:
            pass
        arguments = " ".join([f"{k}: `{v}`" for k, v in args.items()])
        send_to_discord_audit("`{}`: {}".format(func_name, arguments),
                              request.user.username)
    except:
        logger.exception("Can't send audit log")
示例#12
0
def upload_vips(request):
    message = "Upload a VIP file!"
    send_to_discord_audit("upload_vips", request.user.username)
    # Handle file upload
    if request.method == "POST":
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            message = ""
            vips = ctl.get_vip_ids()
            for vip in vips:
                ctl.do_remove_vip(vip["steam_id_64"])
            message = f"{len(vips)} removed\n"
            count = 0
            for name, data in request.FILES.items():
                if name.endswith(".json"):
                    message = "JSON is not handled yet"
                    break
                else:
                    for l in data:
                        try:
                            l = l.decode()
                            steam_id, name = l.split(" ", 1)
                            if len(steam_id) != 17:
                                raise ValueError
                            ctl.do_add_vip(name.strip(), steam_id)
                            count += 1
                        except UnicodeDecodeError:
                            message = "File encoding is not supported. Must use UTF8"
                            break
                        except ValueError:
                            message += f"Line: '{l}' is invalid, skipped\n"
                        except CommandFailedError:
                            message = "The game serveur returned an error while adding a VIP. You need to upload again"
                            break

                    message += f"{count} added"
        else:
            message = "The form is not valid. Fix the following error:"
    else:
        form = DocumentForm()  # An empty, unbound form

    # Render list page with the documents and the form
    context = {"form": form, "message": message}
    return render(request, "list.html", context)
示例#13
0
def unflag_player(request):
    # Note is this really not restful
    data = _get_data(request)
    res = None
    try:
        player, flag = remove_flag(data["flag_id"])
        res = flag
        send_to_discord_audit(
            "`unflag`: flag: `{}` player: `{}`".format(
                flag["flag"], " | ".join(n["name"] for n in player["names"])
            ),
            request.user.username,
        )
    except KeyError:
        logger.warning("Missing parameters")
        # TODO return 400
    except CommandFailedError:
        logger.exception("Failed to remove flag")
    return JsonResponse(
        {"result": res, "command": "flag_player", "arguments": data, "failed": not res}
    )
示例#14
0
def set_standard_messages(request):
    failed = False
    data = _get_data(request)

    try:
        msgs = StandardMessages()
        res = msgs.set_messages(data["message_type"], data["messages"])
        send_to_discord_audit("set_standard_messages", request.user.username)
    except CommandFailedError as e:
        failed = True
        res = repr(e)
    except:
        logger.exception("Error setting standard messages config")
        failed = True
        res = "Error setting standard messages config"

    return JsonResponse({
        "result": res,
        "command": "get_standard_messages",
        "arguments": data,
        "failed": failed,
    })
示例#15
0
def notify_camera(rcon: RecordedRcon, struct_log):
    send_to_discord_audit(message=struct_log["message"], by=struct_log["player"])

    try:
        if hooks := get_prepared_discord_hooks("camera"):
            embeded = DiscordEmbed(
                title=f'{struct_log["player"]}  - {struct_log["steam_id_64_1"]}',
                description=struct_log["sub_content"],
                color=242424,
            )
            for h in hooks:
                h.add_embed(embeded)
                h.execute()
    except Exception:
        logger.exception("Unable to forward to hooks")

    config = CameraConfig()
    if config.is_broadcast():
        temporary_broadcast(rcon, struct_log["message"], 60)

    if config.is_welcome():
        temporary_welcome(rcon, struct_log["message"], 60)
示例#16
0
def auto_ban_if_tks_right_after_connection(rcon: RecordedRcon, log):
    config = get_config()
    config = config.get("BAN_TK_ON_CONNECT")
    if not config or not config.get("enabled"):
        return

    player_name = log["player"]
    player_steam_id = log["steam_id_64_1"]
    player_profile = None
    vips = {}
    try:
        player_profile = get_player_profile(player_steam_id, 0)
    except:
        logger.exception("Unable to get player profile")
    try:
        vips = set(v['steam_id_64'] for v in rcon.get_vip_ids())
    except:
        logger.exception("Unable to get VIPS")

    last_logs = get_recent_logs(end=500,
                                player_search=player_name,
                                exact_player_match=True)
    logger.debug("Checking TK from %s", player_name)
    author = config.get("author_name", "Automation")
    reason = config.get("message", "No reasons provided")
    discord_msg = config.get("discord_webhook_message", "No message provided")
    webhook = config.get("discord_webhook_url")
    max_time_minute = config.get("max_time_after_connect_minutes", 5)
    excluded_weapons = [w.lower() for w in config.get("exclude_weapons", [])]
    ignore_after_kill = config.get("ignore_tk_after_n_kills", 1)
    ignore_after_death = config.get("ignore_tk_after_n_death", 1)
    whitelist_players = config.get("whitelist_players", {})
    tk_tolerance_count = config.get("teamkill_tolerance_count", 1)

    if player_profile:
        if whitelist_players.get('is_vip') and player_steam_id in vips:
            logger.debug("Not checking player because he's VIP")
            return

        if whitelist_players.get('has_at_least_n_sessions') and player_profile[
                'sessions_count'] >= whitelist_players.get(
                    'has_at_least_n_sessions'):
            logger.debug("Not checking player because he has %s sessions",
                         player_profile['sessions_count'])
            return

        flags = whitelist_players.get('has_flag', [])
        if not isinstance(flags, list):
            flags = [flags]

        for f in flags:
            if player_has_flag(player_profile, f):
                logger.debug("Not checking player because he has flag %s", f)
                return

    last_action_is_connect = False
    last_connect_time = None
    kill_counter = 0
    death_counter = 0
    tk_counter = 0
    for log in reversed(last_logs["logs"]):
        logger.debug(log)

        if log["action"] == "CONNECTED":
            last_action_is_connect = log
            last_connect_time = log["timestamp_ms"]
            kill_counter = 0
            death_counter = 0
            continue
        if log["action"] == "TEAM KILL" and log[
                'player'] == player_name and last_action_is_connect:
            if excluded_weapons and log["weapon"].lower() in excluded_weapons:
                logger.debug(
                    "Not counting TK as offense due to weapon exclusion")
                continue
            if log['timestamp_ms'] - last_connect_time > max_time_minute * 60 * 1000:
                logger.debug(
                    "Not counting TK as offense due to elapsed time exclusion, last connection time %s, tk time %s",
                    datetime.datetime.fromtimestamp(last_connect_time / 1000),
                    datetime.datetime.fromtimestamp(log["timestamp_ms"]))
                continue
            logger.info("Banning player %s for TEAMKILL after connect %s",
                        player_name, log)
            tk_counter += 1
            if tk_counter > tk_tolerance_count:
                rcon.do_perma_ban(
                    player=player_name,
                    reason=reason,
                    by=author,
                )
                send_to_discord_audit(discord_msg.format(player=player_name),
                                      by=author,
                                      webhookurl=webhook)
        elif is_player_death(player_name, log):
            death_counter += 1
            if death_counter >= ignore_after_death:
                last_action_is_connect = False
        elif is_player_kill(player_name, log):
            kill_counter += 1
            if kill_counter >= ignore_after_kill:
                last_action_is_connect = False