Beispiel #1
0
def display_player_name(follow_id):
    follow_player = SERVER.get_player_by_id(follow_id)
    if follow_player is not None:
        player_name = follow_player.n
        display_name = player_name if player_name.strip(
        ) not in config.BLACKLISTED_WORDS else "*" * len(player_name)
        api.exec_command(f"set player-name {display_name}", verbose=False)
Beispiel #2
0
def start():
    """
    The main gateway for fetching the server state through /svinfo_report. It runs through a loop indefinitely and
    attempts to extract new data only if state is not paused through the PAUSE_STATE flag.
    """
    global STATE
    global PAUSE_STATE
    global VID_RESTARTING

    prev_state, curr_state = None, None
    initialize_state()
    while True:
        try:
            if PAUSE_STATE:
                raise Exception("Paused")

            # Only refresh the STATE object if new data has been read and if state is not paused
            while not new_report_exists(
                    config.INITIAL_REPORT_P) and not PAUSE_STATE:
                time.sleep(2)

                if not PAUSE_STATE:
                    api.exec_command(
                        "varmath color2 = $chsinfo(152);"  # Store inputs in color2
                        "silent svinfo_report serverstate.txt",
                        verbose=False)  # Write a new report
                elif not VID_RESTARTING:
                    raise Exception("Paused")

                if new_report_exists(config.STATE_REPORT_P):
                    # Given that a new report exists, read this new data.
                    server_info, players, num_players = get_svinfo_report(
                        config.STATE_REPORT_P)

                    if bool(
                            server_info
                    ):  # New data is not empty and valid. Update the state object.
                        STATE.players = players
                        STATE.update_info(server_info)
                        STATE.num_players = num_players
                        validate_state(
                        )  # Check for nospec, self spec, afk, and any other problems.
                        if STATE.current_player is not None and STATE.current_player_id != STATE.bot_id:
                            curr_state = f"Spectating {STATE.current_player.n} on {STATE.mapname}" \
                                         f" in server {STATE.hostname} | ip: {STATE.ip}"
                        if curr_state != prev_state:
                            logging.info(curr_state)
                        prev_state = curr_state
                        display_player_name(STATE.current_player_id)
                if getattr(STATE, 'vote_active', False):
                    STATE.handle_vote()
        except Exception as e:
            if e.args[0] == 'Paused':
                pass
            else:
                prev_state, curr_state = None, None
                initialize_state(
                )  # Handle the first state fetch. Some extra processing needs to be done this time.
                logging.info(f"State failed: {e}")
            time.sleep(1)
Beispiel #3
0
async def switch_spec(direction='next', channel=None):
    """
    Handles "smart" spec switch. Resets data relevant to old connections and players. Can move either forward (default)
    or backwards (used by ?prev).
    """
    global STATE
    global IGNORE_IPS

    IGNORE_IPS = []
    STATE.afk_list = []
    spec_ids = STATE.spec_ids if direction == 'next' else STATE.spec_ids[::
                                                                         -1]  # Reverse spec_list if going backwards.

    if STATE.current_player_id != STATE.bot_id:
        # Determine the next followable id. If current id is at the last index, wrap to the beginning of the list.
        next_id_index = spec_ids.index(STATE.current_player_id) + 1
        if next_id_index > len(spec_ids) - 1:
            next_id_index = 0
        follow_id = spec_ids[next_id_index]

        if follow_id == STATE.current_player_id:  # Landed on the same id (list is length 1). No other players to spec.
            msg = "No other players to spectate."
            api.display_message(f"^7{msg}")
            logging.info(msg)
            if channel is not None:
                await channel.send(msg)
        else:
            display_player_name(follow_id)
            api.exec_command(f"follow {follow_id}")  # Follow this player.
            STATE.idle_counter = 0  # Reset idle strike flag since a followable non-bot id was found.
            STATE.current_player_id = follow_id  # Notify the state object of the new player we are spectating.
            STATE.afk_counter = 0

    return True
Beispiel #4
0
def mapdataHook():
    global CURRENT_MAP
    while True:
        if serverstate.STATE.mapname != CURRENT_MAP and serverstate.STATE.mapname != None:
            CURRENT_MAP = serverstate.STATE.mapname
            logging.info("Map changed to : " + str(CURRENT_MAP))

            data = MapData.load(CURRENT_MAP)
            cmd = f"cg_centertime 3;displaymessage 140 10 Changing settings for map: {CURRENT_MAP}"

            if data is not None:
                data = json.loads(data[2])
                for key, value in SAVED_CMDS.items():
                    if key in data:
                        cmd += ";" + SAVED_CMDS[key]['cmd'] + " " + str(data[key])
                    else:
                        cmd += ";" + SAVED_CMDS[key]['cmd'] + " " + str(SAVED_CMDS[key]['default'])
            else:
                for key, value in SAVED_CMDS.items():
                    cmd += ";" + SAVED_CMDS[key]['cmd'] + " " + str(SAVED_CMDS[key]['default'])

            serverstate.VID_RESTARTING = True
            serverstate.PAUSE_STATE = True
            api.exec_command(cmd + "; vid_restart")

        time.sleep(4)
Beispiel #5
0
def restart_connect(ip):
    api.press_key_mult("{Esc}", 2)
    api.press_key("{Enter}")
    api.press_key_mult("{Tab}", 10)
    api.press_key("{Enter}")
    time.sleep(1)
    api.exec_command("connect " + ip, verbose=False)
Beispiel #6
0
    def handle_vote(self):
        if time.time() - self.vote_time > VOTE_TALLY_TIME:
            logging.info("Voting tally done.")
            if self.vn_count > self.vy_count:
                api.exec_command(
                    f"say ^3{self.vy_count} ^2f1 ^7vs. ^3{self.vn_count} ^1f2^7. Voting ^3f2^7."
                )
                logging.info(
                    f"{self.vy_count} f1s vs. {self.vn_count} f2s. Voting f2.")
                api.exec_command("vote no")
            elif self.vy_count > self.vn_count:
                api.exec_command(
                    f"say ^3{self.vy_count} ^2f1 ^7vs. ^3{self.vn_count} ^1f2^7. Voting ^3f1^7."
                )
                logging.info(
                    f"{self.vy_count} f1s vs. {self.vn_count} f2s. Voting f1.")
                api.exec_command("vote yes")
            else:
                api.exec_command(
                    f"say ^3{self.vy_count} ^2f1 ^7vs. ^3{self.vn_count} ^1f2^7. No action."
                )
                logging.info(
                    f"{self.vy_count} f1s vs. {self.vn_count} f2s. Not voting."
                )

            self.vote_time = 0
            self.voter_names = []
            self.vy_count = 0
            self.vn_count = 0
            self.vote_active = False
        else:
            return
Beispiel #7
0
async def spec(ctx, author, args):
    follow_id = args[0]
    msg = serverstate.spectate_player(follow_id)
    await ctx.channel.send(msg)
    time.sleep(1)
    api.exec_command(
        f"cg_centertime 3;varcommand displaymessage 140 10 ^3{author} ^7has switched to $chsinfo(117)"
    )
Beispiel #8
0
def initialize_state():
    """
    Handles necessary processing on the first iteration of state retrieval.
    Important steps done here:
        - Check if there's a valid connection
        - Retrieve the bot's client id using the "color1" cvar and a fresh secret code
    """
    global STATE
    global PAUSE_STATE
    global INIT_TIMEOUT
    global STATE_INITIALIZED

    try:
        # Create a secret code. Only "secret" for one use.
        secret = ''.join(random.choice('0123456789ABCDEF') for i in range(16))
        server_info, bot_player, timeout_flag = None, [], 0

        init_counter = 0
        while server_info is None or bot_player == []:  # Continue running this block until valid data and bot id found
            init_counter += 1
            if not PAUSE_STATE:
                # Set color1 to secret code to determine bot's client id
                api.exec_command(
                    f"seta color1 {secret};silent svinfo_report serverstate.txt",
                    verbose=False)
            else:
                raise Exception("Paused.")

            if new_report_exists(config.STATE_REPORT_P):  # New data detected
                server_info, players, num_players = get_svinfo_report(
                    config.STATE_REPORT_P)  # Read data
                # Select player that contains this secret as their color1, this will be the bot player.
                bot_player = [
                    player for player in players if player.c1 == secret
                ]

            # If loop hits the max iterations, the connection was not established properly
            if init_counter >= INIT_TIMEOUT:
                # Retry a connection to best server
                new_ip = servers.get_next_active_server(IGNORE_IPS)
                connect(new_ip)

        bot_id = bot_player[0].id  # Find our own ID

        # Create global server object
        STATE = State(secret, server_info, players, bot_id)
        STATE.current_player_id = bot_id
        STATE.num_players = num_players
        STATE_INITIALIZED = True
        logging.info("State Initialized.")
    except:
        return False

    # if not mapdata_thread.is_alive():
    #     mapdata_thread.start()

    return True
Beispiel #9
0
async def connect(ctx, author, args):
    ip = args[0]
    if ip.split(':')[0] not in config.get_list("whitelist_servers"):
        msg = f"Server \"{ip}\" is not whitelisted. Refusing connection."
        api.exec_command(
            f"cg_centertime 5;displaymessage 140 8 ^3{author} ^1{msg};")
        logging.info(msg)
        await ctx.channel.send(msg)
        return
    serverstate.connect(ip, author)
Beispiel #10
0
def handle_howmany(line_data):
    client_id = environ['TWITCH_API']['client_id']
    client_secret = environ['TWITCH_API']['client_secret']
    token_url = f"https://id.twitch.tv/oauth2/token?client_id={client_id}&client_secret={client_secret}&grant_type=client_credentials"
    r = requests.post(token_url)
    token = r.json()['access_token']
    stream_url = f"https://api.twitch.tv/helix/streams?user_login={'defraglive'}"
    headers = {"Authorization": f"Bearer {token}", "Client-Id": client_id}
    r = requests.get(stream_url, headers=headers)
    stream_data = r.json()['data']
    viewer_count = stream_data[0]['viewer_count']
    reply_string = f"$chsinfo(117) ^7-- you are being watched by ^3{viewer_count} ^7viewer" + ("s" if viewer_count > 0 else "")
    api.exec_command(f"varcommand say {reply_string}")
    return None
Beispiel #11
0
def spectate_player(follow_id):
    """Spectate player chosen by twich users based on their client id"""
    global IGNORE_IPS
    IGNORE_IPS = []
    STATE.afk_list = []
    if follow_id in STATE.spec_ids:
        display_player_name(follow_id)
        api.exec_command(f"follow {follow_id}")  # Follow this player.
        STATE.idle_counter = 0  # Reset idle strike flag since a followable non-bot id was found.
        STATE.current_player_id = follow_id  # Notify the state object of the new player we are spectating.
        STATE.afk_counter = 0
        return f"Spectating {STATE.get_player_by_id(follow_id).n}"
    else:
        return f"Sorry, that player (id {follow_id}) is not available for spectating."
Beispiel #12
0
def handle_stonk(line_data):
    try:
        line_list = line_data['content'].split()
        stonk = line_list[1]
        region = 'US'
        headers = {
            'x-rapidapi-key': environ['STONK_API']['key'],
            'x-rapidapi-host': environ['STONK_API']['host']
        }
        url = "https://apidojo-yahoo-finance-v1.p.rapidapi.com/auto-complete"
        querystring = {"q": stonk, "region": region}
        response = requests.request("GET", url, headers=headers, params=querystring)
        symbol = response.json()['quotes'][0]['symbol']

        url = "https://apidojo-yahoo-finance-v1.p.rapidapi.com/stock/v2/get-summary"
        querystring = {"symbol": symbol, "region": region}
        response = requests.request("GET", url, headers=headers, params=querystring)
        short_name, symbol, exchange = [response.json()['quoteType'][i] for i in ('shortName', 'symbol', 'exchange')]
        price, change = [response.json()['price'][i]['fmt'] for i in ('regularMarketPrice', 'regularMarketChangePercent')]
        currency = response.json()['price']['currency']
        color = "^1" if '-' in change else "^2"
        change = change.replace('%',' p/c')
        reply_string = f"^7{symbol}^3: {color}{price} {currency} ({change}) ^7{short_name} ({exchange})"
    except:
        reply_string = "Invalid input. Usage: ?stonk <symbol>"
    return api.exec_command(f"say {reply_string}")
Beispiel #13
0
async def gamma(ctx, author, args):
    whitelisted_twitch_users = config.get_list('whitelist_twitchusers')
    if USE_WHITELIST and author not in whitelisted_twitch_users and not ctx.author.is_mod:
        await ctx.channel.send(
            f"{author}, you do not have the correct permissions to use this command."
            f"If you wanna be whitelisted to use such a command, please contact neyo#0382 on discord."
        )
        return
    value = float(args[0])
    if 0.5 <= (value) <= 1.6:
        logging.info("i did it..")
        api.exec_command(f"r_gamma {value}")
        MapData.save(serverstate.STATE.mapname, 'gamma', value)
    else:
        await ctx.channel.send(
            f"{author}, the allowed values for gamma are 1.0-1.6")
Beispiel #14
0
def send_message():
    author = request.form.get('author', None)
    message = request.form.get('message', None)
    command = request.form.get('command', None)

    if command is not None and command.startswith("!"):
        if ";" in command:  # prevent q3 command injections
            command = command[:command.index(";")]
        api.exec_command(command)
        return jsonify(result=f"Sent mdd command {command}")
    else:
        if ";" in message:  # prevent q3 command injections
            message = message[:message.index(";")]
        api.exec_command(f"say {author} ^7> ^2{message}")
        return jsonify(result=f"Sent {author} ^7> ^2{message}")

    return jsonify(result="Unknown message")
Beispiel #15
0
async def picmip(ctx, author, args):
    whitelisted_twitch_users = config.get_list('whitelist_twitchusers')
    if USE_WHITELIST and author not in whitelisted_twitch_users and not ctx.author.is_mod:
        await ctx.channel.send(
            f"{author}, you do not have the correct permissions to use this command."
            f"If you wanna be whitelisted to use such a command, please contact neyo#0382 on discord."
        )
        return
    value = args[0]
    if value.isdigit() and (0 <= int(value) <= 6):
        logging.info("vid_restarting..")
        serverstate.VID_RESTARTING = True
        serverstate.PAUSE_STATE = True
        api.exec_command(f"r_picmip {value};vid_restart")
        MapData.save(serverstate.STATE.mapname, 'picmip', value)
    else:
        await ctx.channel.send(
            f"{author}, the allowed values for picmip are 0-5.")
Beispiel #16
0
def connect(ip, caller=None):
    """
    Handles connection to a server and re-attempts if connection is not resolved.
    """
    global PAUSE_STATE
    global STATE_INITIALIZED
    global CONNECTING
    global IGNORE_IPS

    STATE_INITIALIZED = False
    logging.info(f"Connecting to {ip}...")
    PAUSE_STATE = True
    CONNECTING = True
    STATE.idle_counter = 0
    STATE.afk_counter = 0
    if caller is not None:
        STATE.connect_msg = f"^7Brought by ^3{caller}"
        IGNORE_IPS = []
    api.exec_command("connect " + ip, verbose=False)
Beispiel #17
0
def refresh_server_state():
    global SERVER
    global STOP_STATE

    while not STOP_STATE.is_set():
        time.sleep(1)
        api.exec_command("silent svinfo_report serverstate.txt", verbose=False)
        server_info, players = get_svinfo_report(config.STATE_REPORT_P)

        if bool(server_info):
            SERVER.players = players
            SERVER.update_info(server_info)
            if SERVER.current_player is not None:
                SERVER.curr_state = f"Spectating {SERVER.current_player.n} on {SERVER.mapname} in server {SERVER.hostname} | ip: {SERVER.ip}"
            if SERVER.curr_state != SERVER.prev_state:
                print(colored(SERVER.curr_state, "blue"))
            continue_refresh = check_status()
            SERVER.prev_state = SERVER.curr_state
            display_player_name(SERVER.current_player_id)
            if not continue_refresh:
                return
Beispiel #18
0
def switch_spec(direction='next'):
    global SERVER
    global STOP_STATE

    spec_ids = SERVER.spec_ids if direction == 'next' else SERVER.spec_ids[::-1]

    if SERVER.current_player_id != -1:
        next_id_index = spec_ids.index(SERVER.current_player_id) + 1
        if next_id_index > len(spec_ids) - 1:
            next_id_index = 0
        follow_id = spec_ids[next_id_index]

        if follow_id == SERVER.current_player_id:
            print("No other players to switch to.")
            api.exec_command(
                "displaymessage 380 10 ^1No other players to switch to.",
                verbose=False)
        else:
            display_player_name(follow_id)
            api.exec_command(f"follow {follow_id}")
            SERVER.idle_counter = 0
            SERVER.current_player_id = follow_id
Beispiel #19
0
def on_ws_message(msg):
    message = {}

    if msg is None:
        return

    try:
        message = json.loads(msg)
    except Exception as e:
        logging.info('ERROR [on_ws_message]:', e)
        return

    # if there is no origin, exit
    # this function only processes messages directly from twitch console extension
    if 'origin' not in message:
            return
    
    if 'message' in message:
        if message['message'] is None:
            message['message'] = {}

        message_text = message['message']['content']

        if ";" in message_text:  # prevent q3 command injections
            message_text = message_text[:message_text.index(";")]

        if message_text.startswith("!"):  # proxy mod commands (!top, !rank, etc.)
            logging.info("proxy command received")
            api.exec_command(message_text)
            time.sleep(1)
        else:
            author = message['message']['author']
            author += ' ^7> '
            author_color_num = min(ord(author[0].lower()), 9) # replace ^[a-z] with ^[0-9]
            message_content = message_text.lstrip('>').lstrip('<')
            api.exec_command(f"say ^{author_color_num}{author} ^2{message_content}")
Beispiel #20
0
def check_status():
    global SERVER
    global STOP_STATE

    MAX_STRIKES = 10  # TODO: move this over to configurable setting by user
    continue_refresh = True
    spectating_self = SERVER.curr_dfn == 'twitchbot'
    spectating_nospec = SERVER.current_player_id not in SERVER.spec_ids

    if spectating_self or spectating_nospec:
        follow_id = random.choice(
            SERVER.spec_ids) if SERVER.spec_ids != [] else -1

        if follow_id != -1:
            msg = "Spectating self, switching..." if spectating_self else "Switching to non-nospec player..."
            print(colored(msg, 'green'))
            display_player_name(follow_id)
            api.exec_command(f"follow {follow_id}")
            SERVER.idle_counter = 0
        else:
            if SERVER.current_player_id != -1:
                api.exec_command(f"follow {follow_id}", verbose=False)
                SERVER.current_player_id = -1

            SERVER.idle_counter += 1
            print(
                f"Not spectating. Strike {SERVER.idle_counter}/{MAX_STRIKES}")

            if SERVER.idle_counter >= MAX_STRIKES:  # There's been no one on the server to spec, switch servers.
                new_ip = servers.get_most_popular_server(ignore_ip=SERVER.ip)
                STOP_STATE.set()
                connection_success = connect(new_ip)
                return connection_success  # if false, continue working as normal. Else, stop refresh.
        SERVER.current_player_id = follow_id

    return continue_refresh
Beispiel #21
0
def connect(ip):
    global SERVER
    global STOP_STATE

    STOP_STATE.set()  # stop server refresh thread
    # Set a secret color cvar
    secret = ''.join(random.choice('0123456789ABCDEF') for i in range(16))
    print(colored(f"Connecting to {ip}...", "green"))
    connection_time = time.time()
    api.exec_command("connect " + ip, verbose=False)
    api.exec_command(f"seta color1 {secret}", verbose=False)
    server_info, bot_player, timeout_flag, reconnect_tries = None, [], 0, 0

    # Read report
    while server_info is None or bot_player == []:
        report_mod_time = os.path.getmtime(config.INITIAL_REPORT_P)
        new_report_exists = report_mod_time > connection_time

        if new_report_exists:
            server_info, players = get_svinfo_report(config.INITIAL_REPORT_P)
            bot_player = [player for player in players if player.c1 == secret]

        timeout_flag += 1
        if timeout_flag >= 10:
            print(colored(f"Connection timed out.", "green"))
            if reconnect_tries < 2:
                print(colored(f"Retrying connection to {ip}...", "green"))
                connection_time = time.time()
                restart_connect(ip)
                api.exec_command(f"seta color1 {secret}", verbose=False)
                server_info, bot_player, timeout_flag = None, [], 0
                reconnect_tries += 1
            else:
                print(f"Could not connect to {ip}")
                return

        time.sleep(1)

    print(colored(f"Connected.", "green"))
    bot_id = bot_player[0].id  # Find our own ID

    # Create global server object
    SERVER = Server(ip, secret, server_info, players, bot_id)
    SERVER.current_player_id = -1

    check_status()
    STOP_STATE.clear()
    state_refresher = threading.Thread(target=refresh_server_state,
                                       daemon=True)
    state_refresher.start()
    return True
Beispiel #22
0
async def prev(ctx, author, args):
    await serverstate.switch_spec('prev', channel=ctx.channel)
    api.exec_command(
        f"cg_centertime 2;displaymessage 140 10 ^3{author} ^7has switched to ^3Previous player"
    )
Beispiel #23
0
async def event_message(ctx):
    """Activates for every message"""
    debounce = 1  # interval between consecutive commands and messages
    author = ctx.author.name
    message = ctx.content

    if ";" in message:  # prevent q3 command injections
        message = message[:message.index(";")]

    # bot.py, at the bottom of event_message
    if message.startswith("?"):  # spectator client customization and controls
        message = message.strip('?').lower()
        split_msg = message.split(' ')
        cmd = split_msg[0]
        args = split_msg[1:] if len(split_msg) > 0 else None
        logging.info(f"TWITCH COMMAND RECEIVED: '{cmd}' from user '{author}'")

        for command in TWITCH_CMDS:
            if cmd in command:
                twitch_function = getattr(twitch_commands, command[0])
                await twitch_function(ctx, author, args)
        time.sleep(debounce)

    elif message.startswith(">") or message.startswith("<"):  # chat bridge
        message = message.lstrip('>').lstrip('<').lstrip(' ')
        blacklisted_words = config.get_list("blacklist_chat")

        for word in blacklisted_words:
            if word in message:
                logging.info(f"Blacklisted word '{word}' detected in message \"{message}\" by \"{author}\". Aborting message.")
                return

        if author.lower() == 'nightbot'.lower():  # ignore twitch Nightbot's name
            author = ''
            author_color_char = 0
        else:
            author += ' ^7> '
            author_color_char = author[0]

        api.exec_command(f"say ^{author_color_char}{author} ^2{message}")
        logging.info("Chat message sent")
        time.sleep(debounce)

    elif message.startswith("**"):  # team chat bridge
        message = message.lstrip('**')
        blacklisted_words = config.get_list("blacklist_chat")

        for word in blacklisted_words:
            if word in message:
                logging.info(f"Blacklisted word '{word}' detected in message \"{message}\" by \"{author}\". Aborting message.")
                return

        if author.lower() == 'nightbot'.lower():  # ignore twitch Nightbot's name
            author = ''
            author_color_char = 0
        else:
            author += ' ^7> '
            author_color_char = author[0]

        api.exec_command(f"say_team ^{author_color_char}{author} ^5{message}")
        logging.info("Chat message sent")
        time.sleep(debounce)

    elif message.startswith("!"):  # proxy mod commands (!top, !rank, etc.)
        logging.info("proxy command received")
        api.exec_command(message)
        time.sleep(debounce)

    elif  message.startswith("$"):  # viewer sound commands
        for sound_cmd in SOUND_CMDS:
            if message.startswith(sound_cmd):
                logging.info(f"Sound command recieved ({sound_cmd})")
                api.play_sound(sound_cmd.replace('$', '') + '.wav') #odfe appears to only support .wav format, not mp3, so we can hardcode it
                time.sleep(debounce)
    return
Beispiel #24
0
async def fixchat(ctx, author, args):
    api.exec_command(
        f"cl_noprint 0;cg_centertime 3;displaymessage 140 10 ^3{author} ^7has fixed: ^3ingame chat"
    )
Beispiel #25
0
async def snaps(ctx, author, args):
    api.exec_command(
        f"toggle mdd_snap 0 3;cg_centertime 3;displaymessage 140 10 ^3{author} ^7has changed: ^3snaps hud"
    )
Beispiel #26
0
async def lagometer(ctx, author, args):
    api.exec_command(
        f"toggle cg_lagometer 0 1;cg_centertime 3;displaymessage 140 10 ^3{author} ^7has changed: ^3Lagometer"
    )
Beispiel #27
0
async def clear(ctx, author, args):
    api.exec_command(
        f"clear;cg_centertime 3;cg_centertime 3;displaymessage 140 10 ^3{author} ^1Ingame chat has been erased ^3:("
    )
Beispiel #28
0
async def event_message(ctx):
    """Activates for every message"""
    debounce = 1  # interval between consecutive commands and messages
    author = ctx.author.name
    message = ctx.content

    if ";" in message:  # prevent q3 command injections
        message = message[:message.index(";")]

    # bot.py, at the bottom of event_message
    if message.startswith("?"):  # spectator client customization and controls
        message = message.strip('?').lower()
        split_msg = message.split(' ')
        cmd = split_msg[0]
        args = split_msg[1:] if len(split_msg) > 0 else None
        print("Command received:", cmd)

        if cmd == "connect":
            serverstate.connect(args[0])
        elif cmd == "restart":
            connect_ip = servers.get_most_popular_server()
            api.press_key_mult("{Esc}", 2)
            api.press_key("{Enter}")
            api.press_key_mult("{Tab}", 10)
            api.press_key("{Enter}")
            time.sleep(1)
            serverstate.connect(connect_ip)
        elif cmd == "next":
            serverstate.switch_spec('next')
        elif cmd == "prev":
            serverstate.switch_spec('prev')
        elif cmd == "scores":
            api.hold_key(config.get_bind("+scores"), 3.5)
        elif cmd == "clear":
            api.press_key(config.get_bind_fuzzy("clear"))
        elif cmd == "triggers":
            api.press_key(config.get_bind_fuzzy("scr_triggers_draw"))
        elif cmd == "clips":
            api.press_key(config.get_bind_fuzzy("scr_clips_draw"))
        elif cmd == "snaps":
            api.press_key(config.get_bind_fuzzy("mdd_snap"))
        elif cmd == "cgaz":
            api.press_key(config.get_bind_fuzzy("mdd_cgaz"))
        elif cmd == "checkpoints":
            api.press_key(config.get_bind_fuzzy("df_checkpoints"))
        elif cmd == "nodraw":
            api.press_key(config.get_bind_fuzzy("df_mp_NoDrawRadius"))
        elif cmd == "angles":
            api.press_key(config.get_bind("toggle df_chs1_Info6 0 40"))
        elif cmd == "obs":
            api.press_key(config.get_bind("toggle df_chs1_Info7 0 50"))
        elif cmd == "clean":
            api.press_key(config.get_bind_fuzzy("cg_draw2D"))
        elif cmd == "sky":
            api.press_key(config.get_bind_fuzzy("r_fastsky"))
        elif cmd == "vote":
            api.press_key(config.get_bind(f"vote {args[0]}"))
        elif cmd == "speedinfo":
            api.press_key(config.get_bind("toggle df_chs1_Info5 0 1"))
        elif cmd == "speedorig":
            api.press_key(config.get_bind_fuzzy("df_drawSpeed"))
        elif cmd == "huds":
            api.press_key(config.get_bind("toggle mdd_hud 0 1"))
        elif cmd == "inputs":
            api.press_key(config.get_bind_fuzzy("df_chs0_draw"))
        elif cmd == "n1":
            api.exec_command(
                api.exec_command(
                    f"varcommand say ^{author[0]}{author} ^7> ^2Nice one, $chsinfo(117) ^2!"
                ))

        # Mod commands
        elif cmd == "brightness":
            if not ctx.author.is_mod:
                await ctx.channel.send(
                    f"{author}, you do not have the correct permissions to use this command."
                )
                return
            value = args[0]
            if value.isdigit() and (0 < int(value) <= 5):
                api.exec_command(f"r_mapoverbrightbits {value};vid_restart")
            else:
                await ctx.channel.send(
                    f" {author}, the valid values for brightness are 1-5.")
        elif cmd == "picmip":
            if not ctx.author.is_mod:
                await ctx.channel.send(
                    f"{author}, you do not have the correct permissions to use this command."
                )
                return
            value = args[0]
            if value.isdigit() and (0 <= int(value) <= 6):
                api.exec_command(f"r_picmip {value};vid_restart")
            else:
                await ctx.channel.send(
                    f"{author}, the allowed values for picmip are 0-5.")

        # Currently disabled. Possibly useful for the future:

        # elif cmd == "cgaz":
        #     mode = args[0] if len(args) > 0 and 0 < int(args[0]) <= 15 else "toggle"
        #     if mode == "toggle":
        #         api.press_key(config.get_bind("toggle mdd_cgaz 0 1"))
        #     else:
        #         api.exec_command(f"df_hud_cgaz {mode}")

        # elif cmd == "cv" and "kick" not in message:
        #     api.exec_command(f"{message}")

        time.sleep(debounce)

    elif message.startswith(">") or message.startswith("<"):  # chat bridge
        if author.lower() == 'nightbot'.lower(
        ):  # ignore twitch Nightbot's name
            author = ''
            author_color_char = 0
        else:
            author += ' ^7> '
            author_color_char = author[0]
        message = message.lstrip('>').lstrip('<')
        api.exec_command(f"say ^{author_color_char}{author} ^2{message}")
        print("Chat message sent")
        time.sleep(debounce)

    elif message.startswith("!"):  # proxy mod commands (!top, !rank, etc.)
        print("proxy command received")
        api.exec_command(message)
        time.sleep(debounce)

    return
Beispiel #29
0
def process_line(line):
    """
    Processes a console line into a more useful format. Extracts type (say, announcement, print) as well as author
    and content if applicable.
    :param line: Console line to be processed
    :return: Data dictionary containing useful data about the line
    """
    import serverstate
    line = line.strip()

    line_data = {
        "id": message_to_id(f"{time.time()}_MISC"),
        "type": "MISC",
        "command": None,
        "author": None,
        "content": line,
        "timestamp": time.time()
    }

    # SERVERCOMMAND

    try:
        # Don't log if it's a report
        if "report written to system/reports/initialstate.txt" in line or "report written to system/reports/serverstate.txt" in line:
            pass
        else:
            logging.info(f"[Q3] {line}")

        if line in {"VoteVote passed.", "RE_Shutdown( 0 )"}:
            if not serverstate.PAUSE_STATE:
                serverstate.PAUSE_STATE = True
                logging.info("Game is loading. Pausing state.")

        if 'broke the server record with' in line and is_server_msg(
                line, 'broke the server record with'):
            """ 
                Maybe we can also add a display message with the player name and/or the record 
                #playerName = line[:line.index(' broke the server record with')]
                #playerRecord = line[line.index(' broke the server record with') + len(' broke the server record with'):]
                #api.display_message("{playerName} broke the record with {playerRecord}")
            """
            api.play_sound("worldrecord.wav")

        if 'called a vote:' in line and is_server_msg(line, 'called a vote:'):
            logging.info("Vote detected.")
            if serverstate.STATE.num_players == 2:  # only bot and 1 other player in game, always f1
                logging.info("1 other player in server, voting yes.")
                api.exec_command("vote yes")
                api.exec_command("say ^7Vote detected. Voted ^3f1^7.")
            else:
                logging.info(
                    "Multiple people in server, initiating vote tally.")
                serverstate.STATE.init_vote()
                api.exec_command(
                    "say ^7Vote detected. Should I vote yes or no? Send ^3?^7f1 for yes and ^3?^7f2 for no."
                )

        if line.startswith('Not recording a demo.') or line.startswith(
                "report written to system/reports/initialstate.txt"):
            if serverstate.CONNECTING:
                time.sleep(1)
                serverstate.CONNECTING = False
            elif serverstate.VID_RESTARTING:
                time.sleep(1)
                logging.info("vid_restart done.")
                serverstate.PAUSE_STATE = False
                serverstate.VID_RESTARTING = False
            elif serverstate.PAUSE_STATE:
                time.sleep(1)
                serverstate.PAUSE_STATE = False
                logging.info("Game loaded. Continuing state.")
                serverstate.STATE.say_connect_msg()
        # sc_r = r"^\^5serverCommand:\s*(\d+?)\s*:\s*(.+?)$"
        # match = re.match(sc_r, line)
        #
        # sv_command_id = match.group(1)
        # sv_command = match.group(2)

        def parse_chat_message(command):
            # CHAT MESSAGE (BY PLAYER)

            # chat_message_r = r"^chat\s*\"[\x19]*\[*(.*?)[\x19]*?\]*?\x19:\s*(.*?)\".*?$" #/developer 1
            chat_message_r = r"(.*)\^7: \^\d(.*)"
            match = re.match(chat_message_r, command)

            chat_name = match.group(1)
            chat_message = match.group(2)

            line_data["id"] = message_to_id(f"SAY_{chat_name}_{chat_message}")
            line_data["type"] = "SAY"
            line_data["author"] = chat_name
            line_data["content"] = chat_message
            line_data["command"] = cmd.scan_for_command(chat_message)

        def parse_chat_announce(command):
            # CHAT ANNOUNCEMENT
            chat_announce_r = r"^chat\s*\"(.*?)\".*?$"
            match = re.match(chat_announce_r, command)

            chat_announcement = match.group(1)

            line_data["id"] = message_to_id(f"ANN_{chat_announcement}")
            line_data["type"] = "ANNOUNCE"
            line_data["author"] = None
            line_data["content"] = chat_announcement

        def parse_print(command):
            # PRINT
            print_r = r"^print\s*\"(.*?)$"  # Prints have their ending quotation mark on the next line, very strange
            match = re.match(print_r, command)

            print_message = match.group(1)

            line_data["id"] = message_to_id(f"PRINT_{print_message}")
            line_data["type"] = "PRINT"
            line_data["author"] = None
            line_data["content"] = print_message

        def parse_scores(command):
            # SCORES
            scores_r = r"^scores\s+(.*?)$"
            match = re.match(scores_r, command)

            scores = match.group(1)

            line_data["id"] = message_to_id(f"SCORES_{scores}")
            line_data["type"] = "SCORES"
            line_data["author"] = None
            line_data["content"] = scores

        for fun in [
                parse_chat_message, parse_chat_announce, parse_print,
                parse_scores
        ]:
            try:
                # fun(sv_command)
                fun(line)
                break
            except:
                continue
    except:
        return line_data

    # print((line_data)
    return line_data
Beispiel #30
0
async def clips(ctx, author, args):
    api.exec_command(
        f"toggle r_renderClipBrushes 0 1;cg_centertime 3;displaymessage 140 10 ^3{author} ^7has changed: ^3Render Clips"
    )