diff --git a/apprise/plugins/NotifyTwitter.py b/apprise/plugins/NotifyTwitter.py index 364eca41..8483fad6 100644 --- a/apprise/plugins/NotifyTwitter.py +++ b/apprise/plugins/NotifyTwitter.py @@ -419,11 +419,36 @@ class NotifyTwitter(NotifyBase): if not postokay: # Track our error has_error = True + + errors = [] + try: + errors = ['Error Code {}: {}'.format( + e.get('code', 'unk'), e.get('message')) + for e in response['errors']] + + except (KeyError, TypeError): + pass + + for error in errors: + self.logger.debug( + 'Tweet [%.2d/%.2d] Details: %s', + no, len(payloads), error) continue + try: + url = 'https://twitter.com/{}/status/{}'.format( + response['user']['screen_name'], + response['id_str']) + + except (KeyError, TypeError): + url = 'unknown' + + self.logger.debug( + 'Tweet [%.2d/%.2d] Details: %s', no, len(payloads), url) + self.logger.info( - 'Sent [{:02d}/{:02d}] Twitter notification as public tweet.' - .format(no, len(payloads))) + 'Sent [%.2d/%.2d] Twitter notification as public tweet.', + no, len(payloads)) return not has_error @@ -691,6 +716,15 @@ class NotifyTwitter(NotifyBase): timeout=self.request_timeout, ) + try: + content = loads(r.content) + + except (AttributeError, TypeError, ValueError): + # ValueError = r.content is Unparsable + # TypeError = r.content is None + # AttributeError = r is None + content = {} + if r.status_code != requests.codes.ok: # We had a problem status_str = \ @@ -710,15 +744,6 @@ class NotifyTwitter(NotifyBase): # Mark our failure return (False, content) - try: - content = loads(r.content) - - except (AttributeError, TypeError, ValueError): - # ValueError = r.content is Unparsable - # TypeError = r.content is None - # AttributeError = r is None - content = {} - try: # Capture rate limiting if possible self.ratelimit_remaining = \ diff --git a/test/test_plugin_twitter.py b/test/test_plugin_twitter.py index 8ac20fde..34878a3e 100644 --- a/test/test_plugin_twitter.py +++ b/test/test_plugin_twitter.py @@ -723,6 +723,86 @@ def test_plugin_twitter_tweet_attachments(mock_get, mock_post): assert mock_post.call_args_list[1][0][0] == \ 'https://api.twitter.com/1.1/statuses/update.json' + # Update our good response to have more details in it + good_tweet_response_obj = { + 'screen_name': screen_name, + 'id': 9876, + # needed for additional logging + 'id_str': '12345', + 'user': { + 'screen_name': screen_name, + } + } + + good_tweet_response.content = dumps(good_tweet_response_obj) + + mock_get.reset_mock() + mock_post.reset_mock() + + mock_post.side_effect = [good_media_response, good_tweet_response] + mock_get.return_value = good_tweet_response + + # instantiate our object + obj = Apprise.instantiate(twitter_url) + + # Send our notification (again); this time there willb e more tweet logging + assert obj.notify( + body='body', title='title', notify_type=NotifyType.INFO, + attach=attach) is True + + # Test our call count + assert mock_get.call_count == 0 + assert mock_post.call_count == 2 + assert mock_post.call_args_list[0][0][0] == \ + 'https://upload.twitter.com/1.1/media/upload.json' + assert mock_post.call_args_list[1][0][0] == \ + 'https://api.twitter.com/1.1/statuses/update.json' + + mock_get.reset_mock() + mock_post.reset_mock() + + mock_post.side_effect = [good_media_response, bad_media_response] + + # instantiate our object + obj = Apprise.instantiate(twitter_url) + + # Our notification will fail now since our tweet will error out + assert obj.notify( + body='body', title='title', notify_type=NotifyType.INFO, + attach=attach) is False + + # Test our call count + assert mock_get.call_count == 0 + assert mock_post.call_count == 2 + assert mock_post.call_args_list[0][0][0] == \ + 'https://upload.twitter.com/1.1/media/upload.json' + assert mock_post.call_args_list[1][0][0] == \ + 'https://api.twitter.com/1.1/statuses/update.json' + + mock_get.reset_mock() + mock_post.reset_mock() + + bad_media_response.content = '' + + mock_post.side_effect = [good_media_response, bad_media_response] + + # instantiate our object + obj = Apprise.instantiate(twitter_url) + + # Our notification will fail now since our tweet will error out + # This is the same test as above, except our error response isn't parseable + assert obj.notify( + body='body', title='title', notify_type=NotifyType.INFO, + attach=attach) is False + + # Test our call count + assert mock_get.call_count == 0 + assert mock_post.call_count == 2 + assert mock_post.call_args_list[0][0][0] == \ + 'https://upload.twitter.com/1.1/media/upload.json' + assert mock_post.call_args_list[1][0][0] == \ + 'https://api.twitter.com/1.1/statuses/update.json' + mock_get.reset_mock() mock_post.reset_mock()