class Boteco: def __init__(self, host, port, nick): self.state = BotState() self.state._host = host self.state._port = port self.state._nick = nick self._command = BotCommands() self._is_connected = False def connect(self): if self._is_connected: return self._sock = Connection() self._sock.connect(self.state._host, self.state._port) while 1: data = self._read_line() if data == "": break print(data) self._send_command("NICK " + self.state._nick) self._send_command("USER " + self.state._nick + " myhost myhost :" + self.state._nick) # Read the whole MOTD and whatever else there is and simply ignore while 1: data = self._read_line() if data == "": return print(data) def get_socket(self): return self._sock def join_channel(self, channel): self._command.join(channel) def is_connected(self): return self._sock.is_connected def parse(self, data): ircmsg = IRCMessage(data) if ircmsg.msg_type == "": return if ircmsg.msg_type == "PING": self._send_command("PONG " + ircmsg.from_host) elif ircmsg.msg_type == "ERROR": self._sock.disconnect() elif ircmsg.msg_type == "JOIN": self._handle_join(ircmsg) elif ircmsg.msg_type == "PRIVMSG": self._handle_privmsg(ircmsg) def _read_line(self): return self._sock.read_line() def _send_command(self, command): self._command.send_command(command) def _send_message(self, to, msg): self._command.send_message(to, msg) def _handle_privmsg(self, ircmsg): if ircmsg.cmd == "": return self._command.execute(ircmsg) # TODO: Might want to rewrite this... def _handle_join(self, ircmsg): # Is it the bot joining? if ircmsg.from_nick == self.state._nick: people_on_channel = [] joined_channel = ircmsg.to while 1: data = self._read_line() print(data) tokens = [el.strip() for el in data.split(" ")] if tokens[1] == "353": for i in tokens[5:]: people_on_channel.append(i.strip(":")) self.state.joined_channel(ircmsg.to, i.strip(":")) print("Joined channel %s\n" % ircmsg.to) elif tokens[1] == "366": # End of names, we are done reading data break print("Joined channel %s" % joined_channel) for i in people_on_channel: print("Person %s is on channel" % i) print("End of JOIN") else: self.state.joined_channel(ircmsg.to, ircmsg.from_nick)
class BotCommands: _modules = [] def __init__(self): self._conn = Connection() self.botstate = BotState() self.load_modules() def load_modules(self): for _, _, files in os.walk("./commands"): for f in files: path, ext = os.path.splitext(f) if path == "__init__": continue if ext == ".py": try: mod_name = "commands.%s" % path mod = importlib.import_module(mod_name) self._modules.append(mod) except: print("Cannot load module %s (%s)" % (f, sys.exc_info())) def execute(self, ircmsg): # Pre-defined commands if ircmsg.cmd == "list": self._list_modules(ircmsg) return elif ircmsg.cmd == "quit": self._quit(ircmsg) return elif ircmsg.cmd == "reload": self._reload_modules(ircmsg) return elif ircmsg.cmd == "join" or ircmsg.cmd == "j": self._join(ircmsg) return elif ircmsg.cmd == "channels": self._list_channels(ircmsg) return elif ircmsg.cmd == "help" or ircmsg.cmd == "h": self._help(ircmsg) return # Commands created by external modules mod_name = "commands.%s" % ircmsg.cmd cmd_found = False for i in self._modules: if i.__name__ == mod_name: cmd_found = True if not cmd_found: # We try to load a module that has the same name as the command try: mod = importlib.import_module(mod_name) line = getattr(sys.modules[mod_name], "run")(self.botstate, ircmsg, self._conn) print("Loaded module %s\n" % mod_name) if len(line) > 0: self.send_command(line) self._modules.append(mod) except: msg = "Module %s does not exist or has errors (%s)" % (ircmsg.cmd, sys.exc_info()) self.send_message(ircmsg.reply_to, msg) else: line = getattr(sys.modules[mod_name], "run")(self.botstate, ircmsg, self._conn) if len(line) > 0: self.send_command(line) def send_command(self, line): self._conn.send(line) def send_message(self, to, msg): line = "PRIVMSG %s :%s" % (to, msg) self.send_command(line) def join(self, channel): ircmsg = IRCMessage("") ircmsg.msg_type = "JOIN" ircmsg.cmd = "join" ircmsg.args = channel self._join(ircmsg) def _read_line(self): return self._conn.read_line() def _list_modules(self, ircmsg): line = "Loaded modules: " if len(self._modules) > 0: loaded_modules = [m.__name__[m.__name__.find(".")+1:] for m in self._modules] line += ", ".join(loaded_modules) else: line = "No modules have been loaded yet" self.send_message(ircmsg.reply_to, line) def _quit(self, ircmsg): if not ircmsg.args: line = "QUIT :%s" % " ".join(ircmsg.args) else: line = "QUIT :Don't kill meeeeeeeeeeeeeeeeee" self.send_command(line) self._conn.disconnect() def _reload_modules(self, ircmsg): ''' Reloads specified module. If no module is specified, reload them all If module specified has not been loaded yet, load it ''' reloaded_modules = [] module_found = False if len(ircmsg.args) > 0: for i in ircmsg.args: mod_name = "commands.%s" % i try: for j in self._modules: if mod_name == j.__name__: imp.reload(j) reloaded_modules.append(i) module_found = True if module_found == False: mod = importlib.import_module(mod_name) reloaded_modules.append(i) self._modules.append(mod) except: msg = "Module %s cannot be reloaded (%s)" % (i, sys.exc_info()) self.send_message(ircmsg.reply_to, msg) else: self.load_modules() # Loads any new modules that have been created for i in self._modules: try: imp.reload(i) name = i.__name__ reloaded_modules.append(name[name.find(".")+1:]) # Remove commands. from the module name except: msg = "Module %s cannot be reloaded (%s)" % (i.__name__, sys.exc_info()) self.send_message(ircmsg.reply_to, msg) if len(reloaded_modules) > 0: msg = "The following modules were reloaded: %s" % " ".join(reloaded_modules) else: msg = "No modules have been reloaded" self.send_message(ircmsg.reply_to, msg) def _join(self, ircmsg): if not ircmsg.args: return for i in ircmsg.args: line = "JOIN %s" % i self.send_command(line) def _list_channels(self, ircmsg): state = self.botstate channels = [] for i in state._joined_channels: channels.append(i.channel) msg = "In channels: %s" % " ".join(channels) self.send_message(ircmsg.reply_to, msg) def _help(self, ircmsg): if not ircmsg.args: return cmd = ircmsg.args[0] if cmd in [m.__name__[m.__name__.find(".")+1:] for m in self._modules]: mod_name = "commands.%s" % cmd help_text = getattr(sys.modules[mod_name], "help")() for l in help_text.split("\n"): self.send_message(ircmsg.from_nick, l)