from random import randint import logging import requests XKCD_TEMPLATE_URL = 'https://xkcd.com/%s/info.0.json' LATEST_XKCD_URL = 'https://xkcd.com/info.0.json' class XkcdHandler(object): ''' This plugin provides several commands that can be used for fetch a comic strip from https://xkcd.com. The bot looks for messages starting with "@xkcd" and responds with a message with the comic based on provided commands. ''' def usage(self): return ''' This plugin allows users to fetch a comic strip provided by https://xkcd.com. Users should preface the command with "@xkcd". There are several commands to use this bot: - @xkcd help -> To show all commands the bot supports. - @xkcd latest -> To fetch the latest comic strip from xkcd. - @xkcd random -> To fetch a random comic strip from xkcd. - @xkcd -> To fetch a comic strip based on ``, e.g `@xkcd 1234`. ''' def triage_message(self, message, client): # Return True if we want to (possibly) response to this message original_content = message['content'] is_xkcd_called = original_content.startswith('@xkcd ') is_xkcd_called_without_command = original_content == '@xkcd' return is_xkcd_called or is_xkcd_called_without_command def handle_message(self, message, client, state_handler): xkcd_bot_response = get_xkcd_bot_response(message) client.send_message(dict( type='stream', to=message['display_recipient'], subject=message['subject'], content=xkcd_bot_response, )) class XkcdBotCommand(object): LATEST = 0 RANDOM = 1 COMIC_ID = 2 class XkcdNotFoundError(Exception): pass class XkcdServerError(Exception): pass def get_xkcd_bot_response(message): original_content = message['content'].strip() cropped = original_content[len('@xkcd '):] command = cropped.strip() xkcd_called_without_command = original_content == '@xkcd' commands_help = ("%s" "\n* `@xkcd help` to show this help message." "\n* `@xkcd latest` to fetch the latest comic strip from xkcd." "\n* `@xkcd random` to fetch a random comic strip from xkcd." "\n* `@xkcd ` to fetch a comic strip based on `` " "e.g `@xkcd 1234`.") try: if command == 'help' or xkcd_called_without_command: return commands_help % ('xkcd bot supports these commands:') elif command == 'latest': fetched = fetch_xkcd_query(XkcdBotCommand.LATEST) elif command == 'random': fetched = fetch_xkcd_query(XkcdBotCommand.RANDOM) elif command.isdigit(): fetched = fetch_xkcd_query(XkcdBotCommand.COMIC_ID, cropped.strip()) else: return commands_help % ('xkcd bot only supports these commands:') except (requests.exceptions.ConnectionError, XkcdServerError): logging.exception('Connection error occurred when trying to connect to xkcd server') return 'Sorry, I cannot process your request right now, please try again later!' except XkcdNotFoundError: logging.exception('XKCD server responded 404 when trying to fetch comic with id %s' % (command)) return 'Sorry, there is likely no xkcd comic strip with id: #%s' % (command) else: return ("#%s: **%s**\n[%s](%s)" % (fetched['num'], fetched['title'], fetched['alt'], fetched['img'])) def fetch_xkcd_query(mode, comic_id=None): try: if mode == XkcdBotCommand.LATEST: # Fetch the latest comic strip. url = LATEST_XKCD_URL elif mode == XkcdBotCommand.RANDOM: # Fetch a random comic strip. latest = requests.get(LATEST_XKCD_URL) if latest.status_code != 200: raise XkcdServerError() latest_id = latest.json()['num'] random_id = randint(1, latest_id) url = XKCD_TEMPLATE_URL % (str(random_id)) elif mode == XkcdBotCommand.COMIC_ID: # Fetch specific comic strip by id number. if comic_id is None: raise Exception('Missing comic_id argument') url = XKCD_TEMPLATE_URL % (comic_id) fetched = requests.get(url) if fetched.status_code == 404: raise XkcdNotFoundError() elif fetched.status_code != 200: raise XkcdServerError() xkcd_json = fetched.json() except requests.exceptions.ConnectionError as e: logging.warning(e) raise return xkcd_json handler_class = XkcdHandler