tweetapi 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tweetapi/__init__.py +37 -0
- tweetapi/client.py +146 -0
- tweetapi/errors.py +122 -0
- tweetapi/py.typed +0 -0
- tweetapi/resources/__init__.py +0 -0
- tweetapi/resources/auth.py +18 -0
- tweetapi/resources/community.py +68 -0
- tweetapi/resources/explore.py +21 -0
- tweetapi/resources/interaction.py +86 -0
- tweetapi/resources/list_.py +27 -0
- tweetapi/resources/post.py +52 -0
- tweetapi/resources/space.py +19 -0
- tweetapi/resources/tweet.py +31 -0
- tweetapi/resources/unencrypted_dm.py +63 -0
- tweetapi/resources/user.py +75 -0
- tweetapi/resources/xchat.py +44 -0
- tweetapi/types.py +484 -0
- tweetapi-1.0.0.dist-info/METADATA +277 -0
- tweetapi-1.0.0.dist-info/RECORD +20 -0
- tweetapi-1.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from ..client import TweetAPI
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UnencryptedDMResource:
|
|
10
|
+
def __init__(self, client: TweetAPI) -> None:
|
|
11
|
+
self._client = client
|
|
12
|
+
|
|
13
|
+
def send_dm(self, *, auth_token: str, conversation_id: str, text: str, proxy: str, request_id: Optional[str] = None, media: Optional[list[Any]] = None) -> dict[str, Any]:
|
|
14
|
+
"""Send an unencrypted direct message."""
|
|
15
|
+
return self._client._post("/tw-v2/interaction/send-dm", {
|
|
16
|
+
"authToken": auth_token, "conversationId": conversation_id,
|
|
17
|
+
"text": text, "proxy": proxy, "requestId": request_id, "media": media,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
def get_dm_permissions(self, *, auth_token: str, recipient_ids: str, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
21
|
+
"""Check DM permissions for recipients."""
|
|
22
|
+
return self._client._get("/tw-v2/interaction/dm-permissions", {
|
|
23
|
+
"authToken": auth_token, "recipientIds": recipient_ids, "proxy": proxy,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
def get_inbox_initial_state(self, *, auth_token: str, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
27
|
+
"""Get initial inbox state."""
|
|
28
|
+
return self._client._get("/tw-v2/interaction/inbox-initial-state", {
|
|
29
|
+
"authToken": auth_token, "proxy": proxy,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
def get_inbox_trusted(self, *, auth_token: str, cursor: str, filter_low_quality: Optional[bool] = None, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
33
|
+
"""Get trusted inbox conversations."""
|
|
34
|
+
return self._client._get("/tw-v2/interaction/inbox-timeline-trusted", {
|
|
35
|
+
"authToken": auth_token, "cursor": cursor,
|
|
36
|
+
"filterLowQuality": filter_low_quality, "proxy": proxy,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
def get_inbox_untrusted(self, *, auth_token: str, cursor: str, filter_low_quality: Optional[bool] = None, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
40
|
+
"""Get untrusted (message requests) inbox conversations."""
|
|
41
|
+
return self._client._get("/tw-v2/interaction/inbox-timeline-untrusted", {
|
|
42
|
+
"authToken": auth_token, "cursor": cursor,
|
|
43
|
+
"filterLowQuality": filter_low_quality, "proxy": proxy,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
def get_conversation(self, *, auth_token: str, conversation_id: str, cursor: Optional[str] = None, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
47
|
+
"""Get messages in a conversation."""
|
|
48
|
+
return self._client._get("/tw-v2/interaction/conversation", {
|
|
49
|
+
"authToken": auth_token, "conversationId": conversation_id,
|
|
50
|
+
"cursor": cursor, "proxy": proxy,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
def get_dm_user_updates(self, *, auth_token: str, cursor: str, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
54
|
+
"""Get DM user updates."""
|
|
55
|
+
return self._client._get("/tw-v2/interaction/dm-user-updates", {
|
|
56
|
+
"authToken": auth_token, "cursor": cursor, "proxy": proxy,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
def accept_conversation(self, *, auth_token: str, conversation_id: str, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
60
|
+
"""Accept a conversation request."""
|
|
61
|
+
return self._client._post("/tw-v2/interaction/accept-conversation", {
|
|
62
|
+
"authToken": auth_token, "conversationId": conversation_id, "proxy": proxy,
|
|
63
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from ..client import TweetAPI
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class UserResource:
|
|
10
|
+
def __init__(self, client: TweetAPI) -> None:
|
|
11
|
+
self._client = client
|
|
12
|
+
|
|
13
|
+
def get_by_username(self, *, username: str) -> dict[str, Any]:
|
|
14
|
+
"""Get a user profile by username."""
|
|
15
|
+
return self._client._get("/tw-v2/user/by-username", {"username": username})
|
|
16
|
+
|
|
17
|
+
def get_by_usernames(self, *, usernames: str) -> dict[str, Any]:
|
|
18
|
+
"""Get multiple user profiles by usernames (comma-separated)."""
|
|
19
|
+
return self._client._get("/tw-v2/user/by-usernames", {"usernames": usernames})
|
|
20
|
+
|
|
21
|
+
def get_by_user_id(self, *, user_id: str) -> dict[str, Any]:
|
|
22
|
+
"""Get a user profile by user ID."""
|
|
23
|
+
return self._client._get("/tw-v2/user/by-id", {"userId": user_id})
|
|
24
|
+
|
|
25
|
+
def get_by_user_ids(self, *, user_ids: str) -> dict[str, Any]:
|
|
26
|
+
"""Get multiple user profiles by user IDs (comma-separated)."""
|
|
27
|
+
return self._client._get("/tw-v2/user/by-ids", {"userIds": user_ids})
|
|
28
|
+
|
|
29
|
+
def get_tweets(self, *, user_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
30
|
+
"""Get a user's tweets."""
|
|
31
|
+
return self._client._get("/tw-v2/user/tweets", {"userId": user_id, "cursor": cursor})
|
|
32
|
+
|
|
33
|
+
def get_tweets_and_replies(self, *, user_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
34
|
+
"""Get a user's tweets and replies."""
|
|
35
|
+
return self._client._get("/tw-v2/user/tweets-and-replies", {"userId": user_id, "cursor": cursor})
|
|
36
|
+
|
|
37
|
+
def get_following(self, *, user_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
38
|
+
"""Get who a user follows (v2 — full user objects)."""
|
|
39
|
+
return self._client._get("/tw-v2/user/following", {"userId": user_id, "cursor": cursor})
|
|
40
|
+
|
|
41
|
+
def get_followers(self, *, user_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
42
|
+
"""Get a user's followers (v2 — full user objects)."""
|
|
43
|
+
return self._client._get("/tw-v2/user/followers", {"userId": user_id, "cursor": cursor})
|
|
44
|
+
|
|
45
|
+
def get_verified_followers(self, *, user_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
46
|
+
"""Get a user's verified followers."""
|
|
47
|
+
return self._client._get("/tw-v2/user/verified-followers", {"userId": user_id, "cursor": cursor})
|
|
48
|
+
|
|
49
|
+
def get_subscriptions(self, *, user_id: str, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
50
|
+
"""Get a user's subscriptions."""
|
|
51
|
+
return self._client._get("/tw-v2/user/subscriptions", {"userId": user_id, "cursor": cursor})
|
|
52
|
+
|
|
53
|
+
def get_following_v1(self, *, user_id: str, count: Optional[str] = None, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
54
|
+
"""Get who a user follows (v1 — supports count)."""
|
|
55
|
+
return self._client._get("/tw-v2/user/following-list", {"userId": user_id, "count": count, "cursor": cursor})
|
|
56
|
+
|
|
57
|
+
def get_followers_v1(self, *, user_id: str, count: Optional[str] = None, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
58
|
+
"""Get a user's followers (v1 — supports count)."""
|
|
59
|
+
return self._client._get("/tw-v2/user/followers-list", {"userId": user_id, "count": count, "cursor": cursor})
|
|
60
|
+
|
|
61
|
+
def get_following_ids(self, *, user_id: str, count: Optional[str] = None, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
62
|
+
"""Get IDs of accounts a user is following."""
|
|
63
|
+
return self._client._get("/tw-v2/user/following-ids", {"userId": user_id, "count": count, "cursor": cursor})
|
|
64
|
+
|
|
65
|
+
def get_followers_ids(self, *, user_id: str, count: Optional[str] = None, cursor: Optional[str] = None) -> dict[str, Any]:
|
|
66
|
+
"""Get IDs of a user's followers."""
|
|
67
|
+
return self._client._get("/tw-v2/user/followers-ids", {"userId": user_id, "count": count, "cursor": cursor})
|
|
68
|
+
|
|
69
|
+
def check_follow(self, *, subject_id: str, target_id: str) -> dict[str, Any]:
|
|
70
|
+
"""Check the follow relationship between two users."""
|
|
71
|
+
return self._client._get("/tw-v2/user/friendship", {"subjectId": subject_id, "targetId": target_id})
|
|
72
|
+
|
|
73
|
+
def about_account(self, *, username: str) -> dict[str, Any]:
|
|
74
|
+
"""Get account creation details and transparency information."""
|
|
75
|
+
return self._client._get("/tw-v2/user/about-account", {"username": username})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from ..client import TweetAPI
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class XChatResource:
|
|
10
|
+
def __init__(self, client: TweetAPI) -> None:
|
|
11
|
+
self._client = client
|
|
12
|
+
|
|
13
|
+
def setup(self, *, auth_token: str, user_id: str, pin: str, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
14
|
+
"""Initialize X Chat (encrypted DMs) for your account."""
|
|
15
|
+
return self._client._post("/tw-v2/xchat/setup", {
|
|
16
|
+
"authToken": auth_token, "userId": user_id, "pin": pin, "proxy": proxy,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
def get_conversations(self, *, auth_token: str, cursor: Optional[str] = None, graph_snapshot_id: Optional[str] = None, limit: Optional[int] = None, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
20
|
+
"""List encrypted DM conversations."""
|
|
21
|
+
return self._client._post("/tw-v2/xchat/conversations", {
|
|
22
|
+
"authToken": auth_token, "cursor": cursor,
|
|
23
|
+
"graphSnapshotId": graph_snapshot_id, "limit": limit, "proxy": proxy,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
def send(self, *, auth_token: str, recipient_id: str, message: str, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
27
|
+
"""Send an encrypted message."""
|
|
28
|
+
return self._client._post("/tw-v2/xchat/send", {
|
|
29
|
+
"authToken": auth_token, "recipientId": recipient_id,
|
|
30
|
+
"message": message, "proxy": proxy,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
def get_history(self, *, auth_token: str, conversation_id: str, cursor: Optional[str] = None, limit: Optional[int] = None, proxy: Optional[str] = None) -> dict[str, Any]:
|
|
34
|
+
"""Get encrypted conversation history."""
|
|
35
|
+
return self._client._post("/tw-v2/xchat/history", {
|
|
36
|
+
"authToken": auth_token, "conversationId": conversation_id,
|
|
37
|
+
"cursor": cursor, "limit": limit, "proxy": proxy,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
def can_dm(self, *, auth_token: str, user_ids: list[str], proxy: Optional[str] = None) -> dict[str, Any]:
|
|
41
|
+
"""Check if you can send encrypted DMs to users."""
|
|
42
|
+
return self._client._post("/tw-v2/xchat/can-dm", {
|
|
43
|
+
"authToken": auth_token, "userIds": user_ids, "proxy": proxy,
|
|
44
|
+
})
|
tweetapi/types.py
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
"""Response types for TweetAPI.
|
|
2
|
+
|
|
3
|
+
Uses TypedDict for lightweight, JSON-compatible type hints.
|
|
4
|
+
All types mirror the API's JSON response shapes exactly.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, List as TypingList, Optional, TypedDict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# ─── Response Wrappers ───────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ApiResponse(TypedDict):
|
|
16
|
+
data: Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Pagination(TypedDict):
|
|
20
|
+
nextCursor: Optional[str]
|
|
21
|
+
prevCursor: Optional[str]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PaginatedResponse(TypedDict):
|
|
25
|
+
data: list[Any]
|
|
26
|
+
pagination: Pagination
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SearchMeta(TypedDict):
|
|
30
|
+
query: str
|
|
31
|
+
resultType: str
|
|
32
|
+
resultCount: int
|
|
33
|
+
completedIn: int
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class SearchResponse(TypedDict):
|
|
37
|
+
data: list[Any]
|
|
38
|
+
pagination: Pagination
|
|
39
|
+
meta: SearchMeta
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ActionData(TypedDict, total=False):
|
|
43
|
+
id: str
|
|
44
|
+
action: str
|
|
45
|
+
timestamp: str
|
|
46
|
+
success: bool
|
|
47
|
+
message: str
|
|
48
|
+
metadata: dict[str, Any]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ActionResponse(TypedDict):
|
|
52
|
+
data: ActionData
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ─── User ────────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Professional(TypedDict, total=False):
|
|
59
|
+
type: Optional[str]
|
|
60
|
+
category: list[str]
|
|
61
|
+
restId: Optional[str]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class BusinessAccount(TypedDict):
|
|
65
|
+
affiliatesCount: int
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class HighlightsInfo(TypedDict):
|
|
69
|
+
canHighlight: bool
|
|
70
|
+
highlightedTweetsCount: str
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class User(TypedDict, total=False):
|
|
74
|
+
id: str
|
|
75
|
+
username: str
|
|
76
|
+
name: str
|
|
77
|
+
bio: str
|
|
78
|
+
location: Optional[str]
|
|
79
|
+
website: Optional[str]
|
|
80
|
+
pinnedTweetIds: list[str]
|
|
81
|
+
avatar: Optional[str]
|
|
82
|
+
banner: Optional[str]
|
|
83
|
+
profileImageShape: Optional[str]
|
|
84
|
+
verified: bool
|
|
85
|
+
isBlueVerified: bool
|
|
86
|
+
verifiedType: Optional[str]
|
|
87
|
+
verifiedSince: Optional[str]
|
|
88
|
+
isIdentityVerified: bool
|
|
89
|
+
isProtected: bool
|
|
90
|
+
possiblySensitive: bool
|
|
91
|
+
profileInterstitialType: Optional[str]
|
|
92
|
+
withheldInCountries: list[str]
|
|
93
|
+
professional: Optional[Professional]
|
|
94
|
+
businessAccount: Optional[BusinessAccount]
|
|
95
|
+
creatorSubscriptionsCount: int
|
|
96
|
+
hasHiddenSubscriptions: bool
|
|
97
|
+
highlightsInfo: Optional[HighlightsInfo]
|
|
98
|
+
hasGraduatedAccess: bool
|
|
99
|
+
isProfileTranslatable: bool
|
|
100
|
+
hasCustomTimelines: bool
|
|
101
|
+
isTranslator: bool
|
|
102
|
+
affiliatesHighlightedLabel: Optional[dict[str, Any]]
|
|
103
|
+
defaultProfile: bool
|
|
104
|
+
defaultProfileImage: bool
|
|
105
|
+
followerCount: int
|
|
106
|
+
followingCount: int
|
|
107
|
+
tweetCount: int
|
|
108
|
+
listedCount: int
|
|
109
|
+
mediaCount: int
|
|
110
|
+
favoritesCount: int
|
|
111
|
+
createdAt: Optional[str]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class UserRelationship(TypedDict, total=False):
|
|
115
|
+
sourceId: str
|
|
116
|
+
targetId: str
|
|
117
|
+
following: bool
|
|
118
|
+
followedBy: bool
|
|
119
|
+
blocking: bool
|
|
120
|
+
blockedBy: bool
|
|
121
|
+
muting: bool
|
|
122
|
+
notificationsEnabled: bool
|
|
123
|
+
canDm: bool
|
|
124
|
+
canMediaTag: bool
|
|
125
|
+
wantRetweets: bool
|
|
126
|
+
markedSpam: bool
|
|
127
|
+
followRequestSent: bool
|
|
128
|
+
followRequestReceived: bool
|
|
129
|
+
allReplies: bool
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class UserAnalytics(TypedDict, total=False):
|
|
133
|
+
userId: str
|
|
134
|
+
period: str
|
|
135
|
+
impressions: int
|
|
136
|
+
engagements: int
|
|
137
|
+
engagementRate: float
|
|
138
|
+
linkClicks: int
|
|
139
|
+
profileVisits: int
|
|
140
|
+
mentions: int
|
|
141
|
+
newFollowers: int
|
|
142
|
+
topTweet: Optional[str]
|
|
143
|
+
detailedMetrics: dict[str, int]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ─── Media ───────────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class MediaSize(TypedDict, total=False):
|
|
150
|
+
width: int
|
|
151
|
+
height: int
|
|
152
|
+
resize: str
|
|
153
|
+
url: str
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class VideoVariant(TypedDict, total=False):
|
|
157
|
+
bitrate: Optional[int]
|
|
158
|
+
contentType: str
|
|
159
|
+
url: str
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class MediaAvailability(TypedDict):
|
|
163
|
+
status: str
|
|
164
|
+
reason: Optional[str]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class Media(TypedDict, total=False):
|
|
168
|
+
id: str
|
|
169
|
+
key: str
|
|
170
|
+
type: str # "photo" | "video" | "animated_gif"
|
|
171
|
+
url: str
|
|
172
|
+
displayUrl: str
|
|
173
|
+
expandedUrl: str
|
|
174
|
+
thumbnailUrl: Optional[str]
|
|
175
|
+
width: int
|
|
176
|
+
height: int
|
|
177
|
+
aspectRatio: list[int]
|
|
178
|
+
sizes: dict[str, MediaSize]
|
|
179
|
+
duration: Optional[float]
|
|
180
|
+
bitrate: Optional[int]
|
|
181
|
+
videoInfo: Optional[dict[str, Any]]
|
|
182
|
+
altText: Optional[str]
|
|
183
|
+
sensitiveMedia: bool
|
|
184
|
+
mediaAvailability: MediaAvailability
|
|
185
|
+
allowDownload: bool
|
|
186
|
+
mediaStats: Optional[dict[str, int]]
|
|
187
|
+
sourceStatusId: Optional[str]
|
|
188
|
+
sourceUserId: Optional[str]
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# ─── Poll ────────────────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class PollOption(TypedDict):
|
|
195
|
+
position: int
|
|
196
|
+
label: str
|
|
197
|
+
voteCount: int
|
|
198
|
+
percentage: float
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class Poll(TypedDict, total=False):
|
|
202
|
+
id: str
|
|
203
|
+
options: list[PollOption]
|
|
204
|
+
endDatetime: str
|
|
205
|
+
durationMinutes: int
|
|
206
|
+
votingStatus: str
|
|
207
|
+
totalVotes: int
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# ─── Card ────────────────────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class CardBindingValues(TypedDict, total=False):
|
|
214
|
+
title: str
|
|
215
|
+
description: str
|
|
216
|
+
domain: str
|
|
217
|
+
thumbnailImageUrl: Optional[str]
|
|
218
|
+
thumbnailImageColor: Optional[str]
|
|
219
|
+
playerUrl: Optional[str]
|
|
220
|
+
playerWidth: Optional[int]
|
|
221
|
+
playerHeight: Optional[int]
|
|
222
|
+
appId: Optional[str]
|
|
223
|
+
appName: Optional[str]
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class Card(TypedDict, total=False):
|
|
227
|
+
name: str
|
|
228
|
+
url: str
|
|
229
|
+
cardType: str
|
|
230
|
+
type: str
|
|
231
|
+
bindingValues: CardBindingValues
|
|
232
|
+
vanityUrl: Optional[str]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ─── Place ───────────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class Place(TypedDict, total=False):
|
|
239
|
+
id: str
|
|
240
|
+
fullName: str
|
|
241
|
+
name: str
|
|
242
|
+
country: str
|
|
243
|
+
countryCode: str
|
|
244
|
+
placeType: str
|
|
245
|
+
url: str
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
# ─── Tweet ───────────────────────────────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class ReplyTo(TypedDict):
|
|
252
|
+
tweetId: str
|
|
253
|
+
userId: str
|
|
254
|
+
username: str
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class Mention(TypedDict):
|
|
258
|
+
id: str
|
|
259
|
+
username: str
|
|
260
|
+
name: str
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class EditControl(TypedDict, total=False):
|
|
264
|
+
editTweetIds: list[str]
|
|
265
|
+
editableUntil: str
|
|
266
|
+
isEditEligible: bool
|
|
267
|
+
editsRemaining: int
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class BirdwatchPivot(TypedDict, total=False):
|
|
271
|
+
calloutText: str
|
|
272
|
+
shortTitle: str
|
|
273
|
+
noteId: str
|
|
274
|
+
iconType: str
|
|
275
|
+
destinationUrl: str
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class ConversationControl(TypedDict, total=False):
|
|
279
|
+
policy: str
|
|
280
|
+
allowedUserIds: list[str]
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class Tweet(TypedDict, total=False):
|
|
284
|
+
id: str
|
|
285
|
+
conversationId: Optional[str]
|
|
286
|
+
text: str
|
|
287
|
+
displayTextRange: list[int]
|
|
288
|
+
author: User
|
|
289
|
+
source: Optional[str]
|
|
290
|
+
type: str # "tweet" | "reply" | "quote" | "retweet" | "thread"
|
|
291
|
+
replyTo: Optional[ReplyTo]
|
|
292
|
+
quotedTweet: Optional[Any] # recursive Tweet
|
|
293
|
+
retweetedTweet: Optional[Any]
|
|
294
|
+
likeCount: int
|
|
295
|
+
retweetCount: int
|
|
296
|
+
replyCount: int
|
|
297
|
+
quoteCount: int
|
|
298
|
+
bookmarkCount: int
|
|
299
|
+
viewCount: Optional[int]
|
|
300
|
+
media: Optional[list[Media]]
|
|
301
|
+
poll: Optional[Poll]
|
|
302
|
+
card: Optional[Card]
|
|
303
|
+
hashtags: list[str]
|
|
304
|
+
mentions: list[Mention]
|
|
305
|
+
urls: list[str]
|
|
306
|
+
symbols: list[str]
|
|
307
|
+
possiblySensitive: bool
|
|
308
|
+
limitedActions: Optional[str]
|
|
309
|
+
hasAIGeneratedMedia: bool
|
|
310
|
+
isPaidPromotion: bool
|
|
311
|
+
isEdited: bool
|
|
312
|
+
editControl: Optional[EditControl]
|
|
313
|
+
isTranslatable: bool
|
|
314
|
+
lang: str
|
|
315
|
+
translatedText: Optional[str]
|
|
316
|
+
hasBirdwatchNotes: bool
|
|
317
|
+
birdwatchPivot: Optional[BirdwatchPivot]
|
|
318
|
+
conversationControl: Optional[ConversationControl]
|
|
319
|
+
isPromoted: bool
|
|
320
|
+
communityId: Optional[str]
|
|
321
|
+
createdAt: Optional[str]
|
|
322
|
+
place: Optional[Place]
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class TweetTranslation(TypedDict):
|
|
326
|
+
text: str
|
|
327
|
+
lang: str
|
|
328
|
+
sourceLanguage: str
|
|
329
|
+
destinationLanguage: str
|
|
330
|
+
translationSource: str
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# ─── List ────────────────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class List(TypedDict, total=False):
|
|
337
|
+
id: str
|
|
338
|
+
name: str
|
|
339
|
+
description: str
|
|
340
|
+
mode: str # "public" | "private"
|
|
341
|
+
owner: User
|
|
342
|
+
bannerUrl: Optional[str]
|
|
343
|
+
facepileUrls: list[str]
|
|
344
|
+
memberCount: int
|
|
345
|
+
subscriberCount: int
|
|
346
|
+
createdAt: Optional[str]
|
|
347
|
+
slug: str
|
|
348
|
+
uri: str
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# ─── Community ───────────────────────────────────────────────────────────────
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class CommunityRule(TypedDict):
|
|
355
|
+
id: str
|
|
356
|
+
name: str
|
|
357
|
+
description: str
|
|
358
|
+
order: int
|
|
359
|
+
createdAt: str
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class Community(TypedDict, total=False):
|
|
363
|
+
id: str
|
|
364
|
+
name: str
|
|
365
|
+
description: str
|
|
366
|
+
bannerUrl: Optional[str]
|
|
367
|
+
avatarUrl: Optional[str]
|
|
368
|
+
rules: list[CommunityRule]
|
|
369
|
+
memberCount: int
|
|
370
|
+
moderatorCount: int
|
|
371
|
+
adminCount: int
|
|
372
|
+
isPrivate: bool
|
|
373
|
+
pinnedTweetId: Optional[str]
|
|
374
|
+
createdAt: Optional[str]
|
|
375
|
+
isMember: bool
|
|
376
|
+
isAdmin: bool
|
|
377
|
+
isModerator: bool
|
|
378
|
+
canPost: bool
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class CommunityMember(TypedDict, total=False):
|
|
382
|
+
user: User
|
|
383
|
+
role: str # "member" | "moderator" | "admin"
|
|
384
|
+
joinedAt: str
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class CommunitySearchResult(TypedDict, total=False):
|
|
388
|
+
id: str
|
|
389
|
+
name: str
|
|
390
|
+
memberCount: int
|
|
391
|
+
topic: Optional[str]
|
|
392
|
+
isNsfw: bool
|
|
393
|
+
bannerUrl: Optional[str]
|
|
394
|
+
defaultBannerUrl: Optional[str]
|
|
395
|
+
membersFacepile: list[str]
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# ─── Space ───────────────────────────────────────────────────────────────────
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class SpaceParticipant(TypedDict, total=False):
|
|
402
|
+
periscopeUserId: str
|
|
403
|
+
twitterUserId: str
|
|
404
|
+
username: str
|
|
405
|
+
displayName: str
|
|
406
|
+
avatarUrl: str
|
|
407
|
+
isVerified: bool
|
|
408
|
+
isBlueVerified: bool
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class SpaceTopic(TypedDict):
|
|
412
|
+
id: str
|
|
413
|
+
name: str
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class SpaceParticipants(TypedDict, total=False):
|
|
417
|
+
admins: list[SpaceParticipant]
|
|
418
|
+
speakers: list[SpaceParticipant]
|
|
419
|
+
listeners: list[SpaceParticipant]
|
|
420
|
+
total: int
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
class Space(TypedDict, total=False):
|
|
424
|
+
id: str
|
|
425
|
+
title: str
|
|
426
|
+
state: str # "Running" | "Ended" | "Scheduled" | "Canceled"
|
|
427
|
+
mediaKey: str
|
|
428
|
+
createdAt: int
|
|
429
|
+
scheduledStart: Optional[int]
|
|
430
|
+
startedAt: Optional[int]
|
|
431
|
+
endedAt: Optional[int]
|
|
432
|
+
updatedAt: Optional[int]
|
|
433
|
+
creator: Optional[User]
|
|
434
|
+
totalLiveListeners: int
|
|
435
|
+
totalReplayWatched: int
|
|
436
|
+
participants: SpaceParticipants
|
|
437
|
+
isAvailableForReplay: bool
|
|
438
|
+
topics: list[SpaceTopic]
|
|
439
|
+
tweetId: Optional[str]
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class SpaceStreamInfo(TypedDict, total=False):
|
|
443
|
+
hlsUrl: str
|
|
444
|
+
status: str
|
|
445
|
+
streamType: str
|
|
446
|
+
shareUrl: Optional[str]
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
# ─── Notification ────────────────────────────────────────────────────────────
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class Notification(TypedDict, total=False):
|
|
453
|
+
id: str
|
|
454
|
+
type: str
|
|
455
|
+
createdAt: str
|
|
456
|
+
message: str
|
|
457
|
+
icon: str
|
|
458
|
+
fromUsers: list[str]
|
|
459
|
+
targetTweetId: Optional[str]
|
|
460
|
+
targetUserId: Optional[str]
|
|
461
|
+
seen: bool
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# ─── Auth ────────────────────────────────────────────────────────────────────
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
class LoginCookies(TypedDict):
|
|
468
|
+
auth_token: str
|
|
469
|
+
ct0: str
|
|
470
|
+
twid: str
|
|
471
|
+
kdt: str
|
|
472
|
+
__cf_bm: str
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class LoginUser(TypedDict):
|
|
476
|
+
id: str
|
|
477
|
+
username: str
|
|
478
|
+
name: str
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
class LoginResponse(TypedDict):
|
|
482
|
+
cookies: LoginCookies
|
|
483
|
+
user: LoginUser
|
|
484
|
+
timestamp: str
|