async def process_subcommands(content, message, user, safe=False): command_indicators = { ')': '(', ']': '[', '`': '`', '}': '{', } while True: updated = False for i in range(len(content)): if content[i] in command_indicators.keys(): for j in range(i - 1, 0, -1): if content[j] == command_indicators[ content[i]] and content[j - 1] == '$': updated = True message.content = content[j + 1:i] command = message.content.split() if not command: return if command[0] not in bc.config.commands.data.keys( ): if command[ 0] in bc.config.commands.aliases.keys( ): command[0] = bc.config.commands.aliases[ command[0]] else: await message.channel.send( f"Unknown command '{command[0]}'") result = "" if command and command[0] in bc.commands.data.keys( ): log.debug( f"Processing subcommand: {command[0]}: {message.content}" ) cmd = bc.commands.data[command[0]] if cmd.can_be_subcommand(): result = await cmd.run(message, command, user, silent=True) if result is None or ( safe and not const. ALNUM_STRING_REGEX.match(content)): result = "" else: await message.channel.send( f"Command '{command[0]}' can not be used as subcommand" ) content = content[:j - 1] + result + content[i + 1:] log.debug2( f"Command (during processing subcommands): {content}" ) break if updated: break if not updated: break return content
def register(self, reload: bool = False) -> None: """Find plugins in plugins directory and register them""" plugin_directory = os.path.join(os.path.dirname(__file__), "plugins") plugin_modules = ['src.plugins.' + os.path.splitext(path)[0] for path in os.listdir(plugin_directory) if os.path.isfile(os.path.join(plugin_directory, path)) and path.endswith(".py")] private_plugin_directory = os.path.join(os.path.dirname(__file__), "plugins", "private") plugin_modules += [Util.path_to_module( f"src.plugins.private.{os.path.relpath(path, private_plugin_directory)}." f"{os.path.splitext(file)[0]}") for path, _, files in os.walk(private_plugin_directory) for file in files if os.path.isfile(os.path.join(private_plugin_directory, path, file)) and file.endswith(".py")] importlib.invalidate_caches() for module in plugin_modules: log.debug2(f"Processing plugins from module: {module}") plugins_file = importlib.import_module(module) if reload: importlib.reload(plugins_file) plugins = [obj[1] for obj in inspect.getmembers(plugins_file, inspect.isclass) if (obj[1].__module__ == module) and issubclass(obj[1], BasePlugin)] if len(plugins) == 1: plugin = plugins[0] actual_functions_list = [ func[0] for func in inspect.getmembers(plugin, inspect.isfunction) if not func[0].startswith('_') ] if all(x in actual_functions_list for x in self._plugin_functions_interface): p = plugin() self._plugins[p.get_classname()] = p log.debug(f"Registered plugin '{p.get_classname()}'") else: log.error(f"Class '{p.get_classname()}' does comply with BasePlugin interface") elif len(plugins) > 1: log.error(f"Module '{module}' have more than 1 class in it") else: log.error(f"Module '{module}' have no classes in it")
async def run(self, message, command, user, silent=False): if len(inspect.stack(0)) >= const.MAX_SUBCOMMAND_DEPTH: return null(await message.channel.send( "ERROR: Maximum subcommand depth is reached!")) log.debug(f"Processing command: {message.content}") channel_id = message.channel.id if isinstance( message.channel, discord.Thread): # Inherit command permissions for threads channel_id = message.channel.parent_id if not self.is_available(channel_id): return null(await message.channel.send( f"Command '{command[0]}' is not available in this channel")) if user is not None and self.permission > user.permission_level: return null(await message.channel.send( f"You don't have permission to call command '{command[0]}'")) self.times_called += 1 postpone_execution = [ "addcmd", "updcmd", "addextcmd", "updextcmd", "addbgevent", "addresponse", "updresponse", ] if message.content.split(' ')[0][1:] not in postpone_execution: log.debug2(f"Command (before processing): {message.content}") message.content = await self.process_subcommands( message.content, message, user) log.debug2( f"Command (after processing subcommands): {message.content}") else: log.debug2("Subcommands are not processed!") command = message.content[1:].split(' ') command = list(filter(None, command)) if self.perform is not None: return await self.get_actor()(message, command, silent) elif self.message is not None: response = self.message log.debug2(f"Command (before processing): {response}") response = await self.process_variables(response, message, command) log.debug2(f"Command (after processing variables): {response}") response = await self.process_subcommands(response, message, user) log.debug2(f"Command (after processing subcommands): {response}") if response: if not silent: if len(response) > const.DISCORD_MAX_MESSAGE_LENGTH * 5: await message.channel.send( "ERROR: Max message length exceeded " f"({len(response)} > {const.DISCORD_MAX_MESSAGE_LENGTH * 5})" ) elif len(const.UNICODE_EMOJI_REGEX.findall(response)) > 50: await message.channel.send( "ERROR: Max amount of Unicode emojis for one message exceeded " f"({len(const.UNICODE_EMOJI_REGEX.findall(response))} > {50})" ) else: for chunk in Msg.split_by_chunks( response, const.DISCORD_MAX_MESSAGE_LENGTH): await message.channel.send(chunk) return response elif self.cmd_line is not None: cmd_line = self.cmd_line[:] log.debug2(f"Command (before processing): {cmd_line}") cmd_line = await self.process_variables(cmd_line, message, command, safe=True) log.debug2(f"Command (after processing variables): {cmd_line}") cmd_line = await self.process_subcommands(cmd_line, message, user, safe=True) log.debug2(f"Command (after processing subcommands): {cmd_line}") return await Util.run_external_command(message, cmd_line, silent) else: await message.channel.send( f"Command '{command[0]}' is not callable")
def _list_env_var_flags(self): log.debug2("--- Environment variable flags: ---") for key, value in FF.get_list().items(): log.debug2(f"{key}: {value}") log.debug2("--- End of environment variable flags: ---")
async def _process_reminders_iteration(self) -> None: log.debug3("Reminder processing iteration has started") now = datetime.datetime.now().replace(second=0).strftime( const.REMINDER_DATETIME_FORMAT) to_remove = [] to_append = [] reminder_do_not_update_flag = False for key, rem in self.config.reminders.items(): for i in range(len(rem.prereminders_list)): prereminder = rem.prereminders_list[i] used_prereminder = rem.used_prereminders_list[i] if prereminder == 0 or used_prereminder: continue prereminder_time = (datetime.datetime.now().replace(second=0) + datetime.timedelta(minutes=prereminder)) if rem == prereminder_time.strftime( const.REMINDER_DATETIME_FORMAT): channel = self.get_channel(rem.channel_id) e = DiscordEmbed() clock_emoji = get_clock_emoji( datetime.datetime.now().strftime("%H:%M")) e.title(f"{prereminder} minutes left until reminder") e.description(rem.message + "\n" + rem.notes) e.color(random.randint(0x000000, 0xffffff)) e.timestamp( datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=prereminder)) e.footer(text=rem.author) await channel.send("", embed=e.get()) rem.used_prereminders_list[i] = True if rem == now: channel = self.get_channel(rem.channel_id) clock_emoji = get_clock_emoji( datetime.datetime.now().strftime("%H:%M")) e = DiscordEmbed() e.title(f"{clock_emoji} You asked to remind") e.description(rem.message + "\n" + rem.notes) e.color(random.randint(0x000000, 0xffffff)) e.timestamp(datetime.datetime.now(datetime.timezone.utc)) e.footer(text=rem.author) await channel.send( ' '.join(rem.ping_users if rem.ping_users else ""), embed=e.get()) for user_id in rem.whisper_users: await Msg.send_direct_message( self.get_user(user_id), f"You asked to remind at {now} -> {rem.message}", False) if rem.email_users: mail = Mail(self.secret_config) mail.send( rem.email_users, f"Reminder: {rem.message}", f"You asked to remind at {now} -> {rem.message}") if rem.repeat_after > 0: new_time = datetime.datetime.now().replace( second=0, microsecond=0) + rem.get_next_event_delta() new_time = new_time.strftime( const.REMINDER_DATETIME_FORMAT) to_append.append( Reminder(str(new_time), rem.message, rem.channel_id, rem.author, rem.time_created)) to_append[-1].repeat_after = rem.repeat_after to_append[ -1].repeat_interval_measure = rem.repeat_interval_measure to_append[-1].prereminders_list = rem.prereminders_list to_append[-1].used_prereminders_list = [False] * len( rem.prereminders_list) to_append[-1].notes = rem.notes log.debug2( f"Scheduled renew of recurring reminder - old id: {key}" ) to_remove.append(key) elif rem < now: log.debug2(f"Scheduled reminder with id {key} removal") to_remove.append(key) else: prereminders_delay = 0 if rem.prereminders_list: prereminders_delay = max(rem.prereminders_list) if ((datetime.datetime.strptime( rem.time, const.REMINDER_DATETIME_FORMAT) - datetime.datetime.now()) < datetime.timedelta( minutes=(5 + prereminders_delay / 60))): reminder_do_not_update_flag = True bc.do_not_update[ DoNotUpdateFlag.REMINDER] = reminder_do_not_update_flag for key in to_remove: self.config.reminders.pop(key) for item in to_append: key = self.config.ids["reminder"] self.config.reminders[key] = item self.config.ids["reminder"] += 1 log.debug3("Reminder processing iteration has finished")