python-twitter-tools / twitter

Python Twitter API
http://mike.verdone.ca/twitter/
MIT License
3.17k stars 710 forks source link

[API v2] OAuth 1.0a failed for auth info is not passed through Headers #438

Closed ryuujo1573 closed 2 years ago

ryuujo1573 commented 2 years ago

Hi, I ran into v2, and the package seemed not evaluated for api v2 yet. I was wondering if there's way to use OAuth 1.0a with api v2? My solution is not working...

excuse the mess Orz

try:
    expansions = ['author_id', 'in_reply_to_user_id',
                  'referenced_tweets.id', 'referenced_tweets.id.author_id']
    user_fields = ['created_at', 'description', 'entities', 'id', 'location', 'name', 'pinned_tweet_id',
                   'profile_image_url', 'protected', 'public_metrics', 'url', 'username', 'verified', 'withheld']
    tweet_fields = ['attachments', 'author_id', 'context_annotations', 'conversation_id', 'created_at', 'entities', 'geo', 'id', 'in_reply_to_user_id', 'lang',
                    'non_public_metrics', 'public_metrics', 'organic_metrics', 'promoted_metrics', 'possibly_sensitive', 'referenced_tweets', 'reply_settings', 'source', 'text', 'withheld']

    args = {'query': 'my query', # 'tweet_mode': 'extended',
            'max_results': 100, 'expansions': ','.join(expansions),
            'user.fields': ','.join(user_fields),
            'tweet.fields': ','.join(tweet_fields)}

    # BASE_URL = 'https://api.twitter.com/2/tweets/search/recent'
    while 'since_id' not in args or len(tweets['statuses']):
        tweets = t.tweets.search.recent(**args)  # Twitter(..., api_version=2, format="")
        await save(tweets, expansions, user_fields, tweet_fields)

        # args['since_id'] = tweets['search_metadata']['max_id_str']
        args['until_id'] = tweets['meta']['newest_id']  # api v2
finally:
    # some delete
pass

I copied the oauth.py

def encode_params(self, base_url, method, params):
    self.base_url = base_url
    self.method = method
    self.params = params
    return urlencode(params)

def generate_headers(self):
    if self.bearer_token:
        headers = {
            'Authorization': 'Bearer {0}'.format(
                self.bearer_token).encode('utf8')
        }
    else:
        auth_dict = {}
        auth_dict['include_entities'] = 'true'
        auth_dict['oauth_consumer_key'] = self.consumer_key
        auth_dict['oauth_nonce'] = str(getrandbits(64))
        auth_dict['oauth_signature_method'] = 'HMAC-SHA1'
        auth_dict['oauth_timestamp'] = str(int(time()))
        auth_dict['oauth_token'] = self.access_token
        auth_dict['oauth_version'] = '1.0'

        enc_params = urlencode(sorted(self.params.items()), safe='~').replace("+", "%20")
        key = self.consumer_secret + "&" + quote(self.access_token_secret, safe='~')

        message = '&'.join(
            quote(i, safe='~') for i in [self.method.upper(), self.base_url, enc_params])

        auth_dict['oauth_signature'] = (base64.b64encode(hmac.new(
                key.encode('ascii'), message.encode('ascii'), hashlib.sha1)
                                    .digest()))
        headers = {
            # 'Content-Type': (b'application/x-www-form-urlencoded;'
            #                  b'charset=UTF-8'),
            'Authorization': ('OAuth '+', '.join(['{}' for i in range(0, auth_dict.__len__())])).format(
                *[('{0}={1}'.format(
                        quote(key),
                        quote(value))
                    ) for key, value in auth_dict.items()
                ]
            )
        }
    return headers

and changed the order of performs in api.py: (around line 300)

if self.auth:
    ######  BEFORE
    # Use urlencoded oauth args with no params when sending media
    # via multipart and send it directly via uri even for post
    url_args = {} if media or jsondata else kwargs
    if method == "PUT" and _id:
        # the two PUT method APIs both require uri id parameter
        url_args['id'] = _id

    arg_data = self.auth.encode_params(url_base, method, url_args)
    headers.update(self.auth.generate_headers())   ######  AFTER 
    if method == 'GET' or media or jsondata:
        url_base += '?' + arg_data
    else:
        body = arg_data.encode('utf-8')

then preyer for result: I got the header 'OAuth include_entities=true, oauth_consumer_key=Bhlb7DP1F69fgqxLrVAlB36QS, oauth_nonce=13575949106932897288, oauth_signature_method=HMAC-SHA1, oauth_timestamp=1637390069, oauth_token=1330472103414534146-Wfhxwik0byG2lgeOmyB1l6UYPzKTtF, oauth_version=1.0, oauth_signature=ebHLt38Yq4QIi/Rpikz7BKnsWV8%3D' and I was told Unauthorized which might indicate that the procedure of signature or sth. is wrong

What should I do? Thank you for your reading and sharing!

ryuujo1573 commented 2 years ago

image auth info is passed through params

RouxRC commented 2 years ago

The support of API v2 is in current development, the way it works is not definitive yet but you can already use it by cloning the repo, switching to the branch api_v2 and run python setup.py install The README is a work in progress and is currently a mess but you should find examples to do what you want below there https://github.com/python-twitter-tools/twitter/tree/api_v2#querying-the-api

ryuujo1573 commented 2 years ago

非常感谢!