From eb00467c88085eb01751347675d761c8ffe3519e Mon Sep 17 00:00:00 2001 From: Taylor Caldwell Date: Fri, 5 Dec 2025 14:47:58 -0800 Subject: [PATCH 1/2] Update Python samples with XDK --- python/README.md | 23 --- python/bookmarks/bookmarks_lookup.py | 152 +++++++---------- python/bookmarks/create.py | 54 ------ python/bookmarks/create_bookmark.py | 157 +++++++---------- python/bookmarks/delete.py | 54 ------ python/bookmarks/delete_bookmark.py | 158 +++++++---------- python/bookmarks/lookup.py | 55 ------ python/compliance/create_compliance_job.py | 44 ++--- python/compliance/create_job.py | 56 ------ .../compliance/download_compliance_results.py | 29 ++-- .../get_compliance_job_information_by_id.py | 42 ++--- python/compliance/get_jobs.py | 39 +---- .../compliance/get_list_of_compliance_jobs.py | 44 ++--- python/compliance/upload_ids.py | 35 ++-- .../get_events_by_conversation.py | 156 +++++++---------- .../get_one_to_one_conversation_events.py | 157 +++++++---------- .../get_user_conversation_events.py | 153 +++++++---------- python/direct_messages/lookup.py | 78 --------- .../post_dm_to_conversation.py | 154 ++++++----------- .../post_group_conversation_dm.py | 159 +++++++---------- python/direct_messages/post_one_to_one_dm.py | 155 ++++++----------- python/direct_messages/send.py | 82 --------- python/lists/List-Tweets.py | 66 +++----- python/lists/Pinned-List.py | 136 ++++++++------- python/lists/add_member.py | 126 +++++++------- python/lists/create.py | 80 --------- python/lists/create_a_list.py | 124 +++++++------- python/lists/delete.py | 76 --------- python/lists/delete_a_list.py | 133 +++++++-------- python/lists/follow_list.py | 129 +++++++------- python/lists/list-followers-lookup.py | 65 +++---- python/lists/list-lookup-by-id.py | 59 +++---- python/lists/list-member-lookup.py | 64 +++---- python/lists/lookup.py | 45 ++--- python/lists/pin_list.py | 129 +++++++------- python/lists/remove_member.py | 121 ++++++------- python/lists/unfollow_list.py | 121 ++++++------- python/lists/unpin_list.py | 121 ++++++------- python/lists/update_a_list.py | 139 +++++++-------- python/lists/user-list-followed.py | 65 +++---- python/lists/user-list-memberships.py | 65 +++---- python/lists/user-owned-list-lookup.py | 65 +++---- python/media/media_upload_v2.py | 98 ++++------- python/media/upload.py | 132 +++++++-------- python/posts/counts_full_archive.py | 38 +---- python/posts/counts_recent.py | 38 +---- python/posts/create_post.py | 80 --------- python/posts/create_tweet.py | 119 ++++++------- python/posts/delete_post.py | 77 --------- python/posts/delete_tweet.py | 131 +++++++------- python/posts/full-archive-search.py | 58 +++---- python/posts/full_archive_tweet_counts.py | 50 +++--- python/posts/get_tweets_with_bearer_token.py | 59 +++---- python/posts/get_tweets_with_user_context.py | 130 +++++++------- python/posts/like.py | 81 --------- python/posts/like_a_tweet.py | 122 +++++++------ python/posts/liked_posts.py | 53 ------ python/posts/liked_tweets.py | 106 +++++++----- python/posts/liking_users.py | 105 +++++++----- python/posts/lookup.py | 46 ++--- python/posts/quote_posts.py | 54 ------ python/posts/quote_tweets.py | 70 +++----- python/posts/recent_search.py | 57 ++++--- python/posts/recent_tweet_counts.py | 49 +++--- python/posts/repost.py | 81 --------- python/posts/reposted_by.py | 51 ++---- python/posts/retweet_a_tweet.py | 127 +++++++------- python/posts/retweeted_by.py | 71 +++----- python/posts/search_full_archive.py | 47 ++--- python/posts/search_recent.py | 46 ++--- python/posts/undo_a_retweet.py | 125 +++++++------- python/posts/undo_repost.py | 81 --------- python/posts/unlike.py | 81 --------- python/posts/unlike_a_tweet.py | 123 +++++++------- python/requirements.txt | 3 +- python/spaces/lookup.py | 55 ------ python/spaces/search.py | 55 ------ python/spaces/search_spaces.py | 53 +++--- python/spaces/spaces_lookup.py | 51 +++--- python/streams/filtered_stream.py | 105 ++++-------- python/streams/sampled-stream.py | 48 ------ python/streams/sampled_stream.py | 35 +--- python/timelines/home_timeline.py | 128 +++++++------- .../timelines/reverse-chron-home-timeline.py | 160 +++++++----------- python/timelines/user_mentions.py | 68 +++----- python/timelines/user_posts.py | 51 ++---- python/timelines/user_tweets.py | 68 +++----- python/usage/get_usage.py | 37 +--- python/usage/get_usage_tweets.py | 46 ++--- python/users/block.py | 81 --------- python/users/block_a_user.py | 129 +++++++------- python/users/blocked.py | 136 +++++++-------- python/users/followers.py | 49 ++---- python/users/followers_lookup.py | 66 +++----- python/users/following.py | 49 ++---- python/users/following_lookup.py | 66 +++----- python/users/get_users_me_user_context.py | 120 ++++++------- python/users/get_users_with_bearer_token.py | 59 +++---- python/users/get_users_with_user_context.py | 124 +++++++------- python/users/lookup.py | 46 ++--- python/users/lookup_blocks.py | 134 ++++++++------- python/users/lookup_mutes.py | 140 ++++++++------- python/users/me.py | 111 ++++++------ python/users/mute.py | 81 --------- python/users/mute_a_user.py | 125 +++++++------- python/users/muted.py | 136 +++++++-------- python/users/unblock.py | 81 --------- python/users/unblock_a_user.py | 125 +++++++------- python/users/unmute.py | 81 --------- python/users/unmute_a_user.py | 121 ++++++------- 110 files changed, 3362 insertions(+), 6212 deletions(-) delete mode 100644 python/bookmarks/create.py delete mode 100644 python/bookmarks/delete.py delete mode 100644 python/bookmarks/lookup.py delete mode 100644 python/compliance/create_job.py delete mode 100644 python/direct_messages/lookup.py delete mode 100644 python/direct_messages/send.py delete mode 100644 python/lists/create.py delete mode 100644 python/lists/delete.py delete mode 100644 python/posts/create_post.py delete mode 100644 python/posts/delete_post.py delete mode 100644 python/posts/like.py delete mode 100644 python/posts/liked_posts.py delete mode 100644 python/posts/quote_posts.py delete mode 100644 python/posts/repost.py delete mode 100644 python/posts/undo_repost.py delete mode 100644 python/posts/unlike.py delete mode 100644 python/spaces/lookup.py delete mode 100644 python/spaces/search.py delete mode 100644 python/streams/sampled-stream.py delete mode 100644 python/users/block.py delete mode 100644 python/users/mute.py delete mode 100644 python/users/unblock.py delete mode 100644 python/users/unmute.py diff --git a/python/README.md b/python/README.md index 0f21f15..2681867 100644 --- a/python/README.md +++ b/python/README.md @@ -21,38 +21,29 @@ export CONSUMER_SECRET='your_consumer_secret' ### Posts - posts/counts_full_archive.py - posts/counts_recent.py -- posts/create_post.py - posts/create_tweet.py -- posts/delete_post.py - posts/delete_tweet.py - posts/full_archive_tweet_counts.py - posts/full-archive-search.py - posts/get_tweets_with_bearer_token.py - posts/get_tweets_with_user_context.py - posts/like_a_tweet.py -- posts/like.py -- posts/liked_posts.py - posts/liked_tweets.py - posts/liking_users.py - posts/lookup.py -- posts/quote_posts.py - posts/quote_tweets.py - posts/recent_search.py - posts/recent_tweet_counts.py -- posts/repost.py - posts/reposted_by.py - posts/retweet_a_tweet.py - posts/retweeted_by.py - posts/search_full_archive.py - posts/search_recent.py - posts/undo_a_retweet.py -- posts/undo_repost.py - posts/unlike_a_tweet.py -- posts/unlike.py ### Users - users/block_a_user.py -- users/block.py - users/blocked.py - users/followers_lookup.py - users/followers.py @@ -66,12 +57,9 @@ export CONSUMER_SECRET='your_consumer_secret' - users/lookup.py - users/me.py - users/mute_a_user.py -- users/mute.py - users/muted.py - users/unblock_a_user.py -- users/unblock.py - users/unmute_a_user.py -- users/unmute.py ### Timelines - timelines/home_timeline.py @@ -83,14 +71,11 @@ export CONSUMER_SECRET='your_consumer_secret' ### Streams - streams/filtered_stream.py - streams/sampled_stream.py -- streams/sampled-stream.py ### Lists - lists/add_member.py - lists/create_a_list.py -- lists/create.py - lists/delete_a_list.py -- lists/delete.py - lists/follow_list.py - lists/list-followers-lookup.py - lists/list-lookup-by-id.py @@ -110,26 +95,19 @@ export CONSUMER_SECRET='your_consumer_secret' ### Bookmarks - bookmarks/bookmarks_lookup.py - bookmarks/create_bookmark.py -- bookmarks/create.py - bookmarks/delete_bookmark.py -- bookmarks/delete.py -- bookmarks/lookup.py ### Spaces -- spaces/lookup.py - spaces/search_spaces.py -- spaces/search.py - spaces/spaces_lookup.py ### Direct Messages - direct_messages/get_events_by_conversation.py - direct_messages/get_one_to_one_conversation_events.py - direct_messages/get_user_conversation_events.py -- direct_messages/lookup.py - direct_messages/post_dm_to_conversation.py - direct_messages/post_group_conversation_dm.py - direct_messages/post_one_to_one_dm.py -- direct_messages/send.py ### Media - media/media_upload_v2.py @@ -137,7 +115,6 @@ export CONSUMER_SECRET='your_consumer_secret' ### Compliance - compliance/create_compliance_job.py -- compliance/create_job.py - compliance/download_compliance_results.py - compliance/get_compliance_job_information_by_id.py - compliance/get_jobs.py diff --git a/python/bookmarks/bookmarks_lookup.py b/python/bookmarks/bookmarks_lookup.py index e864e22..192198d 100644 --- a/python/bookmarks/bookmarks_lookup.py +++ b/python/bookmarks/bookmarks_lookup.py @@ -1,103 +1,71 @@ -import base64 -import hashlib +""" +Bookmarks Lookup - X API v2 +=========================== +Endpoint: GET https://api.x.com/2/users/:id/bookmarks +Docs: https://developer.x.com/en/docs/twitter-api/bookmarks/api-reference/get-users-id-bookmarks + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os -import re import json -import requests -from requests.auth import AuthBase, HTTPBasicAuth -from requests_oauthlib import OAuth2Session +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# First, you will need to enable OAuth 2.0 in your App’s auth settings in the Developer Portal to get your client ID. -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_ID='your-client-id' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' client_id = os.environ.get("CLIENT_ID") - -# If you have selected a type of App that is a confidential client you will need to set a client secret. -# Confidential Clients securely authenticate with the authorization server. - -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_SECRET='your-client-secret' - -# Remove the comment on the following line if you are using a confidential client -# client_secret = os.environ.get("CLIENT_SECRET") +client_secret = os.environ.get("CLIENT_SECRET") # Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://www.example.com" +redirect_uri = "https://example.com" # Set the scopes scopes = ["bookmark.read", "tweet.read", "users.read", "offline.access"] -# Create a code verifier -code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") -code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) - -# Create a code challenge -code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() -code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") -code_challenge = code_challenge.replace("=", "") - -# Start an OAuth 2.0 session -oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - -# Create an authorize URL -auth_url = "https://twitter.com/i/oauth2/authorize" -authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" -) - -# Visit the URL to authorize your App to make requests on behalf of a user -print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" -) -print(authorization_url) - -# Paste in your authorize URL to complete the request -authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" -) - -# Fetch your access token -token_url = "https://api.x.com/2/oauth2/token" - -# The following line of code will only work if you are using a type of App that is a public client -auth = False - -# If you are using a confidential client you will need to pass in basic encoding of your client ID and client secret. - -# Please remove the comment on the following line if you are using a type of App that is a confidential client -# auth = HTTPBasicAuth(client_id, client_secret) - -token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, - client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, -) - -# Your access token -access = token["access_token"] - -# Make a request to the users/me endpoint to get your user ID -user_me = requests.request( - "GET", - "https://api.x.com/2/users/me", - headers={"Authorization": "Bearer {}".format(access)}, -).json() -user_id = user_me["data"]["id"] - -# Make a request to the bookmarks url -url = "https://api.x.com/2/users/{}/bookmarks".format(user_id) -headers = { - "Authorization": "Bearer {}".format(access), - "User-Agent": "BookmarksSampleCode", -} -response = requests.request("GET", url, headers=headers) -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) -print("Response code: {}".format(response.status_code)) -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get authenticated user ID + user_me = client.users.get_me() + user_id = user_me.data["id"] + + # Step 7: Get bookmarks with automatic pagination + all_bookmarks = [] + for page in client.users.get_bookmarks( + user_id, + max_results=100, + tweetfields=["created_at"] + ): + all_bookmarks.extend(page.data) + print(f"Fetched {len(page.data)} bookmarks (total: {len(all_bookmarks)})") + + print(f"\nTotal Bookmarks: {len(all_bookmarks)}") + print(json.dumps({"data": all_bookmarks[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + +if __name__ == "__main__": + main() diff --git a/python/bookmarks/create.py b/python/bookmarks/create.py deleted file mode 100644 index 0499013..0000000 --- a/python/bookmarks/create.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Create Bookmark - X API v2 -========================== -Endpoint: POST https://api.x.com/2/users/:id/bookmarks -Docs: https://developer.x.com/en/docs/twitter-api/tweets/bookmarks/api-reference/post-users-id-bookmarks - -Authentication: OAuth 2.0 with PKCE (User Context) -Required env vars: BEARER_TOKEN (OAuth 2.0 user access token) -""" - -import requests -import os -import json - -# Note: This endpoint requires OAuth 2.0 User Context -# The bearer_token here should be the user's access token from OAuth 2.0 PKCE flow -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(user_id): - return "https://api.x.com/2/users/{}/bookmarks".format(user_id) - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2CreateBookmarkPython" - return r - - -def connect_to_endpoint(url, payload): - response = requests.post(url, auth=bearer_oauth, json=payload) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - # Replace with the authenticated user's ID - user_id = "your-user-id" - - # Replace with the post ID you want to bookmark - payload = {"tweet_id": "post-id-to-bookmark"} - - url = create_url(user_id) - json_response = connect_to_endpoint(url, payload) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/bookmarks/create_bookmark.py b/python/bookmarks/create_bookmark.py index 56346d9..6f7c722 100644 --- a/python/bookmarks/create_bookmark.py +++ b/python/bookmarks/create_bookmark.py @@ -1,107 +1,68 @@ -import base64 -import hashlib +""" +Create Bookmark - X API v2 +========================== +Endpoint: POST https://api.x.com/2/users/:id/bookmarks +Docs: https://developer.x.com/en/docs/twitter-api/tweets/bookmarks/api-reference/post-users-id-bookmarks + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os -import re import json -import requests -from requests.auth import AuthBase, HTTPBasicAuth -from requests_oauthlib import OAuth2Session - -# Replace with a Tweet ID you want to Bookmark -tweet_to_bookmark = {"tweet_id": "1460323737035677698"} +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# You will need to enable OAuth 2.0 in your App’s auth settings in the Developer Portal to get your client ID. -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_ID='your-client-id' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' client_id = os.environ.get("CLIENT_ID") - -# If you have selected a type of App that is a confidential client you will need to set a client secret. -# Confidential Clients securely authenticate with the authorization server. - -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_SECRET='your-client-secret' - -# Remove the comment on the following line if you are using a confidential client -# client_secret = os.environ.get("CLIENT_SECRET") +client_secret = os.environ.get("CLIENT_SECRET") # Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://www.example.com" +redirect_uri = "https://example.com" # Set the scopes -scopes = ["bookmark.write", "tweet.read", "users.read", "offline.access"] - -# Create a code verifier -code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") -code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) - -# Create a code challenge -code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() -code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") -code_challenge = code_challenge.replace("=", "") - -# Start and OAuth 2.0 session -oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - -# Create an authorize URL -auth_url = "https://twitter.com/i/oauth2/authorize" -authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" -) - -# Visit the URL to authorize your App to make requests on behalf of a user -print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" -) -print(authorization_url) - -# Paste in your authorize URL to complete the request -authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" -) - -# Fetch your access token -token_url = "https://api.x.com/2/oauth2/token" - -# The following line of code will only work if you are using a type of App that is a public client -auth = False - -# If you are using a confidential client you will need to pass in basic encoding of your client ID and client secret. - -# Please remove the comment on the following line if you are using a type of App that is a confidential client -# auth = HTTPBasicAuth(client_id, client_secret) - -token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, - client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, -) - -# Your access token -access = token["access_token"] - -# Make a request to the users/me endpoint to get your user ID -user_me = requests.request( - "GET", - "https://api.x.com/2/users/me", - headers={"Authorization": "Bearer {}".format(access)}, -).json() -user_id = user_me["data"]["id"] - -# Make a request to the bookmarks url -url = "https://api.x.com/2/users/{}/bookmarks".format(user_id) -headers = { - "Authorization": "Bearer {}".format(access), - "Content-Type": "application/json", - "User-Agent": "BookmarksSampleCode", -} -response = requests.request("POST", url, headers=headers, json=tweet_to_bookmark) -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +scopes = ["tweet.read", "users.read", "bookmark.write", "offline.access"] + +# Replace with a Post ID you want to Bookmark +post_id = "post-id-to-bookmark" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) -print("Response code: {}".format(response.status_code)) -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get authenticated user ID + me_response = client.users.get_me() + user_id = me_response.data["id"] + + # Step 7: Create bookmark + payload = {"tweet_id": post_id} + response = client.users.create_bookmark(user_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/bookmarks/delete.py b/python/bookmarks/delete.py deleted file mode 100644 index d962f16..0000000 --- a/python/bookmarks/delete.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Delete Bookmark - X API v2 -========================== -Endpoint: DELETE https://api.x.com/2/users/:id/bookmarks/:tweet_id -Docs: https://developer.x.com/en/docs/twitter-api/tweets/bookmarks/api-reference/delete-users-id-bookmarks-tweet_id - -Authentication: OAuth 2.0 with PKCE (User Context) -Required env vars: BEARER_TOKEN (OAuth 2.0 user access token) -""" - -import requests -import os -import json - -# Note: This endpoint requires OAuth 2.0 User Context -# The bearer_token here should be the user's access token from OAuth 2.0 PKCE flow -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(user_id, post_id): - return "https://api.x.com/2/users/{}/bookmarks/{}".format(user_id, post_id) - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2DeleteBookmarkPython" - return r - - -def connect_to_endpoint(url): - response = requests.delete(url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - # Replace with the authenticated user's ID - user_id = "your-user-id" - - # Replace with the post ID you want to remove from bookmarks - post_id = "post-id-to-unbookmark" - - url = create_url(user_id, post_id) - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/bookmarks/delete_bookmark.py b/python/bookmarks/delete_bookmark.py index fae427b..4241303 100644 --- a/python/bookmarks/delete_bookmark.py +++ b/python/bookmarks/delete_bookmark.py @@ -1,109 +1,67 @@ -import base64 -import hashlib +""" +Delete Bookmark - X API v2 +========================== +Endpoint: DELETE https://api.x.com/2/users/:id/bookmarks/:tweet_id +Docs: https://developer.x.com/en/docs/twitter-api/tweets/bookmarks/api-reference/delete-users-id-bookmarks-tweet_id + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os -import re import json -import requests -from requests.auth import AuthBase, HTTPBasicAuth -from requests_oauthlib import OAuth2Session - -# Replace with a Tweet ID you want to remove Bookmark of -bookmarked_tweet_id = "1460323737035677698" +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# You will need to enable OAuth 2.0 in your App’s auth settings in the Developer Portal to get your client ID. -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_ID='your-client-id' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' client_id = os.environ.get("CLIENT_ID") - -# If you have selected a type of App that is a confidential client you will need to set a client secret. -# Confidential Clients securely authenticate with the authorization server. - -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_SECRET='your-client-secret' - -# Remove the comment on the following line if you are using a confidential client -# client_secret = os.environ.get("CLIENT_SECRET") - +client_secret = os.environ.get("CLIENT_SECRET") # Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://www.example.com" +redirect_uri = "https://example.com" # Set the scopes -scopes = ["bookmark.write", "tweet.read", "users.read", "offline.access"] - -# Create a code verifier -code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") -code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) - -# Create a code challenge -code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() -code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") -code_challenge = code_challenge.replace("=", "") - -# Start and OAuth 2.0 session -oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - -# Create an authorize URL -auth_url = "https://twitter.com/i/oauth2/authorize" -authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" -) - -# Visit the URL to authorize your App to make requests on behalf of a user -print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" -) -print(authorization_url) - -# Paste in your authorize URL to complete the request -authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" -) - -# Fetch your access token -token_url = "https://api.x.com/2/oauth2/token" - -# The following line of code will only work if you are using a type of App that is a public client -auth = False - -# If you are using a confidential client you will need to pass in basic encoding of your client ID and client secret. - -# Please remove the comment on the following line if you are using a type of App that is a confidential client -# auth = HTTPBasicAuth(client_id, client_secret) - -token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, - client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, -) - -# Your access token -access = token["access_token"] - -# Make a request to the users/me endpoint to get the user ID of the authenticated user -user_me = requests.request( - "GET", - "https://api.x.com/2/users/me", - headers={"Authorization": "Bearer {}".format(access)}, -).json() -user_id = user_me["data"]["id"] - -# Make a request to the bookmarks url -url = "https://api.x.com/2/users/{}/bookmarks/{}".format( - user_id, bookmarked_tweet_id -) -headers = { - "Authorization": "Bearer {}".format(access), - "User-Agent": "BookmarksSampleCode", -} -response = requests.request("DELETE", url, headers=headers) -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +scopes = ["tweet.read", "users.read", "bookmark.write", "offline.access"] + +# Replace with a Post ID you want to remove Bookmark of +post_id = "post-id-to-unbookmark" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) -print("Response code: {}".format(response.status_code)) -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get authenticated user ID + me_response = client.users.get_me() + user_id = me_response.data["id"] + + # Step 7: Delete bookmark + response = client.users.delete_bookmark(user_id, post_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/bookmarks/lookup.py b/python/bookmarks/lookup.py deleted file mode 100644 index 6af4463..0000000 --- a/python/bookmarks/lookup.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Bookmarks Lookup - X API v2 -=========================== -Endpoint: GET https://api.x.com/2/users/:id/bookmarks -Docs: https://developer.x.com/en/docs/twitter-api/tweets/bookmarks/api-reference/get-users-id-bookmarks - -Authentication: OAuth 2.0 with PKCE (User Context) -Required env vars: BEARER_TOKEN (OAuth 2.0 user access token) -""" - -import requests -import os -import json - -# Note: This endpoint requires OAuth 2.0 User Context -# The bearer_token here should be the user's access token from OAuth 2.0 PKCE flow -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(user_id): - return "https://api.x.com/2/users/{}/bookmarks".format(user_id) - - -def get_params(): - return {"tweet.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2BookmarksLookupPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - # Replace with the authenticated user's ID - user_id = "your-user-id" - url = create_url(user_id) - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/compliance/create_compliance_job.py b/python/compliance/create_compliance_job.py index f0ceca4..f91daaa 100644 --- a/python/compliance/create_compliance_job.py +++ b/python/compliance/create_compliance_job.py @@ -1,40 +1,28 @@ -import requests +""" +Create Compliance Job - X API v2 +================================ +Endpoint: POST https://api.x.com/2/compliance/jobs +Docs: https://developer.x.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs + +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") - -compliance_job_url = "https://api.x.com/2/compliance/jobs" +client = Client(bearer_token=bearer_token) # For User Compliance Job, replace the type value with users instead of tweets # Also replace the name value with your desired job name -body = {"type": "tweets", "name": "my_batch_compliance_job"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2BatchCompliancePython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("POST", url, auth=bearer_oauth, json=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +payload = {"type": "tweets", "name": "my_batch_compliance_job"} def main(): - json_response = connect_to_endpoint(compliance_job_url, body) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.compliance.create_job(body=payload) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/compliance/create_job.py b/python/compliance/create_job.py deleted file mode 100644 index ba7ce03..0000000 --- a/python/compliance/create_job.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Create Compliance Job - X API v2 -================================ -Endpoint: POST https://api.x.com/2/compliance/jobs -Docs: https://developer.x.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN -""" - -import requests -import os -import json - -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(): - return "https://api.x.com/2/compliance/jobs" - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ComplianceJobPython" - return r - - -def connect_to_endpoint(url, payload): - response = requests.post(url, auth=bearer_oauth, json=payload) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - url = create_url() - - # Type can be "tweets" or "users" - payload = { - "type": "tweets", - "name": "my_compliance_job" - } - - json_response = connect_to_endpoint(url, payload) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - # Note the job id and upload_url from the response - # You'll need these to upload your dataset and download results - - -if __name__ == "__main__": - main() diff --git a/python/compliance/download_compliance_results.py b/python/compliance/download_compliance_results.py index 7c46399..edb0e3d 100644 --- a/python/compliance/download_compliance_results.py +++ b/python/compliance/download_compliance_results.py @@ -1,23 +1,28 @@ +""" +Download Compliance Results - X API v2 +====================================== +Endpoint: GET (provided in compliance job response) +Docs: https://developer.x.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs-id + +Note: This endpoint uses a pre-signed URL from the compliance job response. +The download_url is provided when you check the status of a compliance job. +""" + import requests -# Replace with your job download_url +# Replace with your job download_url (from the compliance job response) download_url = '' - -def connect_to_endpoint(url): - response = requests.request("GET", url) - print(response.status_code) +def main(): + response = requests.get(download_url) + + print(f"Response code: {response.status_code}") if response.status_code != 200: raise Exception(response.status_code, response.text) - return response.text - - -def main(): - response = connect_to_endpoint(download_url) - entries = response.splitlines() + + entries = response.text.splitlines() for entry in entries: print(entry) - if __name__ == "__main__": main() diff --git a/python/compliance/get_compliance_job_information_by_id.py b/python/compliance/get_compliance_job_information_by_id.py index 5d46e72..5a139d6 100644 --- a/python/compliance/get_compliance_job_information_by_id.py +++ b/python/compliance/get_compliance_job_information_by_id.py @@ -1,39 +1,27 @@ -import requests +""" +Get Compliance Job by ID - X API v2 +=================================== +Endpoint: GET https://api.x.com/2/compliance/jobs/:id +Docs: https://developer.x.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs-id + +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) # Replace with your job ID below job_id = '' -compliance_job_url = f"https://api.x.com/2/compliance/jobs/{job_id}".format(job_id) - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2BatchCompliancePython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - def main(): - json_response = connect_to_endpoint(compliance_job_url) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.compliance.get_job(job_id) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/compliance/get_jobs.py b/python/compliance/get_jobs.py index de65383..15035d6 100644 --- a/python/compliance/get_jobs.py +++ b/python/compliance/get_jobs.py @@ -8,45 +8,20 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def create_url(): - return "https://api.x.com/2/compliance/jobs" - - -def get_params(): - # Type can be "tweets" or "users" - return {"type": "tweets"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ComplianceJobsPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.get(url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +# Type can be "tweets" or "users" +job_type = "tweets" def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.compliance.get_jobs(type=job_type) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/compliance/get_list_of_compliance_jobs.py b/python/compliance/get_list_of_compliance_jobs.py index 54a9c01..84ddf59 100644 --- a/python/compliance/get_list_of_compliance_jobs.py +++ b/python/compliance/get_list_of_compliance_jobs.py @@ -1,39 +1,27 @@ -import requests +""" +Get Compliance Jobs - X API v2 +============================== +Endpoint: GET https://api.x.com/2/compliance/jobs +Docs: https://developer.x.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs + +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") - -compliance_job_url = "https://api.x.com/2/compliance/jobs" +client = Client(bearer_token=bearer_token) # For User Compliance job, replace the value for type with users -query_params = {"type": "tweets"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2BatchCompliancePython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +job_type = "tweets" def main(): - json_response = connect_to_endpoint(compliance_job_url, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.compliance.get_jobs(type=job_type) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/compliance/upload_ids.py b/python/compliance/upload_ids.py index 4c41aba..3083c8d 100644 --- a/python/compliance/upload_ids.py +++ b/python/compliance/upload_ids.py @@ -1,26 +1,33 @@ +""" +Upload IDs for Compliance Job - X API v2 +======================================== +Endpoint: PUT (provided in compliance job response) +Docs: https://developer.x.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs + +Note: This endpoint uses a pre-signed URL from the compliance job response. +The upload_url is provided when you create a compliance job. +""" + +import os import requests -# Replace with your job download_url +# Replace with your job upload_url (from the compliance job response) upload_url = '' # Replace with your file path that contains the list of Tweet IDs or User IDs, one ID per line file_path = '' -headers = {'Content-Type': "text/plain"} - - -def connect_to_endpoint(url): - response = requests.put(url, data=open(file_path, 'rb'), headers=headers) - print(response.status_code) +def main(): + headers = {'Content-Type': "text/plain"} + + with open(file_path, 'rb') as f: + response = requests.put(upload_url, data=f, headers=headers) + + print(f"Response code: {response.status_code}") if response.status_code != 200: raise Exception(response.status_code, response.text) - return response.text - - -def main(): - response = connect_to_endpoint(upload_url) - print(response) - + + print(response.text) if __name__ == "__main__": main() diff --git a/python/direct_messages/get_events_by_conversation.py b/python/direct_messages/get_events_by_conversation.py index 93515f7..0f3e4b4 100644 --- a/python/direct_messages/get_events_by_conversation.py +++ b/python/direct_messages/get_events_by_conversation.py @@ -1,107 +1,75 @@ -import base64 -import hashlib -import os -import re -import json -import requests -from requests_oauthlib import OAuth2Session - -# This example is set up to retrieve Direct Message events by conversation ID. This supports both -# one-to-one and group conversations. -GET_DMS_EVENTS_URL = "https://api.x.com/2/dm_conversations/:dm_conversation_id/dm_events" - -#----------------------------------------------------------------------------------------------------------------------- -# These variables need to be updated to the setting that match how your Twitter App is set-up at -# https://developer.twitter.com/en/portal/dashboard. These will not change from run-by-run. -client_id = '' -#This must match *exactly* the redirect URL specified in the Developer Portal. -redirect_uri = "https://www.example.com" -#----------------------------------------------------------------------------------------------------------------------- -# This variable specifies the conversation to retrieve. A more ready-to-be used example would -# have this passed in from some calling code. -# What is the ID of the conversatikon to retrieve? -dm_conversation_id = "1512210732774948865" -#----------------------------------------------------------------------------------------------------------------------- - -def handle_oauth(): - - # Set the scopes needed to be granted by the authenticating user. - scopes = ["dm.read", "tweet.read", "users.read", "offline.access"] +""" +Get Events by Conversation ID - X API v2 +========================================= +Endpoint: GET https://api.x.com/2/dm_conversations/:dm_conversation_id/dm_events +Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm-conversations-dm_conversation_id-dm_events - # Create a code verifier - code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") - code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET - # Create a code challenge - code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") - code_challenge = code_challenge.replace("=", "") +This example retrieves Direct Message events by conversation ID. +This supports both one-to-one and group conversations. +""" - # Start and OAuth 2.0 session - oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - - # Create an authorize URL - auth_url = "https://twitter.com/i/oauth2/authorize" - authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" - ) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth - # Visit the URL to authorize your App to make requests on behalf of a user - print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" - ) - print(authorization_url) +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") - # Paste in your authorize URL to complete the request - authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" - ) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" - # Fetch your access token - token_url = "https://api.x.com/2/oauth2/token" +# Set the scopes +scopes = ["dm.read", "tweet.read", "users.read", "offline.access"] - # The following line of code will only work if you are using a type of App that is a public client - auth = False +# What is the ID of the conversation to retrieve? +dm_conversation_id = "1512210732774948865" - token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - - # Your access token - access = token["access_token"] - - return access - -def get_events_by_conservation_id(dm_conversation_id): - - access = handle_oauth() - - headers = { - "Authorization": "Bearer {}".format(access), - "Content-Type": "application/json", - "User-Agent": "TwitterDevSampleCode", - "X-TFE-Experiment-environment": "staging1", - "Dtab-Local": "/s/gizmoduck/test-users-temporary => /s/gizmoduck/gizmoduck" - } - - request_url = GET_DMS_EVENTS_URL.replace(':dm_conversation_id', str(dm_conversation_id)) - - response = requests.request("GET", request_url, headers=headers) - - if response.status_code != 200: - print("Request returned an error: {} {}".format(response.status_code, response.text)) - else: - print(f"Response code: {response.status_code}") - return response - -def main(): - response = get_events_by_conservation_id(dm_conversation_id) - print(json.dumps(json.loads(response.text), indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get events by conversation ID with automatic pagination + all_events = [] + for page in client.direct_messages.get_events_by_conversation_id( + dm_conversation_id, + max_results=100 + ): + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_events.extend(page_data) + print(f"Fetched {len(page_data)} events (total: {len(all_events)})") + + print(f"\nTotal Conversation Events: {len(all_events)}") + print(json.dumps({"data": all_events[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/direct_messages/get_one_to_one_conversation_events.py b/python/direct_messages/get_one_to_one_conversation_events.py index b03f552..1c2b7aa 100644 --- a/python/direct_messages/get_one_to_one_conversation_events.py +++ b/python/direct_messages/get_one_to_one_conversation_events.py @@ -1,108 +1,75 @@ -import base64 -import hashlib -import os -import re -import json -import requests -from requests_oauthlib import OAuth2Session - -# This example is set up to retrieve Direct Message conversation events associated with a one-to-one message. -# Currently, the v2 DM endpoints support three conversation event types: MessageCreate, ParticipantsJoin, and -# ParticipantsLeave. -GET_DM_EVENTS_URL = "https://api.x.com/2/dm_conversations/with/:participant_id/dm_events" - -#----------------------------------------------------------------------------------------------------------------------- -# These variables need to be updated to the setting that match how your Twitter App is set-up at -# https://developer.twitter.com/en/portal/dashboard. These will not change from run-by-run. -client_id = '' -#This must match *exactly* the redirect URL specified in the Developer Portal. -redirect_uri = "https://www.example.com" -#----------------------------------------------------------------------------------------------------------------------- -# This variable indicates the participant of the one-to-one conversation. A more ready-to-be used example would -# have this passed in from some calling code. -# Who is this one-to-one conversation with? -participant_id = "906948460078698496" -#----------------------------------------------------------------------------------------------------------------------- - -def handle_oauth(): - - # Set the scopes needed to be granted by the authenticating user. - scopes = ["dm.read", "tweet.read", "users.read", "offline.access"] +""" +Get One-to-One Conversation Events - X API v2 +============================================= +Endpoint: GET https://api.x.com/2/dm_conversations/with/:participant_id/dm_events +Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm-conversations-with-participant_id-dm_events - # Create a code verifier - code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") - code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET - # Create a code challenge - code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") - code_challenge = code_challenge.replace("=", "") +This example retrieves Direct Message conversation events associated with a one-to-one message. +Currently, the v2 DM endpoints support three conversation event types: MessageCreate, ParticipantsJoin, and ParticipantsLeave. +""" - # Start and OAuth 2.0 session - oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - - # Create an authorize URL - auth_url = "https://twitter.com/i/oauth2/authorize" - authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" - ) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth - # Visit the URL to authorize your App to make requests on behalf of a user - print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" - ) - print(authorization_url) +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") - # Paste in your authorize URL to complete the request - authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" - ) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" - # Fetch your access token - token_url = "https://api.x.com/2/oauth2/token" +# Set the scopes +scopes = ["dm.read", "tweet.read", "users.read", "offline.access"] - # The following line of code will only work if you are using a type of App that is a public client - auth = False +# Who is this one-to-one conversation with? +participant_id = "1716450569358098432" - token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - - # Your access token - access = token["access_token"] - - return access - -def get_one_to_one_conversation_events(participant_id): - - access = handle_oauth() - - headers = { - "Authorization": "Bearer {}".format(access), - "Content-Type": "application/json", - "User-Agent": "TwitterDevSampleCode", - "X-TFE-Experiment-environment": "staging1", - "Dtab-Local": "/s/gizmoduck/test-users-temporary => /s/gizmoduck/gizmoduck" - } - - request_url = GET_DM_EVENTS_URL.replace(':participant_id', str(participant_id)) - - response = requests.request("GET", request_url, headers=headers) - - if response.status_code != 200: - print("Request returned an error: {} {}".format(response.status_code, response.text)) - else: - print(f"Response code: {response.status_code}") - return response - -def main(): - response = get_one_to_one_conversation_events(participant_id) - print(json.dumps(json.loads(response.text), indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get one-to-one conversation events with automatic pagination + all_events = [] + for page in client.direct_messages.get_events_by_participant_id( + participant_id, + max_results=100 + ): + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_events.extend(page_data) + print(f"Fetched {len(page_data)} events (total: {len(all_events)})") + + print(f"\nTotal Conversation Events: {len(all_events)}") + print(json.dumps({"data": all_events[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/direct_messages/get_user_conversation_events.py b/python/direct_messages/get_user_conversation_events.py index b5bc8c5..da3c132 100644 --- a/python/direct_messages/get_user_conversation_events.py +++ b/python/direct_messages/get_user_conversation_events.py @@ -1,102 +1,75 @@ -import base64 -import hashlib -import os -import re -import json -import requests -from requests_oauthlib import OAuth2Session - -# This example is set up to retrieve Direct Message events of the authenticating user. This supports both -# one-to-one and group conversations. -GET_DM_EVENTS_URL = "https://api.x.com/2/dm_events" - -#----------------------------------------------------------------------------------------------------------------------- -# These variables need to be updated to the setting that match how your Twitter App is set-up at -# https://developer.twitter.com/en/portal/dashboard. These will not change from run-by-run. -client_id = "" -#This must match *exactly* the redirect URL specified in the Developer Portal. -redirect_uri = "https://www.example.com" -#----------------------------------------------------------------------------------------------------------------------- - -def handle_oauth(): - - # Set the scopes needed to be granted by the authenticating user. - scopes = ["dm.read", "tweet.read", "users.read", "offline.access"] +""" +Get User Conversation Events - X API v2 +======================================= +Endpoint: GET https://api.x.com/2/dm_events +Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm-events - # Create a code verifier. - code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") - code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET - # Create a code challenge. - code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") - code_challenge = code_challenge.replace("=", "") +This example retrieves Direct Message events of the authenticating user. +This supports both one-to-one and group conversations. +""" - # Start an OAuth 2.0 session. - oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - - # Create an authorize URL. - auth_url = "https://twitter.com/i/oauth2/authorize" - authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" - ) - - # Visit the URL to authorize your App to make requests on behalf of a user. - print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" - ) - print(authorization_url) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth - # Paste in your authorize URL to complete the request. - authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" - ) +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") - # Fetch your access token. - token_url = "https://api.x.com/2/oauth2/token" +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" - # The following line of code will only work if you are using a type of App that is a public client. - auth = False +# Set the scopes +scopes = ["dm.read", "tweet.read", "users.read", "offline.access"] - token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - - # The access token. - access = token["access_token"] - - return access - -def get_user_conversation_events(): - - access = handle_oauth() - - headers = { - "Authorization": "Bearer {}".format(access), - "Content-Type": "application/json", - "User-Agent": "TwitterDevSampleCode", - "X-TFE-Experiment-environment": "staging1", - "Dtab-Local": "/s/gizmoduck/test-users-temporary => /s/gizmoduck/gizmoduck" - } - - request_url = GET_DM_EVENTS_URL - - response = requests.request("GET", request_url, headers=headers) - - if response.status_code != 200: - print("Request returned an error: {} {}".format(response.status_code, response.text)) - else: - print(f"Response code: {response.status_code}") - return response - -def main(): - response = get_user_conversation_events() - print(json.dumps(json.loads(response.text), indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get user conversation events with automatic pagination + # DM event fields are adjustable. Options include: + # id, text, event_type, created_at, dm_conversation_id, + # sender_id, participant_ids, referenced_tweets, attachments + all_events = [] + for page in client.direct_messages.get_events( + max_results=100, + dm_event_fields=["id", "text", "event_type", "created_at", "sender_id"] + ): + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_events.extend(page_data) + print(f"Fetched {len(page_data)} events (total: {len(all_events)})") + + print(f"\nTotal DM Events: {len(all_events)}") + print(json.dumps({"data": all_events[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/direct_messages/lookup.py b/python/direct_messages/lookup.py deleted file mode 100644 index 3ba8f24..0000000 --- a/python/direct_messages/lookup.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Direct Messages Lookup - X API v2 -================================= -Endpoint: GET https://api.x.com/2/dm_events -Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm-events - -Authentication: OAuth 1.0a or OAuth 2.0 (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# DM event fields are adjustable. Options include: -# id, text, event_type, created_at, dm_conversation_id, -# sender_id, participant_ids, referenced_tweets, attachments -params = {"dm_event.fields": "id,text,event_type,created_at,sender_id"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.get("https://api.x.com/2/dm_events", params=params) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/direct_messages/post_dm_to_conversation.py b/python/direct_messages/post_dm_to_conversation.py index 18d6af3..e82afe6 100644 --- a/python/direct_messages/post_dm_to_conversation.py +++ b/python/direct_messages/post_dm_to_conversation.py @@ -1,113 +1,67 @@ -import base64 -import hashlib -import os -import re -import json -import requests -from requests_oauthlib import OAuth2Session - -#This example is set up to add a DM to a specified conversation by referencing its ID. -POST_DM_URL = "https://api.x.com/2/dm_conversations/:dm_conversation_id/messages" - -#----------------------------------------------------------------------------------------------------------------------- -# These variables need to be updated to the setting that match how your Twitter App is set-up at -# https://developer.twitter.com/en/portal/dashboard. These will not change from run-by-run. -client_id = '' -#This must match *exactly* the redirect URL specified in the Developer Portal. -redirect_uri = "https://www.example.com" -#----------------------------------------------------------------------------------------------------------------------- -# These variables indicate the conversation that the message should be added to. A more ready-to-be used example would -# have these passed in from some calling code. -# Provide the ID of the conversation this message should be added to. -dm_conversation_id = "1512210732774948865" -#Set the text of the message to be sent. -text_message = "Hi, I am adding a message to an existing conversation by referencing its ID." -#----------------------------------------------------------------------------------------------------------------------- - -def handle_oauth(): +""" +Post DM to Conversation - X API v2 +=================================== +Endpoint: POST https://api.x.com/2/dm_conversations/:dm_conversation_id/messages +Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm-conversations-dm_conversation_id-messages - # Set the scopes needed to be granted by the authenticating user. - scopes = ["dm.read", "dm.write", "tweet.read", "users.read", "offline.access"] +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" - # Create a code verifier. - code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") - code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) - - # Create a code challenge. - code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") - code_challenge = code_challenge.replace("=", "") +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth - # Start an OAuth 2.0 session. - oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") - # Create an authorize URL. - auth_url = "https://twitter.com/i/oauth2/authorize" - authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" - ) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" - # Visit the URL to authorize your App to make requests on behalf of a user. - print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" - ) - print(authorization_url) - - # Paste in your authorize URL to complete the request. - authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" - ) +# Set the scopes +scopes = ["dm.read", "dm.write", "tweet.read", "users.read", "offline.access"] - # Fetch your access token. - token_url = "https://api.x.com/2/oauth2/token" +# Provide the ID of the conversation this message should be added to. +dm_conversation_id = "1856843062712176900" - # The following line of code will only work if you are using a type of App that is a public client - auth = False +# Set the text of the message to be sent. +text_message = "Hi, I am adding a message to an existing conversation by referencing its ID." - token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - - # The access token. - access = token["access_token"] - - return access - -def add_dm_to_conversation(dm_text, dm_conversation_id): - request_body = {} - - access = handle_oauth() - - headers = { - "Authorization": "Bearer {}".format(access), - "Content-Type": "application/json", - "User-Agent": "TwitterDevSampleCode", - "X-TFE-Experiment-environment": "staging1", - "Dtab-Local": "/s/gizmoduck/test-users-temporary => /s/gizmoduck/gizmoduck" - } - - request_url = POST_DM_URL.replace(':dm_conversation_id', str(dm_conversation_id)) - request_body['text'] = dm_text - json_body = json.dumps(request_body) - - #Send DM - response = requests.request("POST", request_url, headers=headers, json=json.loads(json_body)) - - if response.status_code != 201: - print("Request returned an error: {} {}".format(response.status_code, response.text)) - else: - print(f"Response code: {response.status_code}") - - return response - -def main(): - response = add_dm_to_conversation(text_message, dm_conversation_id) - print(json.dumps(json.loads(response.text), indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Add DM to conversation + payload = {"text": text_message} + response = client.direct_messages.create_by_conversation_id(dm_conversation_id, body=payload) + + print("Response code: 201") + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/direct_messages/post_group_conversation_dm.py b/python/direct_messages/post_group_conversation_dm.py index 135a9e5..bcc58b0 100644 --- a/python/direct_messages/post_group_conversation_dm.py +++ b/python/direct_messages/post_group_conversation_dm.py @@ -1,116 +1,71 @@ -import base64 -import hashlib -import os -import re -import json -import requests -from requests_oauthlib import OAuth2Session - -#This example is set up to create a new group conversation and add a new DM to it. -POST_DM_URL = "https://api.x.com/2/dm_conversations" - -#----------------------------------------------------------------------------------------------------------------------- -# These variables need to be updated to the setting that match how your Twitter App is set-up at -# https://developer.twitter.com/en/portal/dashboard. These will not change from run-by-run. -client_id = '' -#This must match *exactly* the redirect URL specified in the Developer Portal. -redirect_uri = "https://www.example.com" -#----------------------------------------------------------------------------------------------------------------------- -# These variables indicate the participants of the new group conversation and the message to add. A more ready-to-be -# used example would have these passed in from some calling code. -#Who is in this group conversation? Reference their User IDs. -participant_ids = ["944480690","906948460078698496"] -#Set the text of the message to be sent. -text_message = "Hi, I am creating a new *group* conversation, and starting it with the this message." -#----------------------------------------------------------------------------------------------------------------------- - -def handle_oauth(): - - # Set the scopes needed to be granted by the authenticating user. - scopes = ["dm.read", "dm.write", "tweet.read", "users.read", "offline.access"] +""" +Post Group Conversation Direct Message - X API v2 +================================================= +Endpoint: POST https://api.x.com/2/dm_conversations +Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm-conversations - # Create a code verifier. - code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") - code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" - # Create a code challenge. - code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") - code_challenge = code_challenge.replace("=", "") - - # Start an OAuth 2.0 session. - oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth - # Create an authorize URL. - auth_url = "https://twitter.com/i/oauth2/authorize" - authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" - ) +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") - # Visit the URL to authorize your App to make requests on behalf of a user. - print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" - ) - print(authorization_url) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" - # Paste in your authorize URL to complete the request. - authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" - ) +# Set the scopes +scopes = ["dm.read", "dm.write", "tweet.read", "users.read", "offline.access"] - # Fetch your access token. - token_url = "https://api.x.com/2/oauth2/token" +# Who is in this group conversation? Reference their User IDs. +participant_ids = ["2244994945", "1716450569358098432"] - # The following line of code will only work if you are using a type of App that is a public client. - auth = False +# Set the text of the message to be sent. +text_message = "Hi, I am creating a new *group* conversation, and starting it with the this message." - token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - - # The access token - access = token["access_token"] - - return access - -def create_new_group_conversation_with_dm(dm_text, participant_ids): - request_body = {} - - access = handle_oauth() - - headers = { - "Authorization": "Bearer {}".format(access), - "Content-Type": "application/json", - "User-Agent": "TwitterDevSampleCode", - "X-TFE-Experiment-environment": "staging1", - "Dtab-Local": "/s/gizmoduck/test-users-temporary => /s/gizmoduck/gizmoduck" + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Create group conversation with DM + payload = { + "message": {"text": text_message}, + "participant_ids": participant_ids, + "conversation_type": "Group" } - - request_url = POST_DM_URL - request_body['message'] = {} - request_body['message']['text'] = dm_text - request_body['participant_ids'] = participant_ids - request_body['conversation_type'] = "Group" - json_body = json.dumps(request_body) - - #Send DM - response = requests.request("POST", request_url, headers=headers, json=json.loads(json_body)) - - if response.status_code != 201: - print("Request returned an error: {} {}".format(response.status_code, response.text)) - else: - print(f"Response code: {response.status_code}") - - return response - -def main(): - response = create_new_group_conversation_with_dm(text_message, participant_ids) - print(json.dumps(json.loads(response.text), indent=4, sort_keys=True)) + response = client.direct_messages.create_conversation(body=payload) + + print("Response code: 201") + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/direct_messages/post_one_to_one_dm.py b/python/direct_messages/post_one_to_one_dm.py index da59ba4..7512281 100644 --- a/python/direct_messages/post_one_to_one_dm.py +++ b/python/direct_messages/post_one_to_one_dm.py @@ -1,114 +1,67 @@ -import base64 -import hashlib -import os -import re -import json -import requests -from requests_oauthlib import OAuth2Session - -#This example is set up to add a new DM to a one-to-one conversation. -POST_DM_URL = "https://api.x.com/2/dm_conversations/with/:participant_id/messages" - -#----------------------------------------------------------------------------------------------------------------------- -# These variables need to be updated to the setting that match how your Twitter App is set-up at -# https://developer.twitter.com/en/portal/dashboard. These will not change from run-by-run. -client_id = '' -#This must match *exactly* the redirect URL specified in the Developer Portal. -redirect_uri = "https://www.example.com" -#----------------------------------------------------------------------------------------------------------------------- -# These variables indicate the participant of the one-to-one conversation and the message to add. A more ready-to-be -# used example would have these passed in from some calling code. -#Who is this DM being sent to? Reference their User ID. -participant_id = "944480690" -#Set the text of the message to be sent. -text_message = "Hi, I am DMing you using the v2 DM one-to-one endpoint." -#----------------------------------------------------------------------------------------------------------------------- - -def handle_oauth(): - - # Set the scopes needed to be granted by the authenticating user. - scopes = ["dm.read", "dm.write", "tweet.read", "users.read", "offline.access"] +""" +Post One-to-One Direct Message - X API v2 +========================================== +Endpoint: POST https://api.x.com/2/dm_conversations/with/:participant_id/messages +Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm-conversations-with-participant_id-messages - # Create a code verifier - code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") - code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" - # Create a code challenge - code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") - code_challenge = code_challenge.replace("=", "") +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth - # Start an OAuth 2.0 session - oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") - # Create an authorize URL - auth_url = "https://twitter.com/i/oauth2/authorize" - authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" - ) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" - # Visit the URL to authorize your App to make requests on behalf of a user - print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" - ) - print(authorization_url) +# Set the scopes +scopes = ["dm.read", "dm.write", "tweet.read", "users.read", "offline.access"] - # Paste in your authorize URL to complete the request - authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" - ) - - # Fetch your access token - token_url = "https://api.x.com/2/oauth2/token" +# Who is this DM being sent to? Reference their User ID. +participant_id = "1716450569358098432" - # The following line of code will only work if you are using a type of App that is a public client - auth = False +# Set the text of the message to be sent. +text_message = "Hi, I am DMing you using the v2 DM one-to-one endpoint." - token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - - # Your access token - access = token["access_token"] - - return access - -def post_dm_to_one_to_one_conversation(participant_id): - - request_body = {} - - access = handle_oauth() - - headers = { - "Authorization": "Bearer {}".format(access), - "Content-Type": "application/json", - "User-Agent": "TwitterDevSampleCode", - "X-TFE-Experiment-environment": "staging1", - "Dtab-Local": "/s/gizmoduck/test-users-temporary => /s/gizmoduck/gizmoduck" - } - - request_url = POST_DM_URL.replace(':participant_id', str(participant_id)) - request_body['text'] = text_message - json_body = json.dumps(request_body) - - #Send DM - response = requests.request("POST", request_url, headers=headers, json=json.loads(json_body)) - - if response.status_code != 201: - print("Request returned an error: {} {}".format(response.status_code, response.text)) - else: - print(f"Response code: {response.status_code}") - - return response - -def main(): - response = post_dm_to_one_to_one_conversation(participant_id) - print(json.dumps(json.loads(response.text), indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Send one-to-one DM + payload = {"text": text_message} + response = client.direct_messages.create_by_participant_id(participant_id, body=payload) + + print("Response code: 201") + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/direct_messages/send.py b/python/direct_messages/send.py deleted file mode 100644 index 9299385..0000000 --- a/python/direct_messages/send.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -Send Direct Message - X API v2 -============================== -Endpoint: POST https://api.x.com/2/dm_conversations/with/:participant_id/messages -Docs: https://developer.x.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm-conversations-with-participant_id-messages - -Authentication: OAuth 1.0a or OAuth 2.0 (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with the user ID you want to send a DM to -participant_id = "recipient-user-id" - -# Message content -payload = {"text": "Hello! This is a test message."} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/dm_conversations/with/{}/messages".format(participant_id), - json=payload -) - -if response.status_code != 201: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/lists/List-Tweets.py b/python/lists/List-Tweets.py index dca8430..6f953a4 100644 --- a/python/lists/List-Tweets.py +++ b/python/lists/List-Tweets.py @@ -1,13 +1,25 @@ -import requests +""" +List Tweets - X API v2 +====================== +Endpoint: GET https://api.x.com/2/lists/:id/tweets +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-tweets/api-reference/get-lists-id-tweets + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Be sure to replace list-id with any List ID +list_id = "list-id" -def create_url(): +def main(): + # Get list tweets with automatic pagination # Tweet fields are adjustable. # Options include: # attachments, author_id, context_annotations, @@ -15,41 +27,17 @@ def create_url(): # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - tweet_fields = "tweet.fields=lang,author_id" - # Be sure to replace list-id with any List ID - id = "list-id" - url = "https://api.x.com/2/lists/{}/tweets".format(id) - return url, tweet_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ListTweetsLookupPython" - return r - - -def connect_to_endpoint(url, tweet_fields): - response = requests.request( - "GET", url, auth=bearer_oauth, params=tweet_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, tweet_fields = create_url() - json_response = connect_to_endpoint(url, tweet_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_posts = [] + for page in client.lists.get_tweets( + list_id, + max_results=100, + tweetfields=["lang", "author_id"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/lists/Pinned-List.py b/python/lists/Pinned-List.py index 898d63f..132faba 100644 --- a/python/lists/Pinned-List.py +++ b/python/lists/Pinned-List.py @@ -1,78 +1,74 @@ -from requests_oauthlib import OAuth1Session +""" +Pinned Lists Lookup - X API v2 +============================== +Endpoint: GET https://api.x.com/2/users/:id/pinned_lists +Docs: https://developer.x.com/en/docs/twitter-api/lists/pinned-lists/api-reference/get-users-id-pinned_lists + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "offline.access"] -# Be sure to replace your-user-id with your own user ID or one of an authenticating user +# Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" - -# List fields are adjustable, options include: -# created_at, description, owner_id, -# private, follower_count, member_count, -list_fields = "list.fields=created_at,description,private" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +user_id = "your-user-id" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.get( - "https://api.x.com/2/users/{}/pinned_lists".format(id), params=list_fields) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get pinned lists with automatic pagination + # List fields are adjustable, options include: + # created_at, description, owner_id, + # private, follower_count, member_count, + all_lists = [] + for page in client.users.get_pinned_lists( + user_id, + max_results=100, + listfields=["created_at", "description", "private"] + ): + all_lists.extend(page.data) + print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + + print(f"\nTotal Pinned Lists: {len(all_lists)}") + print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + +if __name__ == "__main__": + main() diff --git a/python/lists/add_member.py b/python/lists/add_member.py index fe985f1..caf5359 100644 --- a/python/lists/add_member.py +++ b/python/lists/add_member.py @@ -1,77 +1,67 @@ -from requests_oauthlib import OAuth1Session +""" +Add List Member - X API v2 +========================== +Endpoint: POST https://api.x.com/2/lists/:id/members +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-members/api-reference/post-lists-id-members + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] -# Be sure to replace your-list-id with your own list ID or one of an authenticating user - -id = "your-list-id" +# Be sure to replace your-list-id with your own list ID or one of an authenticated user +list_id = "your-list-id" # Be sure to replace user-id-to-add with the user id you wish to add. -payload = {"user_id": "user-id-to-add"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +user_id = "user-id-to-add" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/lists/{}/members".format(id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Add member to list + payload = {"user_id": user_id} + response = client.lists.add_member(list_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/create.py b/python/lists/create.py deleted file mode 100644 index cd51ff4..0000000 --- a/python/lists/create.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -Create List - X API v2 -====================== -Endpoint: POST https://api.x.com/2/lists -Docs: https://developer.x.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-lists - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# List parameters -payload = { - "name": "My new list", - "description": "A description for my list", - "private": False -} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post("https://api.x.com/2/lists", json=payload) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/lists/create_a_list.py b/python/lists/create_a_list.py index cb4d5b0..8e58d71 100644 --- a/python/lists/create_a_list.py +++ b/python/lists/create_a_list.py @@ -1,76 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Create List - X API v2 +====================== +Endpoint: POST https://api.x.com/2/lists +Docs: https://developer.x.com/en/docs/twitter-api/lists/manage-lists/api-reference/post-lists + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] -# Be sure to add replace name-of-list with the name you wish to call the list. +# Be sure to replace name-of-list with the name you wish to call the list. # description and private keys are optional -payload = { "name": "name-of-list", - "description": "description-of-list", - "private": False} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) +payload = { + "name": "name-of-list", + "description": "description-of-list", + "private": False +} -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/lists", json=payload -) - -if response.status_code != 201: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Create the list + response = client.lists.create_list(body=payload) + + print("Response code: 201") + print(json.dumps(response.data, indent=4, sort_keys=True)) -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/lists/delete.py b/python/lists/delete.py deleted file mode 100644 index 6d0b4ec..0000000 --- a/python/lists/delete.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -Delete List - X API v2 -====================== -Endpoint: DELETE https://api.x.com/2/lists/:id -Docs: https://developer.x.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-lists-id - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with the list ID you want to delete -list_id = "list-id-to-delete" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete("https://api.x.com/2/lists/{}".format(list_id)) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/lists/delete_a_list.py b/python/lists/delete_a_list.py index c00bded..927b67c 100644 --- a/python/lists/delete_a_list.py +++ b/python/lists/delete_a_list.py @@ -1,75 +1,64 @@ -from requests_oauthlib import OAuth1Session -import os -import json - -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - - -# Be sure to replace your-list-id with the id of the list you wish to delete. The authenticated user must own the list in order to delete - -id = "your-list-id" - - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] +""" +Delete List - X API v2 +====================== +Endpoint: DELETE https://api.x.com/2/lists/:id +Docs: https://developer.x.com/en/docs/twitter-api/lists/manage-lists/api-reference/delete-lists-id -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" -# Making the request -response = oauth.delete( - "https://api.x.com/2/lists/{}".format(id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") + +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" + +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] + +# Be sure to replace your-list-id with the id of the list you wish to delete. +# The authenticated user must own the list in order to delete it. +list_id = "your-list-id" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Delete the list + response = client.lists.delete_list(list_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/follow_list.py b/python/lists/follow_list.py index d52adc0..bafb9e3 100644 --- a/python/lists/follow_list.py +++ b/python/lists/follow_list.py @@ -1,77 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Follow List - X API v2 +===================== +Endpoint: POST https://api.x.com/2/users/:id/followed_lists +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-follows/api-reference/post-users-id-followed_lists + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] -# Be sure to replace your-user-id with your own user ID or one of an authenticating user +# Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" - -# Be sure to add replace list-id-to-follow with the list id you wish to follow. -payload = {"list_id": "list-id-to-follow"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +user_id = "your-user-id" + +# Be sure to replace list-id-to-follow with the list id you wish to follow. +list_id = "list-id-to-follow" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/followed_lists".format(id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Follow the list + payload = {"list_id": list_id} + response = client.users.follow_list(user_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/list-followers-lookup.py b/python/lists/list-followers-lookup.py index 85566d0..2a1b6c8 100644 --- a/python/lists/list-followers-lookup.py +++ b/python/lists/list-followers-lookup.py @@ -1,51 +1,40 @@ -import requests +""" +List Followers Lookup - X API v2 +================================= +Endpoint: GET https://api.x.com/2/lists/:id/followers +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-follows/api-reference/get-lists-id-followers + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# You can replace list-id with the List ID you wish to find followers of. +list_id = "list-id" -def create_url(): +def main(): + # Get list followers with automatic pagination # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - user_fields = "user.fields=created_at,description,verified" - # You can replace list-id with the List ID you wish to find followers of. - id = "list-id" - url = "https://api.x.com/2/lists/{}/followers".format(id) - return url, user_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ListFollowersLookupPython" - return r - - -def connect_to_endpoint(url, user_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=user_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, user_fields = create_url() - json_response = connect_to_endpoint(url, user_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_users = [] + for page in client.lists.get_followers( + list_id, + max_results=100, + userfields=["created_at", "description", "verified"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Followers: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/lists/list-lookup-by-id.py b/python/lists/list-lookup-by-id.py index 6f47eff..07679bd 100644 --- a/python/lists/list-lookup-by-id.py +++ b/python/lists/list-lookup-by-id.py @@ -1,50 +1,33 @@ -import requests +""" +List Lookup by ID - X API v2 +============================= +Endpoint: GET https://api.x.com/2/lists/:id +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-lookup/api-reference/get-lists-id + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# You can replace the ID given with the List ID you wish to lookup. +list_id = "list-id" -def create_url(): +def main(): # List fields are adjustable, options include: # created_at, description, owner_id, # private, follower_count, member_count, - list_fields = "list.fields=created_at,follower_count" - # You can replace the ID given with the List ID you wish to lookup. - id = "list-id" - url = "https://api.x.com/2/lists/{}".format(id) - return url, list_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ListLookupPython" - return r - - -def connect_to_endpoint(url, list_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=list_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, list_fields = create_url() - json_response = connect_to_endpoint(url, list_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.lists.get_list( + list_id, + listfields=["created_at", "follower_count"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/lists/list-member-lookup.py b/python/lists/list-member-lookup.py index 0f10dd3..dbe9f20 100644 --- a/python/lists/list-member-lookup.py +++ b/python/lists/list-member-lookup.py @@ -1,50 +1,40 @@ -import requests +""" +List Members Lookup - X API v2 +============================== +Endpoint: GET https://api.x.com/2/lists/:id/members +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-members/api-reference/get-lists-id-members + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# You can replace list-id with the List ID you wish to find members of. +list_id = "list-id" -def create_url(): +def main(): + # Get list members with automatic pagination # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - user_fields = "user.fields=created_at,description,verified" - # You can replace list-id with the List ID you wish to find members of. - id = "list-id" - url = "https://api.x.com/2/lists/{}/members".format(id) - return url, user_fields - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ListMembersLookupPython" - return r - - -def connect_to_endpoint(url, user_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=user_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, user_fields = create_url() - json_response = connect_to_endpoint(url, user_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_users = [] + for page in client.lists.get_members( + list_id, + max_results=100, + userfields=["created_at", "description", "verified"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Members: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/lists/lookup.py b/python/lists/lookup.py index b736289..291c7be 100644 --- a/python/lists/lookup.py +++ b/python/lists/lookup.py @@ -8,48 +8,25 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Replace with the list ID you want to look up +list_id = "84839422" -def create_url(): - # Replace with the list ID you want to look up - list_id = "84839422" - return "https://api.x.com/2/lists/{}".format(list_id) - - -def get_params(): +def main(): # List fields are adjustable. Options include: # created_at, follower_count, member_count, private, description, owner_id - return {"list.fields": "created_at,follower_count,member_count,owner_id,description"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ListLookupPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.lists.get_list( + list_id, + listfields=["created_at", "follower_count", "member_count", "owner_id", "description"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/lists/pin_list.py b/python/lists/pin_list.py index 20a03b4..f53952a 100644 --- a/python/lists/pin_list.py +++ b/python/lists/pin_list.py @@ -1,77 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Pin List - X API v2 +================== +Endpoint: POST https://api.x.com/2/users/:id/pinned_lists +Docs: https://developer.x.com/en/docs/twitter-api/lists/pinned-lists/api-reference/post-users-id-pinned_lists + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] -# Be sure to replace your-user-id with your own user ID or one of an authenticating user +# Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" - -# Be sure to add replace list-id-to-pin with the list id you wish to pin. -payload = {"list_id": "list-id-to-pin"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +user_id = "your-user-id" + +# Be sure to replace list-id-to-pin with the list id you wish to pin. +list_id = "list-id-to-pin" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/pinned_lists".format(id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Pin the list + payload = {"list_id": list_id} + response = client.users.pin_list(user_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/remove_member.py b/python/lists/remove_member.py index cf51b34..5e80701 100644 --- a/python/lists/remove_member.py +++ b/python/lists/remove_member.py @@ -1,78 +1,67 @@ -from requests_oauthlib import OAuth1Session +""" +Remove List Member - X API v2 +============================== +Endpoint: DELETE https://api.x.com/2/lists/:id/members/:user_id +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-members/api-reference/delete-lists-id-members-user_id + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] # Be sure to replace your-list-id with your own list ID or one of an authenticated user -id = "your-list-id" +list_id = "your-list-id" -# Be sure to add replace user-id-to-remove with the id of the user you wish to remove. +# Be sure to replace user-id-to-remove with the id of the user you wish to remove. # You can find a user ID by using the user lookup endpoint user_id = "user-id-to-remove" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/lists/{}/members/{}".format(id, user_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Remove member from list + response = client.lists.remove_member(list_id, user_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/unfollow_list.py b/python/lists/unfollow_list.py index d28b62a..9a47163 100644 --- a/python/lists/unfollow_list.py +++ b/python/lists/unfollow_list.py @@ -1,78 +1,67 @@ -from requests_oauthlib import OAuth1Session +""" +Unfollow List - X API v2 +======================== +Endpoint: DELETE https://api.x.com/2/users/:id/followed_lists/:list_id +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-follows/api-reference/delete-users-id-followed_lists-list_id + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] # Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" +user_id = "your-user-id" -# Be sure to add replace list-id-to-unfollow with the id of the user you wish to unfollow. +# Be sure to replace list-id-to-unfollow with the id of the list you wish to unfollow. list_id = "list-id-to-unfollow" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/followed_lists/{}".format(id, list_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Unfollow the list + response = client.users.unfollow_list(user_id, list_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/unpin_list.py b/python/lists/unpin_list.py index 03e76d7..d5049eb 100644 --- a/python/lists/unpin_list.py +++ b/python/lists/unpin_list.py @@ -1,78 +1,67 @@ -from requests_oauthlib import OAuth1Session +""" +Unpin List - X API v2 +===================== +Endpoint: DELETE https://api.x.com/2/users/:id/pinned_lists/:list_id +Docs: https://developer.x.com/en/docs/twitter-api/lists/pinned-lists/api-reference/delete-users-id-pinned_lists-list_id + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] # Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" +user_id = "your-user-id" -# Be sure to add replace list-id-to-unpin with the id of the user you wish to unpin. +# Be sure to replace list-id-to-unpin with the id of the list you wish to unpin. list_id = "list-id-to-unpin" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/pinned_lists/{}".format(id, list_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Unpin the list + response = client.users.unpin_list(user_id, list_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/update_a_list.py b/python/lists/update_a_list.py index 8abf2ba..9d90632 100644 --- a/python/lists/update_a_list.py +++ b/python/lists/update_a_list.py @@ -1,81 +1,72 @@ -from requests_oauthlib import OAuth1Session -import os -import json - -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Be sure to add replace update-name-of-list with the name you wish to call the list. -# name, description and private are all optional -payload = { "name": "update-name-of-list", - "description": "update-description-of-list", - "private": False} - - -# Be sure to replace your-list-id with the id of the list you wish to update. The authenticated user must own the list in order to update - -id = "your-list-id" - +""" +Update List - X API v2 +====================== +Endpoint: PUT https://api.x.com/2/lists/:id +Docs: https://developer.x.com/en/docs/twitter-api/lists/manage-lists/api-reference/put-lists-id -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# Making the request -response = oauth.put( - "https://api.x.com/2/lists/{}".format(id), json=payload -) +# Set the scopes +scopes = ["tweet.read", "users.read", "list.read", "list.write", "offline.access"] -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +# Be sure to replace update-name-of-list with the name you wish to call the list. +# name, description and private are all optional +payload = { + "name": "update-name-of-list", + "description": "update-description-of-list", + "private": False +} + +# Be sure to replace your-list-id with the id of the list you wish to update. +# The authenticated user must own the list in order to update it. +list_id = "your-list-id" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Update the list + response = client.lists.update_list(list_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/lists/user-list-followed.py b/python/lists/user-list-followed.py index f28b3be..ced444f 100644 --- a/python/lists/user-list-followed.py +++ b/python/lists/user-list-followed.py @@ -1,50 +1,39 @@ -import requests +""" +User Followed Lists Lookup - X API v2 +====================================== +Endpoint: GET https://api.x.com/2/users/:id/followed_lists +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-follows/api-reference/get-users-id-followed_lists + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# You can replace the user-id with any valid User ID you wish to find what Lists they are following. +user_id = "user-id" -def create_url(): +def main(): + # Get user followed lists with automatic pagination # List fields are adjustable, options include: # created_at, description, owner_id, # private, follower_count, member_count, - list_fields = "list.fields=created_at,follower_count" - # You can replace the user-id with any valid User ID you wish to find what Lists they are following. - id = "user-id" - url = "https://api.x.com/2/users/{}/followed_lists".format(id) - return url, list_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2userListMembershipsPython" - return r - - -def connect_to_endpoint(url, list_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=list_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, list_fields = create_url() - json_response = connect_to_endpoint(url, list_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_lists = [] + for page in client.users.get_followed_lists( + user_id, + max_results=100, + listfields=["created_at", "follower_count"] + ): + all_lists.extend(page.data) + print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + + print(f"\nTotal Followed Lists: {len(all_lists)}") + print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/lists/user-list-memberships.py b/python/lists/user-list-memberships.py index 3455aaa..34b9330 100644 --- a/python/lists/user-list-memberships.py +++ b/python/lists/user-list-memberships.py @@ -1,50 +1,39 @@ -import requests +""" +User List Memberships Lookup - X API v2 +======================================= +Endpoint: GET https://api.x.com/2/users/:id/list_memberships +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-members/api-reference/get-users-id-list_memberships + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# You can replace the user-id with any valid User ID you wish to find what Lists they are members of. +user_id = "user-id" -def create_url(): +def main(): + # Get user list memberships with automatic pagination # List fields are adjustable, options include: # created_at, description, owner_id, # private, follower_count, member_count, - list_fields = "list.fields=created_at,follower_count" - # You can replace the user-id with any valid User ID you wish to find what Lists they are members of. - id = "user-id" - url = "https://api.x.com/2/users/{}/list_memberships".format(id) - return url, list_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2userListMembershipsPython" - return r - - -def connect_to_endpoint(url, list_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=list_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, list_fields = create_url() - json_response = connect_to_endpoint(url, list_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_lists = [] + for page in client.users.get_list_memberships( + user_id, + max_results=100, + listfields=["created_at", "follower_count"] + ): + all_lists.extend(page.data) + print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + + print(f"\nTotal List Memberships: {len(all_lists)}") + print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/lists/user-owned-list-lookup.py b/python/lists/user-owned-list-lookup.py index 3bbcd99..a77ac49 100644 --- a/python/lists/user-owned-list-lookup.py +++ b/python/lists/user-owned-list-lookup.py @@ -1,50 +1,39 @@ -import requests +""" +User Owned Lists Lookup - X API v2 +=================================== +Endpoint: GET https://api.x.com/2/users/:id/owned_lists +Docs: https://developer.x.com/en/docs/twitter-api/lists/list-lookup/api-reference/get-users-id-owned_lists + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# You can replace user-id with any valid User ID to see if they own any Lists. +user_id = "user-id" -def create_url(): +def main(): + # Get user owned lists with automatic pagination # List fields are adjustable, options include: # created_at, description, owner_id, # private, follower_count, member_count, - list_fields = "list.fields=created_at,follower_count" - # You can replace user-id with any valid User ID to see if they own any Lists. - id = "user-id" - url = "https://api.x.com/2/users/{}/owned_lists".format(id) - return url, list_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2ListLookupPython" - return r - - -def connect_to_endpoint(url, list_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=list_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, list_fields = create_url() - json_response = connect_to_endpoint(url, list_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_lists = [] + for page in client.users.get_owned_lists( + user_id, + max_results=100, + listfields=["created_at", "follower_count"] + ): + all_lists.extend(page.data) + print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + + print(f"\nTotal Owned Lists: {len(all_lists)}") + print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/media/media_upload_v2.py b/python/media/media_upload_v2.py index 0b28df2..5ac66d1 100644 --- a/python/media/media_upload_v2.py +++ b/python/media/media_upload_v2.py @@ -1,11 +1,20 @@ +""" +Media Upload v2 (Video) - X API v2 +=================================== +Endpoint: POST https://api.x.com/2/media/upload +Docs: https://developer.x.com/en/docs/twitter-api/media/upload-media/api-reference + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET + +This example demonstrates uploading a video file using chunked uploads. +""" + import os import sys -import base64 -import hashlib -import re import time import requests -from requests_oauthlib import OAuth2Session +from xdk.oauth2_auth import OAuth2PKCEAuth MEDIA_ENDPOINT_URL = 'https://api.x.com/2/media/upload' POST_TO_X_URL = 'https://api.x.com/2/tweets' @@ -13,77 +22,38 @@ # Replace with path to file VIDEO_FILENAME = 'REPLACE_ME' -# You will need to enable OAuth 2.0 in your App’s auth settings in the Developer Portal to get your client ID. -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_ID='your-client-id' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' client_id = os.environ.get("CLIENT_ID") - -# If you have selected a type of App that is a confidential client you will need to set a client secret. -# Confidential Clients securely authenticate with the authorization server. - -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_SECRET='your-client-secret' - -# Remove the comment on the following line if you are using a confidential client -# client_secret = os.environ.get("CLIENT_SECRET") +client_secret = os.environ.get("CLIENT_SECRET") # Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://www.example.com" +redirect_uri = "https://example.com" # Set the scopes scopes = ["media.write", "users.read", "tweet.read", "tweet.write", "offline.access"] -# Create a code verifier -code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") -code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) - -# Create a code challenge -code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() -code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") -code_challenge = code_challenge.replace("=", "") - -# Start and OAuth 2.0 session -oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - -# Create an authorize URL -auth_url = "https://x.com/i/oauth2/authorize" -authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" -) - -# Visit the URL to authorize your App to make requests on behalf of a user -print( - "Visit the following URL to authorize your App on behalf of your X handle in a browser:" -) -print(authorization_url) - -# Paste in your authorize URL to complete the request -authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" +# Step 1: Create PKCE instance +auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) -# Fetch your access token -token_url = "https://api.x.com/2/oauth2/token" - -# The following line of code will only work if you are using a type of App that is a public client -auth = False +# Step 2: Get authorization URL +auth_url = auth.get_authorization_url() +print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") +print(auth_url) -# If you are using a confidential client you will need to pass in basic encoding of your client ID and client secret. - -# Please remove the comment on the following line if you are using a type of App that is a confidential client -# auth = HTTPBasicAuth(client_id, client_secret) - -token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, - client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, -) +# Step 3: Handle callback +callback_url = input("Paste the full callback URL here: ") -# Your access token -access = token["access_token"] +# Step 4: Exchange code for tokens +tokens = auth.fetch_token(authorization_response=callback_url) +access = tokens["access_token"] headers = { "Authorization": "Bearer {}".format(access), diff --git a/python/media/upload.py b/python/media/upload.py index 130fa5b..f3139bd 100644 --- a/python/media/upload.py +++ b/python/media/upload.py @@ -1,91 +1,77 @@ """ Media Upload - X API v2 ======================= -Endpoint: POST https://api.x.com/2/media/upload +Endpoint: POST https://upload.x.com/1.1/media/upload.json Docs: https://developer.x.com/en/docs/twitter-api/media/upload-media/api-reference -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET This example demonstrates uploading an image to attach to a post. """ -from requests_oauthlib import OAuth1Session import os import json import base64 +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# Path to the media file you want to upload -media_path = "path/to/your/image.jpg" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] +# Set the scopes +scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"] -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Read and encode the media file -with open(media_path, "rb") as media_file: - media_data = base64.b64encode(media_file.read()).decode("utf-8") - -# Upload the media (using v1.1 endpoint as v2 media upload is similar) -upload_url = "https://upload.twitter.com/1.1/media/upload.json" -payload = {"media_data": media_data} - -response = oauth.post(upload_url, data=payload) +# Path to the media file you want to upload +media_path = "path/to/your/image.jpg" -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Get the media_id to use when creating a post -json_response = response.json() -media_id = json_response["media_id_string"] -print("Media ID: {}".format(media_id)) -print(json.dumps(json_response, indent=4, sort_keys=True)) - -# You can now use this media_id when creating a post: -# payload = {"text": "My post with media!", "media": {"media_ids": [media_id]}} + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Read and upload the media file + with open(media_path, "rb") as media_file: + media_data = base64.b64encode(media_file.read()).decode("utf-8") + + payload = {"media_data": media_data} + response = client.media.upload_media(body=payload) + + print("Response code: 200") + + # Get the media_id to use when creating a post + media_id = response.data["media_id_string"] + print("Media ID: {}".format(media_id)) + print(json.dumps(response.data, indent=4, sort_keys=True)) + + # You can now use this media_id when creating a post: + # payload = {"text": "My post with media!", "media": {"media_ids": [media_id]}} + +if __name__ == "__main__": + main() diff --git a/python/posts/counts_full_archive.py b/python/posts/counts_full_archive.py index 963c026..e61601b 100644 --- a/python/posts/counts_full_archive.py +++ b/python/posts/counts_full_archive.py @@ -10,42 +10,22 @@ Note: Requires Academic Research access. Returns counts from the entire archive. """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FullArchivePostCountsPython" - return r - - -def get_params(): - return { - "query": "from:XDevelopers", - "granularity": "day" - } - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +query = "from:XDevelopers" def main(): - url = "https://api.x.com/2/tweets/counts/all" - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.posts.get_tweet_counts_all( + query=query, + granularity="day" + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/posts/counts_recent.py b/python/posts/counts_recent.py index 8f51666..f293923 100644 --- a/python/posts/counts_recent.py +++ b/python/posts/counts_recent.py @@ -10,42 +10,22 @@ Note: Returns count of posts from the last 7 days matching your query. """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2PostCountsPython" - return r - - -def get_params(): - return { - "query": "from:XDevelopers", - "granularity": "day" - } - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +query = "from:XDevelopers" def main(): - url = "https://api.x.com/2/tweets/counts/recent" - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.posts.get_tweet_counts_recent( + query=query, + granularity="day" + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/posts/create_post.py b/python/posts/create_post.py deleted file mode 100644 index 63cf6fd..0000000 --- a/python/posts/create_post.py +++ /dev/null @@ -1,80 +0,0 @@ -""" -Create Post - X API v2 -====================== -Endpoint: POST https://api.x.com/2/tweets -Docs: https://developer.x.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# The text content of the post. You can also add parameters for polls, -# quote posts, reply settings, and more. -payload = {"text": "Hello world!"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token?oauth_callback=oob&x_auth_access_type=write" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/tweets", - json=payload, -) - -if response.status_code != 201: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/posts/create_tweet.py b/python/posts/create_tweet.py index 92c570b..a55d7a1 100644 --- a/python/posts/create_tweet.py +++ b/python/posts/create_tweet.py @@ -1,73 +1,64 @@ -from requests_oauthlib import OAuth1Session -import os -import json +""" +Create Tweet - X API v2 +======================= +Endpoint: POST https://api.x.com/2/tweets +Docs: https://developer.x.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/post-tweets -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Be sure to add replace the text of the with the text you wish to Tweet. You can also add parameters to post polls, quote Tweets, Tweet with reply settings, and Tweet to Super Followers in addition to other features. -payload = {"text": "Hello world!"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token?oauth_callback=oob&x_auth_access_type=write" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) +# Set the scopes +scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"] -# Making the request -response = oauth.post( - "https://api.x.com/2/tweets", - json=payload, -) +# Be sure to replace the text with the text you wish to Tweet. +# You can also add parameters to post polls, quote Tweets, Tweet with reply settings, and Tweet to Super Followers in addition to other features. +payload = {"text": "Hello world!"} -if response.status_code != 201: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Create the tweet + response = client.posts.create_tweet(body=payload) + + print("Response code: 201") + print(json.dumps(response.data, indent=4, sort_keys=True)) -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/posts/delete_post.py b/python/posts/delete_post.py deleted file mode 100644 index 0199ab0..0000000 --- a/python/posts/delete_post.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -Delete Post - X API v2 -====================== -Endpoint: DELETE https://api.x.com/2/tweets/:id -Docs: https://developer.x.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/delete-tweets-id - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with the ID of the post you wish to delete. -# The authenticated user must own the post. -post_id = "post-id-to-delete" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete("https://api.x.com/2/tweets/{}".format(post_id)) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json_response) diff --git a/python/posts/delete_tweet.py b/python/posts/delete_tweet.py index 2184284..f1369ba 100644 --- a/python/posts/delete_tweet.py +++ b/python/posts/delete_tweet.py @@ -1,73 +1,64 @@ -from requests_oauthlib import OAuth1Session -import os -import json - -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - - -# Be sure to replace tweet-id-to-delete with the id of the Tweet you wish to delete. The authenticated user must own the list in order to delete - -id = "tweet-id-to-delete" - - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] +""" +Delete Tweet - X API v2 +======================= +Endpoint: DELETE https://api.x.com/2/tweets/:id +Docs: https://developer.x.com/en/docs/twitter-api/tweets/manage-tweets/api-reference/delete-tweets-id -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" -# Making the request -response = oauth.delete("https://api.x.com/2/tweets/{}".format(id)) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") + +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" + +# Set the scopes +scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"] + +# Be sure to replace tweet-id-to-delete with the id of the Tweet you wish to delete. +# The authenticated user must own the Tweet in order to delete it. +post_id = "tweet-id-to-delete" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json_response) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Delete the tweet + response = client.posts.delete_tweet(post_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/posts/full-archive-search.py b/python/posts/full-archive-search.py index 785c487..5927d51 100644 --- a/python/posts/full-archive-search.py +++ b/python/posts/full-archive-search.py @@ -1,39 +1,39 @@ -import requests -import os -import json - -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") - -search_url = "https://api.x.com/2/tweets/search/all" +""" +Full-Archive Search - X API v2 +============================== +Endpoint: GET https://api.x.com/2/tweets/search/all +Docs: https://developer.x.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-all -# Optional params: start_time,end_time,since_id,until_id,max_results,next_token, -# expansions,tweet.fields,media.fields,poll.fields,place.fields,user.fields -query_params = {'query': '(from:twitterdev -is:retweet) OR #twitterdev','tweet.fields': 'author_id'} +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +Note: Requires Academic Research access. Returns posts from the entire archive. +This example demonstrates automatic pagination using the iterate() method +to fetch all pages of results. +""" -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FullArchiveSearchPython" - return r - +import os +import json +from xdk import Client -def connect_to_endpoint(url, params): - response = requests.request("GET", search_url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +query = '(from:twitterdev -is:retweet) OR #twitterdev' def main(): - json_response = connect_to_endpoint(search_url, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + # Search with automatic pagination + all_posts = [] + for page in client.posts.search_all( + query=query, + max_results=100, # Per page + tweetfields=["author_id"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": diff --git a/python/posts/full_archive_tweet_counts.py b/python/posts/full_archive_tweet_counts.py index 697a0be..a8e8ca5 100644 --- a/python/posts/full_archive_tweet_counts.py +++ b/python/posts/full_archive_tweet_counts.py @@ -1,38 +1,32 @@ -import requests -import os -import json - -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") - -search_url = "https://api.x.com/2/tweets/counts/all" +""" +Full-Archive Tweet Counts - X API v2 +==================================== +Endpoint: GET https://api.x.com/2/tweets/counts/all +Docs: https://developer.x.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-all -# Optional params: start_time,end_time,since_id,until_id,next_token,granularity -query_params = {'query': 'from:twitterdev','granularity': 'day', 'start_time': '2021-01-01T00:00:00Z'} +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +Note: Requires Academic Research access. Returns counts from the entire archive. +""" -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FullArchiveTweetCountsPython" - return r - +import os +import json +from xdk import Client -def connect_to_endpoint(url, params): - response = requests.request("GET", search_url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +query = 'from:twitterdev' def main(): - json_response = connect_to_endpoint(search_url, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.posts.get_tweet_counts_all( + query=query, + granularity="day", + start_time="2021-01-01T00:00:00Z" + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/posts/get_tweets_with_bearer_token.py b/python/posts/get_tweets_with_bearer_token.py index e646f70..972d668 100644 --- a/python/posts/get_tweets_with_bearer_token.py +++ b/python/posts/get_tweets_with_bearer_token.py @@ -1,14 +1,24 @@ -import requests +""" +Post Lookup - X API v2 +====================== +Endpoint: GET https://api.x.com/2/tweets +Docs: https://developer.x.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Post IDs to look up (list, up to 100) +post_ids = ["1278747501642657792", "1255542774432063488"] -def create_url(): - tweet_fields = "tweet.fields=lang,author_id" +def main(): # Tweet fields are adjustable. # Options include: # attachments, author_id, context_annotations, @@ -16,39 +26,12 @@ def create_url(): # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - ids = "ids=1278747501642657792,1255542774432063488" - # You can adjust ids to include a single Tweets. - # Or you can add to up to 100 comma-separated IDs - url = "https://api.x.com/2/tweets?{}&{}".format(ids, tweet_fields) - return url - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2TweetLookupPython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url = create_url() - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.posts.get_tweets( + ids=post_ids, + tweetfields=["lang", "author_id"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/posts/get_tweets_with_user_context.py b/python/posts/get_tweets_with_user_context.py index 8ab866e..6d81471 100644 --- a/python/posts/get_tweets_with_user_context.py +++ b/python/posts/get_tweets_with_user_context.py @@ -1,77 +1,73 @@ -from requests_oauthlib import OAuth1Session +""" +Post Lookup (User Context) - X API v2 +====================================== +Endpoint: GET https://api.x.com/2/tweets +Docs: https://developer.x.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# To set your enviornment variables in your terminal run the following line: -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# You can adjust ids to include a single Tweets -# Or you can add to up to 100 comma-separated IDs -params = {"ids": "1278747501642657792", "tweet.fields": "created_at"} -# Tweet fields are adjustable. -# Options include: -# attachments, author_id, context_annotations, -# conversation_id, created_at, entities, geo, id, -# in_reply_to_user_id, lang, non_public_metrics, organic_metrics, -# possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, -# source, text, and withheld +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access"] -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +# You can adjust ids to include a single Post +# Or you can add to up to 100 comma-separated IDs +post_ids = ["1278747501642657792"] -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -response = oauth.get( - "https://api.x.com/2/tweets", params=params -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get posts + # Tweet fields are adjustable. + # Options include: + # attachments, author_id, context_annotations, + # conversation_id, created_at, entities, geo, id, + # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, + # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, + # source, text, and withheld + response = client.posts.get_tweets( + ids=post_ids, + tweetfields=["created_at"] ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) -print("Response code: {}".format(response.status_code)) -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/posts/like.py b/python/posts/like.py deleted file mode 100644 index 5bcb4d6..0000000 --- a/python/posts/like.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Like a Post - X API v2 -====================== -Endpoint: POST https://api.x.com/2/users/:id/likes -Docs: https://developer.x.com/en/docs/twitter-api/tweets/likes/api-reference/post-users-id-likes - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID -user_id = "your-user-id" - -# Replace with the post ID you want to like -payload = {"tweet_id": "1354143047324299264"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/likes".format(user_id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/posts/like_a_tweet.py b/python/posts/like_a_tweet.py index 00a24ce..a28743a 100644 --- a/python/posts/like_a_tweet.py +++ b/python/posts/like_a_tweet.py @@ -1,77 +1,69 @@ -from requests_oauthlib import OAuth1Session +""" +Like a Tweet - X API v2 +====================== +Endpoint: POST https://api.x.com/2/users/:id/likes +Docs: https://developer.x.com/en/docs/twitter-api/tweets/likes/api-reference/post-users-id-likes + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# Be sure to replace your-user-id with your own user ID or one of an authenticating user +# Set the scopes +scopes = ["tweet.read", "tweet.write", "users.read", "offline.access", "like.write"] + +# Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" +user_id = "your-user-id" # You can replace Tweet ID given with the Tweet ID you wish to like. # You can find a Tweet ID by using the Tweet lookup endpoint -payload = {"tweet_id": "1354143047324299264"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +post_id = "1354143047324299264" -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Like the tweet + payload = {"tweet_id": post_id} + response = client.users.like_tweet(user_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/likes".format(id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/posts/liked_posts.py b/python/posts/liked_posts.py deleted file mode 100644 index cfafbf5..0000000 --- a/python/posts/liked_posts.py +++ /dev/null @@ -1,53 +0,0 @@ -""" -Posts Liked by User - X API v2 -============================== -Endpoint: GET https://api.x.com/2/users/:id/liked_tweets -Docs: https://developer.x.com/en/docs/twitter-api/tweets/likes/api-reference/get-users-id-liked_tweets - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import requests -import os -import json - -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(): - # Replace with the user ID you want to get liked posts for - user_id = "2244994945" - return "https://api.x.com/2/users/{}/liked_tweets".format(user_id) - - -def get_params(): - return {"tweet.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2LikedPostsPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/posts/liked_tweets.py b/python/posts/liked_tweets.py index b668621..40fe363 100644 --- a/python/posts/liked_tweets.py +++ b/python/posts/liked_tweets.py @@ -1,13 +1,60 @@ -import requests +""" +Liked Tweets - X API v2 +====================== +Endpoint: GET https://api.x.com/2/users/:id/liked_tweets +Docs: https://developer.x.com/en/docs/twitter-api/tweets/likes/api-reference/get-users-id-liked_tweets + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "like.read", "offline.access"] -def create_url(): +# Be sure to replace your-user-id with your own user ID or one of an authenticated user +# You can find a user ID by using the user lookup endpoint +user_id = "your-user-id" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes + ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get liked tweets with automatic pagination # Tweet fields are adjustable. # Options include: # attachments, author_id, context_annotations, @@ -15,44 +62,17 @@ def create_url(): # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - tweet_fields = "tweet.fields=lang,author_id" - # Be sure to replace your-user-id with your own user ID or one of an authenticating user - # You can find a user ID by using the user lookup endpoint - id = "your-user-id" - # You can adjust ids to include a single Tweets. - # Or you can add to up to 100 comma-separated IDs - url = "https://api.x.com/2/users/{}/liked_tweets".format(id) - return url, tweet_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2LikedTweetsPython" - return r - - -def connect_to_endpoint(url, tweet_fields): - response = requests.request( - "GET", url, auth=bearer_oauth, params=tweet_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, tweet_fields = create_url() - json_response = connect_to_endpoint(url, tweet_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_posts = [] + for page in client.users.get_liked_tweets( + user_id, + max_results=100, + tweetfields=["lang", "author_id"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Liked Tweets: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/posts/liking_users.py b/python/posts/liking_users.py index 1b08116..7f33ea9 100644 --- a/python/posts/liking_users.py +++ b/python/posts/liking_users.py @@ -1,54 +1,75 @@ -import requests +""" +Liking Users - X API v2 +====================== +Endpoint: GET https://api.x.com/2/tweets/:id/liking_users +Docs: https://developer.x.com/en/docs/twitter-api/tweets/likes/api-reference/get-tweets-id-liking_users + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "like.read", "offline.access"] -def create_url(): +# You can replace the ID given with the Post ID you wish to get liking users for. +# You can find an ID by using the Post lookup endpoint +post_id = "1354143047324299264" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes + ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get liking users with automatic pagination # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - user_fields = "user.fields=created_at,description" - # You can replace the ID given with the Tweet ID you wish to like. - # You can find an ID by using the Tweet lookup endpoint - id = "1354143047324299264" - # You can adjust ids to include a single Tweets. - # Or you can add to up to 100 comma-separated IDs - url = "https://api.x.com/2/tweets/{}/liking_users".format(id) - return url, user_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2LikingUsersPython" - return r - - -def connect_to_endpoint(url, user_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=user_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, tweet_fields = create_url() - json_response = connect_to_endpoint(url, tweet_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_users = [] + for page in client.posts.get_liking_users( + post_id, + max_results=100, + userfields=["created_at", "description"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/posts/lookup.py b/python/posts/lookup.py index 802a466..0c9de3f 100644 --- a/python/posts/lookup.py +++ b/python/posts/lookup.py @@ -8,53 +8,29 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Post IDs to look up (comma-separated list, up to 100) +post_ids = ["1278747501642657792", "1255542774432063488"] -def create_url(): - # Post IDs to look up (comma-separated, up to 100) - post_ids = "ids=1278747501642657792,1255542774432063488" - +def main(): # Post fields are adjustable. Options include: # attachments, author_id, context_annotations, conversation_id, # created_at, entities, geo, id, in_reply_to_user_id, lang, # non_public_metrics, organic_metrics, possibly_sensitive, # promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - post_fields = "tweet.fields=created_at,author_id,lang,source,public_metrics,context_annotations,entities" - url = "https://api.x.com/2/tweets?{}&{}".format(post_ids, post_fields) - return url - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2PostLookupPython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url = create_url() - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.posts.get_tweets( + ids=post_ids, + tweetfields=["created_at", "author_id", "lang", "source", "public_metrics", "context_annotations", "entities"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/posts/quote_posts.py b/python/posts/quote_posts.py deleted file mode 100644 index 038ebea..0000000 --- a/python/posts/quote_posts.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Quote Posts Lookup - X API v2 -============================= -Endpoint: GET https://api.x.com/2/tweets/:id/quote_tweets -Docs: https://developer.x.com/en/docs/twitter-api/tweets/quote-tweets/api-reference/get-tweets-id-quote_tweets - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import requests -import os -import json - -bearer_token = os.environ.get("BEARER_TOKEN") - -# Replace with the post ID you want to get quotes for -post_id = "1409931481552543749" - - -def get_params(): - return {"tweet.fields": "created_at"} - - -def create_url(): - return "https://api.x.com/2/tweets/{}/quote_tweets".format(post_id) - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2QuotePostsPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/posts/quote_tweets.py b/python/posts/quote_tweets.py index a56204c..abac584 100644 --- a/python/posts/quote_tweets.py +++ b/python/posts/quote_tweets.py @@ -1,22 +1,25 @@ -import requests -import os -import json - -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' - +""" +Quote Tweets Lookup - X API v2 +============================== +Endpoint: GET https://api.x.com/2/tweets/:id/quote_tweets +Docs: https://developer.x.com/en/docs/twitter-api/tweets/quote-tweets/api-reference/get-tweets-id-quote_tweets -def auth(): - return os.environ.get("BEARER_TOKEN") +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" +import os +import json +from xdk import Client -def create_url(): - # Replace with Tweet ID below - tweet_id = 20 - return "https://api.x.com/2/tweets/{}/quote_tweets".format(tweet_id) +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Replace with the post ID you want to get quotes for +post_id = "1409931481552543749" -def get_params(): +def main(): + # Get quote tweets with automatic pagination # Tweet fields are adjustable. # Options include: # attachments, author_id, context_annotations, @@ -24,34 +27,17 @@ def get_params(): # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - return {"tweet.fields": "created_at"} - - -def create_headers(bearer_token): - headers = {"Authorization": "Bearer {}".format(bearer_token)} - return headers - - -def connect_to_endpoint(url, headers, params): - response = requests.request("GET", url, headers=headers, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - bearer_token = auth() - url = create_url() - headers = create_headers(bearer_token) - params = get_params() - json_response = connect_to_endpoint(url, headers, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_posts = [] + for page in client.posts.get_quoted( + post_id, + max_results=100, + tweetfields=["created_at"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Quote Tweets: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/posts/recent_search.py b/python/posts/recent_search.py index 58b19a9..e84a338 100644 --- a/python/posts/recent_search.py +++ b/python/posts/recent_search.py @@ -1,38 +1,39 @@ -import requests -import os -import json - -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") - -search_url = "https://api.x.com/2/tweets/search/recent" +""" +Recent Search - X API v2 +======================== +Endpoint: GET https://api.x.com/2/tweets/search/recent +Docs: https://developer.x.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent -# Optional params: start_time,end_time,since_id,until_id,max_results,next_token, -# expansions,tweet.fields,media.fields,poll.fields,place.fields,user.fields -query_params = {'query': '(from:twitterdev -is:retweet) OR #twitterdev','tweet.fields': 'author_id'} +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +Note: Returns posts from the last 7 days. +This example demonstrates automatic pagination using the iterate() method +to fetch all pages of results. +""" -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2RecentSearchPython" - return r +import os +import json +from xdk import Client -def connect_to_endpoint(url, params): - response = requests.get(url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +query = '(from:twitterdev -is:retweet) OR #twitterdev' def main(): - json_response = connect_to_endpoint(search_url, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + # Search with automatic pagination + all_posts = [] + for page in client.posts.search_recent( + query=query, + max_results=100, # Per page + tweetfields=["author_id"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": diff --git a/python/posts/recent_tweet_counts.py b/python/posts/recent_tweet_counts.py index 2b94f03..c636eaf 100644 --- a/python/posts/recent_tweet_counts.py +++ b/python/posts/recent_tweet_counts.py @@ -1,38 +1,31 @@ -import requests -import os -import json - -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") - -search_url = "https://api.x.com/2/tweets/counts/recent" +""" +Recent Tweet Counts - X API v2 +============================== +Endpoint: GET https://api.x.com/2/tweets/counts/recent +Docs: https://developer.x.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-recent -# Optional params: start_time,end_time,since_id,until_id,next_token,granularity -query_params = {'query': 'from:twitterdev','granularity': 'day'} +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +Note: Returns count of posts from the last 7 days matching your query. +""" -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2RecentTweetCountsPython" - return r - +import os +import json +from xdk import Client -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +query = 'from:twitterdev' def main(): - json_response = connect_to_endpoint(search_url, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.posts.get_tweet_counts_recent( + query=query, + granularity="day" + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/posts/repost.py b/python/posts/repost.py deleted file mode 100644 index e5cd46e..0000000 --- a/python/posts/repost.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Repost (Retweet) - X API v2 -=========================== -Endpoint: POST https://api.x.com/2/users/:id/retweets -Docs: https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/post-users-id-retweets - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID (find using user lookup endpoint) -user_id = "your-user-id" - -# Replace with the post ID you want to repost -payload = {"tweet_id": "1412865600439738368"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/retweets".format(user_id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/posts/reposted_by.py b/python/posts/reposted_by.py index c2ff4fc..2b56429 100644 --- a/python/posts/reposted_by.py +++ b/python/posts/reposted_by.py @@ -1,5 +1,5 @@ """ -Reposted By (Users who retweeted) - X API v2 +Reposted By (Users who reposted) - X API v2 ============================================ Endpoint: GET https://api.x.com/2/tweets/:id/retweeted_by Docs: https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/get-tweets-id-retweeted_by @@ -8,46 +8,29 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def create_url(): - # Replace with the post ID you want to get reposters for - post_id = "1354143047324299264" - return "https://api.x.com/2/tweets/{}/retweeted_by".format(post_id) - - -def get_params(): - return {"user.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2RepostedByPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +# Replace with the post ID you want to get reposters for +post_id = "1354143047324299264" def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + # Get reposted by users with automatic pagination + all_users = [] + for page in client.posts.get_reposted_by( + post_id, + max_results=100, + userfields=["created_at"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/posts/retweet_a_tweet.py b/python/posts/retweet_a_tweet.py index 40c218b..552a7f5 100644 --- a/python/posts/retweet_a_tweet.py +++ b/python/posts/retweet_a_tweet.py @@ -1,78 +1,69 @@ -from requests_oauthlib import OAuth1Session +""" +Retweet (Repost) - X API v2 +=========================== +Endpoint: POST https://api.x.com/2/users/:id/retweets +Docs: https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/post-users-id-retweets + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"] -# Be sure to replace your-user-id with your own user ID or one of an authenticating user +# Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" +user_id = "your-user-id" -# You can replace the given Tweet ID with your the Tweet ID you want to Retweet +# You can replace the given Tweet ID with the Tweet ID you want to Retweet # You can find a Tweet ID by using the Tweet lookup endpoint -payload = {"tweet_id": "1412865600439738368"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/retweets".format(id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +post_id = "1412865600439738368" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Retweet the tweet + payload = {"tweet_id": post_id} + response = client.users.repost_tweet(user_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/posts/retweeted_by.py b/python/posts/retweeted_by.py index 9876a36..f0eaf79 100644 --- a/python/posts/retweeted_by.py +++ b/python/posts/retweeted_by.py @@ -1,56 +1,41 @@ -import requests -import os -import json +""" +Retweeted By (Users who reposted) - X API v2 +============================================ +Endpoint: GET https://api.x.com/2/tweets/:id/retweeted_by +Docs: https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/get-tweets-id-retweeted_by -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" +import os +import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# You can replace the ID given with the Post ID you wish to lookup reposting users for +# You can find an ID by using the Post lookup endpoint +post_id = "1354143047324299264" -def create_url(): +def main(): + # Get reposted by users with automatic pagination # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - user_fields = "user.fields=created_at,description" - # You can replace the ID given with the Tweet ID you wish to lookup Retweeting users for - # You can find an ID by using the Tweet lookup endpoint - id = "1354143047324299264" - # You can adjust ids to include a single Tweets. - # Or you can add to up to 100 comma-separated IDs - url = "https://api.x.com/2/tweets/{}/retweeted_by".format(id) - return url, user_fields - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2RetweetedByPython" - return r - - -def connect_to_endpoint(url, user_fields): - response = requests.request("GET", url, auth=bearer_oauth, params=user_fields) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url, user_fields = create_url() - json_response = connect_to_endpoint(url, user_fields) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_users = [] + for page in client.posts.get_reposted_by( + post_id, + max_results=100, + userfields=["created_at", "description"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/posts/search_full_archive.py b/python/posts/search_full_archive.py index e9c9bc8..505a5fa 100644 --- a/python/posts/search_full_archive.py +++ b/python/posts/search_full_archive.py @@ -8,45 +8,32 @@ Required env vars: BEARER_TOKEN Note: Requires Academic Research access. Returns posts from the entire archive. +This example demonstrates automatic pagination using the iterate() method +to fetch all pages of results. """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) -search_url = "https://api.x.com/2/tweets/search/all" - -# Optional params: start_time, end_time, since_id, until_id, max_results, -# next_token, expansions, tweet.fields, media.fields, poll.fields, -# place.fields, user.fields -query_params = { - 'query': '(from:XDevelopers -is:retweet) OR #xapi', - 'tweet.fields': 'author_id' -} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FullArchiveSearchPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.get(url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +query = '(from:XDevelopers -is:retweet) OR #xapi' def main(): - json_response = connect_to_endpoint(search_url, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + # Search with automatic pagination + all_posts = [] + for page in client.posts.search_all( + query=query, + max_results=100, # Per page + tweetfields=["author_id", "created_at"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": diff --git a/python/posts/search_recent.py b/python/posts/search_recent.py index 9538a83..a7a4f80 100644 --- a/python/posts/search_recent.py +++ b/python/posts/search_recent.py @@ -8,44 +8,32 @@ Required env vars: BEARER_TOKEN Note: Returns posts from the last 7 days. +This example demonstrates automatic pagination using the iterate() method +to fetch all pages of results. """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) -search_url = "https://api.x.com/2/tweets/search/recent" - -# Optional params: start_time, end_time, since_id, until_id, max_results, next_token, -# expansions, tweet.fields, media.fields, poll.fields, place.fields, user.fields -query_params = { - 'query': '(from:XDevelopers -is:retweet) OR #xapi', - 'tweet.fields': 'author_id' -} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2RecentSearchPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.get(url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +query = '(from:XDevelopers -is:retweet) OR #xapi' def main(): - json_response = connect_to_endpoint(search_url, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) + # Search with automatic pagination + all_posts = [] + for page in client.posts.search_recent( + query=query, + max_results=100, # Per page + tweetfields=["author_id", "created_at"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": diff --git a/python/posts/undo_a_retweet.py b/python/posts/undo_a_retweet.py index 51fe32d..5529330 100644 --- a/python/posts/undo_a_retweet.py +++ b/python/posts/undo_a_retweet.py @@ -1,79 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Undo Retweet (Unrepost) - X API v2 +================================== +Endpoint: DELETE https://api.x.com/2/users/:id/retweets/:source_tweet_id +Docs: https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/delete-users-id-retweets-tweet_id + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"] # Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" +user_id = "your-user-id" -# You can replace the given Tweet ID with your the Tweet ID you want to Retweet +# You can replace the given Tweet ID with the Tweet ID you want to undo retweet for # You can find a Tweet ID by using the Tweet lookup endpoint -source_tweet_id = "1412865600439738368" - - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +source_post_id = "1412865600439738368" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/retweets/{}".format(id, source_tweet_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Undo retweet + response = client.users.unrepost_tweet(user_id, source_post_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/posts/undo_repost.py b/python/posts/undo_repost.py deleted file mode 100644 index fb87599..0000000 --- a/python/posts/undo_repost.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Undo Repost (Unretweet) - X API v2 -================================== -Endpoint: DELETE https://api.x.com/2/users/:id/retweets/:source_tweet_id -Docs: https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/delete-users-id-retweets-tweet_id - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID -user_id = "your-user-id" - -# Replace with the post ID you want to undo repost for -source_post_id = "post-id-to-unrepost" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/retweets/{}".format(user_id, source_post_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/posts/unlike.py b/python/posts/unlike.py deleted file mode 100644 index 4691558..0000000 --- a/python/posts/unlike.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Unlike a Post - X API v2 -======================== -Endpoint: DELETE https://api.x.com/2/users/:id/likes/:tweet_id -Docs: https://developer.x.com/en/docs/twitter-api/tweets/likes/api-reference/delete-users-id-likes-tweet_id - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID -user_id = "your-user-id" - -# Replace with the post ID you want to unlike -post_id = "post-id-to-unlike" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/likes/{}".format(user_id, post_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/posts/unlike_a_tweet.py b/python/posts/unlike_a_tweet.py index 91ff611..304ab0f 100644 --- a/python/posts/unlike_a_tweet.py +++ b/python/posts/unlike_a_tweet.py @@ -1,77 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Unlike a Tweet - X API v2 +======================== +Endpoint: DELETE https://api.x.com/2/users/:id/likes/:tweet_id +Docs: https://developer.x.com/en/docs/twitter-api/tweets/likes/api-reference/delete-users-id-likes-tweet_id + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") + +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Set the scopes +scopes = ["tweet.read", "tweet.write", "users.read", "offline.access", "like.write"] # Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" +user_id = "your-user-id" -# You can replace Tweet ID given with the Tweet ID you wish to like. +# You can replace Tweet ID given with the Tweet ID you wish to unlike. # You can find a Tweet ID by using the Tweet lookup endpoint -tweet_id = "1354143047324299264" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) +post_id = "1354143047324299264" -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/likes/{}".format(id, tweet_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Unlike the tweet + response = client.users.unlike_tweet(user_id, post_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/requirements.txt b/python/requirements.txt index a85ab73..ebc8553 100644 --- a/python/requirements.txt +++ b/python/requirements.txt @@ -1,2 +1 @@ -requests>=2.28.0 -requests-oauthlib>=1.3.0 +xdk>=0.4.5 diff --git a/python/spaces/lookup.py b/python/spaces/lookup.py deleted file mode 100644 index 5a0f185..0000000 --- a/python/spaces/lookup.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Spaces Lookup - X API v2 -======================== -Endpoint: GET https://api.x.com/2/spaces -Docs: https://developer.x.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import requests -import os -import json - -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(): - # Space IDs to look up (comma-separated) - space_ids = "ids=1DXxyRYNejbKM" - - # Space fields are adjustable. Options include: - # host_ids, created_at, creator_id, id, lang, invited_user_ids, - # participant_count, speaker_ids, started_at, ended_at, subscriber_count, - # topic_ids, state, title, updated_at, scheduled_start, is_ticketed - space_fields = "space.fields=host_ids,created_at,creator_id,participant_count,title,state" - url = "https://api.x.com/2/spaces?{}&{}".format(space_ids, space_fields) - return url - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2SpacesLookupPython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - url = create_url() - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/spaces/search.py b/python/spaces/search.py deleted file mode 100644 index 7678e2e..0000000 --- a/python/spaces/search.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Spaces Search - X API v2 -======================== -Endpoint: GET https://api.x.com/2/spaces/search -Docs: https://developer.x.com/en/docs/twitter-api/spaces/search/api-reference/get-spaces-search - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import requests -import os -import json - -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(): - query = "query=crypto" - state = "state=live" - - # Space fields are adjustable. Options include: - # host_ids, created_at, creator_id, id, lang, invited_user_ids, - # participant_count, speaker_ids, started_at, ended_at, subscriber_count, - # topic_ids, state, title, updated_at, scheduled_start, is_ticketed - space_fields = "space.fields=host_ids,created_at,participant_count,title" - url = "https://api.x.com/2/spaces/search?{}&{}&{}".format(query, state, space_fields) - return url - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2SpacesSearchPython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - url = create_url() - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/spaces/search_spaces.py b/python/spaces/search_spaces.py index d0c6340..c5a1a9d 100644 --- a/python/spaces/search_spaces.py +++ b/python/spaces/search_spaces.py @@ -1,40 +1,33 @@ -import requests +""" +Search Spaces - X API v2 +======================== +Endpoint: GET https://api.x.com/2/spaces/search +Docs: https://developer.x.com/en/docs/twitter-api/spaces/search/api-reference/get-spaces-search + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) -search_url = "https://api.x.com/2/spaces/search" - -search_term = 'NBA' # Replace this value with your search term - -# Optional params: host_ids,conversation_controls,created_at,creator_id,id,invited_user_ids,is_ticketed,lang,media_key,participants,scheduled_start,speaker_ids,started_at,state,title,updated_at -query_params = {'query': search_term, 'space.fields': 'title,created_at', 'expansions': 'creator_id'} - - -def create_headers(bearer_token): - headers = { - "Authorization": "Bearer {}".format(bearer_token), - "User-Agent": "v2SpacesSearchPython" - } - return headers - - -def connect_to_endpoint(url, headers, params): - response = requests.request("GET", search_url, headers=headers, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +# Replace this value with your search term +search_term = 'NBA' def main(): - headers = create_headers(bearer_token) - json_response = connect_to_endpoint(search_url, headers, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + # Optional params: host_ids, conversation_controls, created_at, creator_id, id, invited_user_ids, + # is_ticketed, lang, media_key, participants, scheduled_start, speaker_ids, started_at, state, title, updated_at + response = client.spaces.search( + query=search_term, + spacefields=["title", "created_at"], + expansions=["creator_id"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() \ No newline at end of file diff --git a/python/spaces/spaces_lookup.py b/python/spaces/spaces_lookup.py index 7c576d7..b6338aa 100644 --- a/python/spaces/spaces_lookup.py +++ b/python/spaces/spaces_lookup.py @@ -1,38 +1,33 @@ -import requests +""" +Spaces Lookup - X API v2 +======================== +Endpoint: GET https://api.x.com/2/spaces +Docs: https://developer.x.com/en/docs/twitter-api/spaces/lookup/api-reference/get-spaces + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) -search_url = "https://api.x.com/2/spaces" - -# Optional params: host_ids,conversation_controls,created_at,creator_id,id,invited_user_ids,is_ticketed,lang,media_key,participants,scheduled_start,speaker_ids,started_at,state,title,updated_at -query_params = {'ids': 'SPACE_ID', 'space.fields': 'title,created_at', 'expansions': 'creator_id'} - - -def create_headers(bearer_token): - headers = { - "Authorization": "Bearer {}".format(bearer_token), - "User-Agent": "v2SpacesLookupPython" - } - return headers - - -def connect_to_endpoint(url, headers, params): - response = requests.request("GET", search_url, headers=headers, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +# Space IDs to look up (list) +space_ids = ["SPACE_ID"] def main(): - headers = create_headers(bearer_token) - json_response = connect_to_endpoint(search_url, headers, query_params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + # Optional params: host_ids, conversation_controls, created_at, creator_id, id, invited_user_ids, + # is_ticketed, lang, media_key, participants, scheduled_start, speaker_ids, started_at, state, title, updated_at + response = client.spaces.get_spaces( + ids=space_ids, + spacefields=["title", "created_at"], + expansions=["creator_id"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() \ No newline at end of file diff --git a/python/streams/filtered_stream.py b/python/streams/filtered_stream.py index 88d578b..e3ebe8a 100644 --- a/python/streams/filtered_stream.py +++ b/python/streams/filtered_stream.py @@ -1,95 +1,60 @@ -import requests -import os -import json - -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") +""" +Filtered Stream - X API v2 +========================== +Endpoint: GET https://api.x.com/2/tweets/search/stream +Docs: https://developer.x.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ +Note: Streams posts matching your filter rules in real-time. +""" - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FilteredStreamPython" - return r +import os +import json +from xdk import Client +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) def get_rules(): - response = requests.get( - "https://api.x.com/2/tweets/search/stream/rules", auth=bearer_oauth - ) - if response.status_code != 200: - raise Exception( - "Cannot get rules (HTTP {}): {}".format(response.status_code, response.text) - ) - print(json.dumps(response.json())) - return response.json() + response = client.streams.get_rules() + print(json.dumps(response.data, indent=4, sort_keys=True)) + return response.data def delete_all_rules(rules): - if rules is None or "data" not in rules: + if rules is None or not rules: return None - ids = list(map(lambda rule: rule["id"], rules["data"])) + ids = [rule["id"] for rule in rules] payload = {"delete": {"ids": ids}} - response = requests.post( - "https://api.x.com/2/tweets/search/stream/rules", - auth=bearer_oauth, - json=payload - ) - if response.status_code != 200: - raise Exception( - "Cannot delete rules (HTTP {}): {}".format( - response.status_code, response.text - ) - ) - print(json.dumps(response.json())) - - -def set_rules(delete): + response = client.streams.delete_rules(body=payload) + print(json.dumps(response.data, indent=4, sort_keys=True)) + + +def set_rules(): # You can adjust the rules if needed sample_rules = [ {"value": "dog has:images", "tag": "dog pictures"}, {"value": "cat has:images -grumpy", "tag": "cat pictures"}, ] payload = {"add": sample_rules} - response = requests.post( - "https://api.x.com/2/tweets/search/stream/rules", - auth=bearer_oauth, - json=payload, - ) - if response.status_code != 201: - raise Exception( - "Cannot add rules (HTTP {}): {}".format(response.status_code, response.text) - ) - print(json.dumps(response.json())) - - -def get_stream(set): - response = requests.get( - "https://api.x.com/2/tweets/search/stream", auth=bearer_oauth, stream=True, - ) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Cannot get stream (HTTP {}): {}".format( - response.status_code, response.text - ) - ) - for response_line in response.iter_lines(): - if response_line: - json_response = json.loads(response_line) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.streams.add_rules(body=payload) + print(json.dumps(response.data, indent=4, sort_keys=True)) + + +def get_stream(): + # Stream posts matching the filter rules in real-time + for post in client.streams.get_filtered_stream(): + print(json.dumps(post.data, indent=4, sort_keys=True)) def main(): rules = get_rules() - delete = delete_all_rules(rules) - set = set_rules(delete) - get_stream(set) + delete_all_rules(rules) + set_rules() + get_stream() if __name__ == "__main__": diff --git a/python/streams/sampled-stream.py b/python/streams/sampled-stream.py deleted file mode 100644 index ef0eb9d..0000000 --- a/python/streams/sampled-stream.py +++ /dev/null @@ -1,48 +0,0 @@ -import requests -import os -import json - -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(): - return "https://api.x.com/2/tweets/sample/stream" - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2SampledStreamPython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth, stream=True) - print(response.status_code) - for response_line in response.iter_lines(): - if response_line: - json_response = json.loads(response_line) - print(json.dumps(json_response, indent=4, sort_keys=True)) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - - -def main(): - url = create_url() - timeout = 0 - while True: - connect_to_endpoint(url) - timeout += 1 - - -if __name__ == "__main__": - main() diff --git a/python/streams/sampled_stream.py b/python/streams/sampled_stream.py index 456b31b..98c2c70 100644 --- a/python/streams/sampled_stream.py +++ b/python/streams/sampled_stream.py @@ -10,42 +10,17 @@ Note: Returns approximately 1% of all public posts in real-time. """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2SampledStreamPython" - return r - - -def get_stream(): - response = requests.get( - "https://api.x.com/2/tweets/sample/stream", auth=bearer_oauth, stream=True, - ) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Cannot get stream (HTTP {}): {}".format( - response.status_code, response.text - ) - ) - for response_line in response.iter_lines(): - if response_line: - json_response = json.loads(response_line) - print(json.dumps(json_response, indent=4, sort_keys=True)) - +client = Client(bearer_token=bearer_token) def main(): - get_stream() - + # Stream posts in real-time + for post in client.streams.get_sampled_stream(): + print(json.dumps(post.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/timelines/home_timeline.py b/python/timelines/home_timeline.py index f8a8d23..d3782bf 100644 --- a/python/timelines/home_timeline.py +++ b/python/timelines/home_timeline.py @@ -1,83 +1,75 @@ """ Reverse Chronological Home Timeline - X API v2 ============================================== -Endpoint: GET https://api.x.com/2/users/:id/reverse_chronological_timeline +Endpoint: GET https://api.x.com/2/users/:id/timelines/reverse_chronological Docs: https://developer.x.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-reverse-chronological-timeline -Authentication: OAuth 1.0a or OAuth 2.0 (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET """ -from requests_oauthlib import OAuth1Session import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# Post fields are adjustable. Options include: -# attachments, author_id, context_annotations, conversation_id, -# created_at, entities, geo, id, in_reply_to_user_id, lang, -# possibly_sensitive, public_metrics, referenced_tweets, source, text -params = {"tweet.fields": "created_at"} +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access"] -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get authenticated user ID + me_response = client.users.get_me() + user_id = me_response.data["id"] + + # Step 7: Get reverse chronological timeline with automatic pagination + # Post fields are adjustable. Options include: + # attachments, author_id, context_annotations, conversation_id, + # created_at, entities, geo, id, in_reply_to_user_id, lang, + # possibly_sensitive, public_metrics, referenced_tweets, source, text + all_posts = [] + for page in client.users.get_reverse_chronological_timeline( + user_id, + max_results=100, + tweetfields=["created_at"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] -user_id = oauth_tokens["user_id"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.get( - "https://api.x.com/2/users/{}/reverse_chronological_timeline".format(user_id), - params=params -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/timelines/reverse-chron-home-timeline.py b/python/timelines/reverse-chron-home-timeline.py index 7b09c66..b4585ee 100644 --- a/python/timelines/reverse-chron-home-timeline.py +++ b/python/timelines/reverse-chron-home-timeline.py @@ -1,112 +1,70 @@ -import base64 -import hashlib +""" +Reverse Chronological Home Timeline - X API v2 +============================================== +Endpoint: GET https://api.x.com/2/users/:id/timelines/reverse_chronological +Docs: https://developer.x.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-reverse-chronological-timeline + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os -import re import json -import requests -from requests.auth import AuthBase, HTTPBasicAuth -from requests_oauthlib import OAuth2Session +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# First, you will need to enable OAuth 2.0 in your App’s auth settings in the Developer Portal to get your client ID. -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_ID='your-client-id' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' client_id = os.environ.get("CLIENT_ID") - -# If you have selected a type of App that is a confidential client you will need to set a client secret. -# Confidential Clients securely authenticate with the authorization server. - -# Inside your terminal you will need to set an enviornment variable -# export CLIENT_SECRET='your-client-secret' - -# Remove the comment on the following line if you are using a confidential client -# client_secret = os.environ.get("CLIENT_SECRET") +client_secret = os.environ.get("CLIENT_SECRET") # Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://www.example.com" +redirect_uri = "https://example.com" # Set the scopes scopes = ["tweet.read", "users.read", "offline.access"] -# Create a code verifier -code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") -code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) - -# Create a code challenge -code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() -code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") -code_challenge = code_challenge.replace("=", "") - -# Start an OAuth 2.0 session -oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes) - -# Create an authorize URL -auth_url = "https://twitter.com/i/oauth2/authorize" -authorization_url, state = oauth.authorization_url( - auth_url, code_challenge=code_challenge, code_challenge_method="S256" -) - -# Visit the URL to authorize your App to make requests on behalf of a user -print( - "Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:" -) -print(authorization_url) - -# Paste in your authorize URL to complete the request -authorization_response = input( - "Paste in the full URL after you've authorized your App:\n" -) - -# Fetch your access token -token_url = "https://api.x.com/2/oauth2/token" - -# The following line of code will only work if you are using a type of App that is a public client -auth = False - -# If you are using a confidential client you will need to pass in basic encoding of your client ID and client secret. - -# Please remove the comment on the following line if you are using a type of App that is a confidential client -# auth = HTTPBasicAuth(client_id, client_secret) - -token = oauth.fetch_token( - token_url=token_url, - authorization_response=authorization_response, - auth=auth, - client_id=client_id, - include_client_id=True, - code_verifier=code_verifier, -) - -# Your access token -access = token["access_token"] - -# Make a request to the users/me endpoint to get your user ID -user_me = requests.request( - "GET", - "https://api.x.com/2/users/me", - headers={"Authorization": "Bearer {}".format(access)}, -).json() - -# -# -# Now that we have user authorization, let's look up their home timeline. -# -# - -# Set the user. This defaults to the ID of the authorizing user. -user_id = user_me["data"]["id"] - -# Set the url. -url = "https://api.x.com/2/users/{}/timelines/reverse_chronological".format(user_id) - -headers = { - "Authorization": "Bearer {}".format(access), - "User-Agent": "ReverseChronSampleCode", -} -response = requests.request("GET", url, headers=headers) -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) -print("Response code: {}".format(response.status_code)) -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get authenticated user ID + me_response = client.users.get_me() + user_id = me_response.data["id"] + + # Step 7: Get reverse chronological timeline with automatic pagination + all_posts = [] + for page in client.users.get_reverse_chronological_timeline( + user_id, + max_results=100 + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + +if __name__ == "__main__": + main() diff --git a/python/timelines/user_mentions.py b/python/timelines/user_mentions.py index 4b9aca8..d47a217 100644 --- a/python/timelines/user_mentions.py +++ b/python/timelines/user_mentions.py @@ -1,19 +1,25 @@ -import requests +""" +User Mentions Timeline - X API v2 +================================== +Endpoint: GET https://api.x.com/2/users/:id/mentions +Docs: https://developer.x.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-mentions + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Replace with user ID below +user_id = "2244994945" -def create_url(): - # Replace with user ID below - user_id = 2244994945 - return "https://api.x.com/2/users/{}/mentions".format(user_id) - - -def get_params(): +def main(): + # Get user mentions with automatic pagination # Tweet fields are adjustable. # Options include: # attachments, author_id, context_annotations, @@ -21,37 +27,17 @@ def get_params(): # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - return {"tweet.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2UserMentionsPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_posts = [] + for page in client.users.get_mentions( + user_id, + max_results=100, + tweetfields=["created_at"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Mentions: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/timelines/user_posts.py b/python/timelines/user_posts.py index a691277..9726b2c 100644 --- a/python/timelines/user_posts.py +++ b/python/timelines/user_posts.py @@ -8,50 +8,33 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Replace with the user ID you want to get posts for +user_id = "2244994945" -def create_url(): - # Replace with the user ID you want to get posts for - user_id = "2244994945" - return "https://api.x.com/2/users/{}/tweets".format(user_id) - - -def get_params(): +def main(): + # Get user posts with automatic pagination # Post fields are adjustable. Options include: # attachments, author_id, context_annotations, conversation_id, # created_at, entities, geo, id, in_reply_to_user_id, lang, # possibly_sensitive, public_metrics, referenced_tweets, source, text - return {"tweet.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2UserPostsPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - - -def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_posts = [] + for page in client.users.get_tweets( + user_id, + max_results=100, + tweetfields=["created_at"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/timelines/user_tweets.py b/python/timelines/user_tweets.py index 8f547ea..05a4397 100644 --- a/python/timelines/user_tweets.py +++ b/python/timelines/user_tweets.py @@ -1,19 +1,25 @@ -import requests +""" +User Tweets Timeline - X API v2 +=============================== +Endpoint: GET https://api.x.com/2/users/:id/tweets +Docs: https://developer.x.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-tweets + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Replace with user ID below +user_id = "2244994945" -def create_url(): - # Replace with user ID below - user_id = 2244994945 - return "https://api.x.com/2/users/{}/tweets".format(user_id) - - -def get_params(): +def main(): + # Get user tweets with automatic pagination # Tweet fields are adjustable. # Options include: # attachments, author_id, context_annotations, @@ -21,37 +27,17 @@ def get_params(): # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - return {"tweet.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2UserTweetsPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + all_posts = [] + for page in client.users.get_tweets( + user_id, + max_results=100, + tweetfields=["created_at"] + ): + all_posts.extend(page.data) + print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + + print(f"\nTotal Posts: {len(all_posts)}") + print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/usage/get_usage.py b/python/usage/get_usage.py index 7042699..7eec457 100644 --- a/python/usage/get_usage.py +++ b/python/usage/get_usage.py @@ -10,44 +10,17 @@ Returns the number of posts read from the API. """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") - - -def create_url(): - return "https://api.x.com/2/usage/tweets" - - -def get_params(): - return {"days": 7} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2UsagePython" - return r - - -def connect_to_endpoint(url, params): - response = requests.get(url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +client = Client(bearer_token=bearer_token) def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.usage.get_tweets_usage(days=7) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/usage/get_usage_tweets.py b/python/usage/get_usage_tweets.py index 23ad0d8..9da6155 100644 --- a/python/usage/get_usage_tweets.py +++ b/python/usage/get_usage_tweets.py @@ -1,38 +1,26 @@ -import requests -import os -import json - -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' -bearer_token = os.environ.get("BEARER_TOKEN") - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ +""" +Usage Tweets - X API v2 +======================= +Endpoint: GET https://api.x.com/2/usage/tweets +Docs: https://developer.x.com/en/docs/twitter-api/usage/api-reference/get-usage-tweets - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2UsageTweetsPython" - return r +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN +Returns the number of posts read from the API. +""" -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() +import os +import json +from xdk import Client +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) def main(): - url = "https://api.x.com/2/usage/tweets" - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + response = client.usage.get_tweets_usage() + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/users/block.py b/python/users/block.py deleted file mode 100644 index 77da5db..0000000 --- a/python/users/block.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Block User - X API v2 -===================== -Endpoint: POST https://api.x.com/2/users/:id/blocking -Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/post-users-user_id-blocking - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID -user_id = "your-user-id" - -# Replace with the user ID you want to block -payload = {"target_user_id": "target-user-id"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/blocking".format(user_id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/users/block_a_user.py b/python/users/block_a_user.py index 2523aa6..87ac115 100644 --- a/python/users/block_a_user.py +++ b/python/users/block_a_user.py @@ -1,77 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Block User - X API v2 +===================== +Endpoint: POST https://api.x.com/2/users/:id/blocking +Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/post-users-user_id-blocking + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "tweet.write", "offline.access", "block.write"] -# Be sure to replace your-user-id with your own user ID or one of an authenticating user +# Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" - -# Be sure to add replace id-to-block with the user id you wish to block. -payload = {"target_user_id": "id-to-block"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +user_id = "your-user-id" + +# Be sure to replace id-to-block with the user id you wish to block. +target_user_id = "id-to-block" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/blocking".format(id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Block the user + payload = {"target_user_id": target_user_id} + response = client.users.block_user(user_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/users/blocked.py b/python/users/blocked.py index 7bb4a0c..edaf9ac 100644 --- a/python/users/blocked.py +++ b/python/users/blocked.py @@ -4,81 +4,71 @@ Endpoint: GET https://api.x.com/2/users/:id/blocking Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/get-users-blocking -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET """ -from requests_oauthlib import OAuth1Session import os import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# User fields are adjustable. Options include: -# created_at, description, entities, id, location, name, -# pinned_tweet_id, profile_image_url, protected, -# public_metrics, url, username, verified, and withheld -fields = "user.fields=created_at,description" -params = {"user.fields": fields} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") + +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" + +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access", "block.read"] + +# Replace with your own user ID +user_id = "your-user-id" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -user_id = oauth_tokens["user_id"] - -# Making the request -response = oauth.get( - "https://api.x.com/2/users/{}/blocking".format(user_id), params=params -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get blocked users with automatic pagination + # User fields are adjustable. Options include: + # created_at, description, entities, id, location, name, + # pinned_tweet_id, profile_image_url, protected, + # public_metrics, url, username, verified, and withheld + all_users = [] + for page in client.users.get_blocking( + user_id, + max_results=100, + userfields=["created_at", "description"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Blocked Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + +if __name__ == "__main__": + main() diff --git a/python/users/followers.py b/python/users/followers.py index 35f87de..216a12a 100644 --- a/python/users/followers.py +++ b/python/users/followers.py @@ -8,46 +8,29 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def create_url(): - # Replace with the user ID you want to get followers for - user_id = "2244994945" - return "https://api.x.com/2/users/{}/followers".format(user_id) - - -def get_params(): - return {"user.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FollowersLookupPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +# Replace with the user ID you want to get followers for +user_id = "2244994945" def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + # Get followers with automatic pagination + all_users = [] + for page in client.users.get_followers( + user_id, + max_results=100, + userfields=["created_at"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Followers: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/users/followers_lookup.py b/python/users/followers_lookup.py index ea51170..ab2bf4a 100644 --- a/python/users/followers_lookup.py +++ b/python/users/followers_lookup.py @@ -1,50 +1,36 @@ -import requests +""" +User Followers Lookup - X API v2 +================================ +Endpoint: GET https://api.x.com/2/users/:id/followers +Docs: https://developer.x.com/en/docs/twitter-api/users/follows/api-reference/get-users-id-followers + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def create_url(): - # Replace with user ID below - user_id = 2244994945 - return "https://api.x.com/2/users/{}/followers".format(user_id) - - -def get_params(): - return {"user.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FollowersLookupPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - +# Replace with user ID below +user_id = "2244994945" def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + # Get followers with automatic pagination + all_users = [] + for page in client.users.get_followers( + user_id, + max_results=100, + userfields=["created_at"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Followers: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/users/following.py b/python/users/following.py index dba3fb3..546a37c 100644 --- a/python/users/following.py +++ b/python/users/following.py @@ -8,46 +8,29 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def create_url(): - # Replace with the user ID you want to get following for - user_id = "2244994945" - return "https://api.x.com/2/users/{}/following".format(user_id) - - -def get_params(): - return {"user.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FollowingLookupPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception(response.status_code, response.text) - return response.json() - +# Replace with the user ID you want to get following for +user_id = "2244994945" def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + # Get following with automatic pagination + all_users = [] + for page in client.users.get_following( + user_id, + max_results=100, + userfields=["created_at"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Following: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/users/following_lookup.py b/python/users/following_lookup.py index be136e5..7054f25 100644 --- a/python/users/following_lookup.py +++ b/python/users/following_lookup.py @@ -1,50 +1,36 @@ -import requests +""" +User Following Lookup - X API v2 +================================ +Endpoint: GET https://api.x.com/2/users/:id/following +Docs: https://developer.x.com/en/docs/twitter-api/users/follows/api-reference/get-users-id-following + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your environment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) - -def create_url(): - # Replace with user ID below - user_id = 2244994945 - return "https://api.x.com/2/users/{}/following".format(user_id) - - -def get_params(): - return {"user.fields": "created_at"} - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2FollowingLookupPython" - return r - - -def connect_to_endpoint(url, params): - response = requests.request("GET", url, auth=bearer_oauth, params=params) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - +# Replace with user ID below +user_id = "2244994945" def main(): - url = create_url() - params = get_params() - json_response = connect_to_endpoint(url, params) - print(json.dumps(json_response, indent=4, sort_keys=True)) - + # Get following with automatic pagination + all_users = [] + for page in client.users.get_following( + user_id, + max_results=100, + userfields=["created_at"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Following: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": main() diff --git a/python/users/get_users_me_user_context.py b/python/users/get_users_me_user_context.py index 95cdafa..7308d94 100644 --- a/python/users/get_users_me_user_context.py +++ b/python/users/get_users_me_user_context.py @@ -1,73 +1,65 @@ -from requests_oauthlib import OAuth1Session +""" +Authenticated User Lookup (Me) - X API v2 +========================================= +Endpoint: GET https://api.x.com/2/users/me +Docs: https://developer.x.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# User fields are adjustable, options include: -# created_at, description, entities, id, location, name, -# pinned_tweet_id, profile_image_url, protected, -# public_metrics, url, username, verified, and withheld -fields = "created_at,description" -params = {"user.fields": fields} +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access"] -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# # Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -response = oauth.get("https://api.x.com/2/users/me", params=params) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get authenticated user info + # User fields are adjustable, options include: + # created_at, description, entities, id, location, name, + # pinned_tweet_id, profile_image_url, protected, + # public_metrics, url, username, verified, and withheld + response = client.users.get_me( + userfields=["created_at", "description"] ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) -print("Response code: {}".format(response.status_code)) - -json_response = response.json() - -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/users/get_users_with_bearer_token.py b/python/users/get_users_with_bearer_token.py index 4bc7c91..8144035 100644 --- a/python/users/get_users_with_bearer_token.py +++ b/python/users/get_users_with_bearer_token.py @@ -1,51 +1,34 @@ -import requests +""" +User Lookup - X API v2 +====================== +Endpoint: GET https://api.x.com/2/users/by +Docs: https://developer.x.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by + +Authentication: Bearer Token (App-only) or OAuth (User Context) +Required env vars: BEARER_TOKEN +""" + import os import json +from xdk import Client -# To set your enviornment variables in your terminal run the following line: -# export 'BEARER_TOKEN'='' bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Specify the usernames that you want to lookup (list, up to 100) +usernames = ["TwitterDev", "TwitterAPI"] -def create_url(): - # Specify the usernames that you want to lookup below - # You can enter up to 100 comma-separated values. - usernames = "usernames=TwitterDev,TwitterAPI" - user_fields = "user.fields=description,created_at" +def main(): # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - url = "https://api.x.com/2/users/by?{}&{}".format(usernames, user_fields) - return url - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2UserLookupPython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth,) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url = create_url() - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.users.get_users_by( + usernames=usernames, + userfields=["description", "created_at"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/users/get_users_with_user_context.py b/python/users/get_users_with_user_context.py index 295e285..94be02f 100644 --- a/python/users/get_users_with_user_context.py +++ b/python/users/get_users_with_user_context.py @@ -1,75 +1,69 @@ -from requests_oauthlib import OAuth1Session +""" +User Lookup (User Context) - X API v2 +===================================== +Endpoint: GET https://api.x.com/2/users/by +Docs: https://developer.x.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# User fields are adjustable, options include: -# created_at, description, entities, id, location, name, -# pinned_tweet_id, profile_image_url, protected, -# public_metrics, url, username, verified, and withheld -fields = "created_at,description" -params = {"usernames": "TwitterDev,TwitterAPI", "user.fields": fields} +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access"] -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +# Specify the usernames that you want to lookup (list, up to 100) +usernames = ["TwitterDev", "TwitterAPI"] -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# # Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -response = oauth.get( - "https://api.x.com/2/users/by", params=params -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get users + # User fields are adjustable, options include: + # created_at, description, entities, id, location, name, + # pinned_tweet_id, profile_image_url, protected, + # public_metrics, url, username, verified, and withheld + response = client.users.get_users_by( + usernames=usernames, + userfields=["created_at", "description"] ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) -print("Response code: {}".format(response.status_code)) - -json_response = response.json() - -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/users/lookup.py b/python/users/lookup.py index fb984e6..7fe48c6 100644 --- a/python/users/lookup.py +++ b/python/users/lookup.py @@ -8,51 +8,27 @@ Required env vars: BEARER_TOKEN """ -import requests import os import json +from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) +# Specify the usernames to lookup (list, up to 100) +usernames = ["XDevelopers", "X"] -def create_url(): - # Specify the usernames to lookup (up to 100 comma-separated) - usernames = "usernames=XDevelopers,X" - +def main(): # User fields are adjustable. Options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - user_fields = "user.fields=description,created_at" - url = "https://api.x.com/2/users/by?{}&{}".format(usernames, user_fields) - return url - - -def bearer_oauth(r): - """ - Method required by bearer token authentication. - """ - r.headers["Authorization"] = f"Bearer {bearer_token}" - r.headers["User-Agent"] = "v2UserLookupPython" - return r - - -def connect_to_endpoint(url): - response = requests.request("GET", url, auth=bearer_oauth) - print(response.status_code) - if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text - ) - ) - return response.json() - - -def main(): - url = create_url() - json_response = connect_to_endpoint(url) - print(json.dumps(json_response, indent=4, sort_keys=True)) + response = client.users.get_users_by( + usernames=usernames, + userfields=["description", "created_at"] + ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/users/lookup_blocks.py b/python/users/lookup_blocks.py index 623c13a..fea3ec8 100644 --- a/python/users/lookup_blocks.py +++ b/python/users/lookup_blocks.py @@ -1,77 +1,75 @@ -from requests_oauthlib import OAuth1Session -import os -import json - -# To set your enviornment variables in your terminal run the following line: -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Be sure to replace your-user-id with your own user ID or one of an authenticating user -# You can find a user ID by using the user lookup endpoint -id = "your-user-id" - -params = {"user.fields": "created_at,description"} -# User fields are adjustable, options include: -# created_at, description, entities, id, location, name, -# pinned_tweet_id, profile_image_url, protected, -# public_metrics, url, username, verified, and withheld +""" +Blocked Users Lookup - X API v2 +=============================== +Endpoint: GET https://api.x.com/2/users/:id/blocking +Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/get-users-blocking -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access", "block.read"] -response = oauth.get( - "https://api.x.com/2/users/{}/blocking".format(id), params=params -) +# Be sure to replace your-user-id with your own user ID or one of an authenticated user +# You can find a user ID by using the user lookup endpoint +user_id = "your-user-id" -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get blocked users with automatic pagination + # User fields are adjustable, options include: + # created_at, description, entities, id, location, name, + # pinned_tweet_id, profile_image_url, protected, + # public_metrics, url, username, verified, and withheld + all_users = [] + for page in client.users.get_blocking( + user_id, + max_results=100, + userfields=["created_at", "description"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Blocked Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example -print("Response code: {}".format(response.status_code)) -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/users/lookup_mutes.py b/python/users/lookup_mutes.py index 298bb58..2ea8c7e 100644 --- a/python/users/lookup_mutes.py +++ b/python/users/lookup_mutes.py @@ -1,79 +1,75 @@ -from requests_oauthlib import OAuth1Session -import os -import json - -# To set your enviornment variables in your terminal run the following line: -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Be sure to replace your-user-id with your own user ID or one of an authenticating user -# You can find a user ID by using the user lookup endpoint -id = "your-user-id" - -params = {"user.fields": "created_at,description"} - - -# User fields are adjustable, options include: -# created_at, description, entities, id, location, name, -# pinned_tweet_id, profile_image_url, protected, -# public_metrics, url, username, verified, and withheld +""" +Muted Users Lookup - X API v2 +============================= +Endpoint: GET https://api.x.com/2/users/:id/muting +Docs: https://developer.x.com/en/docs/twitter-api/users/mutes/api-reference/get-users-muting -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" +import os +import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -response = oauth.get( - "https://api.x.com/2/users/{}/muting".format(id),params=params -) +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access", "mute.read"] -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format( - response.status_code, response.text) +# Be sure to replace your-user-id with your own user ID or one of an authenticated user +# You can find a user ID by using the user lookup endpoint +user_id = "your-user-id" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) \ No newline at end of file + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get muted users with automatic pagination + # User fields are adjustable, options include: + # created_at, description, entities, id, location, name, + # pinned_tweet_id, profile_image_url, protected, + # public_metrics, url, username, verified, and withheld + all_users = [] + for page in client.users.get_muting( + user_id, + max_results=100, + userfields=["created_at", "description"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Muted Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/python/users/me.py b/python/users/me.py index 819ea46..1de488b 100644 --- a/python/users/me.py +++ b/python/users/me.py @@ -4,77 +4,62 @@ Endpoint: GET https://api.x.com/2/users/me Docs: https://developer.x.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me -Authentication: OAuth 1.0a or OAuth 2.0 (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET """ -from requests_oauthlib import OAuth1Session import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -# User fields are adjustable. Options include: -# created_at, description, entities, id, location, name, -# pinned_tweet_id, profile_image_url, protected, -# public_metrics, url, username, verified, and withheld -fields = "user.fields=description,created_at" -params = {"user.fields": fields} +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access"] -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.get("https://api.x.com/2/users/me", params=params) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get authenticated user info + # User fields are adjustable. Options include: + # created_at, description, entities, id, location, name, + # pinned_tweet_id, profile_image_url, protected, + # public_metrics, url, username, verified, and withheld + response = client.users.get_me( + userfields=["description", "created_at"] ) + + print(json.dumps(response.data, indent=4, sort_keys=True)) -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) +if __name__ == "__main__": + main() diff --git a/python/users/mute.py b/python/users/mute.py deleted file mode 100644 index ce37e70..0000000 --- a/python/users/mute.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Mute User - X API v2 -==================== -Endpoint: POST https://api.x.com/2/users/:id/muting -Docs: https://developer.x.com/en/docs/twitter-api/users/mutes/api-reference/post-users-user_id-muting - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID -user_id = "your-user-id" - -# Replace with the user ID you want to mute -payload = {"target_user_id": "target-user-id"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/muting".format(user_id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/users/mute_a_user.py b/python/users/mute_a_user.py index 7a664a7..34fc956 100644 --- a/python/users/mute_a_user.py +++ b/python/users/mute_a_user.py @@ -1,77 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Mute User - X API v2 +==================== +Endpoint: POST https://api.x.com/2/users/:id/muting +Docs: https://developer.x.com/en/docs/twitter-api/users/mutes/api-reference/post-users-user_id-muting + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "tweet.write", "offline.access", "mute.write"] -# Be sure to replace your-user-id with your own user ID or one of an authenticating user +# Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -id = "your-user-id" +user_id = "your-user-id" # Be sure to replace id-to-mute with the user id you wish to mute. -payload = {"target_user_id": "id-to-mute"} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.post( - "https://api.x.com/2/users/{}/muting".format(id), json=payload -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +target_user_id = "id-to-mute" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Mute the user + payload = {"target_user_id": target_user_id} + response = client.users.mute_user(user_id, body=payload) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() diff --git a/python/users/muted.py b/python/users/muted.py index 01aaa29..1bf64db 100644 --- a/python/users/muted.py +++ b/python/users/muted.py @@ -4,81 +4,71 @@ Endpoint: GET https://api.x.com/2/users/:id/muting Docs: https://developer.x.com/en/docs/twitter-api/users/mutes/api-reference/get-users-muting -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET """ -from requests_oauthlib import OAuth1Session import os import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# User fields are adjustable. Options include: -# created_at, description, entities, id, location, name, -# pinned_tweet_id, profile_image_url, protected, -# public_metrics, url, username, verified, and withheld -fields = "user.fields=created_at,description" -params = {"user.fields": fields} - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth + +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") + +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" + +# Set the scopes +scopes = ["tweet.read", "users.read", "offline.access", "mute.read"] + +# Replace with your own user ID +user_id = "your-user-id" + +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -user_id = oauth_tokens["user_id"] - -# Making the request -response = oauth.get( - "https://api.x.com/2/users/{}/muting".format(user_id), params=params -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Get muted users with automatic pagination + # User fields are adjustable. Options include: + # created_at, description, entities, id, location, name, + # pinned_tweet_id, profile_image_url, protected, + # public_metrics, url, username, verified, and withheld + all_users = [] + for page in client.users.get_muting( + user_id, + max_results=100, + userfields=["created_at", "description"] + ): + all_users.extend(page.data) + print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + + print(f"\nTotal Muted Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + +if __name__ == "__main__": + main() diff --git a/python/users/unblock.py b/python/users/unblock.py deleted file mode 100644 index be374b8..0000000 --- a/python/users/unblock.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Unblock User - X API v2 -======================= -Endpoint: DELETE https://api.x.com/2/users/:source_user_id/blocking/:target_user_id -Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/delete-users-user_id-blocking - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID -user_id = "your-user-id" - -# Replace with the user ID you want to unblock -target_user_id = "target-user-id" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/blocking/{}".format(user_id, target_user_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/users/unblock_a_user.py b/python/users/unblock_a_user.py index 62ea052..21fe778 100644 --- a/python/users/unblock_a_user.py +++ b/python/users/unblock_a_user.py @@ -1,81 +1,70 @@ -from requests_oauthlib import OAuth1Session +""" +Unblock User - X API v2 +======================= +Endpoint: DELETE https://api.x.com/2/users/:source_user_id/blocking/:target_user_id +Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/delete-users-user_id-blocking + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "tweet.write", "offline.access", "block.write"] # Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -source_user_id = "your-user-id" +user_id = "your-user-id" -# Be sure to add replace id-to-unblock with the id of the user you wish to unblock. +# Be sure to replace id-to-unblock with the id of the user you wish to unblock. # You can find a user ID by using the user lookup endpoint target_user_id = "id-to-unblock" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/blocking/{}".format(id, target_user_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) - -# Note: If you were following the user you blocked, you have removed your follow and will have to refollow the user, even if you did unblock the user. + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Unblock the user + response = client.users.unblock_user(user_id, target_user_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + + # Note: If you were following the user you blocked, you have removed your follow and will have to refollow the user, even if you did unblock the user. + +if __name__ == "__main__": + main() diff --git a/python/users/unmute.py b/python/users/unmute.py deleted file mode 100644 index b1d607f..0000000 --- a/python/users/unmute.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Unmute User - X API v2 -====================== -Endpoint: DELETE https://api.x.com/2/users/:source_user_id/muting/:target_user_id -Docs: https://developer.x.com/en/docs/twitter-api/users/mutes/api-reference/delete-users-user_id-muting - -Authentication: OAuth 1.0a (User Context) -Required env vars: CONSUMER_KEY, CONSUMER_SECRET -""" - -from requests_oauthlib import OAuth1Session -import os -import json - -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") - -# Replace with your own user ID -user_id = "your-user-id" - -# Replace with the user ID you want to unmute -target_user_id = "target-user-id" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/muting/{}".format(user_id, target_user_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) - ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) diff --git a/python/users/unmute_a_user.py b/python/users/unmute_a_user.py index 268e545..5060013 100644 --- a/python/users/unmute_a_user.py +++ b/python/users/unmute_a_user.py @@ -1,79 +1,68 @@ -from requests_oauthlib import OAuth1Session +""" +Unmute User - X API v2 +====================== +Endpoint: DELETE https://api.x.com/2/users/:source_user_id/muting/:target_user_id +Docs: https://developer.x.com/en/docs/twitter-api/users/mutes/api-reference/delete-users-user_id-muting + +Authentication: OAuth 2.0 (User Context) +Required env vars: CLIENT_ID, CLIENT_SECRET +""" + import os import json +from xdk import Client +from xdk.oauth2_auth import OAuth2PKCEAuth -# In your terminal please set your environment variables by running the following lines of code. -# export 'CONSUMER_KEY'='' -# export 'CONSUMER_SECRET'='' +# The code below sets the client ID and client secret from your environment variables +# To set environment variables on macOS or Linux, run the export commands below from the terminal: +# export CLIENT_ID='YOUR-CLIENT-ID' +# export CLIENT_SECRET='YOUR-CLIENT-SECRET' +client_id = os.environ.get("CLIENT_ID") +client_secret = os.environ.get("CLIENT_SECRET") -consumer_key = os.environ.get("CONSUMER_KEY") -consumer_secret = os.environ.get("CONSUMER_SECRET") +# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. +redirect_uri = "https://example.com" +# Set the scopes +scopes = ["tweet.read", "users.read", "tweet.write", "offline.access", "mute.write"] # Be sure to replace your-user-id with your own user ID or one of an authenticated user # You can find a user ID by using the user lookup endpoint -source_user_id = "your-user-id" +user_id = "your-user-id" -# Be sure to add replace id-to-unmute with the id of the user you wish to unmute. +# Be sure to replace id-to-unmute with the id of the user you wish to unmute. # You can find a user ID by using the user lookup endpoint target_user_id = "id-to-unmute" - -# Get request token -request_token_url = "https://api.x.com/oauth/request_token" -oauth = OAuth1Session(consumer_key, client_secret=consumer_secret) - -try: - fetch_response = oauth.fetch_request_token(request_token_url) -except ValueError: - print( - "There may have been an issue with the consumer_key or consumer_secret you entered." - ) - -resource_owner_key = fetch_response.get("oauth_token") -resource_owner_secret = fetch_response.get("oauth_token_secret") -print("Got OAuth token: %s" % resource_owner_key) - -# Get authorization -base_authorization_url = "https://api.x.com/oauth/authorize" -authorization_url = oauth.authorization_url(base_authorization_url) -print("Please go here and authorize: %s" % authorization_url) -verifier = input("Paste the PIN here: ") - -# Get the access token -access_token_url = "https://api.x.com/oauth/access_token" -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=resource_owner_key, - resource_owner_secret=resource_owner_secret, - verifier=verifier, -) -oauth_tokens = oauth.fetch_access_token(access_token_url) - -access_token = oauth_tokens["oauth_token"] -access_token_secret = oauth_tokens["oauth_token_secret"] - -# Make the request -oauth = OAuth1Session( - consumer_key, - client_secret=consumer_secret, - resource_owner_key=access_token, - resource_owner_secret=access_token_secret, -) - -# Making the request -response = oauth.delete( - "https://api.x.com/2/users/{}/muting/{}".format(id, target_user_id) -) - -if response.status_code != 200: - raise Exception( - "Request returned an error: {} {}".format(response.status_code, response.text) +def main(): + # Step 1: Create PKCE instance + auth = OAuth2PKCEAuth( + client_id=client_id, + client_secret=client_secret, + redirect_uri=redirect_uri, + scope=scopes ) - -print("Response code: {}".format(response.status_code)) - -# Saving the response as JSON -json_response = response.json() -print(json.dumps(json_response, indent=4, sort_keys=True)) + + # Step 2: Get authorization URL + auth_url = auth.get_authorization_url() + print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") + print(auth_url) + + # Step 3: Handle callback + callback_url = input("Paste the full callback URL here: ") + + # Step 4: Exchange code for tokens + tokens = auth.fetch_token(authorization_response=callback_url) + access_token = tokens["access_token"] + + # Step 5: Create client + client = Client(access_token=access_token) + + # Step 6: Unmute the user + response = client.users.unmute_user(user_id, target_user_id) + + print("Response code: 200") + print(json.dumps(response.data, indent=4, sort_keys=True)) + +if __name__ == "__main__": + main() From 9f4cc148017141142c8edf3458aa928991ccf238 Mon Sep 17 00:00:00 2001 From: Taylor Caldwell Date: Fri, 5 Dec 2025 19:12:41 -0800 Subject: [PATCH 2/2] Update samples --- python/README.md | 58 +++++--------- .../{add_member.py => add_list_member.py} | 0 .../{create_a_list.py => create_list.py} | 2 +- .../{delete_a_list.py => delete_list.py} | 0 ...list-followed.py => get_followed_lists.py} | 9 ++- ...list-lookup-by-id.py => get_list_by_id.py} | 12 +-- ...lowers-lookup.py => get_list_followers.py} | 15 +++- ...t-member-lookup.py => get_list_members.py} | 9 ++- ...memberships.py => get_list_memberships.py} | 9 ++- .../{List-Tweets.py => get_list_posts.py} | 11 ++- ...wned-list-lookup.py => get_owned_lists.py} | 9 ++- .../{Pinned-List.py => get_pinned_lists.py} | 23 +++--- python/lists/lookup.py | 32 -------- ...remove_member.py => remove_list_member.py} | 0 .../{update_a_list.py => update_list.py} | 4 +- python/posts/counts_full_archive.py | 32 -------- .../posts/{create_tweet.py => create_post.py} | 2 +- .../posts/{delete_tweet.py => delete_post.py} | 5 +- python/posts/full_archive_tweet_counts.py | 33 -------- .../{liked_tweets.py => get_liked_posts.py} | 21 ++--- .../{liking_users.py => get_liking_users.py} | 12 ++- python/posts/get_post_counts_all.py | 43 ++++++++++ ...ts_recent.py => get_post_counts_recent.py} | 14 +++- .../posts/{lookup.py => get_posts_by_ids.py} | 7 +- .../{quote_tweets.py => get_quoted_posts.py} | 2 +- .../{retweeted_by.py => get_resposted_by.py} | 11 ++- python/posts/get_tweets_with_bearer_token.py | 38 --------- python/posts/get_tweets_with_user_context.py | 73 ----------------- .../posts/{like_a_tweet.py => like_post.py} | 14 ++-- python/posts/recent_search.py | 40 ---------- python/posts/recent_tweet_counts.py | 32 -------- .../{retweet_a_tweet.py => repost_post.py} | 14 ++-- python/posts/reposted_by.py | 36 --------- .../{full-archive-search.py => search_all.py} | 11 ++- python/posts/search_full_archive.py | 40 ---------- python/posts/search_recent.py | 13 +-- .../{unlike_a_tweet.py => unlike_post.py} | 14 ++-- .../{undo_a_retweet.py => unrepost_post.py} | 14 ++-- .../{spaces_lookup.py => get_by_ids.py} | 7 +- python/spaces/search_spaces.py | 2 +- python/streams/filtered_stream.py | 61 -------------- python/streams/stream_posts_filtered.py | 80 +++++++++++++++++++ ...mpled_stream.py => stream_posts_sample.py} | 2 +- .../{user_mentions.py => get_mentions.py} | 9 ++- .../timelines/{user_posts.py => get_posts.py} | 14 ++-- .../{home_timeline.py => get_timeline.py} | 11 ++- .../timelines/reverse-chron-home-timeline.py | 70 ---------------- python/timelines/user_tweets.py | 43 ---------- python/usage/get_usage.py | 12 ++- python/usage/get_usage_tweets.py | 26 ------ python/users/block_a_user.py | 68 ---------------- python/users/blocked.py | 74 ----------------- python/users/followers_lookup.py | 36 --------- python/users/following_lookup.py | 36 --------- .../{lookup_blocks.py => get_blocking.py} | 46 +++++++---- .../users/{followers.py => get_followers.py} | 9 ++- .../users/{following.py => get_following.py} | 9 ++- .../users/{lookup_mutes.py => get_muting.py} | 46 +++++++---- ...y => get_user_by_usernams_user_context.py} | 13 ++- ...=> get_users_by_usernames_bearer_token.py} | 13 ++- python/users/get_users_me_user_context.py | 2 +- python/users/lookup.py | 35 -------- python/users/me.py | 65 --------------- python/users/{mute_a_user.py => mute_user.py} | 0 python/users/muted.py | 74 ----------------- python/users/unblock_a_user.py | 70 ---------------- .../{unmute_a_user.py => unmute_user.py} | 0 67 files changed, 431 insertions(+), 1216 deletions(-) rename python/lists/{add_member.py => add_list_member.py} (100%) rename python/lists/{create_a_list.py => create_list.py} (97%) rename python/lists/{delete_a_list.py => delete_list.py} (100%) rename python/lists/{user-list-followed.py => get_followed_lists.py} (75%) rename python/lists/{list-lookup-by-id.py => get_list_by_id.py} (65%) rename python/lists/{list-followers-lookup.py => get_list_followers.py} (67%) rename python/lists/{list-member-lookup.py => get_list_members.py} (75%) rename python/lists/{user-list-memberships.py => get_list_memberships.py} (75%) rename python/lists/{List-Tweets.py => get_list_posts.py} (74%) rename python/lists/{user-owned-list-lookup.py => get_owned_lists.py} (74%) rename python/lists/{Pinned-List.py => get_pinned_lists.py} (75%) delete mode 100644 python/lists/lookup.py rename python/lists/{remove_member.py => remove_list_member.py} (100%) rename python/lists/{update_a_list.py => update_list.py} (96%) delete mode 100644 python/posts/counts_full_archive.py rename python/posts/{create_tweet.py => create_post.py} (97%) rename python/posts/{delete_tweet.py => delete_post.py} (96%) delete mode 100644 python/posts/full_archive_tweet_counts.py rename python/posts/{liked_tweets.py => get_liked_posts.py} (80%) rename python/posts/{liking_users.py => get_liking_users.py} (85%) create mode 100644 python/posts/get_post_counts_all.py rename python/posts/{counts_recent.py => get_post_counts_recent.py} (52%) rename python/posts/{lookup.py => get_posts_by_ids.py} (77%) rename python/posts/{quote_tweets.py => get_quoted_posts.py} (97%) rename python/posts/{retweeted_by.py => get_resposted_by.py} (76%) delete mode 100644 python/posts/get_tweets_with_bearer_token.py delete mode 100644 python/posts/get_tweets_with_user_context.py rename python/posts/{like_a_tweet.py => like_post.py} (87%) delete mode 100644 python/posts/recent_search.py delete mode 100644 python/posts/recent_tweet_counts.py rename python/posts/{retweet_a_tweet.py => repost_post.py} (87%) delete mode 100644 python/posts/reposted_by.py rename python/posts/{full-archive-search.py => search_all.py} (70%) delete mode 100644 python/posts/search_full_archive.py rename python/posts/{unlike_a_tweet.py => unlike_post.py} (87%) rename python/posts/{undo_a_retweet.py => unrepost_post.py} (87%) rename python/spaces/{spaces_lookup.py => get_by_ids.py} (81%) delete mode 100644 python/streams/filtered_stream.py create mode 100644 python/streams/stream_posts_filtered.py rename python/streams/{sampled_stream.py => stream_posts_sample.py} (92%) rename python/timelines/{user_mentions.py => get_mentions.py} (77%) rename python/timelines/{user_posts.py => get_posts.py} (65%) rename python/timelines/{home_timeline.py => get_timeline.py} (86%) delete mode 100644 python/timelines/reverse-chron-home-timeline.py delete mode 100644 python/timelines/user_tweets.py delete mode 100644 python/usage/get_usage_tweets.py delete mode 100644 python/users/block_a_user.py delete mode 100644 python/users/blocked.py delete mode 100644 python/users/followers_lookup.py delete mode 100644 python/users/following_lookup.py rename python/users/{lookup_blocks.py => get_blocking.py} (60%) rename python/users/{followers.py => get_followers.py} (72%) rename python/users/{following.py => get_following.py} (72%) rename python/users/{lookup_mutes.py => get_muting.py} (60%) rename python/users/{get_users_with_user_context.py => get_user_by_usernams_user_context.py} (84%) rename python/users/{get_users_with_bearer_token.py => get_users_by_usernames_bearer_token.py} (67%) delete mode 100644 python/users/lookup.py delete mode 100644 python/users/me.py rename python/users/{mute_a_user.py => mute_user.py} (100%) delete mode 100644 python/users/muted.py delete mode 100644 python/users/unblock_a_user.py rename python/users/{unmute_a_user.py => unmute_user.py} (100%) diff --git a/python/README.md b/python/README.md index 2681867..b3b4b0b 100644 --- a/python/README.md +++ b/python/README.md @@ -19,36 +19,25 @@ export CONSUMER_SECRET='your_consumer_secret' ## Examples ### Posts -- posts/counts_full_archive.py -- posts/counts_recent.py -- posts/create_tweet.py -- posts/delete_tweet.py -- posts/full_archive_tweet_counts.py -- posts/full-archive-search.py -- posts/get_tweets_with_bearer_token.py -- posts/get_tweets_with_user_context.py -- posts/like_a_tweet.py -- posts/liked_tweets.py -- posts/liking_users.py -- posts/lookup.py -- posts/quote_tweets.py -- posts/recent_search.py -- posts/recent_tweet_counts.py -- posts/reposted_by.py -- posts/retweet_a_tweet.py +- posts/create_post.py +- posts/delete_post.py +- posts/get_liked_posts.py +- posts/get_liking_users.py +- posts/get_post_counts_all.py +- posts/get_post_counts_recent.py +- posts/get_posts_by_ids.py +- posts/get_quoted_posts.py +- posts/like_post.py +- posts/repost_post.py - posts/retweeted_by.py -- posts/search_full_archive.py +- posts/search_all.py - posts/search_recent.py - posts/undo_a_retweet.py - posts/unlike_a_tweet.py ### Users -- users/block_a_user.py -- users/blocked.py -- users/followers_lookup.py -- users/followers.py - users/following_lookup.py -- users/following.py +- users/get_followers.py - users/get_users_me_user_context.py - users/get_users_with_bearer_token.py - users/get_users_with_user_context.py @@ -62,26 +51,24 @@ export CONSUMER_SECRET='your_consumer_secret' - users/unmute_a_user.py ### Timelines -- timelines/home_timeline.py +- timelines/get_mentions.py +- timelines/get_posts.py +- timelines/get_timeline.py - timelines/reverse-chron-home-timeline.py -- timelines/user_mentions.py -- timelines/user_posts.py -- timelines/user_tweets.py ### Streams - streams/filtered_stream.py - streams/sampled_stream.py ### Lists -- lists/add_member.py -- lists/create_a_list.py -- lists/delete_a_list.py +- lists/add_list_member.py +- lists/create_list.py +- lists/delete_list.py - lists/follow_list.py -- lists/list-followers-lookup.py -- lists/list-lookup-by-id.py -- lists/list-member-lookup.py -- lists/List-Tweets.py -- lists/lookup.py +- lists/get_list_by_id.py +- lists/get_list_followers.py +- lists/get_list_members.py +- lists/get_list_posts.py - lists/pin_list.py - lists/Pinned-List.py - lists/remove_member.py @@ -122,6 +109,5 @@ export CONSUMER_SECRET='your_consumer_secret' - compliance/upload_ids.py ### Usage -- usage/get_usage_tweets.py - usage/get_usage.py diff --git a/python/lists/add_member.py b/python/lists/add_list_member.py similarity index 100% rename from python/lists/add_member.py rename to python/lists/add_list_member.py diff --git a/python/lists/create_a_list.py b/python/lists/create_list.py similarity index 97% rename from python/lists/create_a_list.py rename to python/lists/create_list.py index 8e58d71..0cdb4e3 100644 --- a/python/lists/create_a_list.py +++ b/python/lists/create_list.py @@ -59,7 +59,7 @@ def main(): client = Client(access_token=access_token) # Step 6: Create the list - response = client.lists.create_list(body=payload) + response = client.lists.create(body=payload) print("Response code: 201") print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/lists/delete_a_list.py b/python/lists/delete_list.py similarity index 100% rename from python/lists/delete_a_list.py rename to python/lists/delete_list.py diff --git a/python/lists/user-list-followed.py b/python/lists/get_followed_lists.py similarity index 75% rename from python/lists/user-list-followed.py rename to python/lists/get_followed_lists.py index ced444f..f046587 100644 --- a/python/lists/user-list-followed.py +++ b/python/lists/get_followed_lists.py @@ -27,10 +27,13 @@ def main(): for page in client.users.get_followed_lists( user_id, max_results=100, - listfields=["created_at", "follower_count"] + list_fields=["created_at", "follower_count"] ): - all_lists.extend(page.data) - print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_lists.extend(page_data) + print(f"Fetched {len(page_data)} lists (total: {len(all_lists)})") print(f"\nTotal Followed Lists: {len(all_lists)}") print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/lists/list-lookup-by-id.py b/python/lists/get_list_by_id.py similarity index 65% rename from python/lists/list-lookup-by-id.py rename to python/lists/get_list_by_id.py index 07679bd..d52df4d 100644 --- a/python/lists/list-lookup-by-id.py +++ b/python/lists/get_list_by_id.py @@ -13,18 +13,20 @@ from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +if not bearer_token: + raise ValueError("BEARER_TOKEN environment variable is not set") + client = Client(bearer_token=bearer_token) # You can replace the ID given with the List ID you wish to lookup. list_id = "list-id" def main(): - # List fields are adjustable, options include: - # created_at, description, owner_id, - # private, follower_count, member_count, - response = client.lists.get_list( + # List fields are adjustable. Options include: + # created_at, follower_count, member_count, private, description, owner_id + response = client.lists.get_by_id( list_id, - listfields=["created_at", "follower_count"] + list_fields=["created_at", "follower_count", "member_count", "owner_id", "description"] ) print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/lists/list-followers-lookup.py b/python/lists/get_list_followers.py similarity index 67% rename from python/lists/list-followers-lookup.py rename to python/lists/get_list_followers.py index 2a1b6c8..a2892ce 100644 --- a/python/lists/list-followers-lookup.py +++ b/python/lists/get_list_followers.py @@ -13,9 +13,13 @@ from xdk import Client bearer_token = os.environ.get("BEARER_TOKEN") +if not bearer_token: + raise ValueError("BEARER_TOKEN environment variable is not set") + client = Client(bearer_token=bearer_token) # You can replace list-id with the List ID you wish to find followers of. +# Note: Private lists require OAuth 2.0 user context authentication. list_id = "list-id" def main(): @@ -28,13 +32,16 @@ def main(): for page in client.lists.get_followers( list_id, max_results=100, - userfields=["created_at", "description", "verified"] + user_fields=["created_at", "description", "verified"] ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") print(f"\nTotal Followers: {len(all_users)}") print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/python/lists/list-member-lookup.py b/python/lists/get_list_members.py similarity index 75% rename from python/lists/list-member-lookup.py rename to python/lists/get_list_members.py index dbe9f20..1654040 100644 --- a/python/lists/list-member-lookup.py +++ b/python/lists/get_list_members.py @@ -28,10 +28,13 @@ def main(): for page in client.lists.get_members( list_id, max_results=100, - userfields=["created_at", "description", "verified"] + user_fields=["created_at", "description", "verified"] ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") print(f"\nTotal Members: {len(all_users)}") print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/lists/user-list-memberships.py b/python/lists/get_list_memberships.py similarity index 75% rename from python/lists/user-list-memberships.py rename to python/lists/get_list_memberships.py index 34b9330..f37b604 100644 --- a/python/lists/user-list-memberships.py +++ b/python/lists/get_list_memberships.py @@ -27,10 +27,13 @@ def main(): for page in client.users.get_list_memberships( user_id, max_results=100, - listfields=["created_at", "follower_count"] + list_fields=["created_at", "follower_count"] ): - all_lists.extend(page.data) - print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_lists.extend(page_data) + print(f"Fetched {len(page_data)} lists (total: {len(all_lists)})") print(f"\nTotal List Memberships: {len(all_lists)}") print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/lists/List-Tweets.py b/python/lists/get_list_posts.py similarity index 74% rename from python/lists/List-Tweets.py rename to python/lists/get_list_posts.py index 6f953a4..283ef42 100644 --- a/python/lists/List-Tweets.py +++ b/python/lists/get_list_posts.py @@ -28,13 +28,16 @@ def main(): # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld all_posts = [] - for page in client.lists.get_tweets( + for page in client.lists.get_posts( list_id, max_results=100, - tweetfields=["lang", "author_id"] + tweet_fields=["lang", "author_id"] ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_posts.extend(page_data) + print(f"Fetched {len(page_data)} posts (total: {len(all_posts)})") print(f"\nTotal Posts: {len(all_posts)}") print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/lists/user-owned-list-lookup.py b/python/lists/get_owned_lists.py similarity index 74% rename from python/lists/user-owned-list-lookup.py rename to python/lists/get_owned_lists.py index a77ac49..88333dc 100644 --- a/python/lists/user-owned-list-lookup.py +++ b/python/lists/get_owned_lists.py @@ -27,10 +27,13 @@ def main(): for page in client.users.get_owned_lists( user_id, max_results=100, - listfields=["created_at", "follower_count"] + list_fields=["created_at", "follower_count"] ): - all_lists.extend(page.data) - print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_lists.extend(page_data) + print(f"Fetched {len(page_data)} lists (total: {len(all_lists)})") print(f"\nTotal Owned Lists: {len(all_lists)}") print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/lists/Pinned-List.py b/python/lists/get_pinned_lists.py similarity index 75% rename from python/lists/Pinned-List.py rename to python/lists/get_pinned_lists.py index 132faba..6a0af67 100644 --- a/python/lists/Pinned-List.py +++ b/python/lists/get_pinned_lists.py @@ -54,21 +54,24 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Get pinned lists with automatic pagination + # Step 6: Get pinned lists # List fields are adjustable, options include: # created_at, description, owner_id, # private, follower_count, member_count, - all_lists = [] - for page in client.users.get_pinned_lists( + response = client.users.get_pinned_lists( user_id, - max_results=100, - listfields=["created_at", "description", "private"] - ): - all_lists.extend(page.data) - print(f"Fetched {len(page.data)} lists (total: {len(all_lists)})") + list_fields=["created_at", "description", "private"] + ) + + # Access data attribute (model uses extra='allow' so data should be available) + response_data = getattr(response, 'data', None) + if response_data is None: + # Try accessing via model_dump if data attribute doesn't exist + response_dict = response.model_dump() if hasattr(response, 'model_dump') else {} + response_data = response_dict.get('data', response_dict) - print(f"\nTotal Pinned Lists: {len(all_lists)}") - print(json.dumps({"data": all_lists[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + print(f"Total Pinned Lists: {len(response_data) if isinstance(response_data, list) else 1}") + print(json.dumps(response_data, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/lists/lookup.py b/python/lists/lookup.py deleted file mode 100644 index 291c7be..0000000 --- a/python/lists/lookup.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -List Lookup - X API v2 -====================== -Endpoint: GET https://api.x.com/2/lists/:id -Docs: https://developer.x.com/en/docs/twitter-api/lists/list-lookup/api-reference/get-lists-id - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -# Replace with the list ID you want to look up -list_id = "84839422" - -def main(): - # List fields are adjustable. Options include: - # created_at, follower_count, member_count, private, description, owner_id - response = client.lists.get_list( - list_id, - listfields=["created_at", "follower_count", "member_count", "owner_id", "description"] - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - -if __name__ == "__main__": - main() diff --git a/python/lists/remove_member.py b/python/lists/remove_list_member.py similarity index 100% rename from python/lists/remove_member.py rename to python/lists/remove_list_member.py diff --git a/python/lists/update_a_list.py b/python/lists/update_list.py similarity index 96% rename from python/lists/update_a_list.py rename to python/lists/update_list.py index 9d90632..5df2d4e 100644 --- a/python/lists/update_a_list.py +++ b/python/lists/update_list.py @@ -36,7 +36,7 @@ # Be sure to replace your-list-id with the id of the list you wish to update. # The authenticated user must own the list in order to update it. -list_id = "your-list-id" +list_id = "list-id" def main(): # Step 1: Create PKCE instance @@ -63,7 +63,7 @@ def main(): client = Client(access_token=access_token) # Step 6: Update the list - response = client.lists.update_list(list_id, body=payload) + response = client.lists.update(list_id, body=payload) print("Response code: 200") print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/posts/counts_full_archive.py b/python/posts/counts_full_archive.py deleted file mode 100644 index e61601b..0000000 --- a/python/posts/counts_full_archive.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Full-Archive Post Counts - X API v2 -=================================== -Endpoint: GET https://api.x.com/2/tweets/counts/all -Docs: https://developer.x.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-all - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN - -Note: Requires Academic Research access. Returns counts from the entire archive. -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -query = "from:XDevelopers" - -def main(): - response = client.posts.get_tweet_counts_all( - query=query, - granularity="day" - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/posts/create_tweet.py b/python/posts/create_post.py similarity index 97% rename from python/posts/create_tweet.py rename to python/posts/create_post.py index a55d7a1..ae7cda5 100644 --- a/python/posts/create_tweet.py +++ b/python/posts/create_post.py @@ -55,7 +55,7 @@ def main(): client = Client(access_token=access_token) # Step 6: Create the tweet - response = client.posts.create_tweet(body=payload) + response = client.posts.create(body=payload) print("Response code: 201") print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/posts/delete_tweet.py b/python/posts/delete_post.py similarity index 96% rename from python/posts/delete_tweet.py rename to python/posts/delete_post.py index f1369ba..dbf7615 100644 --- a/python/posts/delete_tweet.py +++ b/python/posts/delete_post.py @@ -28,7 +28,7 @@ # Be sure to replace tweet-id-to-delete with the id of the Tweet you wish to delete. # The authenticated user must own the Tweet in order to delete it. -post_id = "tweet-id-to-delete" +post_id = "1997118644242014409" def main(): # Step 1: Create PKCE instance @@ -55,10 +55,11 @@ def main(): client = Client(access_token=access_token) # Step 6: Delete the tweet - response = client.posts.delete_tweet(post_id) + response = client.posts.delete(post_id) print("Response code: 200") print(json.dumps(response.data, indent=4, sort_keys=True)) if __name__ == "__main__": + main() diff --git a/python/posts/full_archive_tweet_counts.py b/python/posts/full_archive_tweet_counts.py deleted file mode 100644 index a8e8ca5..0000000 --- a/python/posts/full_archive_tweet_counts.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -Full-Archive Tweet Counts - X API v2 -==================================== -Endpoint: GET https://api.x.com/2/tweets/counts/all -Docs: https://developer.x.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-all - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN - -Note: Requires Academic Research access. Returns counts from the entire archive. -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -query = 'from:twitterdev' - -def main(): - response = client.posts.get_tweet_counts_all( - query=query, - granularity="day", - start_time="2021-01-01T00:00:00Z" - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/python/posts/liked_tweets.py b/python/posts/get_liked_posts.py similarity index 80% rename from python/posts/liked_tweets.py rename to python/posts/get_liked_posts.py index 40fe363..0c58ab4 100644 --- a/python/posts/liked_tweets.py +++ b/python/posts/get_liked_posts.py @@ -26,10 +26,6 @@ # Set the scopes scopes = ["tweet.read", "users.read", "like.read", "offline.access"] -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - def main(): # Step 1: Create PKCE instance auth = OAuth2PKCEAuth( @@ -54,7 +50,11 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Get liked tweets with automatic pagination + # Step 6: Get the authenticated user's ID + user_me = client.users.get_me() + user_id = user_me.data["id"] + + # Step 7: Get liked tweets with automatic pagination # Tweet fields are adjustable. # Options include: # attachments, author_id, context_annotations, @@ -63,13 +63,16 @@ def main(): # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld all_posts = [] - for page in client.users.get_liked_tweets( + for page in client.users.get_liked_posts( user_id, max_results=100, - tweetfields=["lang", "author_id"] + tweet_fields=["lang", "author_id"] ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_posts.extend(page_data) + print(f"Fetched {len(page_data)} posts (total: {len(all_posts)})") print(f"\nTotal Liked Tweets: {len(all_posts)}") print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/posts/liking_users.py b/python/posts/get_liking_users.py similarity index 85% rename from python/posts/liking_users.py rename to python/posts/get_liking_users.py index 7f33ea9..4918bd2 100644 --- a/python/posts/liking_users.py +++ b/python/posts/get_liking_users.py @@ -12,6 +12,7 @@ import json from xdk import Client from xdk.oauth2_auth import OAuth2PKCEAuth +from requests.exceptions import HTTPError # The code below sets the client ID and client secret from your environment variables # To set environment variables on macOS or Linux, run the export commands below from the terminal: @@ -28,7 +29,7 @@ # You can replace the ID given with the Post ID you wish to get liking users for. # You can find an ID by using the Post lookup endpoint -post_id = "1354143047324299264" +post_id = "post-id" def main(): # Step 1: Create PKCE instance @@ -63,10 +64,13 @@ def main(): for page in client.posts.get_liking_users( post_id, max_results=100, - userfields=["created_at", "description"] + user_fields=["created_at", "description"] ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") print(f"\nTotal Users: {len(all_users)}") print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/posts/get_post_counts_all.py b/python/posts/get_post_counts_all.py new file mode 100644 index 0000000..5b64b9c --- /dev/null +++ b/python/posts/get_post_counts_all.py @@ -0,0 +1,43 @@ +""" +Full-Archive Post Counts - X API v2 +=================================== +Endpoint: GET https://api.x.com/2/tweets/counts/all +Docs: https://developer.x.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-all + +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN + +Note: Requires Academic Research access. Returns counts from the entire archive. +""" + +import os +import json +from xdk import Client + +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) + +query = "from:XDevelopers" + +def main(): + # Get counts with automatic pagination + # Optional: You can add start_time parameter to limit the date range + # Example: start_time="2021-01-01T00:00:00Z" + all_counts = [] + for page in client.posts.get_counts_all( + query=query, + granularity="day" + # start_time="2021-01-01T00:00:00Z" # Optional: uncomment to add date range + ): + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_counts.extend(page_data) + print(f"Fetched {len(page_data)} count buckets (total: {len(all_counts)})") + + print(f"\nTotal Count Buckets: {len(all_counts)}") + print(json.dumps({"data": all_counts}, indent=4, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/python/posts/counts_recent.py b/python/posts/get_post_counts_recent.py similarity index 52% rename from python/posts/counts_recent.py rename to python/posts/get_post_counts_recent.py index f293923..cf959ff 100644 --- a/python/posts/counts_recent.py +++ b/python/posts/get_post_counts_recent.py @@ -20,12 +20,20 @@ query = "from:XDevelopers" def main(): - response = client.posts.get_tweet_counts_recent( + # Get counts with automatic pagination + all_counts = [] + for page in client.posts.get_counts_recent( query=query, granularity="day" - ) + ): + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_counts.extend(page_data) + print(f"Fetched {len(page_data)} count buckets (total: {len(all_counts)})") - print(json.dumps(response.data, indent=4, sort_keys=True)) + print(f"\nTotal Count Buckets: {len(all_counts)}") + print(json.dumps({"data": all_counts}, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/posts/lookup.py b/python/posts/get_posts_by_ids.py similarity index 77% rename from python/posts/lookup.py rename to python/posts/get_posts_by_ids.py index 0c9de3f..f8c489e 100644 --- a/python/posts/lookup.py +++ b/python/posts/get_posts_by_ids.py @@ -16,7 +16,8 @@ client = Client(bearer_token=bearer_token) # Post IDs to look up (comma-separated list, up to 100) -post_ids = ["1278747501642657792", "1255542774432063488"] +# You can adjust ids to include a single Post or add up to 100 comma-separated IDs +post_ids = ["post-id-1", "post-id-2"] def main(): # Post fields are adjustable. Options include: @@ -25,9 +26,9 @@ def main(): # non_public_metrics, organic_metrics, possibly_sensitive, # promoted_metrics, public_metrics, referenced_tweets, # source, text, and withheld - response = client.posts.get_tweets( + response = client.posts.get_by_ids( ids=post_ids, - tweetfields=["created_at", "author_id", "lang", "source", "public_metrics", "context_annotations", "entities"] + tweet_fields=["created_at", "author_id", "lang", "source", "public_metrics", "context_annotations", "entities"] ) print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/posts/quote_tweets.py b/python/posts/get_quoted_posts.py similarity index 97% rename from python/posts/quote_tweets.py rename to python/posts/get_quoted_posts.py index abac584..2c02a1a 100644 --- a/python/posts/quote_tweets.py +++ b/python/posts/get_quoted_posts.py @@ -31,7 +31,7 @@ def main(): for page in client.posts.get_quoted( post_id, max_results=100, - tweetfields=["created_at"] + tweet_fields=["created_at"] ): all_posts.extend(page.data) print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") diff --git a/python/posts/retweeted_by.py b/python/posts/get_resposted_by.py similarity index 76% rename from python/posts/retweeted_by.py rename to python/posts/get_resposted_by.py index f0eaf79..861b479 100644 --- a/python/posts/retweeted_by.py +++ b/python/posts/get_resposted_by.py @@ -17,7 +17,7 @@ # You can replace the ID given with the Post ID you wish to lookup reposting users for # You can find an ID by using the Post lookup endpoint -post_id = "1354143047324299264" +post_id = "post-id" def main(): # Get reposted by users with automatic pagination @@ -29,10 +29,13 @@ def main(): for page in client.posts.get_reposted_by( post_id, max_results=100, - userfields=["created_at", "description"] + user_fields=["created_at", "description"] ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") print(f"\nTotal Users: {len(all_users)}") print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/posts/get_tweets_with_bearer_token.py b/python/posts/get_tweets_with_bearer_token.py deleted file mode 100644 index 972d668..0000000 --- a/python/posts/get_tweets_with_bearer_token.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Post Lookup - X API v2 -====================== -Endpoint: GET https://api.x.com/2/tweets -Docs: https://developer.x.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -# Post IDs to look up (list, up to 100) -post_ids = ["1278747501642657792", "1255542774432063488"] - -def main(): - # Tweet fields are adjustable. - # Options include: - # attachments, author_id, context_annotations, - # conversation_id, created_at, entities, geo, id, - # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, - # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, - # source, text, and withheld - response = client.posts.get_tweets( - ids=post_ids, - tweetfields=["lang", "author_id"] - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/posts/get_tweets_with_user_context.py b/python/posts/get_tweets_with_user_context.py deleted file mode 100644 index 6d81471..0000000 --- a/python/posts/get_tweets_with_user_context.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Post Lookup (User Context) - X API v2 -====================================== -Endpoint: GET https://api.x.com/2/tweets -Docs: https://developer.x.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets - -Authentication: OAuth 2.0 (User Context) -Required env vars: CLIENT_ID, CLIENT_SECRET -""" - -import os -import json -from xdk import Client -from xdk.oauth2_auth import OAuth2PKCEAuth - -# The code below sets the client ID and client secret from your environment variables -# To set environment variables on macOS or Linux, run the export commands below from the terminal: -# export CLIENT_ID='YOUR-CLIENT-ID' -# export CLIENT_SECRET='YOUR-CLIENT-SECRET' -client_id = os.environ.get("CLIENT_ID") -client_secret = os.environ.get("CLIENT_SECRET") - -# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://example.com" - -# Set the scopes -scopes = ["tweet.read", "users.read", "offline.access"] - -# You can adjust ids to include a single Post -# Or you can add to up to 100 comma-separated IDs -post_ids = ["1278747501642657792"] - -def main(): - # Step 1: Create PKCE instance - auth = OAuth2PKCEAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, - scope=scopes - ) - - # Step 2: Get authorization URL - auth_url = auth.get_authorization_url() - print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") - print(auth_url) - - # Step 3: Handle callback - callback_url = input("Paste the full callback URL here: ") - - # Step 4: Exchange code for tokens - tokens = auth.fetch_token(authorization_response=callback_url) - access_token = tokens["access_token"] - - # Step 5: Create client - client = Client(access_token=access_token) - - # Step 6: Get posts - # Tweet fields are adjustable. - # Options include: - # attachments, author_id, context_annotations, - # conversation_id, created_at, entities, geo, id, - # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, - # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, - # source, text, and withheld - response = client.posts.get_tweets( - ids=post_ids, - tweetfields=["created_at"] - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - -if __name__ == "__main__": - main() diff --git a/python/posts/like_a_tweet.py b/python/posts/like_post.py similarity index 87% rename from python/posts/like_a_tweet.py rename to python/posts/like_post.py index a28743a..fbed43f 100644 --- a/python/posts/like_a_tweet.py +++ b/python/posts/like_post.py @@ -26,13 +26,9 @@ # Set the scopes scopes = ["tweet.read", "tweet.write", "users.read", "offline.access", "like.write"] -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - # You can replace Tweet ID given with the Tweet ID you wish to like. # You can find a Tweet ID by using the Tweet lookup endpoint -post_id = "1354143047324299264" +post_id = "post-id" def main(): # Step 1: Create PKCE instance @@ -58,9 +54,13 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Like the tweet + # Step 6: Get the authenticated user's ID + user_me = client.users.get_me() + user_id = user_me.data["id"] + + # Step 7: Like the tweet payload = {"tweet_id": post_id} - response = client.users.like_tweet(user_id, body=payload) + response = client.users.like_post(user_id, body=payload) print("Response code: 200") print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/posts/recent_search.py b/python/posts/recent_search.py deleted file mode 100644 index e84a338..0000000 --- a/python/posts/recent_search.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Recent Search - X API v2 -======================== -Endpoint: GET https://api.x.com/2/tweets/search/recent -Docs: https://developer.x.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN - -Note: Returns posts from the last 7 days. -This example demonstrates automatic pagination using the iterate() method -to fetch all pages of results. -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -query = '(from:twitterdev -is:retweet) OR #twitterdev' - -def main(): - # Search with automatic pagination - all_posts = [] - for page in client.posts.search_recent( - query=query, - max_results=100, # Per page - tweetfields=["author_id"] - ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") - - print(f"\nTotal Posts: {len(all_posts)}") - print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - - -if __name__ == "__main__": - main() diff --git a/python/posts/recent_tweet_counts.py b/python/posts/recent_tweet_counts.py deleted file mode 100644 index c636eaf..0000000 --- a/python/posts/recent_tweet_counts.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Recent Tweet Counts - X API v2 -============================== -Endpoint: GET https://api.x.com/2/tweets/counts/recent -Docs: https://developer.x.com/en/docs/twitter-api/tweets/counts/api-reference/get-tweets-counts-recent - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN - -Note: Returns count of posts from the last 7 days matching your query. -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -query = 'from:twitterdev' - -def main(): - response = client.posts.get_tweet_counts_recent( - query=query, - granularity="day" - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/posts/retweet_a_tweet.py b/python/posts/repost_post.py similarity index 87% rename from python/posts/retweet_a_tweet.py rename to python/posts/repost_post.py index 552a7f5..646a8cd 100644 --- a/python/posts/retweet_a_tweet.py +++ b/python/posts/repost_post.py @@ -26,13 +26,9 @@ # Set the scopes scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"] -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - # You can replace the given Tweet ID with the Tweet ID you want to Retweet # You can find a Tweet ID by using the Tweet lookup endpoint -post_id = "1412865600439738368" +post_id = "post-id" def main(): # Step 1: Create PKCE instance @@ -58,9 +54,13 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Retweet the tweet + # Step 6: Get the authenticated user's ID + user_me = client.users.get_me() + user_id = user_me.data["id"] + + # Step 7: Retweet the tweet payload = {"tweet_id": post_id} - response = client.users.repost_tweet(user_id, body=payload) + response = client.users.repost_post(user_id, body=payload) print("Response code: 200") print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/posts/reposted_by.py b/python/posts/reposted_by.py deleted file mode 100644 index 2b56429..0000000 --- a/python/posts/reposted_by.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Reposted By (Users who reposted) - X API v2 -============================================ -Endpoint: GET https://api.x.com/2/tweets/:id/retweeted_by -Docs: https://developer.x.com/en/docs/twitter-api/tweets/retweets/api-reference/get-tweets-id-retweeted_by - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -# Replace with the post ID you want to get reposters for -post_id = "1354143047324299264" - -def main(): - # Get reposted by users with automatic pagination - all_users = [] - for page in client.posts.get_reposted_by( - post_id, - max_results=100, - userfields=["created_at"] - ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") - - print(f"\nTotal Users: {len(all_users)}") - print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - -if __name__ == "__main__": - main() diff --git a/python/posts/full-archive-search.py b/python/posts/search_all.py similarity index 70% rename from python/posts/full-archive-search.py rename to python/posts/search_all.py index 5927d51..2f47372 100644 --- a/python/posts/full-archive-search.py +++ b/python/posts/search_all.py @@ -19,7 +19,7 @@ bearer_token = os.environ.get("BEARER_TOKEN") client = Client(bearer_token=bearer_token) -query = '(from:twitterdev -is:retweet) OR #twitterdev' +query = '(from:xdevelopers -is:retweet) OR #xdevelopers' def main(): # Search with automatic pagination @@ -27,10 +27,13 @@ def main(): for page in client.posts.search_all( query=query, max_results=100, # Per page - tweetfields=["author_id"] + tweet_fields=["author_id", "created_at"] ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_posts.extend(page_data) + print(f"Fetched {len(page_data)} Posts (total: {len(all_posts)})") print(f"\nTotal Posts: {len(all_posts)}") print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/posts/search_full_archive.py b/python/posts/search_full_archive.py deleted file mode 100644 index 505a5fa..0000000 --- a/python/posts/search_full_archive.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Full-Archive Search - X API v2 -============================== -Endpoint: GET https://api.x.com/2/tweets/search/all -Docs: https://developer.x.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-all - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN - -Note: Requires Academic Research access. Returns posts from the entire archive. -This example demonstrates automatic pagination using the iterate() method -to fetch all pages of results. -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -query = '(from:XDevelopers -is:retweet) OR #xapi' - -def main(): - # Search with automatic pagination - all_posts = [] - for page in client.posts.search_all( - query=query, - max_results=100, # Per page - tweetfields=["author_id", "created_at"] - ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") - - print(f"\nTotal Posts: {len(all_posts)}") - print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - - -if __name__ == "__main__": - main() diff --git a/python/posts/search_recent.py b/python/posts/search_recent.py index a7a4f80..8eb36f8 100644 --- a/python/posts/search_recent.py +++ b/python/posts/search_recent.py @@ -4,7 +4,7 @@ Endpoint: GET https://api.x.com/2/tweets/search/recent Docs: https://developer.x.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent -Authentication: Bearer Token (App-only) or OAuth (User Context) +Authentication: Bearer Token (App-only) Required env vars: BEARER_TOKEN Note: Returns posts from the last 7 days. @@ -19,7 +19,7 @@ bearer_token = os.environ.get("BEARER_TOKEN") client = Client(bearer_token=bearer_token) -query = '(from:XDevelopers -is:retweet) OR #xapi' +query = '(from:XDevelopers -is:retweet) OR #XDevelopers' def main(): # Search with automatic pagination @@ -27,10 +27,13 @@ def main(): for page in client.posts.search_recent( query=query, max_results=100, # Per page - tweetfields=["author_id", "created_at"] + tweet_fields=["author_id", "created_at"] ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} Posts (total: {len(all_posts)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_posts.extend(page_data) + print(f"Fetched {len(page_data)} Posts (total: {len(all_posts)})") print(f"\nTotal Posts: {len(all_posts)}") print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/posts/unlike_a_tweet.py b/python/posts/unlike_post.py similarity index 87% rename from python/posts/unlike_a_tweet.py rename to python/posts/unlike_post.py index 304ab0f..7de7542 100644 --- a/python/posts/unlike_a_tweet.py +++ b/python/posts/unlike_post.py @@ -26,13 +26,9 @@ # Set the scopes scopes = ["tweet.read", "tweet.write", "users.read", "offline.access", "like.write"] -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - # You can replace Tweet ID given with the Tweet ID you wish to unlike. # You can find a Tweet ID by using the Tweet lookup endpoint -post_id = "1354143047324299264" +post_id = "post-id" def main(): # Step 1: Create PKCE instance @@ -58,8 +54,12 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Unlike the tweet - response = client.users.unlike_tweet(user_id, post_id) + # Step 6: Get the authenticated user's ID + user_me = client.users.get_me() + user_id = user_me.data["id"] + + # Step 7: Unlike the tweet + response = client.users.unlike_post(user_id, tweet_id=post_id) print("Response code: 200") print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/posts/undo_a_retweet.py b/python/posts/unrepost_post.py similarity index 87% rename from python/posts/undo_a_retweet.py rename to python/posts/unrepost_post.py index 5529330..f513ce5 100644 --- a/python/posts/undo_a_retweet.py +++ b/python/posts/unrepost_post.py @@ -26,13 +26,9 @@ # Set the scopes scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"] -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - # You can replace the given Tweet ID with the Tweet ID you want to undo retweet for # You can find a Tweet ID by using the Tweet lookup endpoint -source_post_id = "1412865600439738368" +source_post_id = "post-id" def main(): # Step 1: Create PKCE instance @@ -58,8 +54,12 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Undo retweet - response = client.users.unrepost_tweet(user_id, source_post_id) + # Step 6: Get the authenticated user's ID + user_me = client.users.get_me() + user_id = user_me.data["id"] + + # Step 7: Undo retweet + response = client.users.unrepost_post(user_id, source_tweet_id=source_post_id) print("Response code: 200") print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/spaces/spaces_lookup.py b/python/spaces/get_by_ids.py similarity index 81% rename from python/spaces/spaces_lookup.py rename to python/spaces/get_by_ids.py index b6338aa..3563d08 100644 --- a/python/spaces/spaces_lookup.py +++ b/python/spaces/get_by_ids.py @@ -16,14 +16,15 @@ client = Client(bearer_token=bearer_token) # Space IDs to look up (list) -space_ids = ["SPACE_ID"] +# You can replace the IDs with actual Space IDs you want to look up +space_ids = ["space-id-1", "space-id-2"] def main(): # Optional params: host_ids, conversation_controls, created_at, creator_id, id, invited_user_ids, # is_ticketed, lang, media_key, participants, scheduled_start, speaker_ids, started_at, state, title, updated_at - response = client.spaces.get_spaces( + response = client.spaces.get_by_ids( ids=space_ids, - spacefields=["title", "created_at"], + space_fields=["title", "created_at"], expansions=["creator_id"] ) diff --git a/python/spaces/search_spaces.py b/python/spaces/search_spaces.py index c5a1a9d..9004708 100644 --- a/python/spaces/search_spaces.py +++ b/python/spaces/search_spaces.py @@ -23,7 +23,7 @@ def main(): # is_ticketed, lang, media_key, participants, scheduled_start, speaker_ids, started_at, state, title, updated_at response = client.spaces.search( query=search_term, - spacefields=["title", "created_at"], + space_fields=["title", "created_at"], expansions=["creator_id"] ) diff --git a/python/streams/filtered_stream.py b/python/streams/filtered_stream.py deleted file mode 100644 index e3ebe8a..0000000 --- a/python/streams/filtered_stream.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Filtered Stream - X API v2 -========================== -Endpoint: GET https://api.x.com/2/tweets/search/stream -Docs: https://developer.x.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN - -Note: Streams posts matching your filter rules in real-time. -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -def get_rules(): - response = client.streams.get_rules() - print(json.dumps(response.data, indent=4, sort_keys=True)) - return response.data - - -def delete_all_rules(rules): - if rules is None or not rules: - return None - - ids = [rule["id"] for rule in rules] - payload = {"delete": {"ids": ids}} - response = client.streams.delete_rules(body=payload) - print(json.dumps(response.data, indent=4, sort_keys=True)) - - -def set_rules(): - # You can adjust the rules if needed - sample_rules = [ - {"value": "dog has:images", "tag": "dog pictures"}, - {"value": "cat has:images -grumpy", "tag": "cat pictures"}, - ] - payload = {"add": sample_rules} - response = client.streams.add_rules(body=payload) - print(json.dumps(response.data, indent=4, sort_keys=True)) - - -def get_stream(): - # Stream posts matching the filter rules in real-time - for post in client.streams.get_filtered_stream(): - print(json.dumps(post.data, indent=4, sort_keys=True)) - - -def main(): - rules = get_rules() - delete_all_rules(rules) - set_rules() - get_stream() - - -if __name__ == "__main__": - main() diff --git a/python/streams/stream_posts_filtered.py b/python/streams/stream_posts_filtered.py new file mode 100644 index 0000000..f4a203c --- /dev/null +++ b/python/streams/stream_posts_filtered.py @@ -0,0 +1,80 @@ +""" +Filtered Stream - X API v2 +========================== +Endpoint: GET https://api.x.com/2/tweets/search/stream +Docs: https://developer.x.com/en/docs/twitter-api/tweets/filtered-stream/api-reference/get-tweets-search-stream + +Authentication: Bearer Token (App-only) +Required env vars: BEARER_TOKEN + +Note: Streams posts matching your filter rules in real-time. +""" + +import os +import json +from xdk import Client + +bearer_token = os.environ.get("BEARER_TOKEN") +client = Client(bearer_token=bearer_token) + +def get_rules(): + response = client.stream.get_rules() + # Access data attribute safely + rules_data = getattr(response, 'data', None) + if rules_data: + print(json.dumps(rules_data, indent=4, sort_keys=True)) + return rules_data + + +def delete_all_rules(rules): + if rules is None or not rules: + return None + + ids = [rule["id"] for rule in rules] + payload = {"delete": {"ids": ids}} + response = client.stream.update_rules(body=payload) + # Access data attribute safely + response_data = getattr(response, 'data', None) + if response_data: + print(json.dumps(response_data, indent=4, sort_keys=True)) + + +def set_rules(): + # You can adjust the rules if needed + sample_rules = [ + {"value": "dog has:images", "tag": "dog pictures"}, + {"value": "cat has:images -grumpy", "tag": "cat pictures"}, + ] + payload = {"add": sample_rules} + response = client.stream.update_rules(body=payload) + # Access data attribute safely + response_data = getattr(response, 'data', None) + if response_data: + print(json.dumps(response_data, indent=4, sort_keys=True)) + + +def get_stream(): + # Stream posts matching the filter rules in real-time + # The posts() method is the filtered stream + try: + for post in client.stream.posts(): + # Access data attribute safely + post_data = getattr(post, 'data', None) + if post_data: + print(json.dumps(post_data, indent=4, sort_keys=True)) + except Exception as e: + print(f"Error streaming posts: {e}") + print("Note: This could be a temporary API issue (503 Service Unavailable)") + print("or the stream endpoint may be experiencing issues.") + raise + + +def main(): + rules = get_rules() + delete_all_rules(rules) + set_rules() + get_stream() + + +if __name__ == "__main__": + main() diff --git a/python/streams/sampled_stream.py b/python/streams/stream_posts_sample.py similarity index 92% rename from python/streams/sampled_stream.py rename to python/streams/stream_posts_sample.py index 98c2c70..445486d 100644 --- a/python/streams/sampled_stream.py +++ b/python/streams/stream_posts_sample.py @@ -19,7 +19,7 @@ def main(): # Stream posts in real-time - for post in client.streams.get_sampled_stream(): + for post in client.stream.posts_sample(): print(json.dumps(post.data, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/timelines/user_mentions.py b/python/timelines/get_mentions.py similarity index 77% rename from python/timelines/user_mentions.py rename to python/timelines/get_mentions.py index d47a217..7b99109 100644 --- a/python/timelines/user_mentions.py +++ b/python/timelines/get_mentions.py @@ -31,10 +31,13 @@ def main(): for page in client.users.get_mentions( user_id, max_results=100, - tweetfields=["created_at"] + tweet_fields=["created_at"] ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_posts.extend(page_data) + print(f"Fetched {len(page_data)} posts (total: {len(all_posts)})") print(f"\nTotal Mentions: {len(all_posts)}") print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/timelines/user_posts.py b/python/timelines/get_posts.py similarity index 65% rename from python/timelines/user_posts.py rename to python/timelines/get_posts.py index 9726b2c..35ec12e 100644 --- a/python/timelines/user_posts.py +++ b/python/timelines/get_posts.py @@ -23,15 +23,19 @@ def main(): # Post fields are adjustable. Options include: # attachments, author_id, context_annotations, conversation_id, # created_at, entities, geo, id, in_reply_to_user_id, lang, - # possibly_sensitive, public_metrics, referenced_tweets, source, text + # non_public_metrics, organic_metrics, possibly_sensitive, + # promoted_metrics, public_metrics, referenced_tweets, source, text, and withheld all_posts = [] - for page in client.users.get_tweets( + for page in client.users.get_posts( user_id, max_results=100, - tweetfields=["created_at"] + tweet_fields=["created_at"] ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_posts.extend(page_data) + print(f"Fetched {len(page_data)} posts (total: {len(all_posts)})") print(f"\nTotal Posts: {len(all_posts)}") print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/timelines/home_timeline.py b/python/timelines/get_timeline.py similarity index 86% rename from python/timelines/home_timeline.py rename to python/timelines/get_timeline.py index d3782bf..0f4d5cb 100644 --- a/python/timelines/home_timeline.py +++ b/python/timelines/get_timeline.py @@ -60,13 +60,16 @@ def main(): # created_at, entities, geo, id, in_reply_to_user_id, lang, # possibly_sensitive, public_metrics, referenced_tweets, source, text all_posts = [] - for page in client.users.get_reverse_chronological_timeline( + for page in client.users.get_timeline( user_id, max_results=100, - tweetfields=["created_at"] + tweet_fields=["created_at"] ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_posts.extend(page_data) + print(f"Fetched {len(page_data)} posts (total: {len(all_posts)})") print(f"\nTotal Posts: {len(all_posts)}") print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/timelines/reverse-chron-home-timeline.py b/python/timelines/reverse-chron-home-timeline.py deleted file mode 100644 index b4585ee..0000000 --- a/python/timelines/reverse-chron-home-timeline.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Reverse Chronological Home Timeline - X API v2 -============================================== -Endpoint: GET https://api.x.com/2/users/:id/timelines/reverse_chronological -Docs: https://developer.x.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-reverse-chronological-timeline - -Authentication: OAuth 2.0 (User Context) -Required env vars: CLIENT_ID, CLIENT_SECRET -""" - -import os -import json -from xdk import Client -from xdk.oauth2_auth import OAuth2PKCEAuth - -# The code below sets the client ID and client secret from your environment variables -# To set environment variables on macOS or Linux, run the export commands below from the terminal: -# export CLIENT_ID='YOUR-CLIENT-ID' -# export CLIENT_SECRET='YOUR-CLIENT-SECRET' -client_id = os.environ.get("CLIENT_ID") -client_secret = os.environ.get("CLIENT_SECRET") - -# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://example.com" - -# Set the scopes -scopes = ["tweet.read", "users.read", "offline.access"] - -def main(): - # Step 1: Create PKCE instance - auth = OAuth2PKCEAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, - scope=scopes - ) - - # Step 2: Get authorization URL - auth_url = auth.get_authorization_url() - print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") - print(auth_url) - - # Step 3: Handle callback - callback_url = input("Paste the full callback URL here: ") - - # Step 4: Exchange code for tokens - tokens = auth.fetch_token(authorization_response=callback_url) - access_token = tokens["access_token"] - - # Step 5: Create client - client = Client(access_token=access_token) - - # Step 6: Get authenticated user ID - me_response = client.users.get_me() - user_id = me_response.data["id"] - - # Step 7: Get reverse chronological timeline with automatic pagination - all_posts = [] - for page in client.users.get_reverse_chronological_timeline( - user_id, - max_results=100 - ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") - - print(f"\nTotal Posts: {len(all_posts)}") - print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - -if __name__ == "__main__": - main() diff --git a/python/timelines/user_tweets.py b/python/timelines/user_tweets.py deleted file mode 100644 index 05a4397..0000000 --- a/python/timelines/user_tweets.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -User Tweets Timeline - X API v2 -=============================== -Endpoint: GET https://api.x.com/2/users/:id/tweets -Docs: https://developer.x.com/en/docs/twitter-api/tweets/timelines/api-reference/get-users-id-tweets - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -# Replace with user ID below -user_id = "2244994945" - -def main(): - # Get user tweets with automatic pagination - # Tweet fields are adjustable. - # Options include: - # attachments, author_id, context_annotations, - # conversation_id, created_at, entities, geo, id, - # in_reply_to_user_id, lang, non_public_metrics, organic_metrics, - # possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, - # source, text, and withheld - all_posts = [] - for page in client.users.get_tweets( - user_id, - max_results=100, - tweetfields=["created_at"] - ): - all_posts.extend(page.data) - print(f"Fetched {len(page.data)} posts (total: {len(all_posts)})") - - print(f"\nTotal Posts: {len(all_posts)}") - print(json.dumps({"data": all_posts[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - -if __name__ == "__main__": - main() diff --git a/python/usage/get_usage.py b/python/usage/get_usage.py index 7eec457..6a1677d 100644 --- a/python/usage/get_usage.py +++ b/python/usage/get_usage.py @@ -18,9 +18,17 @@ client = Client(bearer_token=bearer_token) def main(): - response = client.usage.get_tweets_usage(days=7) + # Get usage statistics for tweets + # days: Number of days to retrieve usage for (default: 7) + # usage_fields: Fields to include in the response (optional) + response = client.usage.get(days=7) - print(json.dumps(response.data, indent=4, sort_keys=True)) + # Access data attribute safely + response_data = getattr(response, 'data', None) + if response_data: + print(json.dumps(response_data, indent=4, sort_keys=True)) + else: + print(json.dumps(response, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/usage/get_usage_tweets.py b/python/usage/get_usage_tweets.py deleted file mode 100644 index 9da6155..0000000 --- a/python/usage/get_usage_tweets.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Usage Tweets - X API v2 -======================= -Endpoint: GET https://api.x.com/2/usage/tweets -Docs: https://developer.x.com/en/docs/twitter-api/usage/api-reference/get-usage-tweets - -Authentication: Bearer Token (App-only) -Required env vars: BEARER_TOKEN - -Returns the number of posts read from the API. -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -def main(): - response = client.usage.get_tweets_usage() - - print(json.dumps(response.data, indent=4, sort_keys=True)) - -if __name__ == "__main__": - main() diff --git a/python/users/block_a_user.py b/python/users/block_a_user.py deleted file mode 100644 index 87ac115..0000000 --- a/python/users/block_a_user.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -Block User - X API v2 -===================== -Endpoint: POST https://api.x.com/2/users/:id/blocking -Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/post-users-user_id-blocking - -Authentication: OAuth 2.0 (User Context) -Required env vars: CLIENT_ID, CLIENT_SECRET -""" - -import os -import json -from xdk import Client -from xdk.oauth2_auth import OAuth2PKCEAuth - -# The code below sets the client ID and client secret from your environment variables -# To set environment variables on macOS or Linux, run the export commands below from the terminal: -# export CLIENT_ID='YOUR-CLIENT-ID' -# export CLIENT_SECRET='YOUR-CLIENT-SECRET' -client_id = os.environ.get("CLIENT_ID") -client_secret = os.environ.get("CLIENT_SECRET") - -# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://example.com" - -# Set the scopes -scopes = ["tweet.read", "users.read", "tweet.write", "offline.access", "block.write"] - -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - -# Be sure to replace id-to-block with the user id you wish to block. -target_user_id = "id-to-block" - -def main(): - # Step 1: Create PKCE instance - auth = OAuth2PKCEAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, - scope=scopes - ) - - # Step 2: Get authorization URL - auth_url = auth.get_authorization_url() - print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") - print(auth_url) - - # Step 3: Handle callback - callback_url = input("Paste the full callback URL here: ") - - # Step 4: Exchange code for tokens - tokens = auth.fetch_token(authorization_response=callback_url) - access_token = tokens["access_token"] - - # Step 5: Create client - client = Client(access_token=access_token) - - # Step 6: Block the user - payload = {"target_user_id": target_user_id} - response = client.users.block_user(user_id, body=payload) - - print("Response code: 200") - print(json.dumps(response.data, indent=4, sort_keys=True)) - -if __name__ == "__main__": - main() diff --git a/python/users/blocked.py b/python/users/blocked.py deleted file mode 100644 index edaf9ac..0000000 --- a/python/users/blocked.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -Blocked Users Lookup - X API v2 -=============================== -Endpoint: GET https://api.x.com/2/users/:id/blocking -Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/get-users-blocking - -Authentication: OAuth 2.0 (User Context) -Required env vars: CLIENT_ID, CLIENT_SECRET -""" - -import os -import json -from xdk import Client -from xdk.oauth2_auth import OAuth2PKCEAuth - -# The code below sets the client ID and client secret from your environment variables -# To set environment variables on macOS or Linux, run the export commands below from the terminal: -# export CLIENT_ID='YOUR-CLIENT-ID' -# export CLIENT_SECRET='YOUR-CLIENT-SECRET' -client_id = os.environ.get("CLIENT_ID") -client_secret = os.environ.get("CLIENT_SECRET") - -# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://example.com" - -# Set the scopes -scopes = ["tweet.read", "users.read", "offline.access", "block.read"] - -# Replace with your own user ID -user_id = "your-user-id" - -def main(): - # Step 1: Create PKCE instance - auth = OAuth2PKCEAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, - scope=scopes - ) - - # Step 2: Get authorization URL - auth_url = auth.get_authorization_url() - print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") - print(auth_url) - - # Step 3: Handle callback - callback_url = input("Paste the full callback URL here: ") - - # Step 4: Exchange code for tokens - tokens = auth.fetch_token(authorization_response=callback_url) - access_token = tokens["access_token"] - - # Step 5: Create client - client = Client(access_token=access_token) - - # Step 6: Get blocked users with automatic pagination - # User fields are adjustable. Options include: - # created_at, description, entities, id, location, name, - # pinned_tweet_id, profile_image_url, protected, - # public_metrics, url, username, verified, and withheld - all_users = [] - for page in client.users.get_blocking( - user_id, - max_results=100, - userfields=["created_at", "description"] - ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") - - print(f"\nTotal Blocked Users: {len(all_users)}") - print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - -if __name__ == "__main__": - main() diff --git a/python/users/followers_lookup.py b/python/users/followers_lookup.py deleted file mode 100644 index ab2bf4a..0000000 --- a/python/users/followers_lookup.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -User Followers Lookup - X API v2 -================================ -Endpoint: GET https://api.x.com/2/users/:id/followers -Docs: https://developer.x.com/en/docs/twitter-api/users/follows/api-reference/get-users-id-followers - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -# Replace with user ID below -user_id = "2244994945" - -def main(): - # Get followers with automatic pagination - all_users = [] - for page in client.users.get_followers( - user_id, - max_results=100, - userfields=["created_at"] - ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") - - print(f"\nTotal Followers: {len(all_users)}") - print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - -if __name__ == "__main__": - main() diff --git a/python/users/following_lookup.py b/python/users/following_lookup.py deleted file mode 100644 index 7054f25..0000000 --- a/python/users/following_lookup.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -User Following Lookup - X API v2 -================================ -Endpoint: GET https://api.x.com/2/users/:id/following -Docs: https://developer.x.com/en/docs/twitter-api/users/follows/api-reference/get-users-id-following - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -# Replace with user ID below -user_id = "2244994945" - -def main(): - # Get following with automatic pagination - all_users = [] - for page in client.users.get_following( - user_id, - max_results=100, - userfields=["created_at"] - ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") - - print(f"\nTotal Following: {len(all_users)}") - print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - -if __name__ == "__main__": - main() diff --git a/python/users/lookup_blocks.py b/python/users/get_blocking.py similarity index 60% rename from python/users/lookup_blocks.py rename to python/users/get_blocking.py index fea3ec8..3d3f4eb 100644 --- a/python/users/lookup_blocks.py +++ b/python/users/get_blocking.py @@ -12,6 +12,7 @@ import json from xdk import Client from xdk.oauth2_auth import OAuth2PKCEAuth +from requests.exceptions import HTTPError # The code below sets the client ID and client secret from your environment variables # To set environment variables on macOS or Linux, run the export commands below from the terminal: @@ -26,10 +27,6 @@ # Set the scopes scopes = ["tweet.read", "users.read", "offline.access", "block.read"] -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - def main(): # Step 1: Create PKCE instance auth = OAuth2PKCEAuth( @@ -54,22 +51,41 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Get blocked users with automatic pagination + # Step 6: Get the authenticated user's ID + me_response = client.users.get_me() + user_id = me_response.data["id"] + + # Step 7: Get blocked users with automatic pagination # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld all_users = [] - for page in client.users.get_blocking( - user_id, - max_results=100, - userfields=["created_at", "description"] - ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") - - print(f"\nTotal Blocked Users: {len(all_users)}") - print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + try: + for page in client.users.get_blocking( + user_id, + max_results=100, + user_fields=["created_at", "description"] + ): + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") + + print(f"\nTotal Blocked Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + except HTTPError as e: + print(f"Error occurred: {e}") + if hasattr(e.response, 'json'): + try: + error_data = e.response.json() + if 'errors' in error_data: + print("Detailed errors:") + print(json.dumps(error_data['errors'], indent=2)) + except: + pass + raise if __name__ == "__main__": main() diff --git a/python/users/followers.py b/python/users/get_followers.py similarity index 72% rename from python/users/followers.py rename to python/users/get_followers.py index 216a12a..93eecee 100644 --- a/python/users/followers.py +++ b/python/users/get_followers.py @@ -24,10 +24,13 @@ def main(): for page in client.users.get_followers( user_id, max_results=100, - userfields=["created_at"] + user_fields=["created_at"] ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") print(f"\nTotal Followers: {len(all_users)}") print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/users/following.py b/python/users/get_following.py similarity index 72% rename from python/users/following.py rename to python/users/get_following.py index 546a37c..7b7c83d 100644 --- a/python/users/following.py +++ b/python/users/get_following.py @@ -24,10 +24,13 @@ def main(): for page in client.users.get_following( user_id, max_results=100, - userfields=["created_at"] + user_fields=["created_at"] ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") print(f"\nTotal Following: {len(all_users)}") print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example diff --git a/python/users/lookup_mutes.py b/python/users/get_muting.py similarity index 60% rename from python/users/lookup_mutes.py rename to python/users/get_muting.py index 2ea8c7e..e84509f 100644 --- a/python/users/lookup_mutes.py +++ b/python/users/get_muting.py @@ -12,6 +12,7 @@ import json from xdk import Client from xdk.oauth2_auth import OAuth2PKCEAuth +from requests.exceptions import HTTPError # The code below sets the client ID and client secret from your environment variables # To set environment variables on macOS or Linux, run the export commands below from the terminal: @@ -26,10 +27,6 @@ # Set the scopes scopes = ["tweet.read", "users.read", "offline.access", "mute.read"] -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - def main(): # Step 1: Create PKCE instance auth = OAuth2PKCEAuth( @@ -54,22 +51,41 @@ def main(): # Step 5: Create client client = Client(access_token=access_token) - # Step 6: Get muted users with automatic pagination + # Step 6: Get the authenticated user's ID + me_response = client.users.get_me() + user_id = me_response.data["id"] + + # Step 7: Get muted users with automatic pagination # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld all_users = [] - for page in client.users.get_muting( - user_id, - max_results=100, - userfields=["created_at", "description"] - ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") - - print(f"\nTotal Muted Users: {len(all_users)}") - print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + try: + for page in client.users.get_muting( + user_id, + max_results=100, + user_fields=["created_at", "description"] + ): + # Access data attribute (model uses extra='allow' so data should be available) + # Use getattr with fallback in case data field is missing from response + page_data = getattr(page, 'data', []) or [] + all_users.extend(page_data) + print(f"Fetched {len(page_data)} users (total: {len(all_users)})") + + print(f"\nTotal Muted Users: {len(all_users)}") + print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example + except HTTPError as e: + print(f"Error occurred: {e}") + if hasattr(e.response, 'json'): + try: + error_data = e.response.json() + if 'errors' in error_data: + print("Detailed errors:") + print(json.dumps(error_data['errors'], indent=2)) + except: + pass + raise if __name__ == "__main__": main() \ No newline at end of file diff --git a/python/users/get_users_with_user_context.py b/python/users/get_user_by_usernams_user_context.py similarity index 84% rename from python/users/get_users_with_user_context.py rename to python/users/get_user_by_usernams_user_context.py index 94be02f..056e382 100644 --- a/python/users/get_users_with_user_context.py +++ b/python/users/get_user_by_usernams_user_context.py @@ -27,7 +27,7 @@ scopes = ["tweet.read", "users.read", "offline.access"] # Specify the usernames that you want to lookup (list, up to 100) -usernames = ["TwitterDev", "TwitterAPI"] +usernames = ["XDevelopers", "API"] def main(): # Step 1: Create PKCE instance @@ -58,12 +58,17 @@ def main(): # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - response = client.users.get_users_by( + response = client.users.get_by_usernames( usernames=usernames, - userfields=["created_at", "description"] + user_fields=["created_at", "description"] ) - print(json.dumps(response.data, indent=4, sort_keys=True)) + # Access data attribute safely + response_data = getattr(response, 'data', None) + if response_data: + print(json.dumps(response_data, indent=4, sort_keys=True)) + else: + print(json.dumps(response, indent=4, sort_keys=True)) if __name__ == "__main__": main() diff --git a/python/users/get_users_with_bearer_token.py b/python/users/get_users_by_usernames_bearer_token.py similarity index 67% rename from python/users/get_users_with_bearer_token.py rename to python/users/get_users_by_usernames_bearer_token.py index 8144035..453e353 100644 --- a/python/users/get_users_with_bearer_token.py +++ b/python/users/get_users_by_usernames_bearer_token.py @@ -16,19 +16,24 @@ client = Client(bearer_token=bearer_token) # Specify the usernames that you want to lookup (list, up to 100) -usernames = ["TwitterDev", "TwitterAPI"] +usernames = ["XDevelopers", "API"] def main(): # User fields are adjustable, options include: # created_at, description, entities, id, location, name, # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld - response = client.users.get_users_by( + response = client.users.get_by_usernames( usernames=usernames, - userfields=["description", "created_at"] + user_fields=["description", "created_at"] ) - print(json.dumps(response.data, indent=4, sort_keys=True)) + # Access data attribute safely + response_data = getattr(response, 'data', None) + if response_data: + print(json.dumps(response_data, indent=4, sort_keys=True)) + else: + print(json.dumps(response, indent=4, sort_keys=True)) if __name__ == "__main__": diff --git a/python/users/get_users_me_user_context.py b/python/users/get_users_me_user_context.py index 7308d94..8f02294 100644 --- a/python/users/get_users_me_user_context.py +++ b/python/users/get_users_me_user_context.py @@ -56,7 +56,7 @@ def main(): # pinned_tweet_id, profile_image_url, protected, # public_metrics, url, username, verified, and withheld response = client.users.get_me( - userfields=["created_at", "description"] + user_fields=["created_at", "description"] ) print(json.dumps(response.data, indent=4, sort_keys=True)) diff --git a/python/users/lookup.py b/python/users/lookup.py deleted file mode 100644 index 7fe48c6..0000000 --- a/python/users/lookup.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -User Lookup - X API v2 -====================== -Endpoint: GET https://api.x.com/2/users/by -Docs: https://developer.x.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by - -Authentication: Bearer Token (App-only) or OAuth (User Context) -Required env vars: BEARER_TOKEN -""" - -import os -import json -from xdk import Client - -bearer_token = os.environ.get("BEARER_TOKEN") -client = Client(bearer_token=bearer_token) - -# Specify the usernames to lookup (list, up to 100) -usernames = ["XDevelopers", "X"] - -def main(): - # User fields are adjustable. Options include: - # created_at, description, entities, id, location, name, - # pinned_tweet_id, profile_image_url, protected, - # public_metrics, url, username, verified, and withheld - response = client.users.get_users_by( - usernames=usernames, - userfields=["description", "created_at"] - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - - -if __name__ == "__main__": - main() diff --git a/python/users/me.py b/python/users/me.py deleted file mode 100644 index 1de488b..0000000 --- a/python/users/me.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -Authenticated User Lookup (Me) - X API v2 -========================================= -Endpoint: GET https://api.x.com/2/users/me -Docs: https://developer.x.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me - -Authentication: OAuth 2.0 (User Context) -Required env vars: CLIENT_ID, CLIENT_SECRET -""" - -import os -import json -from xdk import Client -from xdk.oauth2_auth import OAuth2PKCEAuth - -# The code below sets the client ID and client secret from your environment variables -# To set environment variables on macOS or Linux, run the export commands below from the terminal: -# export CLIENT_ID='YOUR-CLIENT-ID' -# export CLIENT_SECRET='YOUR-CLIENT-SECRET' -client_id = os.environ.get("CLIENT_ID") -client_secret = os.environ.get("CLIENT_SECRET") - -# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://example.com" - -# Set the scopes -scopes = ["tweet.read", "users.read", "offline.access"] - -def main(): - # Step 1: Create PKCE instance - auth = OAuth2PKCEAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, - scope=scopes - ) - - # Step 2: Get authorization URL - auth_url = auth.get_authorization_url() - print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") - print(auth_url) - - # Step 3: Handle callback - callback_url = input("Paste the full callback URL here: ") - - # Step 4: Exchange code for tokens - tokens = auth.fetch_token(authorization_response=callback_url) - access_token = tokens["access_token"] - - # Step 5: Create client - client = Client(access_token=access_token) - - # Step 6: Get authenticated user info - # User fields are adjustable. Options include: - # created_at, description, entities, id, location, name, - # pinned_tweet_id, profile_image_url, protected, - # public_metrics, url, username, verified, and withheld - response = client.users.get_me( - userfields=["description", "created_at"] - ) - - print(json.dumps(response.data, indent=4, sort_keys=True)) - -if __name__ == "__main__": - main() diff --git a/python/users/mute_a_user.py b/python/users/mute_user.py similarity index 100% rename from python/users/mute_a_user.py rename to python/users/mute_user.py diff --git a/python/users/muted.py b/python/users/muted.py deleted file mode 100644 index 1bf64db..0000000 --- a/python/users/muted.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -Muted Users Lookup - X API v2 -============================= -Endpoint: GET https://api.x.com/2/users/:id/muting -Docs: https://developer.x.com/en/docs/twitter-api/users/mutes/api-reference/get-users-muting - -Authentication: OAuth 2.0 (User Context) -Required env vars: CLIENT_ID, CLIENT_SECRET -""" - -import os -import json -from xdk import Client -from xdk.oauth2_auth import OAuth2PKCEAuth - -# The code below sets the client ID and client secret from your environment variables -# To set environment variables on macOS or Linux, run the export commands below from the terminal: -# export CLIENT_ID='YOUR-CLIENT-ID' -# export CLIENT_SECRET='YOUR-CLIENT-SECRET' -client_id = os.environ.get("CLIENT_ID") -client_secret = os.environ.get("CLIENT_SECRET") - -# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://example.com" - -# Set the scopes -scopes = ["tweet.read", "users.read", "offline.access", "mute.read"] - -# Replace with your own user ID -user_id = "your-user-id" - -def main(): - # Step 1: Create PKCE instance - auth = OAuth2PKCEAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, - scope=scopes - ) - - # Step 2: Get authorization URL - auth_url = auth.get_authorization_url() - print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") - print(auth_url) - - # Step 3: Handle callback - callback_url = input("Paste the full callback URL here: ") - - # Step 4: Exchange code for tokens - tokens = auth.fetch_token(authorization_response=callback_url) - access_token = tokens["access_token"] - - # Step 5: Create client - client = Client(access_token=access_token) - - # Step 6: Get muted users with automatic pagination - # User fields are adjustable. Options include: - # created_at, description, entities, id, location, name, - # pinned_tweet_id, profile_image_url, protected, - # public_metrics, url, username, verified, and withheld - all_users = [] - for page in client.users.get_muting( - user_id, - max_results=100, - userfields=["created_at", "description"] - ): - all_users.extend(page.data) - print(f"Fetched {len(page.data)} users (total: {len(all_users)})") - - print(f"\nTotal Muted Users: {len(all_users)}") - print(json.dumps({"data": all_users[:5]}, indent=4, sort_keys=True)) # Print first 5 as example - -if __name__ == "__main__": - main() diff --git a/python/users/unblock_a_user.py b/python/users/unblock_a_user.py deleted file mode 100644 index 21fe778..0000000 --- a/python/users/unblock_a_user.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Unblock User - X API v2 -======================= -Endpoint: DELETE https://api.x.com/2/users/:source_user_id/blocking/:target_user_id -Docs: https://developer.x.com/en/docs/twitter-api/users/blocks/api-reference/delete-users-user_id-blocking - -Authentication: OAuth 2.0 (User Context) -Required env vars: CLIENT_ID, CLIENT_SECRET -""" - -import os -import json -from xdk import Client -from xdk.oauth2_auth import OAuth2PKCEAuth - -# The code below sets the client ID and client secret from your environment variables -# To set environment variables on macOS or Linux, run the export commands below from the terminal: -# export CLIENT_ID='YOUR-CLIENT-ID' -# export CLIENT_SECRET='YOUR-CLIENT-SECRET' -client_id = os.environ.get("CLIENT_ID") -client_secret = os.environ.get("CLIENT_SECRET") - -# Replace the following URL with your callback URL, which can be obtained from your App's auth settings. -redirect_uri = "https://example.com" - -# Set the scopes -scopes = ["tweet.read", "users.read", "tweet.write", "offline.access", "block.write"] - -# Be sure to replace your-user-id with your own user ID or one of an authenticated user -# You can find a user ID by using the user lookup endpoint -user_id = "your-user-id" - -# Be sure to replace id-to-unblock with the id of the user you wish to unblock. -# You can find a user ID by using the user lookup endpoint -target_user_id = "id-to-unblock" - -def main(): - # Step 1: Create PKCE instance - auth = OAuth2PKCEAuth( - client_id=client_id, - client_secret=client_secret, - redirect_uri=redirect_uri, - scope=scopes - ) - - # Step 2: Get authorization URL - auth_url = auth.get_authorization_url() - print("Visit the following URL to authorize your App on behalf of your X handle in a browser:") - print(auth_url) - - # Step 3: Handle callback - callback_url = input("Paste the full callback URL here: ") - - # Step 4: Exchange code for tokens - tokens = auth.fetch_token(authorization_response=callback_url) - access_token = tokens["access_token"] - - # Step 5: Create client - client = Client(access_token=access_token) - - # Step 6: Unblock the user - response = client.users.unblock_user(user_id, target_user_id) - - print("Response code: 200") - print(json.dumps(response.data, indent=4, sort_keys=True)) - - # Note: If you were following the user you blocked, you have removed your follow and will have to refollow the user, even if you did unblock the user. - -if __name__ == "__main__": - main() diff --git a/python/users/unmute_a_user.py b/python/users/unmute_user.py similarity index 100% rename from python/users/unmute_a_user.py rename to python/users/unmute_user.py