Skip to content

Commit 99992dd

Browse files
authored
Merge pull request #212 from xdevplatform/subscriptions
missing scripts for subscriptions, webhooks, posts, follows
2 parents 3356e06 + 5471b09 commit 99992dd

16 files changed

+767
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
Create Activity Subscription - X API v2
3+
========================================
4+
Endpoint: POST https://api.x.com/2/activity/subscriptions
5+
Docs: https://docs.x.com/x-api/activity/introduction
6+
7+
Creates a subscription to receive real-time activity events for a specified
8+
event type and filter. Once created, matching events will be delivered to the
9+
activity stream (see stream_events.py) and optionally to a registered webhook.
10+
11+
Supported public event types include:
12+
- profile.update.bio
13+
- profile.update.picture
14+
- profile.update.banner
15+
- profile.update.location
16+
- profile.update.url
17+
- profile.update.username
18+
19+
Authentication: Bearer Token (App-only)
20+
Required env vars: BEARER_TOKEN
21+
"""
22+
23+
import os
24+
import json
25+
from xdk import Client
26+
27+
bearer_token = os.environ.get("BEARER_TOKEN")
28+
client = Client(bearer_token=bearer_token)
29+
30+
# Replace with the user ID you want to monitor for activity events
31+
user_id = "2244994945"
32+
33+
# Replace with the event type you want to subscribe to.
34+
# See the supported event types listed in the docstring above.
35+
event_type = "profile.update.bio"
36+
37+
# Optional: replace with a registered webhook ID to also receive events via webhook delivery.
38+
# If omitted, events are only available on the activity stream.
39+
webhook_id = None
40+
41+
def main():
42+
payload = {
43+
"event_type": event_type,
44+
"filter": {
45+
"user_id": user_id
46+
}
47+
}
48+
49+
# Attach a label to help identify this subscription in the stream
50+
payload["tag"] = f"{event_type} for user {user_id}"
51+
52+
# Optionally route events to a registered webhook in addition to the stream
53+
if webhook_id:
54+
payload["webhook_id"] = webhook_id
55+
56+
response = client.activity.create_subscription(body=payload)
57+
58+
print("Response code: 201")
59+
print(json.dumps(response.data, indent=4, sort_keys=True))
60+
61+
62+
if __name__ == "__main__":
63+
main()
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""
2+
Delete Activity Subscription - X API v2
3+
========================================
4+
Endpoint: DELETE https://api.x.com/2/activity/subscriptions/:id
5+
Docs: https://docs.x.com/x-api/activity/introduction
6+
7+
Deletes an activity subscription. Once deleted, events matching that subscription
8+
will no longer be delivered to the stream or associated webhook. Use
9+
list_subscriptions.py to find the subscription_id you wish to remove.
10+
11+
Authentication: Bearer Token (App-only)
12+
Required env vars: BEARER_TOKEN
13+
"""
14+
15+
import os
16+
import json
17+
from xdk import Client
18+
19+
bearer_token = os.environ.get("BEARER_TOKEN")
20+
client = Client(bearer_token=bearer_token)
21+
22+
# Replace with the subscription ID you wish to delete.
23+
# You can find subscription IDs by running list_subscriptions.py
24+
subscription_id = "your-subscription-id"
25+
26+
def main():
27+
response = client.activity.delete_subscription(subscription_id)
28+
29+
print("Response code: 200")
30+
print(json.dumps(response.data, indent=4, sort_keys=True))
31+
32+
33+
if __name__ == "__main__":
34+
main()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
List Activity Subscriptions - X API v2
3+
=======================================
4+
Endpoint: GET https://api.x.com/2/activity/subscriptions
5+
Docs: https://docs.x.com/x-api/activity/introduction
6+
7+
Returns all active activity subscriptions for your app. Use the subscription_id
8+
from the response to update or delete individual subscriptions.
9+
10+
Authentication: Bearer Token (App-only)
11+
Required env vars: BEARER_TOKEN
12+
"""
13+
14+
import os
15+
import json
16+
from xdk import Client
17+
18+
bearer_token = os.environ.get("BEARER_TOKEN")
19+
client = Client(bearer_token=bearer_token)
20+
21+
def main():
22+
response = client.activity.get_subscriptions()
23+
24+
# Access data attribute safely
25+
response_data = getattr(response, 'data', None)
26+
if response_data:
27+
print(json.dumps(response_data, indent=4, sort_keys=True))
28+
else:
29+
print(json.dumps(response, indent=4, sort_keys=True))
30+
31+
32+
if __name__ == "__main__":
33+
main()

python/activity/stream_events.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
Activity Stream - X API v2
3+
==========================
4+
Endpoint: GET https://api.x.com/2/activity/stream
5+
Docs: https://docs.x.com/x-api/activity/introduction
6+
7+
Opens a persistent HTTP connection and streams real-time activity events
8+
matching your active subscriptions. Events are delivered as they occur on
9+
the platform — no polling required.
10+
11+
You must create at least one subscription (see create_subscription.py) before
12+
events will be delivered to this stream.
13+
14+
Authentication: Bearer Token (App-only)
15+
Required env vars: BEARER_TOKEN
16+
"""
17+
18+
import os
19+
import json
20+
from xdk import Client
21+
22+
bearer_token = os.environ.get("BEARER_TOKEN")
23+
client = Client(bearer_token=bearer_token)
24+
25+
def main():
26+
print("Connecting to activity stream... (press Ctrl+C to stop)")
27+
28+
# The stream() method returns a generator that yields events as they arrive.
29+
# The SDK manages reconnection with exponential backoff automatically.
30+
for event in client.activity.stream():
31+
# Access data attribute (model uses extra='allow' so data should be available)
32+
# Use getattr with fallback in case data field is missing from response
33+
event_data = getattr(event, 'data', None)
34+
if event_data:
35+
print(json.dumps(event_data, indent=4, sort_keys=True))
36+
37+
38+
if __name__ == "__main__":
39+
main()
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""
2+
Update Activity Subscription - X API v2
3+
========================================
4+
Endpoint: PUT https://api.x.com/2/activity/subscriptions/:id
5+
Docs: https://docs.x.com/x-api/activity/introduction
6+
7+
Updates an existing activity subscription. You can change the filter (e.g. target
8+
a different user ID), the tag, or the associated webhook. Use list_subscriptions.py
9+
to find the subscription_id you wish to update.
10+
11+
Authentication: Bearer Token (App-only)
12+
Required env vars: BEARER_TOKEN
13+
"""
14+
15+
import os
16+
import json
17+
from xdk import Client
18+
19+
bearer_token = os.environ.get("BEARER_TOKEN")
20+
client = Client(bearer_token=bearer_token)
21+
22+
# Replace with the subscription ID you wish to update.
23+
# You can find subscription IDs by running list_subscriptions.py
24+
subscription_id = "your-subscription-id"
25+
26+
# Replace with the updated user ID you want to monitor
27+
updated_user_id = "2244994945"
28+
29+
def main():
30+
payload = {
31+
"filter": {
32+
"user_id": updated_user_id
33+
}
34+
}
35+
36+
response = client.activity.update_subscription(subscription_id, body=payload)
37+
38+
print("Response code: 200")
39+
print(json.dumps(response.data, indent=4, sort_keys=True))
40+
41+
42+
if __name__ == "__main__":
43+
main()

python/posts/get_post_by_id.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""
2+
Single Post Lookup - X API v2
3+
=============================
4+
Endpoint: GET https://api.x.com/2/tweets/:id
5+
Docs: https://developer.x.com/en/docs/twitter-api/tweets/lookup/api-reference/get-tweets-id
6+
7+
Authentication: Bearer Token (App-only) or OAuth (User Context)
8+
Required env vars: BEARER_TOKEN
9+
"""
10+
11+
import os
12+
import json
13+
from xdk import Client
14+
15+
bearer_token = os.environ.get("BEARER_TOKEN")
16+
client = Client(bearer_token=bearer_token)
17+
18+
# Replace with the Post ID you want to look up
19+
post_id = "post-id"
20+
21+
def main():
22+
# Post fields are adjustable. Options include:
23+
# attachments, author_id, context_annotations, conversation_id,
24+
# created_at, entities, geo, id, in_reply_to_user_id, lang,
25+
# non_public_metrics, organic_metrics, possibly_sensitive,
26+
# promoted_metrics, public_metrics, referenced_tweets,
27+
# source, text, and withheld
28+
response = client.posts.get_by_id(
29+
post_id,
30+
tweet_fields=["created_at", "author_id", "lang", "source", "public_metrics", "entities"]
31+
)
32+
33+
print(json.dumps(response.data, indent=4, sort_keys=True))
34+
35+
36+
if __name__ == "__main__":
37+
main()

python/posts/hide_reply.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Hide Reply - X API v2
3+
=====================
4+
Endpoint: PUT https://api.x.com/2/tweets/:id/hidden
5+
Docs: https://developer.x.com/en/docs/twitter-api/tweets/hide-replies/api-reference/put-tweets-id-hidden
6+
7+
Authentication: OAuth 2.0 (User Context)
8+
Required env vars: CLIENT_ID, CLIENT_SECRET
9+
10+
Note: You can only hide or unhide replies to conversations you authored.
11+
Pass hidden=True to hide a reply, or hidden=False to unhide one.
12+
"""
13+
14+
import os
15+
import json
16+
from xdk import Client
17+
from xdk.oauth2_auth import OAuth2PKCEAuth
18+
19+
# The code below sets the client ID and client secret from your environment variables
20+
# To set environment variables on macOS or Linux, run the export commands below from the terminal:
21+
# export CLIENT_ID='YOUR-CLIENT-ID'
22+
# export CLIENT_SECRET='YOUR-CLIENT-SECRET'
23+
client_id = os.environ.get("CLIENT_ID")
24+
client_secret = os.environ.get("CLIENT_SECRET")
25+
26+
# Replace the following URL with your callback URL, which can be obtained from your App's auth settings.
27+
redirect_uri = "https://example.com"
28+
29+
# Set the scopes
30+
scopes = ["tweet.read", "tweet.write", "users.read", "offline.access"]
31+
32+
# Replace with the ID of the reply you wish to hide.
33+
# You can only hide replies to conversations you authored.
34+
tweet_id = "reply-tweet-id-to-hide"
35+
36+
def main():
37+
# Step 1: Create PKCE instance
38+
auth = OAuth2PKCEAuth(
39+
client_id=client_id,
40+
client_secret=client_secret,
41+
redirect_uri=redirect_uri,
42+
scope=scopes
43+
)
44+
45+
# Step 2: Get authorization URL
46+
auth_url = auth.get_authorization_url()
47+
print("Visit the following URL to authorize your App on behalf of your X handle in a browser:")
48+
print(auth_url)
49+
50+
# Step 3: Handle callback
51+
callback_url = input("Paste the full callback URL here: ")
52+
53+
# Step 4: Exchange code for tokens
54+
tokens = auth.fetch_token(authorization_response=callback_url)
55+
access_token = tokens["access_token"]
56+
57+
# Step 5: Create client
58+
client = Client(access_token=access_token)
59+
60+
# Step 6: Hide the reply
61+
# Set hidden=False to unhide a previously hidden reply
62+
response = client.posts.hide_reply(tweet_id, hidden=True)
63+
64+
print("Response code: 200")
65+
print(json.dumps(response.data, indent=4, sort_keys=True))
66+
67+
68+
if __name__ == "__main__":
69+
main()

python/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
xdk>=0.4.5
2+
flask>=3.0.0
3+
waitress>=3.0.0

python/users/follow/follow_user.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
Follow User - X API v2
3+
======================
4+
Endpoint: POST https://api.x.com/2/users/:id/following
5+
Docs: https://developer.x.com/en/docs/twitter-api/users/follows/api-reference/post-users-id-following
6+
7+
Authentication: OAuth 2.0 (User Context)
8+
Required env vars: CLIENT_ID, CLIENT_SECRET
9+
"""
10+
11+
import os
12+
import json
13+
from xdk import Client
14+
from xdk.oauth2_auth import OAuth2PKCEAuth
15+
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'
20+
client_id = os.environ.get("CLIENT_ID")
21+
client_secret = os.environ.get("CLIENT_SECRET")
22+
23+
# Replace the following URL with your callback URL, which can be obtained from your App's auth settings.
24+
redirect_uri = "https://example.com"
25+
26+
# Set the scopes
27+
scopes = ["tweet.read", "users.read", "follows.write", "offline.access"]
28+
29+
# Be sure to replace user-id-to-follow with the user id you wish to follow.
30+
# You can find a user ID by using the user lookup endpoint
31+
target_user_id = "user-id-to-follow"
32+
33+
def main():
34+
# Step 1: Create PKCE instance
35+
auth = OAuth2PKCEAuth(
36+
client_id=client_id,
37+
client_secret=client_secret,
38+
redirect_uri=redirect_uri,
39+
scope=scopes
40+
)
41+
42+
# Step 2: Get authorization URL
43+
auth_url = auth.get_authorization_url()
44+
print("Visit the following URL to authorize your App on behalf of your X handle in a browser:")
45+
print(auth_url)
46+
47+
# Step 3: Handle callback
48+
callback_url = input("Paste the full callback URL here: ")
49+
50+
# Step 4: Exchange code for tokens
51+
tokens = auth.fetch_token(authorization_response=callback_url)
52+
access_token = tokens["access_token"]
53+
54+
# Step 5: Create client
55+
client = Client(access_token=access_token)
56+
57+
# Step 6: Get the authenticated user's ID
58+
me_response = client.users.get_me()
59+
user_id = me_response.data["id"]
60+
61+
# Step 7: Follow the user
62+
payload = {"target_user_id": target_user_id}
63+
response = client.users.follow_user(user_id, body=payload)
64+
65+
print("Response code: 200")
66+
print(json.dumps(response.data, indent=4, sort_keys=True))
67+
68+
69+
if __name__ == "__main__":
70+
main()

0 commit comments

Comments
 (0)