From 5d057034824f5edfacfabb03dcafd8eb3f4eb7e9 Mon Sep 17 00:00:00 2001 From: Rafid Aslam Date: Mon, 26 Dec 2016 23:55:42 +0700 Subject: [PATCH] interactive bots: Create Giphy bot --- contrib_bots/lib/giphy.py | 133 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 contrib_bots/lib/giphy.py diff --git a/contrib_bots/lib/giphy.py b/contrib_bots/lib/giphy.py new file mode 100644 index 0000000..43c2299 --- /dev/null +++ b/contrib_bots/lib/giphy.py @@ -0,0 +1,133 @@ +# To use this plugin, you need to set up the Giphy API key for this bot in +# ~/.giphy_config + +from __future__ import absolute_import +from __future__ import print_function +from six.moves.configparser import SafeConfigParser +import requests +import logging +import sys +import os +import re + +GIPHY_TRANSLATE_API = 'http://api.giphy.com/v1/gifs/translate' + +if not os.path.exists(os.environ['HOME'] + '/.giphy_config'): + print('Giphy bot config file not found, please set up it in ~/.giphy_config' + '\n\nUsing format:\n\n[giphy-config]\nkey=\n\n') + sys.exit(1) + + +class GiphyHandler(object): + ''' + This plugin posts a GIF in response to the keywords provided by the user. + Images are provided by Giphy, through the public API. + The bot looks for messages starting with "@giphy" or @mention of the bot + and responds with a message with the GIF based on provided keywords. + It also responds to private messages. + ''' + def usage(self): + return ''' + This plugin allows users to post GIFs provided by Giphy. + Users should preface keywords with "@giphy" or the Giphy-bot @mention. + The bot responds also to private messages. + ''' + + def triage_message(self, message, client): + # To prevent infinite loop in private message, bot will detect + # if the sender name is the bot name it will return false. + if message['type'] == 'private': + return client.full_name != message['sender_full_name'] + + # Return True if we want to (possibly) response to this message. + original_content = message['content'] + is_giphy_called = (original_content.startswith('@giphy ') or + message['is_mentioned']) + + return is_giphy_called + + def handle_message(self, message, client, state_handler): + bot_response = get_bot_giphy_response(message, client) + + if message['type'] == 'private': + client.send_message(dict( + type='private', + to=message['sender_email'], + content=bot_response, + )) + else: + client.send_message(dict( + type='stream', + to=message['display_recipient'], + subject=message['subject'], + content=bot_response, + )) + + +class GiphyNoResultException(Exception): + pass + + +def get_giphy_api_key_from_config(): + config = SafeConfigParser() + with open(os.environ['HOME'] + '/.giphy_config', 'r') as config_file: + config.readfp(config_file) + return config.get("giphy-config", "key") + + +def get_url_gif_giphy(keyword, api_key): + # Return a URL for a Giphy GIF based on keywords given. + # In case of error, e.g. failure to fetch a GIF URL, it will + # return a number. + query = {'s': keyword, + 'api_key': api_key} + try: + data = requests.get(GIPHY_TRANSLATE_API, params=query) + except requests.exceptions.ConnectionError as e: # Usually triggered by bad connection. + logging.warning(e) + raise + + search_status = data.json()['meta']['status'] + if search_status != 200 or not data.ok: + raise requests.exceptions.ConnectionError + + try: + gif_url = data.json()['data']['images']['original']['url'] + except (TypeError, KeyError): # Usually triggered by no result in Giphy. + raise GiphyNoResultException() + + return gif_url + + +def get_bot_giphy_response(message, client): + # Handle the message that called through mention. + if message['is_mentioned']: + bot_mention = r'^@(\*\*{0}\*\*\s|{0}\s)(?=.*)'.format(client.full_name) + start_with_mention = re.compile(bot_mention).match(message['content']) + if start_with_mention: + keyword = message['content'][len(start_with_mention.group()):] + else: + return 'Please mention me first, then type the keyword.' + # Handle the message that called through the specified keyword. + elif message['content'].startswith('@giphy '): + keyword = message['content'][len('@giphy '):] + # Handle the private message. + elif message['type'] == 'private': + keyword = message['content'] + + # Each exception has a specific reply should "gif_url" return a number. + # The bot will post the appropriate message for the error. + try: + gif_url = get_url_gif_giphy(keyword, get_giphy_api_key_from_config()) + except requests.exceptions.ConnectionError: + return ('Uh oh, sorry :slightly_frowning_face:, I ' + 'cannot process your request right now. But, ' + 'let\'s try again later! :grin:') + except GiphyNoResultException: + return ('Sorry, I don\'t have a GIF for "%s"! ' + ':astonished:' % (keyword)) + return ('[Click to enlarge](%s)' + '[](/static/images/interactive-bot/giphy/powered-by-giphy.png)' + % (gif_url)) + +handler_class = GiphyHandler