Esempio n. 1
0
 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
Esempio n. 2
0
 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")
Esempio n. 3
0
 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")
Esempio n. 4
0
 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: ---")
Esempio n. 5
0
 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")