twitter-openapi-python 0.0.44__py3-none-any.whl → 0.1.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.
- 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.1.0.dist-info}/METADATA +118 -117
- twitter_openapi_python-0.1.0.dist-info/RECORD +27 -0
- {twitter_openapi_python-0.0.44.dist-info → twitter_openapi_python-0.1.0.dist-info}/WHEEL +1 -2
- {twitter_openapi_python-0.0.44.dist-info → twitter_openapi_python-0.1.0.dist-info}/licenses/LICENSE +31 -31
- {twitter_openapi_python-0.0.44.dist-info → twitter_openapi_python-0.1.0.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,70 +1,67 @@
|
|
|
1
|
-
from typing import Any, Callable, Optional, TypeVar
|
|
2
|
-
|
|
3
|
-
import twitter_openapi_python_generated as twitter
|
|
4
|
-
|
|
5
|
-
from twitter_openapi_python.models import TwitterApiUtilsResponse
|
|
6
|
-
from twitter_openapi_python.utils import build_response, get_legacy_kwargs
|
|
7
|
-
|
|
8
|
-
T = TypeVar("T")
|
|
9
|
-
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
10
|
-
ParamType = dict[str, Any]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class V11GetApiUtils:
|
|
14
|
-
api: twitter.V11GetApi
|
|
15
|
-
flag: ParamType
|
|
16
|
-
|
|
17
|
-
def __init__(self, api: twitter.V11GetApi, flag: ParamType):
|
|
18
|
-
self.api = api
|
|
19
|
-
self.flag = flag
|
|
20
|
-
|
|
21
|
-
def request(
|
|
22
|
-
self,
|
|
23
|
-
apiFn: "ApiFnType[T]",
|
|
24
|
-
key: str,
|
|
25
|
-
param: ParamType,
|
|
26
|
-
) -> TwitterApiUtilsResponse[T]:
|
|
27
|
-
args = get_legacy_kwargs(flag=self.flag[key], additional=param)
|
|
28
|
-
res = apiFn(**args)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
q:
|
|
59
|
-
extra_param
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
key="search/typeahead.json",
|
|
69
|
-
)
|
|
70
|
-
return response
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
2
|
+
|
|
3
|
+
import twitter_openapi_python_generated as twitter
|
|
4
|
+
|
|
5
|
+
from twitter_openapi_python.models import TwitterApiUtilsResponse
|
|
6
|
+
from twitter_openapi_python.utils import build_response, get_legacy_kwargs
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
10
|
+
ParamType = dict[str, Any]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class V11GetApiUtils:
|
|
14
|
+
api: twitter.V11GetApi
|
|
15
|
+
flag: ParamType
|
|
16
|
+
|
|
17
|
+
def __init__(self, api: twitter.V11GetApi, flag: ParamType):
|
|
18
|
+
self.api = api
|
|
19
|
+
self.flag = flag
|
|
20
|
+
|
|
21
|
+
def request(
|
|
22
|
+
self,
|
|
23
|
+
apiFn: "ApiFnType[T]",
|
|
24
|
+
key: str,
|
|
25
|
+
param: ParamType,
|
|
26
|
+
) -> TwitterApiUtilsResponse[T]:
|
|
27
|
+
args = get_legacy_kwargs(flag=self.flag[key], additional=param)
|
|
28
|
+
res = apiFn(**args)
|
|
29
|
+
return build_response(response=res, data=res.data)
|
|
30
|
+
|
|
31
|
+
def get_friends_following_list(
|
|
32
|
+
self,
|
|
33
|
+
user_id: str,
|
|
34
|
+
cursor: Optional[str] = None,
|
|
35
|
+
count: Optional[int] = None,
|
|
36
|
+
extra_param: Optional[ParamType] = None,
|
|
37
|
+
) -> TwitterApiUtilsResponse[None]:
|
|
38
|
+
param: ParamType = {"user_id": user_id}
|
|
39
|
+
if cursor is not None:
|
|
40
|
+
param["cursor"] = cursor
|
|
41
|
+
if count is not None:
|
|
42
|
+
param["count"] = count
|
|
43
|
+
if extra_param is not None:
|
|
44
|
+
param.update(extra_param)
|
|
45
|
+
|
|
46
|
+
response = self.request(
|
|
47
|
+
apiFn=self.api.get_friends_following_list_with_http_info,
|
|
48
|
+
param=param,
|
|
49
|
+
key="friends/following/list.json",
|
|
50
|
+
)
|
|
51
|
+
return response
|
|
52
|
+
|
|
53
|
+
def get_search_typeahead(
|
|
54
|
+
self,
|
|
55
|
+
q: str,
|
|
56
|
+
extra_param: Optional[ParamType] = None,
|
|
57
|
+
) -> TwitterApiUtilsResponse[None]:
|
|
58
|
+
param = {"q": q}
|
|
59
|
+
if extra_param is not None:
|
|
60
|
+
param.update(extra_param)
|
|
61
|
+
|
|
62
|
+
response = self.request(
|
|
63
|
+
apiFn=self.api.get_search_typeahead_with_http_info,
|
|
64
|
+
param=param,
|
|
65
|
+
key="search/typeahead.json",
|
|
66
|
+
)
|
|
67
|
+
return response
|
|
@@ -1,66 +1,61 @@
|
|
|
1
|
-
from typing import Any, Callable, Optional, TypeVar
|
|
2
|
-
|
|
3
|
-
import twitter_openapi_python_generated as twitter
|
|
4
|
-
|
|
5
|
-
from twitter_openapi_python.models import TwitterApiUtilsResponse
|
|
6
|
-
from twitter_openapi_python.utils import build_response, get_legacy_kwargs
|
|
7
|
-
|
|
8
|
-
T = TypeVar("T")
|
|
9
|
-
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
10
|
-
ParamType = dict[str, Any]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class V11PostApiUtils:
|
|
14
|
-
api: twitter.V11PostApi
|
|
15
|
-
flag: ParamType
|
|
16
|
-
|
|
17
|
-
def __init__(self, api: twitter.V11PostApi, flag: ParamType):
|
|
18
|
-
self.api = api
|
|
19
|
-
self.flag = flag
|
|
20
|
-
|
|
21
|
-
def request(
|
|
22
|
-
self,
|
|
23
|
-
apiFn: "ApiFnType[T]",
|
|
24
|
-
key: str,
|
|
25
|
-
param: ParamType,
|
|
26
|
-
) -> TwitterApiUtilsResponse[T]:
|
|
27
|
-
args = get_legacy_kwargs(flag=self.flag[key], additional=param)
|
|
28
|
-
res = apiFn(**args)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
response
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
response
|
|
62
|
-
apiFn=self.api.post_destroy_friendships_with_http_info,
|
|
63
|
-
param=param,
|
|
64
|
-
key="friendships/destroy.json",
|
|
65
|
-
)
|
|
66
|
-
return response
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
2
|
+
|
|
3
|
+
import twitter_openapi_python_generated as twitter
|
|
4
|
+
|
|
5
|
+
from twitter_openapi_python.models import TwitterApiUtilsResponse
|
|
6
|
+
from twitter_openapi_python.utils import build_response, get_legacy_kwargs
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
10
|
+
ParamType = dict[str, Any]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class V11PostApiUtils:
|
|
14
|
+
api: twitter.V11PostApi
|
|
15
|
+
flag: ParamType
|
|
16
|
+
|
|
17
|
+
def __init__(self, api: twitter.V11PostApi, flag: ParamType):
|
|
18
|
+
self.api = api
|
|
19
|
+
self.flag = flag
|
|
20
|
+
|
|
21
|
+
def request(
|
|
22
|
+
self,
|
|
23
|
+
apiFn: "ApiFnType[T]",
|
|
24
|
+
key: str,
|
|
25
|
+
param: ParamType,
|
|
26
|
+
) -> TwitterApiUtilsResponse[T]:
|
|
27
|
+
args = get_legacy_kwargs(flag=self.flag[key], additional=param)
|
|
28
|
+
res = apiFn(**args)
|
|
29
|
+
return build_response(response=res, data=res.data)
|
|
30
|
+
|
|
31
|
+
def post_create_friendships(
|
|
32
|
+
self,
|
|
33
|
+
user_id: str,
|
|
34
|
+
extra_param: Optional[ParamType] = None,
|
|
35
|
+
) -> TwitterApiUtilsResponse[None]:
|
|
36
|
+
param = {"user_id": user_id}
|
|
37
|
+
if extra_param is not None:
|
|
38
|
+
param.update(extra_param)
|
|
39
|
+
|
|
40
|
+
response = self.request(
|
|
41
|
+
apiFn=self.api.post_create_friendships_with_http_info,
|
|
42
|
+
param=param,
|
|
43
|
+
key="friendships/create.json",
|
|
44
|
+
)
|
|
45
|
+
return response
|
|
46
|
+
|
|
47
|
+
def post_destroy_friendships(
|
|
48
|
+
self,
|
|
49
|
+
user_id: str,
|
|
50
|
+
extra_param: Optional[ParamType] = None,
|
|
51
|
+
) -> TwitterApiUtilsResponse[None]:
|
|
52
|
+
param = {"user_id": user_id}
|
|
53
|
+
if extra_param is not None:
|
|
54
|
+
param.update(extra_param)
|
|
55
|
+
|
|
56
|
+
response = self.request(
|
|
57
|
+
apiFn=self.api.post_destroy_friendships_with_http_info,
|
|
58
|
+
param=param,
|
|
59
|
+
key="friendships/destroy.json",
|
|
60
|
+
)
|
|
61
|
+
return response
|
twitter_openapi_python/client.py
CHANGED
|
@@ -1,194 +1,194 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import re
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
import twitter_openapi_python_generated as twitter
|
|
6
|
-
import twitter_openapi_python_generated.configuration as conf
|
|
7
|
-
import urllib3
|
|
8
|
-
from x_client_transaction import ClientTransaction
|
|
9
|
-
|
|
10
|
-
from twitter_openapi_python.api import (
|
|
11
|
-
DefaultApiUtils,
|
|
12
|
-
InitialStateApiUtils,
|
|
13
|
-
PostApiUtils,
|
|
14
|
-
TweetApiUtils,
|
|
15
|
-
UserApiUtils,
|
|
16
|
-
UserListApiUtils,
|
|
17
|
-
UsersApiUtils,
|
|
18
|
-
V11GetApiUtils,
|
|
19
|
-
V11PostApiUtils,
|
|
20
|
-
)
|
|
21
|
-
from twitter_openapi_python.tid import get_tid
|
|
22
|
-
|
|
23
|
-
ParamType = dict[str, Any]
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class TwitterOpenapiPythonClient:
|
|
27
|
-
api: twitter.ApiClient
|
|
28
|
-
placeholder: ParamType
|
|
29
|
-
|
|
30
|
-
def __init__(self, api: twitter.ApiClient, placeholder: ParamType, ct: ClientTransaction):
|
|
31
|
-
self.api = api
|
|
32
|
-
self.placeholder = placeholder
|
|
33
|
-
self.ct = ct
|
|
34
|
-
|
|
35
|
-
def get_default_api(self) -> DefaultApiUtils:
|
|
36
|
-
return DefaultApiUtils(twitter.DefaultApi(self.api), self.placeholder, self.ct)
|
|
37
|
-
|
|
38
|
-
def get_initial_state_api(self) -> InitialStateApiUtils:
|
|
39
|
-
return InitialStateApiUtils(self.api)
|
|
40
|
-
|
|
41
|
-
def get_post_api(self) -> PostApiUtils:
|
|
42
|
-
return PostApiUtils(twitter.PostApi(self.api), self.placeholder, self.ct)
|
|
43
|
-
|
|
44
|
-
def get_tweet_api(self) -> TweetApiUtils:
|
|
45
|
-
return TweetApiUtils(twitter.TweetApi(self.api), self.placeholder, self.ct)
|
|
46
|
-
|
|
47
|
-
def get_user_api(self) -> UserApiUtils:
|
|
48
|
-
return UserApiUtils(twitter.UserApi(self.api), self.placeholder, self.ct)
|
|
49
|
-
|
|
50
|
-
def get_users_api(self) -> UsersApiUtils:
|
|
51
|
-
return UsersApiUtils(twitter.UsersApi(self.api), self.placeholder, self.ct)
|
|
52
|
-
|
|
53
|
-
def get_user_list_api(self) -> UserListApiUtils:
|
|
54
|
-
return UserListApiUtils(twitter.UserListApi(self.api), self.placeholder, self.ct)
|
|
55
|
-
|
|
56
|
-
def get_v11_get_api(self) -> V11GetApiUtils:
|
|
57
|
-
return V11GetApiUtils(twitter.V11GetApi(self.api), self.placeholder)
|
|
58
|
-
|
|
59
|
-
def get_v11_post_api(self) -> V11PostApiUtils:
|
|
60
|
-
return V11PostApiUtils(twitter.V11PostApi(self.api), self.placeholder)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class TwitterOpenapiPython:
|
|
64
|
-
hash: str = "
|
|
65
|
-
placeholder_url = "https://raw.githubusercontent.com/fa0311/twitter-openapi/{hash}/src/config/placeholder.json"
|
|
66
|
-
header = "https://raw.githubusercontent.com/fa0311/latest-user-agent/refs/heads/main/header.json"
|
|
67
|
-
access_token: str = (
|
|
68
|
-
"AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
|
|
69
|
-
)
|
|
70
|
-
twitter_url: str = "https://x.com/home"
|
|
71
|
-
additional_browser_headers = {}
|
|
72
|
-
additional_api_headers = {}
|
|
73
|
-
|
|
74
|
-
def get_header(self) -> tuple[dict[str, str], dict[str, str]]:
|
|
75
|
-
http = urllib3.PoolManager()
|
|
76
|
-
res = http.request("GET", self.header)
|
|
77
|
-
data = json.loads(res.data)
|
|
78
|
-
ignore = ["host", "connection"]
|
|
79
|
-
|
|
80
|
-
def getHader(name: str) -> dict[str, str]:
|
|
81
|
-
return {key: value for key, value in data[name].items() if key not in ignore}
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
{
|
|
85
|
-
**getHader("chrome-fetch"),
|
|
86
|
-
"accept-encoding": "identity",
|
|
87
|
-
"referer": self.twitter_url,
|
|
88
|
-
"priority": "u=1, i",
|
|
89
|
-
"x-twitter-client-language": "en",
|
|
90
|
-
"x-twitter-active-user": "yes",
|
|
91
|
-
# 'x-twitter-auth-type': 'xxxx'
|
|
92
|
-
# 'x-csrf-token': 'xxxx',
|
|
93
|
-
# 'x-guest-token': 'xxxx',
|
|
94
|
-
"authorization": f"Bearer {self.access_token}",
|
|
95
|
-
**self.additional_api_headers,
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
**getHader("chrome"),
|
|
99
|
-
**self.additional_browser_headers,
|
|
100
|
-
},
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
def remove_prefix(self, text: str) -> str:
|
|
104
|
-
if text.startswith("x-twitter-"):
|
|
105
|
-
return text[10:]
|
|
106
|
-
if text.startswith("x-"):
|
|
107
|
-
return text[2:]
|
|
108
|
-
return text
|
|
109
|
-
|
|
110
|
-
def kebab_to_upper_camel(self, text: dict[str, Any]) -> dict[str, Any]:
|
|
111
|
-
res = {}
|
|
112
|
-
for key, value in text.items():
|
|
113
|
-
new_key = "".join([x.capitalize() for x in self.remove_prefix(key).split("-")])
|
|
114
|
-
res[new_key] = value
|
|
115
|
-
return res
|
|
116
|
-
|
|
117
|
-
def cookie_normalize(self, cookie: list[str]) -> dict[str, str]:
|
|
118
|
-
return {x.split("; ")[0].split("=")[0]: x.split("; ")[0].split("=")[1] for x in cookie}
|
|
119
|
-
|
|
120
|
-
def cookie_to_str(self, cookie: dict[str, str]) -> str:
|
|
121
|
-
return "; ".join([f"{key}={value}" for key, value in cookie.items()])
|
|
122
|
-
|
|
123
|
-
def get_twitter_openapi_python_client(
|
|
124
|
-
self,
|
|
125
|
-
api: twitter.ApiClient,
|
|
126
|
-
) -> TwitterOpenapiPythonClient:
|
|
127
|
-
http = urllib3.PoolManager()
|
|
128
|
-
flag = http.request("GET", self.placeholder_url.format(hash=self.hash))
|
|
129
|
-
ct = get_tid()
|
|
130
|
-
return TwitterOpenapiPythonClient(api, json.loads(flag.data), ct)
|
|
131
|
-
|
|
132
|
-
def get_client_from_cookies(
|
|
133
|
-
self,
|
|
134
|
-
cookies: dict[str, str],
|
|
135
|
-
) -> TwitterOpenapiPythonClient:
|
|
136
|
-
api_key = self.kebab_to_upper_camel(self.get_header()[0])
|
|
137
|
-
|
|
138
|
-
if cookies.get("ct0"):
|
|
139
|
-
api_key.update({"AuthType": "OAuth2Session"})
|
|
140
|
-
api_key.update({"CsrfToken": cookies["ct0"]})
|
|
141
|
-
|
|
142
|
-
if cookies.get("gt"):
|
|
143
|
-
api_key.update({"GuestToken": cookies["gt"]})
|
|
144
|
-
|
|
145
|
-
api_conf = conf.Configuration(api_key=api_key)
|
|
146
|
-
api_conf.access_token = self.access_token
|
|
147
|
-
api_client = twitter.ApiClient(
|
|
148
|
-
configuration=api_conf,
|
|
149
|
-
cookie=self.cookie_to_str(cookies),
|
|
150
|
-
)
|
|
151
|
-
api_client.user_agent = api_key["UserAgent"]
|
|
152
|
-
return self.get_twitter_openapi_python_client(api_client)
|
|
153
|
-
|
|
154
|
-
def get_guest_client(self) -> TwitterOpenapiPythonClient:
|
|
155
|
-
http = urllib3.PoolManager()
|
|
156
|
-
session = {}
|
|
157
|
-
|
|
158
|
-
header = self.get_header()
|
|
159
|
-
|
|
160
|
-
res_1 = http.request(
|
|
161
|
-
"GET",
|
|
162
|
-
self.twitter_url,
|
|
163
|
-
redirect=False,
|
|
164
|
-
headers=header[1],
|
|
165
|
-
)
|
|
166
|
-
cookie = res_1.headers._container["set-cookie"][1:]
|
|
167
|
-
session.update(self.cookie_normalize(cookie))
|
|
168
|
-
res_2 = http.request(
|
|
169
|
-
"GET",
|
|
170
|
-
self.twitter_url,
|
|
171
|
-
redirect=False,
|
|
172
|
-
headers=header[1],
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
find = re.findall(r'document.cookie="(.*?)";', res_2.data.decode())
|
|
176
|
-
session.update(self.cookie_normalize(find))
|
|
177
|
-
|
|
178
|
-
session.pop("ct0")
|
|
179
|
-
|
|
180
|
-
if not session.get("gt"):
|
|
181
|
-
activate_header = header[0] | {
|
|
182
|
-
"cookie": self.cookie_to_str(session),
|
|
183
|
-
}
|
|
184
|
-
res_3 = http.request(
|
|
185
|
-
"POST",
|
|
186
|
-
"https://api.x.com/1.1/guest/activate.json",
|
|
187
|
-
headers=activate_header,
|
|
188
|
-
)
|
|
189
|
-
gt = json.loads(res_3.data.decode())["guest_token"]
|
|
190
|
-
session.update({"gt": gt})
|
|
191
|
-
|
|
192
|
-
# ct0 = cookies.get("ct0", "".join(random.choices("0123456789abcdef", k=32)))
|
|
193
|
-
|
|
194
|
-
return self.get_client_from_cookies(session)
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import twitter_openapi_python_generated as twitter
|
|
6
|
+
import twitter_openapi_python_generated.configuration as conf
|
|
7
|
+
import urllib3
|
|
8
|
+
from x_client_transaction import ClientTransaction
|
|
9
|
+
|
|
10
|
+
from twitter_openapi_python.api import (
|
|
11
|
+
DefaultApiUtils,
|
|
12
|
+
InitialStateApiUtils,
|
|
13
|
+
PostApiUtils,
|
|
14
|
+
TweetApiUtils,
|
|
15
|
+
UserApiUtils,
|
|
16
|
+
UserListApiUtils,
|
|
17
|
+
UsersApiUtils,
|
|
18
|
+
V11GetApiUtils,
|
|
19
|
+
V11PostApiUtils,
|
|
20
|
+
)
|
|
21
|
+
from twitter_openapi_python.tid import get_tid
|
|
22
|
+
|
|
23
|
+
ParamType = dict[str, Any]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TwitterOpenapiPythonClient:
|
|
27
|
+
api: twitter.ApiClient
|
|
28
|
+
placeholder: ParamType
|
|
29
|
+
|
|
30
|
+
def __init__(self, api: twitter.ApiClient, placeholder: ParamType, ct: ClientTransaction):
|
|
31
|
+
self.api = api
|
|
32
|
+
self.placeholder = placeholder
|
|
33
|
+
self.ct = ct
|
|
34
|
+
|
|
35
|
+
def get_default_api(self) -> DefaultApiUtils:
|
|
36
|
+
return DefaultApiUtils(twitter.DefaultApi(self.api), self.placeholder, self.ct)
|
|
37
|
+
|
|
38
|
+
def get_initial_state_api(self) -> InitialStateApiUtils:
|
|
39
|
+
return InitialStateApiUtils(self.api)
|
|
40
|
+
|
|
41
|
+
def get_post_api(self) -> PostApiUtils:
|
|
42
|
+
return PostApiUtils(twitter.PostApi(self.api), self.placeholder, self.ct)
|
|
43
|
+
|
|
44
|
+
def get_tweet_api(self) -> TweetApiUtils:
|
|
45
|
+
return TweetApiUtils(twitter.TweetApi(self.api), self.placeholder, self.ct)
|
|
46
|
+
|
|
47
|
+
def get_user_api(self) -> UserApiUtils:
|
|
48
|
+
return UserApiUtils(twitter.UserApi(self.api), self.placeholder, self.ct)
|
|
49
|
+
|
|
50
|
+
def get_users_api(self) -> UsersApiUtils:
|
|
51
|
+
return UsersApiUtils(twitter.UsersApi(self.api), self.placeholder, self.ct)
|
|
52
|
+
|
|
53
|
+
def get_user_list_api(self) -> UserListApiUtils:
|
|
54
|
+
return UserListApiUtils(twitter.UserListApi(self.api), self.placeholder, self.ct)
|
|
55
|
+
|
|
56
|
+
def get_v11_get_api(self) -> V11GetApiUtils:
|
|
57
|
+
return V11GetApiUtils(twitter.V11GetApi(self.api), self.placeholder)
|
|
58
|
+
|
|
59
|
+
def get_v11_post_api(self) -> V11PostApiUtils:
|
|
60
|
+
return V11PostApiUtils(twitter.V11PostApi(self.api), self.placeholder)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TwitterOpenapiPython:
|
|
64
|
+
hash: str = "103dca20cc540be8dc8291e6f573c3d48ba7af3e"
|
|
65
|
+
placeholder_url = "https://raw.githubusercontent.com/fa0311/twitter-openapi/{hash}/src/config/placeholder.json"
|
|
66
|
+
header = "https://raw.githubusercontent.com/fa0311/latest-user-agent/refs/heads/main/header.json"
|
|
67
|
+
access_token: str = (
|
|
68
|
+
"AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
|
|
69
|
+
)
|
|
70
|
+
twitter_url: str = "https://x.com/home"
|
|
71
|
+
additional_browser_headers = {}
|
|
72
|
+
additional_api_headers = {}
|
|
73
|
+
|
|
74
|
+
def get_header(self) -> tuple[dict[str, str], dict[str, str]]:
|
|
75
|
+
http = urllib3.PoolManager()
|
|
76
|
+
res = http.request("GET", self.header)
|
|
77
|
+
data = json.loads(res.data)
|
|
78
|
+
ignore = ["host", "connection"]
|
|
79
|
+
|
|
80
|
+
def getHader(name: str) -> dict[str, str]:
|
|
81
|
+
return {key: value for key, value in data[name].items() if key not in ignore}
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
{
|
|
85
|
+
**getHader("chrome-fetch"),
|
|
86
|
+
"accept-encoding": "identity",
|
|
87
|
+
"referer": self.twitter_url,
|
|
88
|
+
"priority": "u=1, i",
|
|
89
|
+
"x-twitter-client-language": "en",
|
|
90
|
+
"x-twitter-active-user": "yes",
|
|
91
|
+
# 'x-twitter-auth-type': 'xxxx'
|
|
92
|
+
# 'x-csrf-token': 'xxxx',
|
|
93
|
+
# 'x-guest-token': 'xxxx',
|
|
94
|
+
"authorization": f"Bearer {self.access_token}",
|
|
95
|
+
**self.additional_api_headers,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
**getHader("chrome"),
|
|
99
|
+
**self.additional_browser_headers,
|
|
100
|
+
},
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def remove_prefix(self, text: str) -> str:
|
|
104
|
+
if text.startswith("x-twitter-"):
|
|
105
|
+
return text[10:]
|
|
106
|
+
if text.startswith("x-"):
|
|
107
|
+
return text[2:]
|
|
108
|
+
return text
|
|
109
|
+
|
|
110
|
+
def kebab_to_upper_camel(self, text: dict[str, Any]) -> dict[str, Any]:
|
|
111
|
+
res = {}
|
|
112
|
+
for key, value in text.items():
|
|
113
|
+
new_key = "".join([x.capitalize() for x in self.remove_prefix(key).split("-")])
|
|
114
|
+
res[new_key] = value
|
|
115
|
+
return res
|
|
116
|
+
|
|
117
|
+
def cookie_normalize(self, cookie: list[str]) -> dict[str, str]:
|
|
118
|
+
return {x.split("; ")[0].split("=")[0]: x.split("; ")[0].split("=")[1] for x in cookie}
|
|
119
|
+
|
|
120
|
+
def cookie_to_str(self, cookie: dict[str, str]) -> str:
|
|
121
|
+
return "; ".join([f"{key}={value}" for key, value in cookie.items()])
|
|
122
|
+
|
|
123
|
+
def get_twitter_openapi_python_client(
|
|
124
|
+
self,
|
|
125
|
+
api: twitter.ApiClient,
|
|
126
|
+
) -> TwitterOpenapiPythonClient:
|
|
127
|
+
http = urllib3.PoolManager()
|
|
128
|
+
flag = http.request("GET", self.placeholder_url.format(hash=self.hash))
|
|
129
|
+
ct = get_tid()
|
|
130
|
+
return TwitterOpenapiPythonClient(api, json.loads(flag.data), ct)
|
|
131
|
+
|
|
132
|
+
def get_client_from_cookies(
|
|
133
|
+
self,
|
|
134
|
+
cookies: dict[str, str],
|
|
135
|
+
) -> TwitterOpenapiPythonClient:
|
|
136
|
+
api_key = self.kebab_to_upper_camel(self.get_header()[0])
|
|
137
|
+
|
|
138
|
+
if cookies.get("ct0"):
|
|
139
|
+
api_key.update({"AuthType": "OAuth2Session"})
|
|
140
|
+
api_key.update({"CsrfToken": cookies["ct0"]})
|
|
141
|
+
|
|
142
|
+
if cookies.get("gt"):
|
|
143
|
+
api_key.update({"GuestToken": cookies["gt"]})
|
|
144
|
+
|
|
145
|
+
api_conf = conf.Configuration(api_key=api_key)
|
|
146
|
+
api_conf.access_token = self.access_token
|
|
147
|
+
api_client = twitter.ApiClient(
|
|
148
|
+
configuration=api_conf,
|
|
149
|
+
cookie=self.cookie_to_str(cookies),
|
|
150
|
+
)
|
|
151
|
+
api_client.user_agent = api_key["UserAgent"]
|
|
152
|
+
return self.get_twitter_openapi_python_client(api_client)
|
|
153
|
+
|
|
154
|
+
def get_guest_client(self) -> TwitterOpenapiPythonClient:
|
|
155
|
+
http = urllib3.PoolManager()
|
|
156
|
+
session = {}
|
|
157
|
+
|
|
158
|
+
header = self.get_header()
|
|
159
|
+
|
|
160
|
+
res_1 = http.request(
|
|
161
|
+
"GET",
|
|
162
|
+
self.twitter_url,
|
|
163
|
+
redirect=False,
|
|
164
|
+
headers=header[1],
|
|
165
|
+
)
|
|
166
|
+
cookie = res_1.headers._container["set-cookie"][1:]
|
|
167
|
+
session.update(self.cookie_normalize(cookie))
|
|
168
|
+
res_2 = http.request(
|
|
169
|
+
"GET",
|
|
170
|
+
self.twitter_url,
|
|
171
|
+
redirect=False,
|
|
172
|
+
headers=header[1],
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
find = re.findall(r'document.cookie="(.*?)";', res_2.data.decode())
|
|
176
|
+
session.update(self.cookie_normalize(find))
|
|
177
|
+
|
|
178
|
+
session.pop("ct0")
|
|
179
|
+
|
|
180
|
+
if not session.get("gt"):
|
|
181
|
+
activate_header = header[0] | {
|
|
182
|
+
"cookie": self.cookie_to_str(session),
|
|
183
|
+
}
|
|
184
|
+
res_3 = http.request(
|
|
185
|
+
"POST",
|
|
186
|
+
"https://api.x.com/1.1/guest/activate.json",
|
|
187
|
+
headers=activate_header,
|
|
188
|
+
)
|
|
189
|
+
gt = json.loads(res_3.data.decode())["guest_token"]
|
|
190
|
+
session.update({"gt": gt})
|
|
191
|
+
|
|
192
|
+
# ct0 = cookies.get("ct0", "".join(random.choices("0123456789abcdef", k=32)))
|
|
193
|
+
|
|
194
|
+
return self.get_client_from_cookies(session)
|