- added bridging functionality

Thu, 15 Jan 2015 19:06:14 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Thu, 15 Jan 2015 19:06:14 +0200
changeset 118
dbf49689af0d
parent 117
6c0609395889
child 119
0f96e3157b7f

- 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
 

mercurial