From 4ccabd030510862747e54a22cac3a980dd28137f Mon Sep 17 00:00:00 2001 From: Alex Shteinikov Date: Sun, 19 Oct 2014 10:30:15 +1000 Subject: [PATCH] migration to the new plugins structure + version bump --- pancake.py | 8 ++++---- plugins/blame.py | 10 ++++++++++ plugins/chuck.py | 22 +++++++++++++++++++++ plugins/geckoboard.py | 46 +++++++++++++++++++++++++++++++++++++++++++ plugins/giphy.py | 6 +++++- plugins/pony.py | 11 +++++++++++ plugins/question.py | 11 +++++++++++ plugins/roll.py | 10 ++++++++++ plugins/rps.py | 14 +++++++++++++ plugins/xkcd.py | 22 +++++++++++++++++++++ src/bot.py | 14 ++++++++----- src/config.py | 40 ++++++++++++++++++++----------------- src/plugin_loader.py | 2 +- 13 files changed, 187 insertions(+), 29 deletions(-) create mode 100644 plugins/blame.py create mode 100644 plugins/chuck.py create mode 100644 plugins/geckoboard.py create mode 100644 plugins/pony.py create mode 100644 plugins/question.py create mode 100644 plugins/roll.py create mode 100644 plugins/rps.py create mode 100644 plugins/xkcd.py diff --git a/pancake.py b/pancake.py index 426d607..6f57057 100755 --- a/pancake.py +++ b/pancake.py @@ -1,7 +1,7 @@ #!/usr/bin/env python __project_name__ = 'Pancake chat bot (HipChat edition)' -__version__ = '1.0' +__version__ = '2.0.0' import argparse import src as library @@ -17,9 +17,9 @@ # Config to use conf_name = args.config - conf = library.config.Settings(conf_name) + settings = library.config.Settings(conf_name) - bot = library.Bot(conf) + bot = library.Bot(settings.get()) - bot.join_rooms(conf.general['rooms']) + bot.join_rooms(settings.get('general:rooms')) bot.start() diff --git a/plugins/blame.py b/plugins/blame.py new file mode 100644 index 0000000..3e16966 --- /dev/null +++ b/plugins/blame.py @@ -0,0 +1,10 @@ + +class BlamePlugin(): + + help = "Blame somebody" + command = "blame" + + @staticmethod + def response(random_user): + message = '{}, this is your fault!' + return message.format(random_user) \ No newline at end of file diff --git a/plugins/chuck.py b/plugins/chuck.py new file mode 100644 index 0000000..119e015 --- /dev/null +++ b/plugins/chuck.py @@ -0,0 +1,22 @@ +import requests + +class ChuckNorrisPlugin(): + + help = "Post a random Chuck's phrase" + command = "chuck" + + @staticmethod + def response(): + + message = "Can't connect to Chuck API =(" + params = {'limitTo': '[nerdy]'} + + r = requests.get('http://api.icndb.com/jokes/random', params=params) + + if r.status_code == 200: + response = r.json() + + if response['type'] == 'success': + message = response['value']['joke'] + + return message \ No newline at end of file diff --git a/plugins/geckoboard.py b/plugins/geckoboard.py new file mode 100644 index 0000000..4d6a9b9 --- /dev/null +++ b/plugins/geckoboard.py @@ -0,0 +1,46 @@ +import requests +import json + +class GeckoboardPlugin(): + + help = "Post message to Geckoboard" + command = "board" + + widget_key = None + api_key = None + + def __init__(self, conf): + if not 'geckoboard' in conf: + raise Exception("There is no [geckoboard] section in the config file") + + if not 'api' in conf['geckoboard'] or not 'widget' in conf['geckoboard']: + raise Exception("You must specify 'widget' and 'api' options " + "in [geckoboard] section in the config file") + + self.widget_key = conf['geckoboard']['widget'] + self.api_key = conf['geckoboard']['api'] + + def response(self, message): + + message_parts = message.split(' ', 1) + + params = { + "api_key": self.api_key, + "data": { + "item": [ + { "text": message_parts[1], "type":0 } + ] + } + } + + headers = {'Content-type': 'application/json'} + r = requests.post('https://push.geckoboard.com/v1/send/' + self.widget_key, + data=json.dumps(params), headers=headers) + + if r.status_code == 200: + response = r.json() + + if response['success']: + return 'Message was posted to Geckoboard' + + return 'Something went wrong' \ No newline at end of file diff --git a/plugins/giphy.py b/plugins/giphy.py index 577ec70..51a1407 100644 --- a/plugins/giphy.py +++ b/plugins/giphy.py @@ -6,7 +6,7 @@ class GiphyPlugin(): command = "gif" @staticmethod - def response(message): + def response(message, author): search_values = message.split('/gif', 1) tags = '' if len(search_values) == 2: @@ -16,6 +16,10 @@ def response(message): if r.status_code == 200: response = r.json() + + if len(response['data']) == 0: + return "{}, I have no images about that!".format(author) + return response['data']['image_url'] else: diff --git a/plugins/pony.py b/plugins/pony.py new file mode 100644 index 0000000..8ba0892 --- /dev/null +++ b/plugins/pony.py @@ -0,0 +1,11 @@ +import random + +class PonyPlugin(): + + help = "Post a random pony image" + command = "pony" + + @staticmethod + def response(): + max_number = 160 + return "http://ponyfac.es/{}/full.jpg".format(random.randint(1, max_number)) \ No newline at end of file diff --git a/plugins/question.py b/plugins/question.py new file mode 100644 index 0000000..1904af3 --- /dev/null +++ b/plugins/question.py @@ -0,0 +1,11 @@ +import random + +class AskMePlugin(): + + help = "Ask me a question" + command = "?" + + @staticmethod + def response(author): + options = ['yes', 'no', 'no way!', 'yep!'] + return '{0}, {1}'.format(author, random.choice(options)) \ No newline at end of file diff --git a/plugins/roll.py b/plugins/roll.py new file mode 100644 index 0000000..3dca8ed --- /dev/null +++ b/plugins/roll.py @@ -0,0 +1,10 @@ +import random + +class RollPlugin(): + + help = "Roll a random number 0 - 100" + command = "roll" + + @staticmethod + def response(author): + return '{0} rolled {1}'.format(author, random.randint(0, 100)) \ No newline at end of file diff --git a/plugins/rps.py b/plugins/rps.py new file mode 100644 index 0000000..be4720f --- /dev/null +++ b/plugins/rps.py @@ -0,0 +1,14 @@ +import random + +class RPSPlugin(): + + help = "Rock - Paper - Scissors - Lizard - Spock (type '/rps help' for help)" + command = "rps" + + @staticmethod + def response(message, author): + if 'help' in message: + return 'http://a.tgcdn.net/images/products/additional/large/db2e_lizard_spock.jpg' + + options = ['Rock', 'Paper', 'Scissors', 'Lizard', 'Spock'] + return '{0} - {1}'.format(author, random.choice(options)) \ No newline at end of file diff --git a/plugins/xkcd.py b/plugins/xkcd.py new file mode 100644 index 0000000..21a6149 --- /dev/null +++ b/plugins/xkcd.py @@ -0,0 +1,22 @@ +import random +import requests + +class XKCDPlugin(): + + help = "Get random xkcd comics" + command = "xkcd" + + @staticmethod + def response(): + + max_value = 1335 + value = random.randint(1, max_value) + + r = requests.get('http://xkcd.com/{}/info.0.json'.format(value)) + + if r.status_code == 200: + response = r.json() + return [response['img'], response['alt']] + + else: + return "Can't connect to xkcd API =(" \ No newline at end of file diff --git a/src/bot.py b/src/bot.py index 9b64176..33eadd8 100644 --- a/src/bot.py +++ b/src/bot.py @@ -26,8 +26,8 @@ class Bot(): def __init__(self, conf): - api_token = conf.general['api_token'] - name = conf.general['bot_name'] + api_token = conf['general']['api_token'] + name = conf['general']['bot_name'] self.hipster = HipChat(token=api_token) @@ -194,15 +194,19 @@ def execute_action(self, action, room_name, message_object): if 'room' in fields: args.update({'room': room_name}) if 'author_id' in fields: args.update({'author_id': message_object['from']['user_id']}) - if 'author' in fields: args.update({'author': message_object['from']['name']}) + if 'author' in fields: args.update({'author': self.__mention_user(message_object['from']['name'])}) if 'message' in fields: args.update({'message': message_object['message']}) if 'random_user' in fields: args.update({'random_user': self.__get_random_user(room_name)}) if 'mentioned_user' in fields: args.update({'mentioned_user': self.__get_mentioned_user(room_name, message_object['message'])}) - message = action(**args) + messages = action(**args) - self.post_message(room_name, message) + if not isinstance(messages, list): + messages = [messages] + + for message in messages: + self.post_message(room_name, message) def start(self): last_dates = self.__get_latest_dates() diff --git a/src/config.py b/src/config.py index 0ce2732..6d769b7 100644 --- a/src/config.py +++ b/src/config.py @@ -7,12 +7,8 @@ class Settings(): - rules = { - 'general': ['api_token', 'rooms', 'bot_name'], - 'geckoboard': ['api', 'widget'] - } - - default_conf_name = 'example.conf' + __default_conf_name = 'example.conf' + __conf = {} def __init__(self, filename=False): """ @@ -25,7 +21,7 @@ def __init__(self, filename=False): self.CONF_DIR = self.APP_DIR + 'conf/' if not filename: - filename = self.default_conf_name + filename = self.__default_conf_name self.FILENAME = self.CONF_DIR + filename self.CONF_NAME = filename @@ -33,19 +29,27 @@ def __init__(self, filename=False): config = ConfigParser.ConfigParser() config.readfp(open(self.CONF_DIR + filename)) - for section in self.rules.keys(): + for section_name in config.sections(): data = {} - for item in self.rules[section]: + for item in config.items(section_name): try: - data.update({item: str(config.get(section, item)).strip()}) + data.update({item[0]: str(config.get(section_name, item[0])).strip()}) except ConfigParser.NoOptionError: - data.update({item: ''}) - except ConfigParser.NoSectionError: - for option in self.rules[section]: - data.update({option: False}) - break + data.update({item[0]: None}) + + if data[item[0]].lower() == 'false': + data[item[0]] = False + + self.__conf.update({ + section_name: data + }) + + def get(self, key=None): + if not key: return self.__conf - if data[item].lower() == 'false': - data[item] = False + try: + section, option = key.split(':') + return self.__conf[section][option] + except KeyError, e: + raise Exception("Can't find config key in file: " + str(e)) - setattr(self, section, data) diff --git a/src/plugin_loader.py b/src/plugin_loader.py index b5dd285..6d55232 100644 --- a/src/plugin_loader.py +++ b/src/plugin_loader.py @@ -51,7 +51,7 @@ def load_plugin(self, filename): args.update({'conf': self.conf}) plugin = class_name(**args) - if not hasattr(plugin, 'help') and not hasattr(plugin, 'command'): + if not hasattr(plugin, 'help') or not hasattr(plugin, 'command') or len(plugin.command) == 0: raise Exception("Plugin class must have 'help' and 'command' attributes") self.__check_conflicts(plugin.command)