def __init__(self, channels=[], nickname="", server="", port=6667, module_list=[]): """MooBot initializer - gets values from config files and uses those unless passed values directly""" # Get values from config files and replace any of the empty ones above configs = self.get_configs() config_nick = configs['nick'] config_server = configs['server'] config_port = configs['port'] config_channels = configs['channels'] config_module_list = configs['module_list'] config_others = configs['others'] # If we are passed any values directly, use those, but if they are empty # we will fall back to the values we got from the config file if channels == []: channels = config_channels if nickname == "": nickname = config_nick if server == "": server = config_server if port == 6667: port = config_port if module_list == []: module_list = config_module_list # Now that we have our values, initialize it all SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname) self.channels = IRCDict() for channel in channels: self.channels[channel] = Channel() self.handlers = [] self.configs = config_others self.module_list = module_list
def _on_disconnect(self, c, e): """[Internal]""" self.channels = IRCDict() if self.runCond: self.connection.execute_delayed(self.reconnection_interval, self._connected_checker) if hasattr(self, "disconnect_event"): self.disconnect_event.set()
def __init__(self, channels=[], nickname="", password="", realname="", server="", port=6667, module_list=[], encoding=""): """MooBot initializer - gets values from config files and uses those unless passed values directly""" Debug("possible config files: " + ", ".join(self.config_files)) # Get values from config files and replace any of the empty ones above configs = self.get_configs() config_nick = configs['nick'] config_username = configs['username'] config_realname = configs['realname'] config_server = configs['server'] config_port = configs['port'] config_encoding = configs['encoding'] config_password = configs['password'] config_channels = configs['channels'] config_module_list = configs['module_list'] config_others = configs['others'] # If we are passed any values directly, use those, but if they are empty # we will fall back to the values we got from the config file # for var in \ # ['channels', 'nickname', 'username', 'server', # 'port', 'module_list', 'others']: # if kwargs.has_key(var): # if kwargs.has_key('channels'): channels = kwargs['channels'] # else: channels = config_channels if channels == []: channels = config_channels if nickname == "": nickname = config_nick if realname == "": realname = config_realname if server == "": server = config_server if port == 6667: port = config_port if password == "": password = config_password if module_list == []: module_list = config_module_list if encoding == "": encoding = config_encoding # Now that we have our values, initialize it all SingleServerIRCBot.__init__( self, [(server, port, password, encoding.upper())], nickname, realname) self.serve_nick = config_nick self.serve_password = config_password self.serve_channels = [ channel for channel in channels if channel.strip() ] self.channels = IRCDict() self.handlers = {} self.configs = config_others self.module_list = module_list
def __init__(self, botConfig): CNBCon.__init__(self, botConfig) config = CNBConfig.getInstance() username = botConfig.get('bot', 'username') nickname = username realname = username channels = botConfig.get('bot', 'channels') password = botConfig.get('bot', 'password') server = botConfig.get('bot', 'server') autostart = botConfig.get('bot', 'auto-start') autoreconnect = botConfig.get('bot', 'auto-reconnect') reconInterval = 60 self.ircobj = IRC() self.connection = self.ircobj.server() self.dcc_connections = [] self.ircobj.add_global_handler("all_events", self._dispatcher, -10) #self.ircobj.add_global_handler("dcc_disconnect", self._dcc_disconnect, -10) self.autoReconnect = autoreconnect self.server_list.append([server, self.IRC_PORT]) self.channelsToJoin = channels self.channels = IRCDict() if not reconInterval or reconInterval < 0: reconInterval = 2**31 self.reconInterval = reconInterval self._nickname = nickname self._realname = realname self._password = password if autostart == '1': self.log.info('Auto-start = 1') self.startBot() if autoreconnect == '1': self.log.info('Auto-reconnect = 1')
class CNBIRCCon(CNBCon): """ IRC Connector class """ # Constants IRC_REPLY_SLEEP = 0.5 IRC_PORT = 6667 IRC_CHAT = 'privmsg' _nickname = None """ @ivar: Nick name of the bot @type: String """ _realname = None """ @ivar: Real Name of the bot @type: String """ _password = None """ @ivar: Bot password for identifying @type: String """ server_list = [] """ @ivar: List of servers to connect @type: List """ channels = {} """ @ivar: List of joined channels @type: hashtable """ channelsToJoin = [] """ @ivar: List of channels to join when the connector start @type: List """ autoReconnect = False """ @ivar: Determine if the bot will auto-reconnect if it disconnect/fail @type: bool """ reconInterval = 60 """ @ivar: Reconnection interval (in sec) @type: int """ connection = None """ @ivar: Connection Object provided by irclib @type: Object """ ircobj = None """ @ivar: IRC Object provided by irclib @type: Object """ def __init__(self, botConfig): CNBCon.__init__(self, botConfig) config = CNBConfig.getInstance() username = botConfig.get('bot', 'username') nickname = username realname = username channels = botConfig.get('bot', 'channels') password = botConfig.get('bot', 'password') server = botConfig.get('bot', 'server') autostart = botConfig.get('bot', 'auto-start') autoreconnect = botConfig.get('bot', 'auto-reconnect') reconInterval = 60 self.ircobj = IRC() self.connection = self.ircobj.server() self.dcc_connections = [] self.ircobj.add_global_handler("all_events", self._dispatcher, -10) #self.ircobj.add_global_handler("dcc_disconnect", self._dcc_disconnect, -10) self.autoReconnect = autoreconnect self.server_list.append([server, self.IRC_PORT]) self.channelsToJoin = channels self.channels = IRCDict() if not reconInterval or reconInterval < 0: reconInterval = 2**31 self.reconInterval = reconInterval self._nickname = nickname self._realname = realname self._password = password if autostart == '1': self.log.info('Auto-start = 1') self.startBot() if autoreconnect == '1': self.log.info('Auto-reconnect = 1') def __del__(self): pass def _shutdown(self): self.log.info('Going down') def _getMsgFromEvent(self, e, et = 'Msg'): """ Generate a message object from an event @param e: Event @type e: Event @param et: @type et: String """ oMsg = CNBMessage() oMsg.protocol = 'irc' oMsg.conId = self._botConfig.get('bot', 'id') oMsg.type = str(e.eventtype()) oMsg.isPrivate = (oMsg.type == 'privmsg' or oMsg.type == 'privnotice' or oMsg.type == 'invite') oMsg.source = str(e.source()) oMsg.target = str(e.target()) oMsg.text = str(e.arguments()[0]).strip() oMsg.domain = self._botConfig.get('bot', 'server') oMsg.initCmd() if oMsg.isPrivate: oMsg.username = str(e.source()).split('!')[0] oMsg.replyTo = str(e.source()).split('!')[0] if oMsg.type == 'invite': oMsg.room = oMsg.text else: #oMsg.username = str(e.target()) oMsg.username = str(e.source()).split('!')[0] oMsg.replyTo = str(e.target()) oMsg.room = str(e.target()) # Logging for i in dir(oMsg): if i not in ['__init__', '__del__', '__module__', '__doc__']: self.log.debug(et + ": oMsg." + str(i) + " = " + str(getattr(oMsg,i))) return oMsg def _dispatcher(self, c, e): """[Internal]""" m = "_on_" + e.eventtype() if hasattr(self, m): getattr(self, m)(c, e) def _dcc_connect(self, address, port, dcctype="chat"): """ Connect to a DCC peer. @param address: IP address of the peer. @param port: Port to connect to. @return: a DCCConnection instance. """ dcc = self.ircobj.dcc(dcctype) self.dcc_connections.append(dcc) dcc.connect(address, port) return dcc def _dcc_listen(self, dcctype="chat"): """Listen for connections from a DCC peer. Returns a DCCConnection instance. """ dcc = self.ircobj.dcc(dcctype) self.dcc_connections.append(dcc) dcc.listen() return dcc def _dcc_disconnect(self, c, e): """[Internal]""" self.dcc_connections.remove(c) def _connected_checker(self): """[Internal]""" if not self.connection.is_connected(): self.log.info('Not connected, reconnecting') self.connection.execute_delayed(self.reconInterval, self._connected_checker) if self.autoReconnect: self._connect() def _connect(self): """[Internal]""" password = None if len(self.server_list[0]) > 2: password = self.server_list[0][2] try: self.log.info('Connecting to ' + self.server_list[0][0] + ':' + str(self.server_list[0][1]) + ' as ' + self._nickname + ' (ircname:' + self._realname + ')') self.connect(self.server_list[0][0], self.server_list[0][1], self._nickname, password, ircname=self._realname) except ServerConnectionError: self.log.info('Could not connect') def _disconnect(self, msg="I'll be back!"): """ Disconnect the bot. The bot will try to reconnect after a while. @param msg: Quit message. @type msg: String """ self.connection.disconnect(msg) def _on_ping(self, c, e): """[Internal]""" #self.log.debug('Received a ping, sending a pong') self.connection.pong(e.target()) def _on_pubmsg(self, c, e): """[Internal]""" oMatrix = CNBMatrix.getInstance() oMsg = self._getMsgFromEvent(e, 'Pub Msg') if self._botConfig.get('bot', 'username') != oMsg.replyTo: replies = oMatrix.processIrcMod(oMsg) if replies != None: for r in replies: c.privmsg(oMsg.replyTo, r) sleep(self.IRC_REPLY_SLEEP) def _on_privmsg(self, c, e): """[Internal]""" oMatrix = CNBMatrix.getInstance() oMsg = self._getMsgFromEvent(e, 'Priv Msg') if self._botConfig.get('bot', 'username') != oMsg.replyTo: replies = oMatrix.processIrcMod(oMsg) if replies != None: for r in replies: c.privmsg(oMsg.replyTo, r) sleep(self.IRC_REPLY_SLEEP) def _on_pubnotice(self, c, e): """ Pub Notice event handler Note: the bot do not answer to pub notice """ oMsg = self._getMsgFromEvent(e, 'Pub Notice') def _on_privnotice(self, c, e): """[Internal]""" oMsg = self._getMsgFromEvent(e, 'Priv Notice') if oMsg.replyTo == 'NickServ' and 'You are now identified for' in oMsg.text: self.setState('identified') def _on_invite(self, c, e): """[Internal]""" oMatrix = CNBMatrix.getInstance() oMsg = self._getMsgFromEvent(e, 'Invite') if self._botConfig.get('bot', 'username') != oMsg.replyTo: replies = oMatrix.processIrcMod(oMsg) if replies != None: for r in replies: c.privmsg(oMsg.replyTo, r) sleep(self.IRC_REPLY_SLEEP) def _on_disconnect(self, c, e): """[Internal]""" self.channels = IRCDict() self.connection.execute_delayed(self.reconInterval, self._connected_checker) def _on_all_raw_messages(self, c, e): """[Internal]""" self.log.debug('Raw Message: ' + str(e.arguments())) def _on_error(self, c, e): """[Internal]""" self.log.info('Error: ' + str(c) + str(e)) self.log.exception(str(c)) self.log.exception(str(e)) def _on_join(self, c, e): """[Internal]""" ch = e.target() nick = nm_to_n(e.source()) self.log.info(nick + ' is joining channel ' + ch) if nick == c.get_nickname(): self.channels[ch] = Channel() self.channels[ch].add_user(nick) def _on_kick(self, c, e): """[Internal]""" nick = e.arguments()[0] ch = e.target() self.log.info(nick + ' was kicked from ' + ch) if nick == c.get_nickname(): del self.channels[ch] else: self.channels[ch].remove_user(nick) def _on_mode(self, c, e): """[Internal]""" modes = parse_channel_modes(" ".join(e.arguments())) t = e.target() self.log.info(t + ' set mode to ' + t) if is_channel(t): ch = self.channels[t] for mode in modes: if mode[0] == "+": f = ch.set_mode else: f = ch.clear_mode f(mode[1], mode[2]) else: # Mode on self... XXX pass def _on_namreply(self, c, e): """ [Internal] e.arguments()[0] == "@" for secret channels, "*" for private channels, "=" for others (public channels) e.arguments()[1] == channel e.arguments()[2] == nick list """ ch = e.arguments()[1] for nick in e.arguments()[2].split(): if nick[0] == "@": nick = nick[1:] self.channels[ch].set_mode("o", nick) elif nick[0] == "+": nick = nick[1:] self.channels[ch].set_mode("v", nick) self.channels[ch].add_user(nick) def _on_nick(self, c, e): """[Internal]""" before = nm_to_n(e.source()) after = e.target() self.log.info(before + ' set nick to ' + after) for ch in self.channels.values(): if ch.has_user(before): ch.change_nick(before, after) def _on_part(self, c, e): """[Internal]""" nick = nm_to_n(e.source()) channel = e.target() self.log.info(nick + ' left the chan ' + channel) if nick == c.get_nickname(): del self.channels[channel] else: self.channels[channel].remove_user(nick) def _on_quit(self, c, e): """[Internal]""" nick = nm_to_n(e.source()) self.log.info(nick + ' has quit') for ch in self.channels.values(): if ch.has_user(nick): ch.remove_user(nick) def _on_ctcp(self, c, e): """Default handler for ctcp events. Replies to VERSION and PING requests and relays DCC requests to the on_dccchat method. """ if e.arguments()[0] == "VERSION": c.ctcp_reply(nm_to_n(e.source()), "VERSION " + self.get_version()) elif e.arguments()[0] == "PING": if len(e.arguments()) > 1: c.ctcp_reply(nm_to_n(e.source()), "PING " + e.arguments()[1]) elif e.arguments()[0] == "DCC" and e.arguments()[1].split(" ", 1)[0] == "CHAT": self.on_dccchat(c, e) def _on_dccchat(self, c, e): """[Internal]""" pass def run(self): """ IRC connector main thread - Listen to commands, parse messages and responses to users. """ while self.isRunning(): if self.getState() == 'dying': break if self.getState() in ['connected', 'identifying', 'joiningChannels']: try: self.ircobj.process_once(1) except KeyboardInterrupt: self.log.info('bot stopped by user request. shutting down.') break except Exception, e: self.log.exception(e) if self.getState() == 'joiningChannels': for ch in self.channelsToJoin: if ':' in ch: ch,pwd = ch.split(':',1) self.joinChan(ch,pwd) else: self.joinChan(ch) self.setState('connected') if self.getState() == 'identified': self.log.info('Identified, joining channels...') self.setState('joiningChannels') #sleep(2) self.ircobj.process_once(0.5) if self.getState() == 'identifying': if self._password: self.log.info('Identifying with password') self.identify(self._password) #sleep(2) self.ircobj.process_once(0.5) self.setState('identified') if self.getState() == 'connecting': self._connect() if self.connection.is_connected(): self.log.info('bot connected, identifying...') self.setState('identifying') sleep(5) self.ircobj.process_once(0.5) else: self.log.warn('could not connect to server - aborting.') self.setState('disconnected') if self.getState() == 'disconnected': #self.log.info('bot disconnected') if self.autoReconnect: self.setState('connecting') self.ircobj.process_once(0.5) if self.getState() == 'disconnecting': self.log.info('bot disconnecting') self._disconnect() self.setState('disconnected') if self.getState() == 'notstarted': pass # self.log.info('Current state: ' + self.getState()) sleep(1) self._shutdown() return 0
def _on_disconnect(self, c, e): """[Internal]""" self.channels = IRCDict() self.connection.execute_delayed(self.reconInterval, self._connected_checker)
class MooBot(SingleServerIRCBot): class MooBotException(Exception): pass class HandlerExists(MooBotException): pass config_files = ['moobot.conf', '/etc/moobot.conf'] def __init__(self, channels=[], nickname="", server="", port=6667, module_list=[]): """MooBot initializer - gets values from config files and uses those unless passed values directly""" # Get values from config files and replace any of the empty ones above configs = self.get_configs() config_nick = configs['nick'] config_server = configs['server'] config_port = configs['port'] config_channels = configs['channels'] config_module_list = configs['module_list'] config_others = configs['others'] # If we are passed any values directly, use those, but if they are empty # we will fall back to the values we got from the config file if channels == []: channels = config_channels if nickname == "": nickname = config_nick if server == "": server = config_server if port == 6667: port = config_port if module_list == []: module_list = config_module_list # Now that we have our values, initialize it all SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname) self.channels = IRCDict() for channel in channels: self.channels[channel] = Channel() self.handlers = [] self.configs = config_others self.module_list = module_list def on_join(self, c, e): """Whenever a client joins a channel the bot is in, this is executed""" pass def on_welcome(self, c, e): """Whenever this bot joins a server, this is executed""" for channel in self.channels.keys(): print "Joining", channel c.join(channel) def on_privmsg(self, c, e): """Whenever someone sends a /msg to our bot, this is executed""" msg = e.arguments()[0] # the string of what was said # build the args dict for the handlers args={} args["text"] = self.connection.get_nickname() + ": " + msg args["type"] = e.eventtype() args["source"] = e.source() args["channel"] = e.target() msg = string.strip(msg) from irclib import nm_to_n # print what was said to the stdout with a bit of colour. print YELLOW + "<" + nm_to_n(args["source"]) + NORMAL + "/" + \ BLUE + args["channel"] + ">" + NORMAL + \ RED + "(" + args["type"] + ")" + NORMAL, args["text"] temp = threading.Thread(target=self.process_privmsg, args=(msg, args), name="privmsg subthread") temp.setDaemon(1) temp.start() def process_privmsg(self, msg, args): """Process private messages (/msg's) to the bot""" eventlist = self.get_local_handler(msg, args) if eventlist != []: for event in eventlist: self.do_event(event) def on_pubmsg(self, c, e): """Whenever someone speaks in a channel where our bot resides, this is executed""" import string msg = e.arguments()[0] args = {} args["text"] = msg args["type"] = e.eventtype() args["source"] = e.source() args["channel"] = e.target() # Then check with all the global handlers, see if any match from irclib import nm_to_n # print what was said to the stdout with a bit of colour. print YELLOW + "<" + nm_to_n(args["source"]) + NORMAL + "/" +\ BLUE + args["channel"] + ">" + NORMAL +\ RED + "(" + args["type"] + ")" + NORMAL, args["text"] temp = threading.Thread(target=self.process_pubmsg, \ args=(msg, args), name="pubmsg subthread") temp.setDaemon(1) temp.start() def process_pubmsg(self, msg, args): """Process messages into the channel""" from re import compile import string eventlist = self.get_global_handler(msg, args) if eventlist != []: for event in eventlist: self.do_event(event) if eventlist[-1].eventtype() != "continue": return # If we are referred to with our shorthand name, make it look # like we were referred to normally. shortname = "^\s*" + self.configs["shorthand"] shortregex = compile(shortname) replace_str = self.connection.get_nickname() + ": " if shortregex.search(msg): msg = string.replace(msg, self.configs["shorthand"], replace_str, 1) args["text"] = msg # Now, check and see if we are being spoken too ourname = "^" + self.connection.get_nickname() regex = compile(ourname) if regex.search(msg): msg = string.strip(msg[string.find(msg, " "):]) eventlist = self.get_local_handler(msg, args) if eventlist != []: for event in eventlist: self.do_event(event) def get_global_handler(self, msg, args): """Used when an event is raised that needs a global handler""" return self.get_handler(Handler.GLOBAL, msg, args) def get_local_handler(self, msg, args): """Used when an event is raised that needs a local handler""" return self.get_handler(Handler.LOCAL, msg, args) def get_handler(self, type, msg, args): """Used when an event is raised that needs an event handler""" # Check through the handlers for a key that matches # the message contents. from irclib import nm_to_n from irclib import Event import weakref nickname = self.connection.get_nickname() if type == Handler.GLOBAL and args["text"][:len(nickname)] != nickname: # For now we are going to rewrite the message with the # name on the front so that modules don't care if they are # local or global. args["text"] = self.connection.get_nickname() + ": " +\ args["text"] # Iterate over the list of registered handlers, looking for a handler # that matches in type and regex. When it is found, call it and get a # resulting event or list of events which we return eventlist = [Event("continue", "", "", [""])] for handler in self.handlers: if eventlist[-1].eventtype() != "continue": break if handler.type == type: if handler.regex.search(msg): instance = handler.instance # result can either be an Event or a list of Events, # in either case, we just add on all the Events to # eventlist result = instance.handler(text=args["text"], type=args["type"], source=args["source"], channel=args["channel"], ref=weakref.ref(self)) if isinstance(result, Event): eventlist.append(result) else: eventlist += result if len(eventlist) >1: return eventlist # This should never come up unless you take out the "dunno" handlers # that generally hand every case that no other handler takes care of if type == Handler.LOCAL: print "Could not get event handler." print "msg:", args["text"] print "type:", args["type"] print "source:", args["source"] print "channel:", args["channel"] return [] def list_handlers(self): """Display the handlers currently registered with the bot""" strings =[] for handler in self.handlers: if handler.function.__doc__ is not None: string = handler.pattern() + ": " + handler.func_name() + \ "() - " + handler.function.__doc__ + " (" if handler.type == Handler.GLOBAL: string += "global)" else: string += "local)" else: string = handler.pattern() + ": " + handler.func_name() + "() (" if handler.type == Handler.GLOBAL: string += "global)" else: string += "local)" strings.append(string) return strings def do_event(self, event): """Does an appropriate action based on event""" if event.eventtype() == "privmsg": for line in string.split(event.arguments()[0], "\n"): # print the output to the STDOUT, with a bit of colour print RED + ">" + \ PURPLE + self.connection.get_nickname() + \ RED + "/" + \ GREEN + event.target() + \ RED + "<" + \ NORMAL, line self.connection.privmsg(event.target(), line) elif event.eventtype() == "action": print RED + " * " + \ PURPLE + self.connection.get_nickname() + \ RED + "/" + \ GREEN + event.target() + \ NORMAL, event.arguments() self.connection.action(event.target(), event.arguments()[0]) elif event.eventtype() == "internal": #print "internal", event.arguments()[0], event.target if event.arguments()[0] == "join": print "Joining", event.target() self.connection.join(event.target()) elif event.arguments()[0] == "part": print "Parting", event.target() self.connection.part(event.target()) elif event.arguments()[0] == "load": self.load_module(event); elif event.arguments()[0] == "unload": self.unload_module(event); elif event.arguments()[0] == "nick": print "Changing nick to ", event.target() self.connection.nick(event.target()) elif event.arguments()[0] == "kick": print "Kicking", event.target(), "from", event.arguments()[1] self.connection.kick(event.arguments()[1], event.target()) elif event.arguments()[0] == "send_raw": print "Sending raw command: " + event.arguments()[0] self.connection.send_raw(event.arguments()[1]) elif event.arguments()[0] == "modules": from irclib import Event # if event.arguments()[1] == "": # strings = self.list_handlers() # reply = "" # for msg in strings: # reply += msg + " ;; " # self.do_event(Event("privmsg", "", event.target(), [reply])) # else: for module in event.arguments()[1].split(" "): match = 0 for handler in self.handlers: if handler.className == module: match = 1 if handler.className is not None: msg = `handler.pattern()` + ": " + \ `handler.func_name()` + "() - " + \ `handler.instance.__doc__` + " (" if handler.type == Handler.GLOBAL: msg += "global)" else: msg += "local)" else: msg = handler.pattern() + ": " + \ handler.func_name() + "() (" if handler.type == Handler.GLOBAL: msg += "global)" else: msg += "local)" if match == 0: msg = "no handler found with function " + module + "()" self.do_event(Event("privmsg", "", event.target(), [msg])) elif event.eventtype() == "continue": return else: print "This event type", event.eventtype(), "has no suitable event" def get_configs(self, filelist=[]): """Gets configuration options from a list of files""" from ConfigParser import ConfigParser, NoSectionError, NoOptionError config = ConfigParser() filelist += MooBot.config_files config.read(filelist) # Initialize the things we will return just in case they aren't in # any of the files that we parse through. Then get their values # and stick the rest in "others" nick=""; server=""; port=6667; channels=[]; others={} try: nick = config.get('connection', 'nick') server = config.get('connection', 'server') port = int(config.get('connection', 'port')) channels = config.get('connection', 'channels').split(" ") module_list = config.get('modules', 'modulefiles').split(" ") except ValueError: print "ERROR: Non-numeric port in config files." except NoSectionError: print "ERROR: [connection] section missing from config files." except NoOptionError: print "ERROR: missing vital option" for section in config.sections(): if section != "connection": # These will all be returned, don't need to be in others for option in config.options(section): others[option] = config.get(section, option) return {'nick': nick, 'server': server, 'port': port, 'channels': channels, 'module_list': module_list, 'others': others} def load_module(self, event): """ this loads a module, at run-time. event is an Event whose arguments() attribute contains a list of modules to write (starting with the second element """ import imp for newmod in event.arguments()[1:]: # why this? --djd fp = "" pathname = "" description = "" # we need to do this to make sure the module is there, and to # get some information needed by imp.load_module try: fp, pathname, description = imp.find_module(newmod) except: print "Module \"%s\" not found " % (newmod) if fp: fp.close() continue # as a side note, imp.load_module doesn't add the module to # the modules table, it only returns a reference to that # module. try: importedModule = imp.load_module(newmod, fp, pathname, description) # each module contains a list called handler_list, which # contains the names of classes to be loaded as bot modules for handlerName in importedModule.handler_list: newHandler = Handler(importedModule, handlerName) self.handlers.append(newHandler) print "Added handler: " , handlerName, "for \"" \ + newHandler.regex.pattern+"\"", \ "priority ", newHandler.instance.priority if importedModule.__name__ not in self.module_list: self.module_list.append(importedModule.__name__) finally: # sort the list (for priorities) self.handlers.sort() # Since we may exit via an exception, close fp explicitly. if fp: fp.close() def unload_module(self, event): """ remove any handlers from self.handlers that are from any of the modules passed in event.arguments()[1:]""" module_list = event.arguments()[1:] for index in range(len(self.handlers)-1, -1, -1): if self.handlers[index].module.__name__ in module_list: self.handlers.pop(index) # sort the list (for priorities) for module in module_list: if module in self.module_list: self.module_list.remove(module) self.handlers.sort()
class CNBIRCCon(CNBCon): """ IRC Connector class """ # Constants IRC_REPLY_SLEEP = 0.5 IRC_PORT = 6667 IRC_CHAT = 'privmsg' _nickname = None """ @ivar: Nick name of the bot @type: String """ _realname = None """ @ivar: Real Name of the bot @type: String """ _password = None """ @ivar: Bot password for identifying @type: String """ server_list = [] """ @ivar: List of servers to connect @type: List """ channels = {} """ @ivar: List of joined channels @type: hashtable """ channelsToJoin = [] """ @ivar: List of channels to join when the connector start @type: List """ autoReconnect = False """ @ivar: Determine if the bot will auto-reconnect if it disconnect/fail @type: bool """ reconInterval = 60 """ @ivar: Reconnection interval (in sec) @type: int """ connection = None """ @ivar: Connection Object provided by irclib @type: Object """ ircobj = None """ @ivar: IRC Object provided by irclib @type: Object """ def __init__(self, botConfig): CNBCon.__init__(self, botConfig) config = CNBConfig.getInstance() username = botConfig.get('bot', 'username') nickname = username realname = username channels = botConfig.get('bot', 'channels') password = botConfig.get('bot', 'password') server = botConfig.get('bot', 'server') autostart = botConfig.get('bot', 'auto-start') autoreconnect = botConfig.get('bot', 'auto-reconnect') reconInterval = 60 self.ircobj = IRC() self.connection = self.ircobj.server() self.dcc_connections = [] self.ircobj.add_global_handler("all_events", self._dispatcher, -10) #self.ircobj.add_global_handler("dcc_disconnect", self._dcc_disconnect, -10) self.autoReconnect = autoreconnect self.server_list.append([server, self.IRC_PORT]) self.channelsToJoin = channels self.channels = IRCDict() if not reconInterval or reconInterval < 0: reconInterval = 2**31 self.reconInterval = reconInterval self._nickname = nickname self._realname = realname self._password = password if autostart == '1': self.log.info('Auto-start = 1') self.startBot() if autoreconnect == '1': self.log.info('Auto-reconnect = 1') def __del__(self): pass def _shutdown(self): self.log.info('Going down') def _getMsgFromEvent(self, e, et='Msg'): """ Generate a message object from an event @param e: Event @type e: Event @param et: @type et: String """ oMsg = CNBMessage() oMsg.protocol = 'irc' oMsg.conId = self._botConfig.get('bot', 'id') oMsg.type = str(e.eventtype()) oMsg.isPrivate = (oMsg.type == 'privmsg' or oMsg.type == 'privnotice' or oMsg.type == 'invite') oMsg.source = str(e.source()) oMsg.target = str(e.target()) oMsg.text = str(e.arguments()[0]).strip() oMsg.domain = self._botConfig.get('bot', 'server') oMsg.initCmd() if oMsg.isPrivate: oMsg.username = str(e.source()).split('!')[0] oMsg.replyTo = str(e.source()).split('!')[0] if oMsg.type == 'invite': oMsg.room = oMsg.text else: #oMsg.username = str(e.target()) oMsg.username = str(e.source()).split('!')[0] oMsg.replyTo = str(e.target()) oMsg.room = str(e.target()) # Logging for i in dir(oMsg): if i not in ['__init__', '__del__', '__module__', '__doc__']: self.log.debug(et + ": oMsg." + str(i) + " = " + str(getattr(oMsg, i))) return oMsg def _dispatcher(self, c, e): """[Internal]""" m = "_on_" + e.eventtype() if hasattr(self, m): getattr(self, m)(c, e) def _dcc_connect(self, address, port, dcctype="chat"): """ Connect to a DCC peer. @param address: IP address of the peer. @param port: Port to connect to. @return: a DCCConnection instance. """ dcc = self.ircobj.dcc(dcctype) self.dcc_connections.append(dcc) dcc.connect(address, port) return dcc def _dcc_listen(self, dcctype="chat"): """Listen for connections from a DCC peer. Returns a DCCConnection instance. """ dcc = self.ircobj.dcc(dcctype) self.dcc_connections.append(dcc) dcc.listen() return dcc def _dcc_disconnect(self, c, e): """[Internal]""" self.dcc_connections.remove(c) def _connected_checker(self): """[Internal]""" if not self.connection.is_connected(): self.log.info('Not connected, reconnecting') self.connection.execute_delayed(self.reconInterval, self._connected_checker) if self.autoReconnect: self._connect() def _connect(self): """[Internal]""" password = None if len(self.server_list[0]) > 2: password = self.server_list[0][2] try: self.log.info('Connecting to ' + self.server_list[0][0] + ':' + str(self.server_list[0][1]) + ' as ' + self._nickname + ' (ircname:' + self._realname + ')') self.connect(self.server_list[0][0], self.server_list[0][1], self._nickname, password, ircname=self._realname) except ServerConnectionError: self.log.info('Could not connect') def _disconnect(self, msg="I'll be back!"): """ Disconnect the bot. The bot will try to reconnect after a while. @param msg: Quit message. @type msg: String """ self.connection.disconnect(msg) def _on_ping(self, c, e): """[Internal]""" #self.log.debug('Received a ping, sending a pong') self.connection.pong(e.target()) def _on_pubmsg(self, c, e): """[Internal]""" oMatrix = CNBMatrix.getInstance() oMsg = self._getMsgFromEvent(e, 'Pub Msg') if self._botConfig.get('bot', 'username') != oMsg.replyTo: replies = oMatrix.processIrcMod(oMsg) if replies != None: for r in replies: c.privmsg(oMsg.replyTo, r) sleep(self.IRC_REPLY_SLEEP) def _on_privmsg(self, c, e): """[Internal]""" oMatrix = CNBMatrix.getInstance() oMsg = self._getMsgFromEvent(e, 'Priv Msg') if self._botConfig.get('bot', 'username') != oMsg.replyTo: replies = oMatrix.processIrcMod(oMsg) if replies != None: for r in replies: c.privmsg(oMsg.replyTo, r) sleep(self.IRC_REPLY_SLEEP) def _on_pubnotice(self, c, e): """ Pub Notice event handler Note: the bot do not answer to pub notice """ oMsg = self._getMsgFromEvent(e, 'Pub Notice') def _on_privnotice(self, c, e): """[Internal]""" oMsg = self._getMsgFromEvent(e, 'Priv Notice') if oMsg.replyTo == 'NickServ' and 'You are now identified for' in oMsg.text: self.setState('identified') def _on_invite(self, c, e): """[Internal]""" oMatrix = CNBMatrix.getInstance() oMsg = self._getMsgFromEvent(e, 'Invite') if self._botConfig.get('bot', 'username') != oMsg.replyTo: replies = oMatrix.processIrcMod(oMsg) if replies != None: for r in replies: c.privmsg(oMsg.replyTo, r) sleep(self.IRC_REPLY_SLEEP) def _on_disconnect(self, c, e): """[Internal]""" self.channels = IRCDict() self.connection.execute_delayed(self.reconInterval, self._connected_checker) def _on_all_raw_messages(self, c, e): """[Internal]""" self.log.debug('Raw Message: ' + str(e.arguments())) def _on_error(self, c, e): """[Internal]""" self.log.info('Error: ' + str(c) + str(e)) self.log.exception(str(c)) self.log.exception(str(e)) def _on_join(self, c, e): """[Internal]""" ch = e.target() nick = nm_to_n(e.source()) self.log.info(nick + ' is joining channel ' + ch) if nick == c.get_nickname(): self.channels[ch] = Channel() self.channels[ch].add_user(nick) def _on_kick(self, c, e): """[Internal]""" nick = e.arguments()[0] ch = e.target() self.log.info(nick + ' was kicked from ' + ch) if nick == c.get_nickname(): del self.channels[ch] else: self.channels[ch].remove_user(nick) def _on_mode(self, c, e): """[Internal]""" modes = parse_channel_modes(" ".join(e.arguments())) t = e.target() self.log.info(t + ' set mode to ' + t) if is_channel(t): ch = self.channels[t] for mode in modes: if mode[0] == "+": f = ch.set_mode else: f = ch.clear_mode f(mode[1], mode[2]) else: # Mode on self... XXX pass def _on_namreply(self, c, e): """ [Internal] e.arguments()[0] == "@" for secret channels, "*" for private channels, "=" for others (public channels) e.arguments()[1] == channel e.arguments()[2] == nick list """ ch = e.arguments()[1] for nick in e.arguments()[2].split(): if nick[0] == "@": nick = nick[1:] self.channels[ch].set_mode("o", nick) elif nick[0] == "+": nick = nick[1:] self.channels[ch].set_mode("v", nick) self.channels[ch].add_user(nick) def _on_nick(self, c, e): """[Internal]""" before = nm_to_n(e.source()) after = e.target() self.log.info(before + ' set nick to ' + after) for ch in self.channels.values(): if ch.has_user(before): ch.change_nick(before, after) def _on_part(self, c, e): """[Internal]""" nick = nm_to_n(e.source()) channel = e.target() self.log.info(nick + ' left the chan ' + channel) if nick == c.get_nickname(): del self.channels[channel] else: self.channels[channel].remove_user(nick) def _on_quit(self, c, e): """[Internal]""" nick = nm_to_n(e.source()) self.log.info(nick + ' has quit') for ch in self.channels.values(): if ch.has_user(nick): ch.remove_user(nick) def _on_ctcp(self, c, e): """Default handler for ctcp events. Replies to VERSION and PING requests and relays DCC requests to the on_dccchat method. """ if e.arguments()[0] == "VERSION": c.ctcp_reply(nm_to_n(e.source()), "VERSION " + self.get_version()) elif e.arguments()[0] == "PING": if len(e.arguments()) > 1: c.ctcp_reply(nm_to_n(e.source()), "PING " + e.arguments()[1]) elif e.arguments()[0] == "DCC" and e.arguments()[1].split( " ", 1)[0] == "CHAT": self.on_dccchat(c, e) def _on_dccchat(self, c, e): """[Internal]""" pass def run(self): """ IRC connector main thread - Listen to commands, parse messages and responses to users. """ while self.isRunning(): if self.getState() == 'dying': break if self.getState() in [ 'connected', 'identifying', 'joiningChannels' ]: try: self.ircobj.process_once(1) except KeyboardInterrupt: self.log.info( 'bot stopped by user request. shutting down.') break except Exception, e: self.log.exception(e) if self.getState() == 'joiningChannels': for ch in self.channelsToJoin: if ':' in ch: ch, pwd = ch.split(':', 1) self.joinChan(ch, pwd) else: self.joinChan(ch) self.setState('connected') if self.getState() == 'identified': self.log.info('Identified, joining channels...') self.setState('joiningChannels') #sleep(2) self.ircobj.process_once(0.5) if self.getState() == 'identifying': if self._password: self.log.info('Identifying with password') self.identify(self._password) #sleep(2) self.ircobj.process_once(0.5) self.setState('identified') if self.getState() == 'connecting': self._connect() if self.connection.is_connected(): self.log.info('bot connected, identifying...') self.setState('identifying') sleep(5) self.ircobj.process_once(0.5) else: self.log.warn('could not connect to server - aborting.') self.setState('disconnected') if self.getState() == 'disconnected': #self.log.info('bot disconnected') if self.autoReconnect: self.setState('connecting') self.ircobj.process_once(0.5) if self.getState() == 'disconnecting': self.log.info('bot disconnecting') self._disconnect() self.setState('disconnected') if self.getState() == 'notstarted': pass # self.log.info('Current state: ' + self.getState()) sleep(1) self._shutdown() return 0