Thu, 15 Jan 2015 19:06:14 +0200
- added bridging functionality
configfile.py | file | annotate | diff | comparison | revisions | |
irc.py | file | annotate | diff | comparison | revisions | |
mod_bridge.py | file | annotate | diff | comparison | revisions | |
modulecore.py | file | annotate | diff | comparison | revisions |
--- a/configfile.py Mon Jan 12 10:55:45 2015 +0200 +++ b/configfile.py Thu Jan 15 19:06:14 2015 +0200 @@ -26,12 +26,19 @@ def set_value (self, key, value): self.obj[key] = value self.save() + + def append_value (self, key, value): + if key not in self.obj: + self.obj[key] = [] + + self.obj[key].append (value) + self.save() def get_node (self, key): return ConfigNode (obj=self.get_value (key, {}), name=self.keyname (key), parent=self) - def get_nodelist (self, key): - data = self.get_value (key) + def get_nodelist (self, key, default=None): + data = self.get_value (key, default) result = [] for entry in data: @@ -39,9 +46,12 @@ result.append (node) return result + + def has_node (self, key): + return key in self.obj def append_nodelist (self, key): - data = self.get_value (key) + data = self.get_value (key, []) obj = {} data.append (obj) return ConfigNode (obj=obj, name=self.keyname (key), parent=self) @@ -76,4 +86,4 @@ global Config Config = ConfigNode (jsondata, name=None, parent=None) -init() \ No newline at end of file +init()
--- a/irc.py Mon Jan 12 10:55:45 2015 +0200 +++ b/irc.py Thu Jan 15 19:06:14 2015 +0200 @@ -37,6 +37,13 @@ def __str__ (self): return self.value +def get_client (name): + for client in all_clients: + if client.name == name: + return client + + raise ValueError ('no such client %s' % name) + # # Main IRC client class # @@ -169,9 +176,19 @@ stuff = message[1:].split(' ') command = stuff[0] args = stuff[1:] - self.handle_command (sender, user, host, replyto, command, args, message) + self.handle_command (sender, user, host, channel, replyto, command, args, message) return + if channel != self.mynick: + ModuleCore.call_hook (bot=self, hookname='chanmsg', channel=channel, sender=sender, + ident=user, host=host, message=message, replyto=replyto) + else: + ModuleCore.call_hook (bot=self, hookname='querymsg', sender=sender, ident=user, + host=host, message=message, replyto=replyto) + + ModuleCore.call_hook (bot=self, hookname='privmsg', target=channel, sender=sender, + ident=user, host=host, message=message, replyto=replyto) + Bt.process_message (self, line, replyto) def add_irc_channel (self, channame): @@ -196,8 +213,9 @@ self.write ('PART ' + channame) self.cfg.save() - def handle_command (self, sender, ident, host, replyto, command, args, message): - kvargs = {'sender': sender, 'ident': ident, 'host': host, 'replyto': replyto, 'cmdname': command, 'message': message} + def handle_command (self, sender, ident, host, target, replyto, command, args, message): + kvargs = {'sender': sender, 'ident': ident, 'host': host, 'target': target, + 'replyto': replyto, 'cmdname': command, 'message': message} try: ModuleCore.call_command (self, **kvargs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_bridge.py Thu Jan 15 19:06:14 2015 +0200 @@ -0,0 +1,99 @@ +import re +from configfile import Config +import irc as Irc + +ModuleData = { + 'commands': + [ + { + 'name': 'bridge', + 'description': 'Creates a bridge between two channels', + 'args': '<destination>', + 'level': 'admin', + }, + + { + 'name': 'unbridge', + 'description': 'Removes a bridge between two channels', + 'args': '<destination>', + 'level': 'admin', + }, + ], + + 'hooks': + { + 'chanmsg': ['hook_chanmsg'], + } +} + +def get_destination (bot, target, destination, error): + match = re.match (r'^([A-Za-z0-9_]+)/(#[#A-Za-z0-9_]*)$', destination) + if not match: + error ('malformed destination') + + destHost, destChannel = match.group (1, 2) + dest = None + + # ensure we know this target + for conndata in Config.get_nodelist ('connections'): + if conndata.get_value('name') == destHost: + break + else: + error ('unknown connection %s' % destHost) + + for channel in conndata.get_value('channels'): + if channel['name'] == destChannel: + break + else: + error ('unknown channel %s' % destChannel) + + if destHost == bot.name and destChannel == target: + error ('cannot establish a bridge to self!') + + return (destHost, destChannel) + +def cmd_bridge (bot, target, args, reply, error, **rest): + source = (bot.name, target) + dest = get_destination (bot, target, args['destination'], error) + sourceName = '%s/%s' % source + destName = '%s/%s' % dest + bridges = Config.get_node('bridges') + + if destName in bridges.get_value (sourceName, []) \ + or sourceName in bridges.get_value (destName, []): + error ('bridge to %s already established' % destName) + + bridges.append_value ('%s/%s' % source, '%s/%s' % dest) + bridges.append_value ('%s/%s' % dest, '%s/%s' % source) + reply ('bridge to %s established' % destName) + +def cmd_unbridge (bot, target, args, reply, error, **rest): + source = (bot.name, target) + dest = get_destination (bot, target, args['destination'], error) + sourceName = '%s/%s' % source + destName = '%s/%s' % dest + bridges = Config.get_node('bridges') + + if destName in bridges.get_value (sourceName, []) \ + or sourceName in bridges.get_value (destName, []): + try: + bridges.get_value (sourceName, []).remove (destName) + bridges.get_value (destName, []).remove (sourceName) + except ValueError: + pass + reply ('bridge to %s dropped' % destName) + else: + error ('no bridge to %s established' % destName) + +def hook_chanmsg (bot, channel, sender, message, **rest): + sourceName = '%s/%s' % (bot.name, channel) + + for dest in Config.get_node('bridges').get_value (sourceName, []): + try: + clientName, channelName = dest.split ('/') + Irc.get_client (clientName).privmsg (channelName, + '[%s/%s] <%s> %s' % (bot.name, channel, sender, message)) + except Exception as e: + Irc.broadcast ('Error while bridging from %s to %s: %s' % + (sourceName, dest, e)) + pass
--- a/modulecore.py Mon Jan 12 10:55:45 2015 +0200 +++ b/modulecore.py Thu Jan 15 19:06:14 2015 +0200 @@ -5,6 +5,7 @@ Modules = {} Commands = {} +Hooks = {} class CommandError (Exception): def __init__ (self, value): @@ -21,6 +22,7 @@ global Commands global Modules files = os.listdir ('.') + numHooks = 0 for fn in files: if fn[0:4] != 'mod_' or fn[-3:] != '.py': @@ -51,9 +53,19 @@ cmd['argnames'].append (argname) + if 'hooks' in module.ModuleData: + for key, hooks in module.ModuleData['hooks'].iteritems(): + for hook in hooks: + if key not in Hooks: + Hooks[key] = [] + + Hooks[key].append ({'name': hook, 'func': getattr (module, hook)}) + numHooks += 1 + print "Loaded module %s" % fn - print 'Loaded %d commands in %d modules' % (len (Commands), len (Modules)) + print ('Loaded %d commands and %d hooks in %d modules' % + (len (Commands), numHooks, len (Modules))) # # command_error (message) @@ -178,7 +190,6 @@ if match == None: # didn't match - print "regex: %s" % cmd['regex'] command_error ('invalid arguments\nusage: %s %s' % (cmd['name'], cmd['args'])) # .more is special as it is an interface to the page system. @@ -205,12 +216,30 @@ commandObject['commandObject'] = commandObject commandObject['info'] = cmd commandObject['module'] = cmd['module'] + commandObject['error'] = command_error exec_command (commandObject) # Print the first page of responses. if cmdname != 'more': print_responses (commandObject) +def call_hook (bot, hookname, **kvargs): + global g_responsePages + global g_responsePageNum + hookObject = kvargs + hookObject['bot'] = bot + g_responsePages = [[]] + g_responsePageNum = 0 + + if 'replyto' in hookObject: + hookObject['reply'] = response_function + + if hookname in Hooks: + for hook in Hooks[hookname]: + hook['func'] (**hookObject) + + print_responses (hookObject) + def confirm (cmd, yes): global g_confirmCommand