Skip to content

Commit c4e6d01

Browse files
authored
Merge pull request #208 from xdevplatform/update-with-python-xdk
Update Python samples with XDK
2 parents 4675040 + 9f4cc14 commit c4e6d01

File tree

149 files changed

+3393
-7028
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+3393
-7028
lines changed

python/README.md

Lines changed: 20 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -19,45 +19,25 @@ export CONSUMER_SECRET='your_consumer_secret'
1919
## Examples
2020

2121
### Posts
22-
- posts/counts_full_archive.py
23-
- posts/counts_recent.py
2422
- posts/create_post.py
25-
- posts/create_tweet.py
2623
- posts/delete_post.py
27-
- posts/delete_tweet.py
28-
- posts/full_archive_tweet_counts.py
29-
- posts/full-archive-search.py
30-
- posts/get_tweets_with_bearer_token.py
31-
- posts/get_tweets_with_user_context.py
32-
- posts/like_a_tweet.py
33-
- posts/like.py
34-
- posts/liked_posts.py
35-
- posts/liked_tweets.py
36-
- posts/liking_users.py
37-
- posts/lookup.py
38-
- posts/quote_posts.py
39-
- posts/quote_tweets.py
40-
- posts/recent_search.py
41-
- posts/recent_tweet_counts.py
42-
- posts/repost.py
43-
- posts/reposted_by.py
44-
- posts/retweet_a_tweet.py
24+
- posts/get_liked_posts.py
25+
- posts/get_liking_users.py
26+
- posts/get_post_counts_all.py
27+
- posts/get_post_counts_recent.py
28+
- posts/get_posts_by_ids.py
29+
- posts/get_quoted_posts.py
30+
- posts/like_post.py
31+
- posts/repost_post.py
4532
- posts/retweeted_by.py
46-
- posts/search_full_archive.py
33+
- posts/search_all.py
4734
- posts/search_recent.py
4835
- posts/undo_a_retweet.py
49-
- posts/undo_repost.py
5036
- posts/unlike_a_tweet.py
51-
- posts/unlike.py
5237

5338
### Users
54-
- users/block_a_user.py
55-
- users/block.py
56-
- users/blocked.py
57-
- users/followers_lookup.py
58-
- users/followers.py
5939
- users/following_lookup.py
60-
- users/following.py
40+
- users/get_followers.py
6141
- users/get_users_me_user_context.py
6242
- users/get_users_with_bearer_token.py
6343
- users/get_users_with_user_context.py
@@ -66,37 +46,29 @@ export CONSUMER_SECRET='your_consumer_secret'
6646
- users/lookup.py
6747
- users/me.py
6848
- users/mute_a_user.py
69-
- users/mute.py
7049
- users/muted.py
7150
- users/unblock_a_user.py
72-
- users/unblock.py
7351
- users/unmute_a_user.py
74-
- users/unmute.py
7552

7653
### Timelines
77-
- timelines/home_timeline.py
54+
- timelines/get_mentions.py
55+
- timelines/get_posts.py
56+
- timelines/get_timeline.py
7857
- timelines/reverse-chron-home-timeline.py
79-
- timelines/user_mentions.py
80-
- timelines/user_posts.py
81-
- timelines/user_tweets.py
8258

8359
### Streams
8460
- streams/filtered_stream.py
8561
- streams/sampled_stream.py
86-
- streams/sampled-stream.py
8762

8863
### Lists
89-
- lists/add_member.py
90-
- lists/create_a_list.py
91-
- lists/create.py
92-
- lists/delete_a_list.py
93-
- lists/delete.py
64+
- lists/add_list_member.py
65+
- lists/create_list.py
66+
- lists/delete_list.py
9467
- lists/follow_list.py
95-
- lists/list-followers-lookup.py
96-
- lists/list-lookup-by-id.py
97-
- lists/list-member-lookup.py
98-
- lists/List-Tweets.py
99-
- lists/lookup.py
68+
- lists/get_list_by_id.py
69+
- lists/get_list_followers.py
70+
- lists/get_list_members.py
71+
- lists/get_list_posts.py
10072
- lists/pin_list.py
10173
- lists/Pinned-List.py
10274
- lists/remove_member.py
@@ -110,41 +82,32 @@ export CONSUMER_SECRET='your_consumer_secret'
11082
### Bookmarks
11183
- bookmarks/bookmarks_lookup.py
11284
- bookmarks/create_bookmark.py
113-
- bookmarks/create.py
11485
- bookmarks/delete_bookmark.py
115-
- bookmarks/delete.py
116-
- bookmarks/lookup.py
11786

11887
### Spaces
119-
- spaces/lookup.py
12088
- spaces/search_spaces.py
121-
- spaces/search.py
12289
- spaces/spaces_lookup.py
12390

12491
### Direct Messages
12592
- direct_messages/get_events_by_conversation.py
12693
- direct_messages/get_one_to_one_conversation_events.py
12794
- direct_messages/get_user_conversation_events.py
128-
- direct_messages/lookup.py
12995
- direct_messages/post_dm_to_conversation.py
13096
- direct_messages/post_group_conversation_dm.py
13197
- direct_messages/post_one_to_one_dm.py
132-
- direct_messages/send.py
13398

13499
### Media
135100
- media/media_upload_v2.py
136101
- media/upload.py
137102

138103
### Compliance
139104
- compliance/create_compliance_job.py
140-
- compliance/create_job.py
141105
- compliance/download_compliance_results.py
142106
- compliance/get_compliance_job_information_by_id.py
143107
- compliance/get_jobs.py
144108
- compliance/get_list_of_compliance_jobs.py
145109
- compliance/upload_ids.py
146110

147111
### Usage
148-
- usage/get_usage_tweets.py
149112
- usage/get_usage.py
150113

Lines changed: 60 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,71 @@
1-
import base64
2-
import hashlib
1+
"""
2+
Bookmarks Lookup - X API v2
3+
===========================
4+
Endpoint: GET https://api.x.com/2/users/:id/bookmarks
5+
Docs: https://developer.x.com/en/docs/twitter-api/bookmarks/api-reference/get-users-id-bookmarks
6+
7+
Authentication: OAuth 2.0 (User Context)
8+
Required env vars: CLIENT_ID, CLIENT_SECRET
9+
"""
10+
311
import os
4-
import re
512
import json
6-
import requests
7-
from requests.auth import AuthBase, HTTPBasicAuth
8-
from requests_oauthlib import OAuth2Session
13+
from xdk import Client
14+
from xdk.oauth2_auth import OAuth2PKCEAuth
915

10-
# First, you will need to enable OAuth 2.0 in your App’s auth settings in the Developer Portal to get your client ID.
11-
# Inside your terminal you will need to set an enviornment variable
12-
# export CLIENT_ID='your-client-id'
16+
# The code below sets the client ID and client secret from your environment variables
17+
# To set environment variables on macOS or Linux, run the export commands below from the terminal:
18+
# export CLIENT_ID='YOUR-CLIENT-ID'
19+
# export CLIENT_SECRET='YOUR-CLIENT-SECRET'
1320
client_id = os.environ.get("CLIENT_ID")
14-
15-
# If you have selected a type of App that is a confidential client you will need to set a client secret.
16-
# Confidential Clients securely authenticate with the authorization server.
17-
18-
# Inside your terminal you will need to set an enviornment variable
19-
# export CLIENT_SECRET='your-client-secret'
20-
21-
# Remove the comment on the following line if you are using a confidential client
22-
# client_secret = os.environ.get("CLIENT_SECRET")
21+
client_secret = os.environ.get("CLIENT_SECRET")
2322

2423
# Replace the following URL with your callback URL, which can be obtained from your App's auth settings.
25-
redirect_uri = "https://www.example.com"
24+
redirect_uri = "https://example.com"
2625

2726
# Set the scopes
2827
scopes = ["bookmark.read", "tweet.read", "users.read", "offline.access"]
2928

30-
# Create a code verifier
31-
code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8")
32-
code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier)
33-
34-
# Create a code challenge
35-
code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest()
36-
code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8")
37-
code_challenge = code_challenge.replace("=", "")
38-
39-
# Start an OAuth 2.0 session
40-
oauth = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=scopes)
41-
42-
# Create an authorize URL
43-
auth_url = "https://twitter.com/i/oauth2/authorize"
44-
authorization_url, state = oauth.authorization_url(
45-
auth_url, code_challenge=code_challenge, code_challenge_method="S256"
46-
)
47-
48-
# Visit the URL to authorize your App to make requests on behalf of a user
49-
print(
50-
"Visit the following URL to authorize your App on behalf of your Twitter handle in a browser:"
51-
)
52-
print(authorization_url)
53-
54-
# Paste in your authorize URL to complete the request
55-
authorization_response = input(
56-
"Paste in the full URL after you've authorized your App:\n"
57-
)
58-
59-
# Fetch your access token
60-
token_url = "https://api.x.com/2/oauth2/token"
61-
62-
# The following line of code will only work if you are using a type of App that is a public client
63-
auth = False
64-
65-
# If you are using a confidential client you will need to pass in basic encoding of your client ID and client secret.
66-
67-
# Please remove the comment on the following line if you are using a type of App that is a confidential client
68-
# auth = HTTPBasicAuth(client_id, client_secret)
69-
70-
token = oauth.fetch_token(
71-
token_url=token_url,
72-
authorization_response=authorization_response,
73-
auth=auth,
74-
client_id=client_id,
75-
include_client_id=True,
76-
code_verifier=code_verifier,
77-
)
78-
79-
# Your access token
80-
access = token["access_token"]
81-
82-
# Make a request to the users/me endpoint to get your user ID
83-
user_me = requests.request(
84-
"GET",
85-
"https://api.x.com/2/users/me",
86-
headers={"Authorization": "Bearer {}".format(access)},
87-
).json()
88-
user_id = user_me["data"]["id"]
89-
90-
# Make a request to the bookmarks url
91-
url = "https://api.x.com/2/users/{}/bookmarks".format(user_id)
92-
headers = {
93-
"Authorization": "Bearer {}".format(access),
94-
"User-Agent": "BookmarksSampleCode",
95-
}
96-
response = requests.request("GET", url, headers=headers)
97-
if response.status_code != 200:
98-
raise Exception(
99-
"Request returned an error: {} {}".format(response.status_code, response.text)
29+
def main():
30+
# Step 1: Create PKCE instance
31+
auth = OAuth2PKCEAuth(
32+
client_id=client_id,
33+
client_secret=client_secret,
34+
redirect_uri=redirect_uri,
35+
scope=scopes
10036
)
101-
print("Response code: {}".format(response.status_code))
102-
json_response = response.json()
103-
print(json.dumps(json_response, indent=4, sort_keys=True))
37+
38+
# Step 2: Get authorization URL
39+
auth_url = auth.get_authorization_url()
40+
print("Visit the following URL to authorize your App on behalf of your X handle in a browser:")
41+
print(auth_url)
42+
43+
# Step 3: Handle callback
44+
callback_url = input("Paste the full callback URL here: ")
45+
46+
# Step 4: Exchange code for tokens
47+
tokens = auth.fetch_token(authorization_response=callback_url)
48+
access_token = tokens["access_token"]
49+
50+
# Step 5: Create client
51+
client = Client(access_token=access_token)
52+
53+
# Step 6: Get authenticated user ID
54+
user_me = client.users.get_me()
55+
user_id = user_me.data["id"]
56+
57+
# Step 7: Get bookmarks with automatic pagination
58+
all_bookmarks = []
59+
for page in client.users.get_bookmarks(
60+
user_id,
61+
max_results=100,
62+
tweetfields=["created_at"]
63+
):
64+
all_bookmarks.extend(page.data)
65+
print(f"Fetched {len(page.data)} bookmarks (total: {len(all_bookmarks)})")
66+
67+
print(f"\nTotal Bookmarks: {len(all_bookmarks)}")
68+
print(json.dumps({"data": all_bookmarks[:5]}, indent=4, sort_keys=True)) # Print first 5 as example
69+
70+
if __name__ == "__main__":
71+
main()

python/bookmarks/create.py

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)