From 3db070b99e6a6c675669874482204d50e98e233d Mon Sep 17 00:00:00 2001 From: novokrest Date: Fri, 27 Apr 2018 21:42:05 +0300 Subject: [PATCH] link_shortener: Migrate from goo.gl to bit.ly service. Shorten URLs by using bit.ly service instead of goo.gl service. Fixes #367 --- .../zulip_bots/bots/link_shortener/doc.md | 9 +++-- .../fixtures/test_invalid_access_token.json | 18 ++++++++++ .../fixtures/test_invalid_key.json | 27 -------------- .../link_shortener/fixtures/test_normal.json | 18 ++++++---- .../bots/link_shortener/link_shortener.py | 35 +++++++++++-------- .../link_shortener/test_link_shortener.py | 10 +++--- 6 files changed, 60 insertions(+), 57 deletions(-) create mode 100644 zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_access_token.json delete mode 100644 zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_key.json diff --git a/zulip_bots/zulip_bots/bots/link_shortener/doc.md b/zulip_bots/zulip_bots/bots/link_shortener/doc.md index d24c2bc..241e8f6 100644 --- a/zulip_bots/zulip_bots/bots/link_shortener/doc.md +++ b/zulip_bots/zulip_bots/bots/link_shortener/doc.md @@ -1,10 +1,9 @@ # 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. +conversation. It uses the [bitly URL shortener API] to shorten its links. -Use [this](https://developers.google.com/url-shortener/v1/getting_started) to get -your API Key. +Use [this](https://dev.bitly.com/get_started.html) to get your API Key. Links can be anywhere in the message, for example, @@ -15,6 +14,6 @@ Links can be anywhere in the message, for example, and LS Bot would respond > https://github.com/zulip/python-zulip-api/tree/master/zulip_bots/zulip_bots/bots: - > **https://goo.gl/NjLZZH** + > **https://bit.ly/2FF3QHu** -[goo.gl URL shortener API]: https://goo.gl +[bitly URL shortener API]: https://bitly.com/ diff --git a/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_access_token.json b/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_access_token.json new file mode 100644 index 0000000..58a0f88 --- /dev/null +++ b/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_access_token.json @@ -0,0 +1,18 @@ +{ + "request": { + "api_url": "https://api-ssl.bitly.com/v3/shorten", + "method": "GET", + "params": { + "access_token": "qwertyuiopx", + "longUrl": "www.youtube.com/watch" + } + }, + "response": { + "status_code": 500, + "status_txt": "INVALID_ARG_ACCESS_TOKEN", + "data": [] + }, + "response-headers": { + "content-type": "application/json; charset=utf-8" + } +} diff --git a/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_key.json b/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_key.json deleted file mode 100644 index 6ce938f..0000000 --- a/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_invalid_key.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "request": { - "api_url": "https://www.googleapis.com/urlshortener/v1/url", - "method": "POST", - "params": { - "key": "qwertyuiopx" - }, - "json": { - "longUrl": "www.youtube.com/watch" - } - }, - "response": { - "error":{ - "errors":[ - { - "reason":"keyInvalid" - } - ] - }, - "code": 400, - "message": "Bad Request" - }, - "response-headers": { - "status": 400, - "content-type": "application/json; charset=utf-8" - } -} 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 index 9c1d87b..9a91f31 100644 --- a/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_normal.json +++ b/zulip_bots/zulip_bots/bots/link_shortener/fixtures/test_normal.json @@ -1,16 +1,22 @@ { "request": { - "api_url": "https://www.googleapis.com/urlshortener/v1/url", - "method": "POST", + "api_url": "https://api-ssl.bitly.com/v3/shorten", + "method": "GET", "params": { - "key": "qwertyuiop" - }, - "json": { + "access_token": "qwertyuiop", "longUrl": "https://www.github.com/zulip/zulip" } }, "response": { - "id": "https://goo.gl/6uoWKb" + "status_code": 200, + "status_txt": "OK", + "data": { + "url": "http://bit.ly/2Ht2hOI", + "hash": "2Ht2hOI", + "global_hash": "3j4ir4", + "long_url": "https://www.github.com/zulip/zulip/", + "new_hash": 0 + } }, "response-headers": { "status": 200, diff --git a/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py b/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py index 0de1289..250606f 100644 --- a/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py +++ b/zulip_bots/zulip_bots/bots/link_shortener/link_shortener.py @@ -20,18 +20,16 @@ class LinkShortenerHandler(object): self.check_api_key(bot_handler) def check_api_key(self, bot_handler: Any) -> None: - test_request = requests.post( - 'https://www.googleapis.com/urlshortener/v1/url', - json={'longUrl': 'www.youtube.com/watch'}, - params={'key': self.config_info['key']} - ) # type: Any - test_request_data = test_request.json() + test_request_data = self.call_link_shorten_service('www.youtube.com/watch') # type: Any try: - if test_request_data['error']['errors'][0]['reason'] == 'keyInvalid': + if self.is_invalid_token_error(test_request_data): bot_handler.quit('Invalid key. Follow the instructions in doc.md for setting API key.') except KeyError: pass + def is_invalid_token_error(self, response_json: Any) -> bool: + return response_json['status_code'] == 500 and response_json['status_txt'] == 'INVALID_ARG_ACCESS_TOKEN' + def handle_message(self, message: Dict[str, str], bot_handler: Any) -> None: REGEX_STR = ( '(' @@ -83,15 +81,24 @@ class LinkShortenerHandler(object): long_url (str): The original URL to shorten. ''' - body = {'longUrl': long_url} - params = {'key': self.config_info['key']} + response_json = self.call_link_shorten_service(long_url) + if response_json['status_code'] == 200 and self.has_shorten_url(response_json): + shorten_url = self.get_shorten_url(response_json) + else: + shorten_url = '' + return shorten_url - request = requests.post( - 'https://www.googleapis.com/urlshortener/v1/url', - json=body, - params=params + def call_link_shorten_service(self, long_url: str) -> Any: + response = requests.get( + 'https://api-ssl.bitly.com/v3/shorten', + params={'access_token': self.config_info['key'], 'longUrl': long_url} ) + return response.json() - return request.json().get('id', '') + def has_shorten_url(self, response_json: Any) -> bool: + return 'data' in response_json and 'url' in response_json['data'] + + def get_shorten_url(self, response_json: Any) -> str: + return response_json['data']['url'] 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 index d1a141a..0073f74 100644 --- a/zulip_bots/zulip_bots/bots/link_shortener/test_link_shortener.py +++ b/zulip_bots/zulip_bots/bots/link_shortener/test_link_shortener.py @@ -12,19 +12,19 @@ class TestLinkShortenerBot(BotTestCase): self.verify_reply(message, response) def test_bot_responds_to_empty_message(self) -> None: - with patch('requests.post'): + with patch('requests.get'): self._test('', 'No links found. Send "help" to see usage instructions.') def test_normal(self) -> None: with self.mock_http_conversation('test_normal'): self._test('Shorten https://www.github.com/zulip/zulip please.', - 'https://www.github.com/zulip/zulip: https://goo.gl/6uoWKb') + 'https://www.github.com/zulip/zulip: http://bit.ly/2Ht2hOI') def test_no_links(self) -> None: # 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 patch('requests.post'): + with patch('requests.get'): self._test('Shorten nothing please.', 'No links found. Send "help" to see usage instructions.') @@ -32,7 +32,7 @@ class TestLinkShortenerBot(BotTestCase): # No `mock_http_conversation` is necessary because the bot will # recognize that the message is 'help' and won't make any HTTP # requests. - with patch('requests.post'): + with patch('requests.get'): self._test('help', ('Mention the link shortener bot in a conversation and then ' 'enter any URLs you want to shorten in the body of the message.')) @@ -40,6 +40,6 @@ class TestLinkShortenerBot(BotTestCase): def test_exception_when_api_key_is_invalid(self)-> None: bot_test_instance = LinkShortenerHandler() with self.mock_config_info({'key': 'qwertyuiopx'}): - with self.mock_http_conversation('test_invalid_key'): + with self.mock_http_conversation('test_invalid_access_token'): with self.assertRaises(StubBotHandler.BotQuitException): bot_test_instance.initialize(StubBotHandler())