diff --git a/twitter/api.py b/twitter/api.py index ecba5d92..1e784709 100755 --- a/twitter/api.py +++ b/twitter/api.py @@ -154,6 +154,7 @@ def __init__(self, request_headers=None, cache=DEFAULT_CACHE, base_url=None, + base_url_v2=None, stream_url=None, upload_url=None, chunk_size=1024 * 1024, @@ -260,6 +261,7 @@ def __init__(self, self.cert_ssl = cert_ssl self.base_url = base_url or 'https://api.twitter.com/1.1' + self.base_url_v2 = base_url_v2 or 'https://api.twitter.com/2' self.stream_url = stream_url or 'https://stream.twitter.com/1.1' self.upload_url = upload_url or 'https://upload.twitter.com/1.1' @@ -748,7 +750,9 @@ def GetUserTimeline(self, count=None, include_rts=True, trim_user=False, - exclude_replies=False): + exclude_replies=False, + media_only=False, + return_json=False): """Fetch the sequence of public Status messages for a single user. The twitter.Api instance must be authenticated if the user is private. @@ -807,11 +811,15 @@ def GetUserTimeline(self, parameters['include_rts'] = enf_type('include_rts', bool, include_rts) parameters['trim_user'] = enf_type('trim_user', bool, trim_user) parameters['exclude_replies'] = enf_type('exclude_replies', bool, exclude_replies) + parameters['media_only'] = enf_type('media_only', int, media_only) resp = self._RequestUrl(url, 'GET', data=parameters) data = self._ParseAndCheckTwitter(resp.content.decode('utf-8')) - return [Status.NewFromJsonDict(x) for x in data] + if return_json: + return data + else: + return [Status.NewFromJsonDict(x) for x in data] def GetStatus(self, status_id, @@ -1660,6 +1668,50 @@ def GetUserRetweets(self, exclude_replies=True, include_rts=True) + def GetConversation(self, + conversation_id, + trim_user=True, + cursor=None, + return_json=True): + if not return_json: + raise RuntimeError('Not implemented') + + url = '%s/timeline/conversation/%s.json' % (self.base_url_v2, conversation_id) + + parameters = { + 'trim_user': enf_type('trim_user', bool, trim_user), + } + if cursor: + parameters['cursor'] = cursor + + resp = self._RequestUrl(url, 'GET', data=parameters) + data = self._ParseAndCheckTwitter(resp.content.decode('utf-8')) + result = {'tweets': data['globalObjects']['tweets']} + + for sid in result['tweets']: + # set 'text' field + if 'text' not in result['tweets'][sid] and \ + 'full_text' in result['tweets'][sid] and \ + 'display_text_range' in result['tweets'][sid]: + result['tweets'][sid]['text'] = result['tweets'][sid]['full_text'][result['tweets'][sid][ + 'display_text_range'][0]:result['tweets'][sid]['display_text_range'][1] + ] + # add int values for *_str fields + for prop in list(result['tweets'][sid]): + if prop.endswith('_str') and prop[:-4] not in result['tweets'][sid]: + result['tweets'][sid][prop[:-4]] = int(result['tweets'][sid][prop]) + + + # get cursor for next page of comments + for instr in data['timeline']['instructions']: + if 'addEntries' in instr: + if next_cursor := instr['addEntries']['entries'][-1].\ + get('content',{}).get('operation',{}).get('cursor',{}).get('value'): + result['next_cursor'] = next_cursor + break + + return result + def GetReplies(self, since_id=None, count=None, @@ -5090,7 +5142,7 @@ def _ParseAndCheckTwitter(self, json_data): raise TwitterError({'message': "Exceeded connection limit for user"}) if "Error 401 Unauthorized" in json_data: raise TwitterError({'message': "Unauthorized"}) - raise TwitterError({'message': 'Unknown error': '{0}'.format(json_data)}) + raise TwitterError({'message': 'Unknown error: {0}'.format(json_data)}) self._CheckForTwitterError(data) return data @@ -5108,6 +5160,8 @@ def _CheckForTwitterError(data): """ # Twitter errors are relatively unlikely, so it is faster # to check first, rather than try and catch the exception + if not data: + raise TwitterError('Empty response') if 'error' in data: raise TwitterError(data['error']) if 'errors' in data: