link_shortener: Migrate from goo.gl to bit.ly service.
Shorten URLs by using bit.ly service instead of goo.gl service. Fixes #367
This commit is contained in:
		
							parent
							
								
									dcd4c4a96e
								
							
						
					
					
						commit
						3db070b99e
					
				
					 6 changed files with 60 additions and 57 deletions
				
			
		| 
						 | 
				
			
			@ -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/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue