From 19731befeaccf6bcece9884eaa32579152447bf8 Mon Sep 17 00:00:00 2001 From: Devon Jones Date: Sun, 10 Mar 2013 16:04:25 -0600 Subject: [PATCH] Added a content decorator that allows hippybot plugins to be able to be written against any text. A sample plugin based on plusplusbot from http://partychapp.appspot.com/ is included --- hippybot/bot.py | 22 ++++++++++ hippybot/decorators.py | 13 ++++++ hippybot/plugins/plusplusbot.py | 75 +++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 hippybot/plugins/plusplusbot.py diff --git a/hippybot/bot.py b/hippybot/bot.py index 73e2a89..e88f0f7 100644 --- a/hippybot/bot.py +++ b/hippybot/bot.py @@ -34,6 +34,7 @@ class HippyBot(JabberBot): hipchat.com chatroom/IM service. """ + _content_commands = {} _global_commands = [] _command_aliases = {} _all_msg_handlers = [] @@ -147,6 +148,12 @@ def callback_message(self, conn, mess): self._last_message = message if ret: return ret + for name in self._content_commands: + cmd = self._content_commands[name] + ret = cmd(mess) + if ret: + self.send_simple_reply(mess, ret) + return ret def join_room(self, room, username=None, password=None): """Overridden from JabberBot to provide history limiting. @@ -206,6 +213,7 @@ def load_plugins(self, mess=None, args=None): plugin.bot = self commands = [c for c in dir(plugin)] funcs = [] + content_funcs = [] for command in commands: m = getattr(plugin, command) @@ -220,6 +228,17 @@ def load_plugins(self, mess=None, args=None): name = getattr(m, '_jabberbot_command_name', False) funcs.append((name, m)) + if ismethod(m) and getattr(m, '_jabberbot_content_command', False): + if command in RESERVED_COMMANDS: + self.log.error('Plugin "%s" attempted to register ' + 'reserved command "%s", skipping..' % ( + plugin, command + )) + continue + self.rewrite_docstring(m) + name = getattr(m, '_jabberbot_command_name', False) + content_funcs.append((name, m)) + # Check for commands that don't need to be directed at # hippybot, e.g. they can just be said in the channel self._global_commands.extend(getattr(plugin, @@ -239,6 +258,9 @@ def load_plugins(self, mess=None, args=None): for command, func in funcs: setattr(self, command, func) self.commands[command] = func + for command, func in content_funcs: + setattr(self, command, func) + self._content_commands[command] = func if mess: return 'Reloading plugin modules and classes..' diff --git a/hippybot/decorators.py b/hippybot/decorators.py index 61710b8..c69b823 100644 --- a/hippybot/decorators.py +++ b/hippybot/decorators.py @@ -8,3 +8,16 @@ def wrapper(self, origin, args): username = unicode(origin.getFrom()).split('/')[1].replace(" ","") return u'@%s %s' % (username, message) return botcmd(wrapper) + +def contentcmd(*args, **kwargs): + """Decorator for bot commentary""" + + def decorate(func, name=None): + setattr(func, '_jabberbot_content_command', True) + setattr(func, '_jabberbot_command_name', name or func.__name__) + return func + + if len(args): + return decorate(args[0], **kwargs) + else: + return lambda func: decorate(func, **kwargs) diff --git a/hippybot/plugins/plusplusbot.py b/hippybot/plugins/plusplusbot.py new file mode 100644 index 0000000..13a9cbb --- /dev/null +++ b/hippybot/plugins/plusplusbot.py @@ -0,0 +1,75 @@ +import os +import os.path +import re +import sqlite3dbm +from threading import RLock +from hippybot.hipchat import HipChatApi +from hippybot.decorators import botcmd, contentcmd + +CONFIG_DIR = os.path.expanduser("~/.techbot") +DB = os.path.expanduser("~/.techbot/score.db") + +class Plugin(object): + """Plugin to handle knewton replacement of ++ bot in partychatapp + """ + def __init__(self): + self.rlock = RLock() + self.db = self.get_db() + + def get_db(self): + self.create_dir() + db = sqlite3dbm.sshelve.open(DB) + return db + + def create_dir(self): + if not os.path.exists(CONFIG_DIR): + os.mkdir(CONFIG_DIR) + + @contentcmd + def change_score(self, mess, **kwargs): + message = mess.getBody() + if message: + room = str(mess.getFrom()).split("/")[0] + user = str(mess.getFrom()).split("/")[1] + results = [] + if message.find('++') > -1 or message.find('--') > -1: + self.bot.log.info("plusplusbot: %s" % mess) + if message.endswith("++") or message.endswith("--"): + results.extend(self.process_message(message, room, user)) + for m in re.findall("\((.*?)\)", message): + if m.endswith("++") or m.endswith("--"): + results.extend(self.process_message(m, room, user)) + if len(results) > 0: + return "\n".join(results) + + def process_message(self, message, room, user): + results = [] + victim = message[:-2] + excl = "woot!" + plus = 1 + if message.endswith('--'): + excl = "ouch!" + plus = -1 + with self.rlock: + scores = self.db.get(room, {}) + score = scores.setdefault(victim, 0) + score += plus + scores[victim] = score + self.db[room] = scores + return ["[%s] %s [%s now at %s]" % (user, victim, excl, score)] + + @botcmd + def scores(self, mess, args, **kwargs): + """ + Prints all scores from this room + Format: @NickName scores + """ + self.bot.log.info("score: %s" % mess) + room = str(mess.getFrom()).split("/")[0] + ret = [] + with self.rlock: + scores = self.db.get(room, {}) + for key in scores: + ret.append("%s: %s" %(key, scores[key])) + return '\n'.join(ret) +