From 638eee0d685c06d258cb55287204ca97bca7c344 Mon Sep 17 00:00:00 2001 From: jesopo Date: Tue, 10 Dec 2019 05:27:35 +0000 Subject: move core modules to src/core_modules, make them uneffected by white/black list --- src/ModuleManager.py | 70 +++-- src/core_modules/admin.py | 208 ++++++++++++++ src/core_modules/channel_access.py | 91 ++++++ src/core_modules/channel_blacklist.py | 40 +++ src/core_modules/channel_keys.py | 55 ++++ src/core_modules/check_mode.py | 43 +++ src/core_modules/commands/__init__.py | 424 ++++++++++++++++++++++++++++ src/core_modules/commands/outs.py | 28 ++ src/core_modules/config.py | 244 ++++++++++++++++ src/core_modules/ctcp.py | 29 ++ src/core_modules/deferred_read.py | 23 ++ src/core_modules/fake_echo.py | 13 + src/core_modules/format_activity.py | 285 +++++++++++++++++++ src/core_modules/help.py | 124 ++++++++ src/core_modules/ignore.py | 163 +++++++++++ src/core_modules/ircv3_chathistory.py | 36 +++ src/core_modules/ircv3_echo_message.py | 21 ++ src/core_modules/ircv3_labeled_responses.py | 67 +++++ src/core_modules/ircv3_message_tracking.py | 17 ++ src/core_modules/ircv3_metadata.py | 16 ++ src/core_modules/ircv3_msgid.py | 31 ++ src/core_modules/ircv3_sasl/README.md | 46 +++ src/core_modules/ircv3_sasl/__init__.py | 195 +++++++++++++ src/core_modules/ircv3_sasl/scram.py | 130 +++++++++ src/core_modules/ircv3_server_time.py | 12 + src/core_modules/ircv3_sts.py | 70 +++++ src/core_modules/line_handler/__init__.py | 260 +++++++++++++++++ src/core_modules/line_handler/channel.py | 160 +++++++++++ src/core_modules/line_handler/core.py | 154 ++++++++++ src/core_modules/line_handler/ircv3.py | 138 +++++++++ src/core_modules/line_handler/message.py | 109 +++++++ src/core_modules/line_handler/user.py | 102 +++++++ src/core_modules/modules.py | 122 ++++++++ src/core_modules/more.py | 23 ++ src/core_modules/nick_regain.py | 48 ++++ src/core_modules/perform.py | 76 +++++ src/core_modules/permissions/__init__.py | 357 +++++++++++++++++++++++ src/core_modules/print_activity.py | 48 ++++ src/core_modules/proxy.py | 38 +++ src/core_modules/signals.py | 65 +++++ src/core_modules/silence.py | 70 +++++ src/core_modules/strip_color.py | 22 ++ src/core_modules/strip_otr.py | 15 + src/core_modules/throttle.py | 17 ++ 44 files changed, 4275 insertions(+), 30 deletions(-) create mode 100644 src/core_modules/admin.py create mode 100644 src/core_modules/channel_access.py create mode 100644 src/core_modules/channel_blacklist.py create mode 100644 src/core_modules/channel_keys.py create mode 100644 src/core_modules/check_mode.py create mode 100644 src/core_modules/commands/__init__.py create mode 100644 src/core_modules/commands/outs.py create mode 100644 src/core_modules/config.py create mode 100644 src/core_modules/ctcp.py create mode 100644 src/core_modules/deferred_read.py create mode 100644 src/core_modules/fake_echo.py create mode 100644 src/core_modules/format_activity.py create mode 100644 src/core_modules/help.py create mode 100644 src/core_modules/ignore.py create mode 100644 src/core_modules/ircv3_chathistory.py create mode 100644 src/core_modules/ircv3_echo_message.py create mode 100644 src/core_modules/ircv3_labeled_responses.py create mode 100644 src/core_modules/ircv3_message_tracking.py create mode 100644 src/core_modules/ircv3_metadata.py create mode 100644 src/core_modules/ircv3_msgid.py create mode 100644 src/core_modules/ircv3_sasl/README.md create mode 100644 src/core_modules/ircv3_sasl/__init__.py create mode 100644 src/core_modules/ircv3_sasl/scram.py create mode 100644 src/core_modules/ircv3_server_time.py create mode 100644 src/core_modules/ircv3_sts.py create mode 100644 src/core_modules/line_handler/__init__.py create mode 100644 src/core_modules/line_handler/channel.py create mode 100644 src/core_modules/line_handler/core.py create mode 100644 src/core_modules/line_handler/ircv3.py create mode 100644 src/core_modules/line_handler/message.py create mode 100644 src/core_modules/line_handler/user.py create mode 100644 src/core_modules/modules.py create mode 100644 src/core_modules/more.py create mode 100644 src/core_modules/nick_regain.py create mode 100644 src/core_modules/perform.py create mode 100644 src/core_modules/permissions/__init__.py create mode 100644 src/core_modules/print_activity.py create mode 100644 src/core_modules/proxy.py create mode 100644 src/core_modules/signals.py create mode 100644 src/core_modules/silence.py create mode 100644 src/core_modules/strip_color.py create mode 100644 src/core_modules/strip_otr.py create mode 100644 src/core_modules/throttle.py (limited to 'src') diff --git a/src/ModuleManager.py b/src/ModuleManager.py index 2cbc3935..c7b4549f 100644 --- a/src/ModuleManager.py +++ b/src/ModuleManager.py @@ -102,28 +102,46 @@ class ModuleManager(object): timers: Timers.Timers, config: Config.Config, log: Logging.Log, - directories: typing.List[str]): + core_modules: str, + extra_modules: typing.List[str]): self.events = events self.exports = exports self.config = config self.timers = timers self.log = log - self.directories = directories + self._core_modules = core_modules + self._extra_modules = extra_modules self.modules = {} # type: typing.Dict[str, LoadedModule] - def list_modules(self) -> typing.List[ModuleDefinition]: + def _list_modules(self, directory: str + ) -> typing.Dict[str, ModuleDefinition]: modules = [] - - for directory in self.directories: - for file_module in glob.glob(os.path.join(directory, "*.py")): - modules.append(self.define_module(ModuleType.FILE, file_module)) - - for directory_module in glob.glob(os.path.join( - directory, "*", "__init__.py")): - modules.append(self.define_module(ModuleType.DIRECTORY, - directory_module)) - return sorted(modules, key=lambda module: module.name) + for file_module in glob.glob(os.path.join(directory, "*.py")): + modules.append(self.define_module(ModuleType.FILE, file_module)) + + for directory_module in glob.glob(os.path.join( + directory, "*", "__init__.py")): + modules.append(self.define_module(ModuleType.DIRECTORY, + directory_module)) + return {definition.name: definition for definition in modules} + + def list_modules(self, whitelist: typing.List[str], + blacklist: typing.List[str]) -> typing.Dict[str, ModuleDefinition]: + core_modules = self._list_modules(self._core_modules) + extra_modules = {} + + for directory in self._extra_modules: + for name, module in self._list_modules(directory).items(): + if (not name in extra_modules and + (name in whitelist or + (not whitelist and not name in blacklist))): + extra_modules[name] = module + + modules = {} + modules.update(extra_modules) + modules.update(core_modules) + return modules def define_module(self, type: ModuleType, filename: str ) -> ModuleDefinition: @@ -180,10 +198,6 @@ class ModuleManager(object): return module return None - def _get_magic(self, obj: typing.Any, magic: str, default: typing.Any - ) -> typing.Any: - return getattr(obj, magic) if hasattr(obj, magic) else default - def _check_hashflags(self, bot: "IRCBot.Bot", definition: ModuleDefinition ) -> None: for hashflag, value in definition.hashflags: @@ -275,7 +289,8 @@ class ModuleManager(object): self.log.debug("Module '%s' loaded", [loaded_module.name]) return loaded_module - def _dependency_sort(self, definitions: typing.List[ModuleDefinition]): + def _dependency_sort(self, definitions: typing.Dict[str, ModuleDefinition] + ) -> typing.List[ModuleDefinition]: definitions_ordered = [] definition_names = {d.name: d for d in definitions} @@ -416,22 +431,17 @@ class ModuleManager(object): def _list_valid_modules(self, bot: "IRCBot.Bot", whitelist: typing.List[str], blacklist: typing.List[str]): - module_definitions = self.list_modules() + module_definitions = self.list_modules(whitelist, blacklist) loadable_definitions = [] nonloadable_definitions = [] - for definition in module_definitions: - if definition.name in whitelist or ( - not whitelist and not definition.name in blacklist): - try: - self._check_hashflags(bot, definition) - except ModuleNotLoadableWarning: - nonloadable_definitions.append(definition) - continue - loadable_definitions.append(definition) - else: + for name, definition in module_definitions.items(): + try: + self._check_hashflags(bot, definition) + except ModuleNotLoadableWarning: nonloadable_definitions.append(definition) - + continue + loadable_definitions.append(definition) return (self._dependency_sort(loadable_definitions), nonloadable_definitions) diff --git a/src/core_modules/admin.py b/src/core_modules/admin.py new file mode 100644 index 00000000..364380d4 --- /dev/null +++ b/src/core_modules/admin.py @@ -0,0 +1,208 @@ +#--depends-on commands +#--depends-on permissions + +from src import IRCLine, ModuleManager, utils + +class Module(ModuleManager.BaseModule): + @utils.hook("received.command.nick", min_args=1) + def change_nickname(self, event): + """ + :help: Change my nickname + :usage: + :permission: changenickname + """ + nickname = event["args_split"][0] + event["server"].send_nick(nickname) + + @utils.hook("received.command.raw", min_args=1) + def raw(self, event): + """ + :help: Send a line of raw IRC data + :usage: + :permission: raw + """ + if IRCLine.is_human(event["args"]): + line = IRCLine.parse_human(event["args"]) + else: + line = IRCLine.parse_line(event["args"]) + line = event["server"].send(line) + + if not line == None: + event["stdout"].write("Sent: %s" % line.parsed_line.format()) + else: + event["stderr"].write("Line was filtered") + + @utils.hook("received.command.part") + def part(self, event): + """ + :help: Part from the current or given channel + :usage: [channel] + :permission: part + """ + if event["args"]: + target = event["args_split"][0] + elif event["is_channel"]: + target = event["target"].name + else: + event["stderr"].write("No channel provided") + event["server"].send_part(target) + + def _id_from_alias(self, alias): + return self.bot.database.servers.get_by_alias(alias) + def _server_from_alias(self, alias): + id, server = self._both_from_alias(alias) + return server + def _both_from_alias(self, alias): + id = self._id_from_alias(alias) + if id == None: + raise utils.EventError("Unknown server alias") + return id, self.bot.get_server_by_id(id) + + @utils.hook("received.command.reconnect") + def reconnect(self, event): + """ + :help: Reconnect to the current network + :permission: reconnect + """ + server = event["server"] + alias = str(event["server"]) + if event["args"]: + alias = event["args_split"][0] + server = self._server_from_alias(alias) + + if server: + line = server.send_quit("Reconnecting") + line.events.on("send").hook(lambda e: self.bot.reconnect( + server.id, server.connection_params)) + if not server == event["server"]: + event["stdout"].write("Reconnecting to %s" % alias) + else: + event["stdout"].write("Not connected to %s" % alias) + + @utils.hook("received.command.connect", min_args=1) + def connect(self, event): + """ + :help: Connect to a network + :usage: + :permission: connect + """ + alias = event["args_split"][0] + server = self._server_from_alias(alias) + if server: + raise utils.EventError("Already connected to %s" % str(server)) + + server = self.bot.add_server(self._id_from_alias(alias)) + event["stdout"].write("Connecting to %s" % str(server)) + + @utils.hook("received.command.disconnect") + def disconnect(self, event): + """ + :help: Disconnect from a server + :usage: [server id] + :permission: disconnect + """ + server = event["server"] + id = -1 + alias = str(event["server"]) + if event["args"]: + alias = event["args_split"][0] + id, server = self._both_from_alias(alias) + + if not server == None: + alias = str(server) + server.disconnect() + self.bot.disconnect(server) + elif id in self.bot.reconnections: + self.bot.reconnections[id].cancel() + del self.bot.reconnections[id] + else: + raise utils.EventError("Server not connected") + + event["stdout"].write("Disconnected from %s" % alias) + + @utils.hook("received.command.shutdown") + def shutdown(self, event): + """ + :help: Shutdown bot + :usage: [reason] + :permission: shutdown + """ + reason = event["args"] or "" + for server in self.bot.servers.values(): + line = server.send_quit(reason) + line.events.on("send").hook(self._shutdown_hook(server)) + def _shutdown_hook(self, server): + def shutdown(e): + server.disconnect() + self.bot.disconnect(server) + return shutdown + + @utils.hook("received.command.addserver", min_args=3) + def add_server(self, event): + """ + :help: Add a new server + :usage: :[+] ![@] + :permission: addserver + """ + alias = event["args_split"][0] + hostname, sep, port = event["args_split"][1].partition(":") + tls = port.startswith("+") + port = port.lstrip("+") + + if not hostname or not port or not port.isdigit(): + raise utils.EventError("Please provide :[+]") + port = int(port) + + hostmask = IRCLine.parse_hostmask(event["args_split"][2]) + nickname = hostmask.nickname + username = hostmask.username or nickname + realname = nickname + bindhost = hostmask.hostname or None + + try: + server_id = self.bot.database.servers.add(alias, hostname, port, "", + tls, bindhost, nickname, username, realname) + except Exception as e: + event["stderr"].write("Failed to add server") + self.log.error("failed to add server \"%s\"", [alias], + exc_info=True) + return + event["stdout"].write("Added server '%s'" % alias) + + @utils.hook("received.command.editserver") + @utils.kwarg("min_args", 3) + @utils.kwarg("help", "Edit server details") + @utils.kwarg("usage", "