From 29e22c2c0ad97caebd452b2a786a1338b6b9f86b Mon Sep 17 00:00:00 2001 From: Skunk Date: Fri, 1 Dec 2017 20:33:52 -0800 Subject: [PATCH] bots: Create Link Shortener Bot. Create Link Shortener Bot using the goo.gl Link Shortening API. Link Shortener Bot can be mentioned in a conversation, and it will respond with shortened, goo.gl links for every URL in the message. For example, > @link_shortener_bot @johnsmith Check out this file: > https://github.com/zulip/python-zulip-api/blob/master/zulip_bots/ and Link Shortener Bot would respond > https://github.com/zulip/python-zulip-api/blob/master/zulip_bots/: > https://goo.gl/Mt5z3c In order to use Link Shortener Bot, an API key for goo.gl must be set in `link_shortener.conf` in the `link_shortener` folder. --- zulip_bots/setup.py | 1 + .../bots/link_shortener/__init__.py | 0 .../zulip_bots/bots/link_shortener/doc.md | 17 ++++ .../link_shortener/fixtures/test_normal.json | 19 +++++ .../bots/link_shortener/link_shortener.conf | 2 + .../bots/link_shortener/link_shortener.py | 80 +++++++++++++++++++ .../link_shortener/test_link_shortener.py | 55 +++++++++++++ 7 files changed, 174 insertions(+) create mode 100644 zulip_bots/zulip_bots/bots/link_shortener/__init__.py create mode 100644 zulip_bots/zulip_bots/bots/link_shortener/doc.md create mode 100644 zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_normal.json create mode 100644 zulip_bots/zulip_bots/bots/link_shortener/link_shortener.conf create mode 100644 zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py create mode 100644 zulip_bots/zulip_bots/bots/link_shortener/test_link_shortener.py diff --git a/zulip_bots/setup.py b/zulip_bots/setup.py index 2c7b8dc..3026fbc 100755 --- a/zulip_bots/setup.py +++ b/zulip_bots/setup.py @@ -52,6 +52,7 @@ setuptools_info = dict( 'html2text', # for bots/define 'BeautifulSoup4', # for bots/googlesearch 'lxml', # for bots/googlesearch + 'requests' # for bots/link_shortener ], ) diff --git a/zulip_bots/zulip_bots/bots/link_shortener/__init__.py b/zulip_bots/zulip_bots/bots/link_shortener/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/zulip_bots/zulip_bots/bots/link_shortener/doc.md b/zulip_bots/zulip_bots/bots/link_shortener/doc.md new file mode 100644 index 0000000..0982157 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/link_shortener/doc.md @@ -0,0 +1,17 @@ +# Link Shortener Bot + +Link Shortener Bot is a Zulip bot that will shorten URLs ("links") in a +conversation. It uses the [goo.gl URL shortener API] to shorten its links. + +Links can be anywhere in the message, for example, + + > @**Link Shortener Bot** @**Joe Smith** See + > https://github.com/zulip/python-zulip-api/tree/master/zulip_bots/zulip_bots/bots + > for a list of all Zulip bots. + +and LS Bot would respond + + > https://github.com/zulip/python-zulip-api/tree/master/zulip_bots/zulip_bots/bots: + > **https://goo.gl/NjLZZH** + +[goo.gl URL shortener API]: https://goo.gl diff --git a/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_normal.json b/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_normal.json new file mode 100644 index 0000000..9c1d87b --- /dev/null +++ b/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_normal.json @@ -0,0 +1,19 @@ +{ + "request": { + "api_url": "https://www.googleapis.com/urlshortener/v1/url", + "method": "POST", + "params": { + "key": "qwertyuiop" + }, + "json": { + "longUrl": "https://www.github.com/zulip/zulip" + } + }, + "response": { + "id": "https://goo.gl/6uoWKb" + }, + "response-headers": { + "status": 200, + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.conf b/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.conf new file mode 100644 index 0000000..c3bacb4 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.conf @@ -0,0 +1,2 @@ +[link_shortener] +key = diff --git a/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py b/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py new file mode 100644 index 0000000..04450b8 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py @@ -0,0 +1,80 @@ +import re +import requests + +class LinkShortenerHandler(object): + '''A Zulip bot that will shorten URLs ("links") in a conversation using the + goo.gl URL shortener. + ''' + + def usage(self): + return ( + 'Mention the link shortener bot in a conversation and then enter ' + 'any URLs you want to shorten in the body of the message. \n\n' + '`key` must be set in `link_shortener.conf`.') + + def initialize(self, bot_handler): + self.config_info = bot_handler.get_config_info('link_shortener') + + def handle_message(self, message, bot_handler): + REGEX_STR = ( + '(' + '(?:http|https):\/\/' # This allows for the HTTP or HTTPS + # protocol. + '[^"<>#%\{\}|\\^~[\]` ]+' # This allows for any character except + # for certain non-URL-safe ones. + ')' + ) + + content = message['content'] + + if content.strip() == 'help': + bot_handler.send_reply( + message, + ( + 'Mention the link shortener bot in a conversation and ' + 'then enter any URLs you want to shorten in the body of ' + 'the message.' + ) + ) + return + + link_matches = re.findall(REGEX_STR, content) + + shortened_links = [self.shorten_link(link) for link in link_matches] + link_pairs = [ + (link_match + ': ' + shortened_link) + for link_match, shortened_link + in zip(link_matches, shortened_links) + if shortened_link != '' + ] + final_response = '\n'.join(link_pairs) + + if final_response == '': + bot_handler.send_reply( + message, + 'No links found. Send "help" to see usage instructions.' + ) + return + + bot_handler.send_reply(message, final_response) + + def shorten_link(self, long_url): + '''Shortens a link using goo.gl Link Shortener and returns it, or + returns an empty string if something goes wrong. + + Parameters: + long_url (str): The original URL to shorten. + ''' + + body = {'longUrl': long_url} + params = {'key': self.config_info['key']} + + request = requests.post( + 'https://www.googleapis.com/urlshortener/v1/url', + json=body, + params=params + ) + + return request.json().get('id', '') + +handler_class = LinkShortenerHandler diff --git a/zulip_bots/zulip_bots/bots/link_shortener/test_link_shortener.py b/zulip_bots/zulip_bots/bots/link_shortener/test_link_shortener.py new file mode 100644 index 0000000..fcff3ba --- /dev/null +++ b/zulip_bots/zulip_bots/bots/link_shortener/test_link_shortener.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +from zulip_bots.test_lib import BotTestCase + +class TestLinkShortenerBot(BotTestCase): + bot_name = "link_shortener" + + def test_bot(self): + MESSAGE = 'Shorten https://www.github.com/zulip/zulip please.' + RESPONSE = 'https://www.github.com/zulip/zulip: https://goo.gl/6uoWKb' + + with self.mock_config_info({'key': 'qwertyuiop'}), \ + self.mock_http_conversation('test_normal'): + self.initialize_bot() + + self.assert_bot_response( + message = {'content': MESSAGE}, + response = {'content': RESPONSE}, + expected_method='send_reply' + ) + + def test_bot_empty(self): + MESSAGE = 'Shorten nothing please.' + RESPONSE = 'No links found. Send "help" to see usage instructions.' + + # No `mock_http_conversation` is necessary because the bot will + # recognize that no links are in the message and won't make any HTTP + # requests. + with self.mock_config_info({'key': 'qwertyuiop'}): + self.initialize_bot() + + self.assert_bot_response( + message = {'content': MESSAGE}, + response = {'content': RESPONSE}, + expected_method='send_reply' + ) + + def test_bot_help(self): + MESSAGE = 'help' + RESPONSE = ( + 'Mention the link shortener bot in a conversation and then enter ' + 'any URLs you want to shorten in the body of the message.' + ) + + # No `mock_http_conversation` is necessary because the bot will + # recognize that the message is 'help' and won't make any HTTP + # requests. + with self.mock_config_info({'key': 'qwertyuiop'}): + self.initialize_bot() + + self.assert_bot_response( + message = {'content': MESSAGE}, + response = {'content': RESPONSE}, + expected_method='send_reply' + )