async def routine_anti_afk(): print("[Subroutine] AntiAFK") try: await CFG.add_action_queue(ActionQueueItem("anti_afk")) CFG.anti_afk_runs += 1 if CFG.anti_afk_runs % 3 == 0: await CFG.add_action_queue(ActionQueueItem("chat", {"msgs": ["/clear"]})) await CFG.add_action_queue(ActionQueueItem("advert")) print("[Subroutine] Queued Advert") CFG.anti_afk_runs = 0 except Exception: error_log(traceback.format_exc())
async def m(self, ctx: commands.Context): args = await self.get_args(ctx) if not args: await ctx.send('[Please specify a message! (i.e. "!m Hello World!"]') return msg = " ".join(args) if len(msg) > 100: await ctx.send( "[In-game character limit is 100! Please shorten your message.]" ) return is_dev = await self.is_dev(ctx.message.author) # Disable whisper functionality if msg.startswith("[") or msg.startswith("/w"): await ctx.send("[You do not have permission to whisper.]") return # Make muting dev-only elif msg.startswith("/mute") or msg.startswith("/unmute"): if is_dev: action = ActionQueueItem("chat", {"msgs": [msg]}) else: await ctx.send("[You do not have permission to mute/unmute.]") return # Allow /e, disable all other commands from twitch elif msg.startswith("/"): if msg.startswith("/e"): CFG.current_emote = msg action = ActionQueueItem("chat", {"msgs": [msg]}) else: return # Chat with CamDev Tag elif is_dev: action = ActionQueueItem( "chat_with_name", {"name": "[CamDev]:", "msgs": [msg]} ) # Standard chat else: action = ActionQueueItem( "chat_with_name", {"name": f"{ctx.message.author.display_name}:", "msgs": [msg]}, ) await CFG.add_action_queue(action)
async def camera_turn_handler( self, turn_camera_direction: str, ctx: commands.Context ): turn_degrees: float = 45 max_turn_degrees: float = 360 args = await self.get_args(ctx) if args: try: turn_degrees = float(args[0]) if not (max_turn_degrees >= turn_degrees > 0): await ctx.send( f"[{args[0]} is too high/low! Please use an angle between 0 and 360.]" ) return except Exception: await ctx.send("[Error! Invalid number specified.]") return action = ActionQueueItem( "camera_turn", { "turn_direction": turn_camera_direction, "turn_degrees": turn_degrees, }, ) await CFG.add_action_queue(action)
async def camera_pitch_handler( self, pitch_camera_direction: str, ctx: commands.Context ): pitch: float = 45 max_pitch: float = 180 args = await self.get_args(ctx) if args: try: pitch = float(args[0]) if not (max_pitch >= pitch > 0): await ctx.send( f"[{args[0]} is too high/low! Please use an angle between 0 and {max_pitch}.]" ) return except Exception: await ctx.send("[Error! Invalid number specified.]") return action = ActionQueueItem( "camera_pitch", { "pitch_direction": pitch_camera_direction, "pitch_degrees": pitch, }, ) await CFG.add_action_queue(action)
async def rejoin(self, ctx: commands.Context): if await self.is_dev(ctx.author): await ctx.send(f"[@{ctx.author.name} Added restart to queue]") CFG.crashed = True await CFG.add_action_queue(ActionQueueItem("rejoin")) CFG.crashed = False else: await ctx.send("[You do not have permission!]")
async def routine_check_better_server(): print("[Subroutine] Better Server Check") try: while CFG.crashed: print("[Better Server Check] Currently crashed, waiting...") await async_sleep(60) await CFG.add_action_queue(ActionQueueItem("check_for_better_server")) except Exception: error_log(traceback.format_exc())
async def routine_reboot(): action_queue_item = ActionQueueItem( "chat", {"msgs": ["[System restart in 2 minutes]"]} ) await CFG.add_action_queue(action_queue_item) await async_sleep(60) action_queue_item = ActionQueueItem( "chat", {"msgs": ["[System restart in 1 minute]"]} ) await CFG.add_action_queue(action_queue_item) await async_sleep(60) log_process("System Shutdown") log("Initiating shutdown sequence") action_queue_item = ActionQueueItem("chat", {"msgs": ["[System restarting]"]}) await CFG.add_action_queue(action_queue_item) await async_sleep(10) system("shutdown /f /r /t 0") # nosec
async def routine_crash_check(): if CFG.crashed: return try: crashed = await do_crash_check() if crashed: print("[Routine] Crash detected") await CFG.add_action_queue(ActionQueueItem("handle_crash")) await async_sleep(60) except Exception: error_log(traceback.format_exc())
async def nav(self, ctx: commands.Context): args = await self.get_args(ctx) if not args or args[0].lower() not in CFG.nav_locations: await ctx.send("[Please specify a valid location!]") await ctx.send(f'[{", ".join(list(CFG.nav_locations.keys()))}]') return location = args[0].lower() await ctx.send("[Requested AutoNav! If we fail, re-run the command!]") await ctx.send( "[If we did not respawn, please run !respawnforce (we're stuck!) and re-run the !nav command.]" ) action = ActionQueueItem("autonav", {"location": location}) await CFG.add_action_queue(action)
async def mouse(self, ctx: commands.Context): args = await self.get_args(ctx) if len(args) < 2: await ctx.send( "[Please specify the two numbers (x and y) that you want the mouse to" " move away from center of the screen!]" ) return try: x = int(args[0]) y = int(args[1]) except Exception: await ctx.send("[Error! Invalid number(s) specified.]") return action = ActionQueueItem("mouse_move", {"x": x, "y": y, "twitch_ctx": ctx}) await CFG.add_action_queue(action)
async def item(self, ctx: commands.Context): args = await self.get_args(ctx) if len(args) < 1: await ctx.send("[Please specify an item number! (Must be 1-8)") return try: item_number = int(args[0]) if item_number not in CFG.backpack_item_positions: raise except Exception: await ctx.send("[Error! Invalid number specified. (Must be 1-8)]") return if not CFG.backpack_open: await ctx.send( "[Doesn't seem like the backpack is open! Clicking anyway, just in case." "(Use !backpack to open, if needed)]" ) action = ActionQueueItem("backpack_item", {"item_number": item_number}) await CFG.add_action_queue(action)
async def zoom_handler(self, zoom_key, ctx: commands.Context): zoom_amount: float = 15 max_zoom_amount: float = 100 args = await self.get_args(ctx) if args: try: zoom_amount = float(args[0]) if not (max_zoom_amount >= zoom_amount > 0): await ctx.send( f"[{args[0]} is too high/low! Please use a percentage between 0 and {max_zoom_amount}.]" ) return except Exception: await ctx.send("[Error! Invalid number specified.]") return action = ActionQueueItem( "camera_zoom", {"zoom_key": zoom_key, "zoom_amount": zoom_amount}, ) await CFG.add_action_queue(action)
async def routine_ocr(): if ( CFG.action_running or CFG.crashed or (not CFG.chat_cleared_after_response) or (not CFG.chat_ocr_ready) ): return if not CFG.chat_ocr_active: if not CFG.chat_ocr_activation_queued and await can_activate_ocr(): await CFG.add_action_queue(ActionQueueItem("activate_ocr")) else: try: # HACK: psuedo-blocking, make non-async CFG.chat_ocr_ready = False await do_chat_ocr() except Exception: CFG.chat_ocr_ready = True CFG.chat_cleared_after_response = True error_log(traceback.format_exc())
async def check_if_game_loaded() -> bool: game_loaded = False log("Loading into game") for attempt in range(CFG.max_attempts_game_loaded): if await check_ui_loaded(): game_loaded = True break log(f"Loading into game (Check #{attempt}/{CFG.max_attempts_game_loaded})" ) await async_sleep(1) if not game_loaded: log("Failed to load into game.") notify_admin("Failed to load into game") await async_sleep(5) await CFG.add_action_queue(ActionQueueItem("handle_crash")) log("") return False log("") return True
async def do_logic_on_messages(messages): response_messages = [] response_message_objs = [] for obj in messages: response = await chat_logic.logic_core(obj) if response is None: continue response_messages.append(response) response_obj = obj response_obj["response"] = response response_message_objs.append(response_obj) if response_messages: insert_interactions_to_db(response_message_objs) output_log("chat_ai_title", "[Chat AI]") output_log("chat_ai_subtitle", "Responding...") action_item = ActionQueueItem("ocr_chat", {"msgs": response_messages}) await CFG.add_action_queue(action_item) else: CFG.chat_ocr_ready = True
async def leap(self, ctx: commands.Context): forward_time = 0.4 jump_time = 0.3 max_forward_time = 1 max_jump_time = 1 args = await self.get_args(ctx) if len(args) > 0: try: number = float(args[0]) if max_forward_time >= number > 0: forward_time = number else: await ctx.send( f"[{args[0]} is too high/low! Please use a time between 0 and {max_forward_time}.]" ) return except Exception: await ctx.send("[Error! Invalid number specified.]") return if len(args) > 1: try: number = float(args[1]) if max_jump_time >= number > 0: jump_time = number else: await ctx.send( f"[{args[1]} is too high/low! Please use a time between 0 and {max_jump_time}.]" ) return except Exception: await ctx.send("[Error! Invalid number specified.]") return action = ActionQueueItem( "leap", {"forward_time": forward_time, "jump_time": jump_time}, ) await CFG.add_action_queue(action)
async def move(self, ctx: commands.Context): move_time: float = 1 max_move_time: float = 10 valid_movement_keys = ["w", "a", "s", "d"] args = await self.get_args(ctx) if not args or args[0].lower() not in valid_movement_keys: await ctx.send('[Please specify a valid direction! (i.e. "!move w")]') return move_key = args[0].lower() if len(args) > 1: try: move_time = float(args[1]) if not (max_move_time >= move_time > 0): await ctx.send( f"[{args[1]} is too high/low! Please use a unit between 0 and {max_move_time}.]" ) return except Exception: await ctx.send("[Error! Invalid number specified.]") return action = ActionQueueItem("move", {"move_key": move_key, "move_time": move_time}) await CFG.add_action_queue(action)
async def backpack(self, ctx: commands.Context): await ctx.send( f"[{'Closing' if CFG.backpack_open else 'Opening'} backpack," " please make sure it's closed when you're done!]" ) await CFG.add_action_queue(ActionQueueItem("backpack_toggle"))
async def grief(self, ctx: commands.Context): await CFG.add_action_queue(ActionQueueItem("grief"))
async def check_for_better_server(): last_check_time = time() output_log("last_check_for_better_server", last_check_time) previous_status_text = read_output_log("change_server_status_text") output_log("change_server_status_text", "") log_process("Checking for better server") current_server_id = await get_current_server_id() if current_server_id == "ERROR": for i in range(CFG.max_attempts_better_server): log_process( f"Attempt {i+1}/{CFG.max_attempts_better_server} failed! Retrying better server check..." ) await async_sleep(5) current_server_id = await get_current_server_id() if current_server_id != "ERROR": break if current_server_id == "ERROR": log_process( f"Failed to connect to Roblox API {CFG.max_attempts_better_server} times! Skipping..." ) await async_sleep(5) log_process("") return True if current_server_id == "N/A": for id in list(CFG.game_ids_other.keys()): current_server_id == await get_current_server_id(id) if current_server_id != "N/A": break if current_server_id == "N/A": log_process("Could not find FumoCam in any servers") await CFG.add_action_queue(ActionQueueItem("handle_crash")) return False else: log_process("") log("") return True ( should_change_servers, change_server_status_text, ) = await check_if_should_change_servers(current_server_id) log(change_server_status_text) output_log("change_server_status_text", change_server_status_text) if not should_change_servers: log("PASS! Current server has sufficient players") log("") log_process("") return True elif previous_status_text != change_server_status_text: if "Could not find FumoCam" in change_server_status_text: for attempt in range(CFG.max_attempts_better_server): log(f"Rechecking (attempt {attempt+1}/{CFG.max_attempts_better_server}" ) current_server_id = await get_current_server_id() while current_server_id == "": log_process("Retrying get current server check") await async_sleep(5) current_server_id = await get_current_server_id() ( should_change_servers, change_server_status_text, ) = await check_if_should_change_servers(current_server_id) if "Could not find FumoCam" not in change_server_status_text: break if should_change_servers: notify_admin(change_server_status_text) await CFG.add_action_queue( ActionQueueItem("handle_join_new_server")) else: await CFG.add_action_queue( ActionQueueItem("handle_join_new_server")) log("") log_process("")
async def hidemouse(self, ctx: commands.Context): await CFG.add_action_queue(ActionQueueItem("mouse_hide"))
async def jump(self, ctx: commands.Context): await CFG.add_action_queue(ActionQueueItem("jump"))
async def unmute(self, ctx: commands.Context): action = ActionQueueItem("mute", {"set_muted": False}) await CFG.add_action_queue(action)
async def respawn(self, ctx: commands.Context): await CFG.add_action_queue(ActionQueueItem("respawn"))
async def sit(self, ctx: commands.Context): await CFG.add_action_queue(ActionQueueItem("sit"))
async def use(self, ctx: commands.Context): await CFG.add_action_queue(ActionQueueItem("use"))
async def click(self, ctx: commands.Context): await CFG.add_action_queue(ActionQueueItem("mouse_click"))