twitter-openapi-python 0.0.44__py3-none-any.whl → 0.0.45__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.
- twitter_openapi_python/__init__.py +15 -15
- twitter_openapi_python/api/__init__.py +13 -13
- twitter_openapi_python/api/default_api.py +80 -80
- twitter_openapi_python/api/initial_state_api.py +72 -72
- twitter_openapi_python/api/post_api.py +166 -166
- twitter_openapi_python/api/tweet_api.py +428 -353
- twitter_openapi_python/api/user_api.py +80 -80
- twitter_openapi_python/api/user_list_api.py +171 -171
- twitter_openapi_python/api/users_api.py +56 -56
- twitter_openapi_python/api/v11_get_api.py +67 -70
- twitter_openapi_python/api/v11_post_api.py +61 -66
- twitter_openapi_python/client.py +194 -194
- twitter_openapi_python/models/__init__.py +22 -22
- twitter_openapi_python/models/base.py +9 -9
- twitter_openapi_python/models/header.py +20 -20
- twitter_openapi_python/models/initial_state.py +17 -17
- twitter_openapi_python/models/response.py +19 -19
- twitter_openapi_python/models/timeline.py +40 -40
- twitter_openapi_python/tid.py +27 -17
- twitter_openapi_python/utils/__init__.py +22 -22
- twitter_openapi_python/utils/api.py +288 -288
- {twitter_openapi_python-0.0.44.dist-info → twitter_openapi_python-0.0.45.dist-info}/METADATA +118 -117
- twitter_openapi_python-0.0.45.dist-info/RECORD +27 -0
- {twitter_openapi_python-0.0.44.dist-info → twitter_openapi_python-0.0.45.dist-info}/WHEEL +1 -2
- {twitter_openapi_python-0.0.44.dist-info → twitter_openapi_python-0.0.45.dist-info}/licenses/LICENSE +31 -31
- {twitter_openapi_python-0.0.44.dist-info → twitter_openapi_python-0.0.45.dist-info}/licenses/LICENSE.AGPL +660 -660
- twitter_openapi_python-0.0.44.dist-info/RECORD +0 -28
- twitter_openapi_python-0.0.44.dist-info/top_level.txt +0 -1
|
@@ -1,288 +1,288 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import Any, Dict, List, Mapping, Optional, TypeGuard, TypeVar
|
|
3
|
-
|
|
4
|
-
import twitter_openapi_python_generated as twitter
|
|
5
|
-
import twitter_openapi_python_generated.models as models
|
|
6
|
-
from x_client_transaction import ClientTransaction
|
|
7
|
-
|
|
8
|
-
from twitter_openapi_python.models import (
|
|
9
|
-
ApiUtilsHeader,
|
|
10
|
-
CursorApiUtilsResponse,
|
|
11
|
-
TweetApiUtilsData,
|
|
12
|
-
TwitterApiUtilsRaw,
|
|
13
|
-
TwitterApiUtilsResponse,
|
|
14
|
-
UserApiUtilsData,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
T = TypeVar("T")
|
|
18
|
-
T1 = TypeVar("T1")
|
|
19
|
-
T2 = TypeVar("T2")
|
|
20
|
-
ParamType = dict[str, Any]
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def flat(matrix: List[List[T]]) -> List[T]:
|
|
24
|
-
return [x for row in matrix for x in row]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def non_nullable(x: Optional[T]) -> T:
|
|
28
|
-
if x is None:
|
|
29
|
-
raise Exception("No data")
|
|
30
|
-
return x
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def non_nullable_list(x: List[Optional[T]]) -> List[T]:
|
|
34
|
-
def filter_fn(x: Optional[T]) -> TypeGuard[T]:
|
|
35
|
-
return x is not None
|
|
36
|
-
|
|
37
|
-
return list(filter(filter_fn, x))
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def get_kwargs(flag: ParamType, additional: ParamType, ct: ClientTransaction) -> ParamType:
|
|
41
|
-
assert flag is not None
|
|
42
|
-
kwargs = {"path_query_id": flag["queryId"]}
|
|
43
|
-
if flag.get("variables") is not None:
|
|
44
|
-
kwargs["variables"] = json.dumps(flag["variables"] | additional)
|
|
45
|
-
if flag.get("features") is not None:
|
|
46
|
-
kwargs["features"] = json.dumps(flag["features"])
|
|
47
|
-
if flag.get("fieldToggles") is not None:
|
|
48
|
-
kwargs["field_toggles"] = json.dumps(flag["fieldToggles"])
|
|
49
|
-
if flag.get("@path") is not None:
|
|
50
|
-
kwargs["_headers"] = get_headers(flag, ct)
|
|
51
|
-
return kwargs
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def get_headers(flag: ParamType, ct: ClientTransaction) -> Dict[str, str]:
|
|
55
|
-
tid = ct.generate_transaction_id(flag["@method"], flag["@path"])
|
|
56
|
-
return {"x-client-transaction-id": tid}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def get_legacy_kwargs(flag: ParamType, additional: ParamType) -> ParamType:
|
|
60
|
-
assert flag is not None
|
|
61
|
-
|
|
62
|
-
def to_snake_case(x: str) -> str:
|
|
63
|
-
return "".join(["_" + i.lower() if i.isupper() else i for i in x]).lstrip("_")
|
|
64
|
-
|
|
65
|
-
res = {to_snake_case(k): v for k, v in flag.items()}
|
|
66
|
-
|
|
67
|
-
return res | additional
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def error_check(data: Optional[T], error: Optional[List[models.ErrorResponse]]) -> T:
|
|
71
|
-
if data is None:
|
|
72
|
-
if error is None:
|
|
73
|
-
raise Exception("No data")
|
|
74
|
-
else:
|
|
75
|
-
raise Exception(", ".join([x.message for x in error]))
|
|
76
|
-
return data
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def instruction_to_entry(
|
|
80
|
-
input: List[models.InstructionUnion],
|
|
81
|
-
) -> List[models.TimelineAddEntry]:
|
|
82
|
-
def map_fn(x: models.InstructionUnion) -> List[models.TimelineAddEntry]:
|
|
83
|
-
one_of = x.actual_instance
|
|
84
|
-
|
|
85
|
-
if isinstance(one_of, models.TimelineAddEntries):
|
|
86
|
-
return one_of.entries
|
|
87
|
-
elif isinstance(one_of, models.TimelineReplaceEntry):
|
|
88
|
-
return [one_of.entry]
|
|
89
|
-
else:
|
|
90
|
-
return []
|
|
91
|
-
|
|
92
|
-
return flat(list(map(map_fn, input)))
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def tweet_entries_converter(
|
|
96
|
-
input: List[models.TimelineAddEntry],
|
|
97
|
-
) -> List[TweetApiUtilsData]:
|
|
98
|
-
def map_fn(x: models.TimelineAddEntry) -> Optional[List[TweetApiUtilsData]]:
|
|
99
|
-
one_of = x.content.actual_instance
|
|
100
|
-
|
|
101
|
-
if isinstance(one_of, models.TimelineTimelineItem):
|
|
102
|
-
item = one_of.item_content.actual_instance
|
|
103
|
-
if isinstance(item, models.TimelineTweet):
|
|
104
|
-
return non_nullable_list(
|
|
105
|
-
[
|
|
106
|
-
build_tweet_api_utils(
|
|
107
|
-
result=item.tweet_results,
|
|
108
|
-
promoted_metadata=item.promoted_metadata,
|
|
109
|
-
reply=[],
|
|
110
|
-
)
|
|
111
|
-
]
|
|
112
|
-
)
|
|
113
|
-
elif isinstance(one_of, models.TimelineTimelineModule):
|
|
114
|
-
module = one_of.items or []
|
|
115
|
-
timelineList = non_nullable_list(list(map(map_fn_2, module)))
|
|
116
|
-
if len(timelineList) == 0:
|
|
117
|
-
return None
|
|
118
|
-
if one_of.display_type == models.DisplayType.VERTICALGRID:
|
|
119
|
-
return non_nullable_list(
|
|
120
|
-
[
|
|
121
|
-
build_tweet_api_utils(
|
|
122
|
-
result=x.tweet_results,
|
|
123
|
-
promoted_metadata=x.promoted_metadata,
|
|
124
|
-
reply=[],
|
|
125
|
-
)
|
|
126
|
-
for x in timelineList
|
|
127
|
-
]
|
|
128
|
-
)
|
|
129
|
-
else:
|
|
130
|
-
timeline = timelineList[0]
|
|
131
|
-
return non_nullable_list(
|
|
132
|
-
[
|
|
133
|
-
build_tweet_api_utils(
|
|
134
|
-
result=timeline.tweet_results,
|
|
135
|
-
promoted_metadata=timeline.promoted_metadata,
|
|
136
|
-
reply=timelineList[1:],
|
|
137
|
-
)
|
|
138
|
-
]
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
def map_fn_2(x: models.ModuleItem) -> Optional[models.TimelineTweet]:
|
|
142
|
-
item = x.item.item_content.actual_instance
|
|
143
|
-
if isinstance(item, models.TimelineTweet):
|
|
144
|
-
return item
|
|
145
|
-
|
|
146
|
-
return flat(non_nullable_list(list(map(map_fn, input))))
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def user_or_null_converter(user: models.UserUnion) -> Optional[models.User]:
|
|
150
|
-
if isinstance(user.actual_instance, models.User):
|
|
151
|
-
return user.actual_instance
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def build_tweet_api_utils(
|
|
155
|
-
result: models.ItemResult,
|
|
156
|
-
promoted_metadata: Optional[dict[str, Any]] = None,
|
|
157
|
-
reply: Optional[List[models.TimelineTweet]] = None,
|
|
158
|
-
) -> Optional[TweetApiUtilsData]:
|
|
159
|
-
tweet = tweet_results_converter(result)
|
|
160
|
-
if tweet is None:
|
|
161
|
-
return None
|
|
162
|
-
if tweet.core is None:
|
|
163
|
-
return None
|
|
164
|
-
if tweet.core.user_results.result is None:
|
|
165
|
-
return None
|
|
166
|
-
user = user_or_null_converter(tweet.core.user_results.result)
|
|
167
|
-
if user is None:
|
|
168
|
-
return None
|
|
169
|
-
|
|
170
|
-
quoted = tweet.quoted_status_result
|
|
171
|
-
|
|
172
|
-
retweeted = None if tweet.legacy is None else tweet.legacy.retweeted_status_result
|
|
173
|
-
|
|
174
|
-
def reply_fn(x: models.TimelineTweet) -> Optional[TweetApiUtilsData]:
|
|
175
|
-
return build_tweet_api_utils(x.tweet_results, x.promoted_metadata, [])
|
|
176
|
-
|
|
177
|
-
return TweetApiUtilsData(
|
|
178
|
-
raw=result,
|
|
179
|
-
promoted_metadata=promoted_metadata,
|
|
180
|
-
tweet=tweet,
|
|
181
|
-
user=user,
|
|
182
|
-
replies=non_nullable_list(list(map(reply_fn, reply or []))),
|
|
183
|
-
retweeted=build_tweet_api_utils(retweeted, None, []) if retweeted else None,
|
|
184
|
-
quoted=build_tweet_api_utils(quoted, None, []) if quoted else None,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def tweet_results_converter(tweetResults: models.ItemResult) -> Optional[models.Tweet]:
|
|
189
|
-
if tweetResults.result is None:
|
|
190
|
-
return None
|
|
191
|
-
properties = tweetResults.result.actual_instance
|
|
192
|
-
if isinstance(properties, models.Tweet):
|
|
193
|
-
return properties
|
|
194
|
-
elif isinstance(properties, models.TweetWithVisibilityResults):
|
|
195
|
-
return properties.tweet
|
|
196
|
-
elif isinstance(properties, models.TweetTombstone):
|
|
197
|
-
return None
|
|
198
|
-
raise Exception()
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def user_entries_converter(
|
|
202
|
-
item: list[models.TimelineAddEntry],
|
|
203
|
-
) -> list[models.UserResults]:
|
|
204
|
-
def map_fn(x: models.TimelineAddEntry) -> Optional[models.UserResults]:
|
|
205
|
-
one_of = x.content.actual_instance
|
|
206
|
-
if isinstance(one_of, models.TimelineTimelineItem):
|
|
207
|
-
item = one_of.item_content.actual_instance
|
|
208
|
-
if isinstance(item, models.TimelineUser):
|
|
209
|
-
return item.user_results
|
|
210
|
-
|
|
211
|
-
return non_nullable_list(list(map(map_fn, item)))
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def user_result_converter(item: list[models.UserResults]) -> List[UserApiUtilsData]:
|
|
215
|
-
def map_fn(raw: models.UserResults) -> Optional[UserApiUtilsData]:
|
|
216
|
-
if raw.result is None:
|
|
217
|
-
return None
|
|
218
|
-
user = user_or_null_converter(raw.result)
|
|
219
|
-
if user is None:
|
|
220
|
-
return None
|
|
221
|
-
|
|
222
|
-
return UserApiUtilsData(
|
|
223
|
-
raw=raw,
|
|
224
|
-
user=user,
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
return non_nullable_list(list(map(map_fn, item)))
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def entries_cursor(item: List[models.TimelineAddEntry]) -> CursorApiUtilsResponse:
|
|
231
|
-
def map_fn(x: models.TimelineAddEntry) -> Optional[models.TimelineTimelineCursor]:
|
|
232
|
-
item = x.content.actual_instance
|
|
233
|
-
if isinstance(item, models.TimelineTimelineCursor):
|
|
234
|
-
return item
|
|
235
|
-
elif isinstance(item, models.TimelineTimelineItem):
|
|
236
|
-
content = item.item_content.actual_instance
|
|
237
|
-
if isinstance(content, models.TimelineTimelineCursor):
|
|
238
|
-
return content
|
|
239
|
-
|
|
240
|
-
return build_cursor(non_nullable_list(list(map(map_fn, item))))
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
def build_cursor(list: list[models.TimelineTimelineCursor]) -> CursorApiUtilsResponse:
|
|
244
|
-
def find_fn_1(x: models.TimelineTimelineCursor) -> bool:
|
|
245
|
-
return x.cursor_type == models.CursorType.TOP
|
|
246
|
-
|
|
247
|
-
def find_fn_2(x: models.TimelineTimelineCursor) -> bool:
|
|
248
|
-
return x.cursor_type == models.CursorType.BOTTOM
|
|
249
|
-
|
|
250
|
-
return CursorApiUtilsResponse(
|
|
251
|
-
top=next(filter(find_fn_1, list), None),
|
|
252
|
-
bottom=next(filter(find_fn_2, list), None),
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def build_header(headers: Dict[str, str]) -> ApiUtilsHeader:
|
|
257
|
-
return ApiUtilsHeader(
|
|
258
|
-
raw=headers,
|
|
259
|
-
connection_hash=headers.get("x-connection-hash"),
|
|
260
|
-
content_type_options=headers.get("x-content-type-options"),
|
|
261
|
-
frame_options=headers.get("x-frame-options"),
|
|
262
|
-
rate_limit_limit=int(headers.get("x-rate-limit-limit", 0)),
|
|
263
|
-
rate_limit_remaining=int(headers.get("x-rate-limit-remaining", 0)),
|
|
264
|
-
rate_limit_reset=int(headers.get("x-rate-limit-reset", 0)),
|
|
265
|
-
response_time=int(headers.get("x-response-time", 0)),
|
|
266
|
-
tfe_preserve_body=headers.get("x-tfe-preserve-body") == "true",
|
|
267
|
-
transaction_id=headers.get("x-transaction-id"),
|
|
268
|
-
twitter_response_tags=headers.get("x-twitter-response-tags"),
|
|
269
|
-
xss_protection= int(headers["x-xss-protection"]) if headers.get("x-xss-protection") is not None else None,
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
def build_response(response: twitter.ApiResponse, data: T1) -> TwitterApiUtilsResponse[T1]:
|
|
274
|
-
if response.headers is None:
|
|
275
|
-
raise Exception("headers is None")
|
|
276
|
-
|
|
277
|
-
if isinstance(response.headers, Dict):
|
|
278
|
-
header = build_header(response.headers)
|
|
279
|
-
elif isinstance(response.headers, Mapping):
|
|
280
|
-
header = build_header(dict(response.headers))
|
|
281
|
-
else:
|
|
282
|
-
raise Exception("headers is not a dict")
|
|
283
|
-
|
|
284
|
-
return TwitterApiUtilsResponse(
|
|
285
|
-
raw=TwitterApiUtilsRaw(response=response),
|
|
286
|
-
header=header,
|
|
287
|
-
data=data,
|
|
288
|
-
)
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, List, Mapping, Optional, TypeGuard, TypeVar
|
|
3
|
+
|
|
4
|
+
import twitter_openapi_python_generated as twitter
|
|
5
|
+
import twitter_openapi_python_generated.models as models
|
|
6
|
+
from x_client_transaction import ClientTransaction
|
|
7
|
+
|
|
8
|
+
from twitter_openapi_python.models import (
|
|
9
|
+
ApiUtilsHeader,
|
|
10
|
+
CursorApiUtilsResponse,
|
|
11
|
+
TweetApiUtilsData,
|
|
12
|
+
TwitterApiUtilsRaw,
|
|
13
|
+
TwitterApiUtilsResponse,
|
|
14
|
+
UserApiUtilsData,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
T = TypeVar("T")
|
|
18
|
+
T1 = TypeVar("T1")
|
|
19
|
+
T2 = TypeVar("T2")
|
|
20
|
+
ParamType = dict[str, Any]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def flat(matrix: List[List[T]]) -> List[T]:
|
|
24
|
+
return [x for row in matrix for x in row]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def non_nullable(x: Optional[T]) -> T:
|
|
28
|
+
if x is None:
|
|
29
|
+
raise Exception("No data")
|
|
30
|
+
return x
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def non_nullable_list(x: List[Optional[T]]) -> List[T]:
|
|
34
|
+
def filter_fn(x: Optional[T]) -> TypeGuard[T]:
|
|
35
|
+
return x is not None
|
|
36
|
+
|
|
37
|
+
return list(filter(filter_fn, x))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_kwargs(flag: ParamType, additional: ParamType, ct: ClientTransaction) -> ParamType:
|
|
41
|
+
assert flag is not None
|
|
42
|
+
kwargs = {"path_query_id": flag["queryId"]}
|
|
43
|
+
if flag.get("variables") is not None:
|
|
44
|
+
kwargs["variables"] = json.dumps(flag["variables"] | additional)
|
|
45
|
+
if flag.get("features") is not None:
|
|
46
|
+
kwargs["features"] = json.dumps(flag["features"])
|
|
47
|
+
if flag.get("fieldToggles") is not None:
|
|
48
|
+
kwargs["field_toggles"] = json.dumps(flag["fieldToggles"])
|
|
49
|
+
if flag.get("@path") is not None:
|
|
50
|
+
kwargs["_headers"] = get_headers(flag, ct)
|
|
51
|
+
return kwargs
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_headers(flag: ParamType, ct: ClientTransaction) -> Dict[str, str]:
|
|
55
|
+
tid = ct.generate_transaction_id(flag["@method"], flag["@path"])
|
|
56
|
+
return {"x-client-transaction-id": tid}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_legacy_kwargs(flag: ParamType, additional: ParamType) -> ParamType:
|
|
60
|
+
assert flag is not None
|
|
61
|
+
|
|
62
|
+
def to_snake_case(x: str) -> str:
|
|
63
|
+
return "".join(["_" + i.lower() if i.isupper() else i for i in x]).lstrip("_")
|
|
64
|
+
|
|
65
|
+
res = {to_snake_case(k): v for k, v in flag.items()}
|
|
66
|
+
|
|
67
|
+
return res | additional
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def error_check(data: Optional[T], error: Optional[List[models.ErrorResponse]]) -> T:
|
|
71
|
+
if data is None:
|
|
72
|
+
if error is None:
|
|
73
|
+
raise Exception("No data")
|
|
74
|
+
else:
|
|
75
|
+
raise Exception(", ".join([x.message for x in error]))
|
|
76
|
+
return data
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def instruction_to_entry(
|
|
80
|
+
input: List[models.InstructionUnion],
|
|
81
|
+
) -> List[models.TimelineAddEntry]:
|
|
82
|
+
def map_fn(x: models.InstructionUnion) -> List[models.TimelineAddEntry]:
|
|
83
|
+
one_of = x.actual_instance
|
|
84
|
+
|
|
85
|
+
if isinstance(one_of, models.TimelineAddEntries):
|
|
86
|
+
return one_of.entries
|
|
87
|
+
elif isinstance(one_of, models.TimelineReplaceEntry):
|
|
88
|
+
return [one_of.entry]
|
|
89
|
+
else:
|
|
90
|
+
return []
|
|
91
|
+
|
|
92
|
+
return flat(list(map(map_fn, input)))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def tweet_entries_converter(
|
|
96
|
+
input: List[models.TimelineAddEntry],
|
|
97
|
+
) -> List[TweetApiUtilsData]:
|
|
98
|
+
def map_fn(x: models.TimelineAddEntry) -> Optional[List[TweetApiUtilsData]]:
|
|
99
|
+
one_of = x.content.actual_instance
|
|
100
|
+
|
|
101
|
+
if isinstance(one_of, models.TimelineTimelineItem):
|
|
102
|
+
item = one_of.item_content.actual_instance
|
|
103
|
+
if isinstance(item, models.TimelineTweet):
|
|
104
|
+
return non_nullable_list(
|
|
105
|
+
[
|
|
106
|
+
build_tweet_api_utils(
|
|
107
|
+
result=item.tweet_results,
|
|
108
|
+
promoted_metadata=item.promoted_metadata,
|
|
109
|
+
reply=[],
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
)
|
|
113
|
+
elif isinstance(one_of, models.TimelineTimelineModule):
|
|
114
|
+
module = one_of.items or []
|
|
115
|
+
timelineList = non_nullable_list(list(map(map_fn_2, module)))
|
|
116
|
+
if len(timelineList) == 0:
|
|
117
|
+
return None
|
|
118
|
+
if one_of.display_type == models.DisplayType.VERTICALGRID:
|
|
119
|
+
return non_nullable_list(
|
|
120
|
+
[
|
|
121
|
+
build_tweet_api_utils(
|
|
122
|
+
result=x.tweet_results,
|
|
123
|
+
promoted_metadata=x.promoted_metadata,
|
|
124
|
+
reply=[],
|
|
125
|
+
)
|
|
126
|
+
for x in timelineList
|
|
127
|
+
]
|
|
128
|
+
)
|
|
129
|
+
else:
|
|
130
|
+
timeline = timelineList[0]
|
|
131
|
+
return non_nullable_list(
|
|
132
|
+
[
|
|
133
|
+
build_tweet_api_utils(
|
|
134
|
+
result=timeline.tweet_results,
|
|
135
|
+
promoted_metadata=timeline.promoted_metadata,
|
|
136
|
+
reply=timelineList[1:],
|
|
137
|
+
)
|
|
138
|
+
]
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def map_fn_2(x: models.ModuleItem) -> Optional[models.TimelineTweet]:
|
|
142
|
+
item = x.item.item_content.actual_instance
|
|
143
|
+
if isinstance(item, models.TimelineTweet):
|
|
144
|
+
return item
|
|
145
|
+
|
|
146
|
+
return flat(non_nullable_list(list(map(map_fn, input))))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def user_or_null_converter(user: models.UserUnion) -> Optional[models.User]:
|
|
150
|
+
if isinstance(user.actual_instance, models.User):
|
|
151
|
+
return user.actual_instance
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def build_tweet_api_utils(
|
|
155
|
+
result: models.ItemResult,
|
|
156
|
+
promoted_metadata: Optional[dict[str, Any]] = None,
|
|
157
|
+
reply: Optional[List[models.TimelineTweet]] = None,
|
|
158
|
+
) -> Optional[TweetApiUtilsData]:
|
|
159
|
+
tweet = tweet_results_converter(result)
|
|
160
|
+
if tweet is None:
|
|
161
|
+
return None
|
|
162
|
+
if tweet.core is None:
|
|
163
|
+
return None
|
|
164
|
+
if tweet.core.user_results.result is None:
|
|
165
|
+
return None
|
|
166
|
+
user = user_or_null_converter(tweet.core.user_results.result)
|
|
167
|
+
if user is None:
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
quoted = tweet.quoted_status_result
|
|
171
|
+
|
|
172
|
+
retweeted = None if tweet.legacy is None else tweet.legacy.retweeted_status_result
|
|
173
|
+
|
|
174
|
+
def reply_fn(x: models.TimelineTweet) -> Optional[TweetApiUtilsData]:
|
|
175
|
+
return build_tweet_api_utils(x.tweet_results, x.promoted_metadata, [])
|
|
176
|
+
|
|
177
|
+
return TweetApiUtilsData(
|
|
178
|
+
raw=result,
|
|
179
|
+
promoted_metadata=promoted_metadata,
|
|
180
|
+
tweet=tweet,
|
|
181
|
+
user=user,
|
|
182
|
+
replies=non_nullable_list(list(map(reply_fn, reply or []))),
|
|
183
|
+
retweeted=build_tweet_api_utils(retweeted, None, []) if retweeted else None,
|
|
184
|
+
quoted=build_tweet_api_utils(quoted, None, []) if quoted else None,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def tweet_results_converter(tweetResults: models.ItemResult) -> Optional[models.Tweet]:
|
|
189
|
+
if tweetResults.result is None:
|
|
190
|
+
return None
|
|
191
|
+
properties = tweetResults.result.actual_instance
|
|
192
|
+
if isinstance(properties, models.Tweet):
|
|
193
|
+
return properties
|
|
194
|
+
elif isinstance(properties, models.TweetWithVisibilityResults):
|
|
195
|
+
return properties.tweet
|
|
196
|
+
elif isinstance(properties, models.TweetTombstone):
|
|
197
|
+
return None
|
|
198
|
+
raise Exception()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def user_entries_converter(
|
|
202
|
+
item: list[models.TimelineAddEntry],
|
|
203
|
+
) -> list[models.UserResults]:
|
|
204
|
+
def map_fn(x: models.TimelineAddEntry) -> Optional[models.UserResults]:
|
|
205
|
+
one_of = x.content.actual_instance
|
|
206
|
+
if isinstance(one_of, models.TimelineTimelineItem):
|
|
207
|
+
item = one_of.item_content.actual_instance
|
|
208
|
+
if isinstance(item, models.TimelineUser):
|
|
209
|
+
return item.user_results
|
|
210
|
+
|
|
211
|
+
return non_nullable_list(list(map(map_fn, item)))
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def user_result_converter(item: list[models.UserResults]) -> List[UserApiUtilsData]:
|
|
215
|
+
def map_fn(raw: models.UserResults) -> Optional[UserApiUtilsData]:
|
|
216
|
+
if raw.result is None:
|
|
217
|
+
return None
|
|
218
|
+
user = user_or_null_converter(raw.result)
|
|
219
|
+
if user is None:
|
|
220
|
+
return None
|
|
221
|
+
|
|
222
|
+
return UserApiUtilsData(
|
|
223
|
+
raw=raw,
|
|
224
|
+
user=user,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
return non_nullable_list(list(map(map_fn, item)))
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def entries_cursor(item: List[models.TimelineAddEntry]) -> CursorApiUtilsResponse:
|
|
231
|
+
def map_fn(x: models.TimelineAddEntry) -> Optional[models.TimelineTimelineCursor]:
|
|
232
|
+
item = x.content.actual_instance
|
|
233
|
+
if isinstance(item, models.TimelineTimelineCursor):
|
|
234
|
+
return item
|
|
235
|
+
elif isinstance(item, models.TimelineTimelineItem):
|
|
236
|
+
content = item.item_content.actual_instance
|
|
237
|
+
if isinstance(content, models.TimelineTimelineCursor):
|
|
238
|
+
return content
|
|
239
|
+
|
|
240
|
+
return build_cursor(non_nullable_list(list(map(map_fn, item))))
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def build_cursor(list: list[models.TimelineTimelineCursor]) -> CursorApiUtilsResponse:
|
|
244
|
+
def find_fn_1(x: models.TimelineTimelineCursor) -> bool:
|
|
245
|
+
return x.cursor_type == models.CursorType.TOP
|
|
246
|
+
|
|
247
|
+
def find_fn_2(x: models.TimelineTimelineCursor) -> bool:
|
|
248
|
+
return x.cursor_type == models.CursorType.BOTTOM
|
|
249
|
+
|
|
250
|
+
return CursorApiUtilsResponse(
|
|
251
|
+
top=next(filter(find_fn_1, list), None),
|
|
252
|
+
bottom=next(filter(find_fn_2, list), None),
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def build_header(headers: Dict[str, str]) -> ApiUtilsHeader:
|
|
257
|
+
return ApiUtilsHeader(
|
|
258
|
+
raw=headers,
|
|
259
|
+
connection_hash=headers.get("x-connection-hash"),
|
|
260
|
+
content_type_options=headers.get("x-content-type-options"),
|
|
261
|
+
frame_options=headers.get("x-frame-options"),
|
|
262
|
+
rate_limit_limit=int(headers.get("x-rate-limit-limit", 0)),
|
|
263
|
+
rate_limit_remaining=int(headers.get("x-rate-limit-remaining", 0)),
|
|
264
|
+
rate_limit_reset=int(headers.get("x-rate-limit-reset", 0)),
|
|
265
|
+
response_time=int(headers.get("x-response-time", 0)),
|
|
266
|
+
tfe_preserve_body=headers.get("x-tfe-preserve-body") == "true",
|
|
267
|
+
transaction_id=headers.get("x-transaction-id"),
|
|
268
|
+
twitter_response_tags=headers.get("x-twitter-response-tags"),
|
|
269
|
+
xss_protection= int(headers["x-xss-protection"]) if headers.get("x-xss-protection") is not None else None,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def build_response(response: twitter.ApiResponse, data: T1) -> TwitterApiUtilsResponse[T1]:
|
|
274
|
+
if response.headers is None:
|
|
275
|
+
raise Exception("headers is None")
|
|
276
|
+
|
|
277
|
+
if isinstance(response.headers, Dict):
|
|
278
|
+
header = build_header(response.headers)
|
|
279
|
+
elif isinstance(response.headers, Mapping):
|
|
280
|
+
header = build_header(dict(response.headers))
|
|
281
|
+
else:
|
|
282
|
+
raise Exception("headers is not a dict")
|
|
283
|
+
|
|
284
|
+
return TwitterApiUtilsResponse(
|
|
285
|
+
raw=TwitterApiUtilsRaw(response=response),
|
|
286
|
+
header=header,
|
|
287
|
+
data=data,
|
|
288
|
+
)
|