twitter-openapi-python 0.0.24__tar.gz → 0.0.27__tar.gz
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-0.0.27/PKG-INFO +102 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/README.md +14 -3
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/pyproject.toml +6 -3
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/setup.py +17 -5
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/default_api.py +11 -18
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/initial_state_api.py +3 -5
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/post_api.py +18 -48
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/tweet_api.py +24 -37
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/user_api.py +7 -17
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/user_list_api.py +12 -21
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/users_api.py +6 -10
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/v11_get_api.py +3 -9
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/v11_post_api.py +3 -7
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/client.py +9 -3
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/utils/__init__.py +1 -1
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/utils/api.py +9 -13
- twitter_openapi_python-0.0.27/twitter_openapi_python.egg-info/PKG-INFO +102 -0
- twitter_openapi_python-0.0.27/twitter_openapi_python.egg-info/requires.txt +2 -0
- twitter_openapi_python-0.0.24/PKG-INFO +0 -15
- twitter_openapi_python-0.0.24/twitter_openapi_python.egg-info/PKG-INFO +0 -15
- twitter_openapi_python-0.0.24/twitter_openapi_python.egg-info/requires.txt +0 -2
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/LICENSE +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/LICENSE.AGPL +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/setup.cfg +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/__init__.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/api/__init__.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/models/__init__.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/models/base.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/models/header.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/models/initial_state.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/models/response.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/models/timeline.py +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/py.typed +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python.egg-info/SOURCES.txt +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python.egg-info/dependency_links.txt +0 -0
- {twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: twitter_openapi_python
|
|
3
|
+
Version: 0.0.27
|
|
4
|
+
Summary: Twitter OpenAPI
|
|
5
|
+
Home-page: https://github.com/fa0311/twitter_openapi_python
|
|
6
|
+
Author: fa0311
|
|
7
|
+
Author-email: yuki@yuki0311.com
|
|
8
|
+
License: proprietary or AGPL-3.0-or-later
|
|
9
|
+
Keywords: OpenAPI,OpenAPI-Generator,Twitter OpenAPI
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: LICENSE.AGPL
|
|
13
|
+
|
|
14
|
+
# twitter_openapi_python
|
|
15
|
+
|
|
16
|
+
Twitter scraping with data validation by pydantic.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
```shell
|
|
23
|
+
pip install twitter-openapi-python
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import json
|
|
30
|
+
import datetime
|
|
31
|
+
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from tweepy_authlib import CookieSessionUserHandler
|
|
34
|
+
from twitter_openapi_python import TwitterOpenapiPython
|
|
35
|
+
|
|
36
|
+
# login by tweepy_authlib
|
|
37
|
+
if Path("cookie.json").exists():
|
|
38
|
+
with open("cookie.json", "r") as f:
|
|
39
|
+
cookies_dict = json.load(f)
|
|
40
|
+
if isinstance(cookies_dict, list):
|
|
41
|
+
cookies_dict = {k["name"]: k["value"] for k in cookies_dict}
|
|
42
|
+
else:
|
|
43
|
+
auth_handler = CookieSessionUserHandler(
|
|
44
|
+
screen_name=input("screen_name: "),
|
|
45
|
+
password=input("password: "),
|
|
46
|
+
)
|
|
47
|
+
cookies_dict = auth_handler.get_cookies().get_dict()
|
|
48
|
+
|
|
49
|
+
# To extract cookies from Windows (Linux by default)
|
|
50
|
+
# If you use tweepy_authlib, you must be on Windows
|
|
51
|
+
client = TwitterOpenapiPython()
|
|
52
|
+
client.additional_api_headers = {
|
|
53
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
54
|
+
}
|
|
55
|
+
client.additional_browser_headers = {
|
|
56
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# get client from cookies
|
|
60
|
+
撫 = client.get_client_from_cookies(cookies=cookies_dict)
|
|
61
|
+
|
|
62
|
+
# tweet "Hello World!!" with current time
|
|
63
|
+
time = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
64
|
+
撫.get_post_api().post_create_tweet(tweet_text=f"Hello World!!{time}")
|
|
65
|
+
|
|
66
|
+
# get user info
|
|
67
|
+
response = 撫.get_user_api().get_user_by_screen_name("elonmusk")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Login
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from twitter_openapi_python import TwitterOpenapiPython
|
|
74
|
+
|
|
75
|
+
# guest client
|
|
76
|
+
撫 = TwitterOpenapiPython().get_guest_client()
|
|
77
|
+
tweet = 撫.get_default_api().get_tweet_result_by_rest_id("1349129669258448897")
|
|
78
|
+
assert tweet.data is not None
|
|
79
|
+
assert tweet.data.tweet.legacy is not None
|
|
80
|
+
print(tweet.data.tweet.legacy.full_text)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Multiple OS
|
|
84
|
+
|
|
85
|
+
The Token can only be used on the same OS that issued the Token
|
|
86
|
+
In other words, if the sec-ch-ua-platform does not match, the Token cannot be used.
|
|
87
|
+
This library uses the Linux Chrome header by default.
|
|
88
|
+
To use Token issued by Windows, do the following.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client = TwitterOpenapiPython()
|
|
92
|
+
client.additional_api_headers = {
|
|
93
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
94
|
+
};
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
This project is dual licensed. You can choose one of the following licenses:
|
|
100
|
+
|
|
101
|
+
- [Custom License](./LICENSE)
|
|
102
|
+
- [GNU Affero General Public License v3.0](./LICENSE.AGPL)
|
|
@@ -33,8 +33,18 @@ else:
|
|
|
33
33
|
)
|
|
34
34
|
cookies_dict = auth_handler.get_cookies().get_dict()
|
|
35
35
|
|
|
36
|
+
# To extract cookies from Windows (Linux by default)
|
|
37
|
+
# If you use tweepy_authlib, you must be on Windows
|
|
38
|
+
client = TwitterOpenapiPython()
|
|
39
|
+
client.additional_api_headers = {
|
|
40
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
41
|
+
}
|
|
42
|
+
client.additional_browser_headers = {
|
|
43
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
# get client from cookies
|
|
37
|
-
𝕏 =
|
|
47
|
+
𝕏 = client.get_client_from_cookies(cookies=cookies_dict)
|
|
38
48
|
|
|
39
49
|
# tweet "Hello World!!" with current time
|
|
40
50
|
time = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -64,8 +74,9 @@ In other words, if the sec-ch-ua-platform does not match, the Token cannot be us
|
|
|
64
74
|
This library uses the Linux Chrome header by default.
|
|
65
75
|
To use Token issued by Windows, do the following.
|
|
66
76
|
|
|
67
|
-
```
|
|
68
|
-
|
|
77
|
+
```python
|
|
78
|
+
client = TwitterOpenapiPython()
|
|
79
|
+
client.additional_api_headers = {
|
|
69
80
|
"sec-ch-ua-platform": '"Windows"',
|
|
70
81
|
};
|
|
71
82
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "twitter_openapi_python"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.27"
|
|
4
4
|
description = "Twitter OpenAPI"
|
|
5
5
|
authors = ["fa0311 <yuki@yuki0311.com>"]
|
|
6
6
|
license = "proprietary" # or "AGPL-3.0-only"
|
|
@@ -14,7 +14,7 @@ include = ["twitter_openapi_python/py.typed"]
|
|
|
14
14
|
python = "^3.7"
|
|
15
15
|
pydantic = ">=2.6"
|
|
16
16
|
|
|
17
|
-
twitter-openapi-python-generated = "0.0.
|
|
17
|
+
twitter-openapi-python-generated = "0.0.25"
|
|
18
18
|
|
|
19
19
|
[tool.poetry.dev-dependencies]
|
|
20
20
|
|
|
@@ -27,4 +27,7 @@ extension-pkg-whitelist = "pydantic"
|
|
|
27
27
|
|
|
28
28
|
[tool.pyright]
|
|
29
29
|
reportPrivateImportUsage = false
|
|
30
|
-
typeCheckingMode = "standard"
|
|
30
|
+
typeCheckingMode = "standard"
|
|
31
|
+
|
|
32
|
+
[tool.ruff]
|
|
33
|
+
line-length = 120
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
2
|
|
|
3
|
+
import re
|
|
4
|
+
|
|
3
5
|
from setuptools import find_packages, setup
|
|
4
6
|
|
|
5
7
|
NAME = "twitter_openapi_python"
|
|
6
|
-
VERSION = "0.0.
|
|
8
|
+
VERSION = "0.0.27"
|
|
7
9
|
PYTHON_REQUIRES = ">=3.7"
|
|
8
10
|
REQUIRES = [
|
|
9
|
-
"twitter_openapi_python_generated == 0.0.
|
|
11
|
+
"twitter_openapi_python_generated == 0.0.25",
|
|
10
12
|
"pydantic >= 2.6",
|
|
11
13
|
]
|
|
14
|
+
GITHUB_RAW_URL = (
|
|
15
|
+
"https://raw.githubusercontent.com/fa0311/twitter_openapi_python/refs/heads/master/twitter_openapi_python/"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def image_replace(text: str, url: str):
|
|
20
|
+
return re.sub(
|
|
21
|
+
r"!\[(.*?)\]\((.*?)\)",
|
|
22
|
+
r"",
|
|
23
|
+
text,
|
|
24
|
+
)
|
|
25
|
+
|
|
12
26
|
|
|
13
27
|
setup(
|
|
14
28
|
name=NAME,
|
|
@@ -23,8 +37,6 @@ setup(
|
|
|
23
37
|
include_package_data=True,
|
|
24
38
|
license="proprietary or AGPL-3.0-or-later",
|
|
25
39
|
long_description_content_type="text/markdown",
|
|
26
|
-
long_description=""
|
|
27
|
-
Twitter scraping with data validation by pydantic.
|
|
28
|
-
""", # noqa: E501
|
|
40
|
+
long_description=image_replace(open("README.md").read(), GITHUB_RAW_URL),
|
|
29
41
|
package_data={"twitter_openapi_python": ["py.typed"]},
|
|
30
42
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Callable, Optional,
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
2
2
|
|
|
3
3
|
import twitter_openapi_python_generated as twitter
|
|
4
4
|
import twitter_openapi_python_generated.models as models
|
|
@@ -7,13 +7,13 @@ from twitter_openapi_python.models import TweetApiUtilsData, TwitterApiUtilsResp
|
|
|
7
7
|
from twitter_openapi_python.utils import (
|
|
8
8
|
build_response,
|
|
9
9
|
build_tweet_api_utils,
|
|
10
|
-
check_error,
|
|
11
10
|
get_kwargs,
|
|
12
11
|
)
|
|
12
|
+
from twitter_openapi_python.utils.api import error_check
|
|
13
13
|
|
|
14
14
|
T1 = TypeVar("T1")
|
|
15
15
|
T2 = TypeVar("T2")
|
|
16
|
-
ApiFnType = Callable[..., twitter.ApiResponse]
|
|
16
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T1]]
|
|
17
17
|
ParamType = dict[str, Any]
|
|
18
18
|
|
|
19
19
|
|
|
@@ -27,20 +27,15 @@ class DefaultApiUtils:
|
|
|
27
27
|
|
|
28
28
|
def request(
|
|
29
29
|
self,
|
|
30
|
-
apiFn: ApiFnType,
|
|
30
|
+
apiFn: "ApiFnType[T1]",
|
|
31
31
|
convertFn: Callable[[T1], T2],
|
|
32
|
-
type1: Type[T1],
|
|
33
|
-
type2: Type[T2],
|
|
34
32
|
key: str,
|
|
35
33
|
param: ParamType,
|
|
36
|
-
)
|
|
34
|
+
):
|
|
37
35
|
args = get_kwargs(flag=self.flag[key], additional=param)
|
|
38
36
|
res = apiFn(**args)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
data = convertFn(checked)
|
|
42
|
-
|
|
43
|
-
return build_response(response=res, data=data)
|
|
37
|
+
data = convertFn(res.data)
|
|
38
|
+
return build_response(res, data)
|
|
44
39
|
|
|
45
40
|
def get_profile_spotlights_query(
|
|
46
41
|
self,
|
|
@@ -55,9 +50,7 @@ class DefaultApiUtils:
|
|
|
55
50
|
|
|
56
51
|
response = self.request(
|
|
57
52
|
apiFn=self.api.get_profile_spotlights_query_with_http_info,
|
|
58
|
-
convertFn=lambda x: x.data.user_result_by_screen_name,
|
|
59
|
-
type1=models.ProfileResponse,
|
|
60
|
-
type2=models.UserResultByScreenName,
|
|
53
|
+
convertFn=lambda x: error_check(x.data.user_result_by_screen_name, x.errors),
|
|
61
54
|
key="ProfileSpotlightsQuery",
|
|
62
55
|
param=param,
|
|
63
56
|
)
|
|
@@ -74,9 +67,9 @@ class DefaultApiUtils:
|
|
|
74
67
|
|
|
75
68
|
response = self.request(
|
|
76
69
|
apiFn=self.api.get_tweet_result_by_rest_id_with_http_info,
|
|
77
|
-
convertFn=lambda x:
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
convertFn=lambda x: error_check(
|
|
71
|
+
build_tweet_api_utils(error_check(x.data.tweet_result, x.errors)), x.errors
|
|
72
|
+
),
|
|
80
73
|
key="TweetResultByRestId",
|
|
81
74
|
param=param,
|
|
82
75
|
)
|
|
@@ -33,9 +33,7 @@ class InitialStateApiUtils:
|
|
|
33
33
|
def get_initial_state(self, url: str) -> InitialStateApiUtilsResponse:
|
|
34
34
|
response = self.request(url=url).data.decode("utf-8")
|
|
35
35
|
reg = '<script type="text/javascript" charset="utf-8" nonce="{nonce}">{any}</script>'
|
|
36
|
-
js = re.search(
|
|
37
|
-
reg.format(nonce=r"([a-zA-Z0-9]{48})", any=r"([\s\S]*?)"), response
|
|
38
|
-
)
|
|
36
|
+
js = re.search(reg.format(nonce=r"([a-zA-Z0-9]{48})", any=r"([\s\S]*?)"), response)
|
|
39
37
|
|
|
40
38
|
if js is None:
|
|
41
39
|
raise Exception("js is None")
|
|
@@ -56,14 +54,14 @@ class InitialStateApiUtils:
|
|
|
56
54
|
def get_user(e: dict[str, Any]) -> Optional[models.UserLegacy]:
|
|
57
55
|
try:
|
|
58
56
|
entities = e["entities"]["users"]["entities"]
|
|
59
|
-
return models.UserLegacy.
|
|
57
|
+
return models.UserLegacy.model_validate(list(entities.values())[0])
|
|
60
58
|
except Exception:
|
|
61
59
|
# todo: fix
|
|
62
60
|
return None
|
|
63
61
|
|
|
64
62
|
def get_session(e: dict[str, Any]) -> Optional[models.Session]:
|
|
65
63
|
try:
|
|
66
|
-
return models.Session.
|
|
64
|
+
return models.Session.model_validate(e["session"])
|
|
67
65
|
except Exception:
|
|
68
66
|
return None
|
|
69
67
|
|
|
@@ -4,7 +4,7 @@ import twitter_openapi_python_generated as twitter
|
|
|
4
4
|
import twitter_openapi_python_generated.models as models
|
|
5
5
|
|
|
6
6
|
from twitter_openapi_python.models import TwitterApiUtilsResponse
|
|
7
|
-
from twitter_openapi_python.utils import build_response,
|
|
7
|
+
from twitter_openapi_python.utils import build_response, non_nullable
|
|
8
8
|
|
|
9
9
|
T = TypeVar("T")
|
|
10
10
|
ResponseType = TwitterApiUtilsResponse[T]
|
|
@@ -19,14 +19,6 @@ class PostApiUtils:
|
|
|
19
19
|
self.api = api
|
|
20
20
|
self.flag = flag
|
|
21
21
|
|
|
22
|
-
def builder(
|
|
23
|
-
self,
|
|
24
|
-
res: twitter.ApiResponse,
|
|
25
|
-
type: type[T],
|
|
26
|
-
) -> ResponseType[T]:
|
|
27
|
-
checked = check_error(data=res, type=type)
|
|
28
|
-
return build_response(response=res, data=checked)
|
|
29
|
-
|
|
30
22
|
def post_create_tweet(
|
|
31
23
|
self,
|
|
32
24
|
tweet_text: str,
|
|
@@ -37,15 +29,9 @@ class PostApiUtils:
|
|
|
37
29
|
conversation_control: Optional[str] = None,
|
|
38
30
|
) -> ResponseType[models.CreateTweetResponse]:
|
|
39
31
|
variables = non_nullable(
|
|
40
|
-
twitter.PostCreateTweetRequestVariables.from_dict(
|
|
41
|
-
self.flag["CreateTweet"]["variables"]
|
|
42
|
-
)
|
|
43
|
-
)
|
|
44
|
-
features = non_nullable(
|
|
45
|
-
twitter.PostCreateTweetRequestFeatures.from_dict(
|
|
46
|
-
self.flag["CreateTweet"]["features"]
|
|
47
|
-
)
|
|
32
|
+
twitter.PostCreateTweetRequestVariables.from_dict(self.flag["CreateTweet"]["variables"])
|
|
48
33
|
)
|
|
34
|
+
features = non_nullable(twitter.PostCreateTweetRequestFeatures.from_dict(self.flag["CreateTweet"]["features"]))
|
|
49
35
|
variables.tweet_text = tweet_text
|
|
50
36
|
if media_ids:
|
|
51
37
|
tagged_or = tagged_users or []
|
|
@@ -58,10 +44,8 @@ class PostApiUtils:
|
|
|
58
44
|
]
|
|
59
45
|
variables.attachment_url = attachment_url
|
|
60
46
|
if conversation_control:
|
|
61
|
-
variables.conversation_control = (
|
|
62
|
-
|
|
63
|
-
mode=conversation_control
|
|
64
|
-
)
|
|
47
|
+
variables.conversation_control = twitter.PostCreateTweetRequestVariablesConversationControl(
|
|
48
|
+
mode=conversation_control
|
|
65
49
|
)
|
|
66
50
|
|
|
67
51
|
if in_reply_to_tweet_id:
|
|
@@ -78,17 +62,14 @@ class PostApiUtils:
|
|
|
78
62
|
features=features,
|
|
79
63
|
),
|
|
80
64
|
)
|
|
81
|
-
|
|
82
|
-
return self.builder(res=res, type=models.CreateTweetResponse)
|
|
65
|
+
return build_response(res, res.data)
|
|
83
66
|
|
|
84
67
|
def post_delete_tweet(
|
|
85
68
|
self,
|
|
86
69
|
tweet_id: str,
|
|
87
70
|
) -> ResponseType[models.DeleteTweetResponse]:
|
|
88
71
|
variables = non_nullable(
|
|
89
|
-
twitter.PostCreateRetweetRequestVariables.from_dict(
|
|
90
|
-
self.flag["DeleteTweet"]["variables"]
|
|
91
|
-
)
|
|
72
|
+
twitter.PostCreateRetweetRequestVariables.from_dict(self.flag["DeleteTweet"]["variables"])
|
|
92
73
|
)
|
|
93
74
|
variables.tweet_id = tweet_id
|
|
94
75
|
|
|
@@ -99,17 +80,14 @@ class PostApiUtils:
|
|
|
99
80
|
variables=variables,
|
|
100
81
|
),
|
|
101
82
|
)
|
|
102
|
-
|
|
103
|
-
return self.builder(res=res, type=models.DeleteTweetResponse)
|
|
83
|
+
return build_response(res, res.data)
|
|
104
84
|
|
|
105
85
|
def post_create_retweet(
|
|
106
86
|
self,
|
|
107
87
|
tweet_id: str,
|
|
108
88
|
) -> ResponseType[models.CreateRetweetResponse]:
|
|
109
89
|
variables = non_nullable(
|
|
110
|
-
twitter.PostCreateRetweetRequestVariables.from_dict(
|
|
111
|
-
self.flag["CreateRetweet"]["variables"]
|
|
112
|
-
)
|
|
90
|
+
twitter.PostCreateRetweetRequestVariables.from_dict(self.flag["CreateRetweet"]["variables"])
|
|
113
91
|
)
|
|
114
92
|
variables.tweet_id = tweet_id
|
|
115
93
|
|
|
@@ -120,17 +98,14 @@ class PostApiUtils:
|
|
|
120
98
|
variables=variables,
|
|
121
99
|
),
|
|
122
100
|
)
|
|
123
|
-
|
|
124
|
-
return self.builder(res=res, type=models.CreateRetweetResponse)
|
|
101
|
+
return build_response(res, res.data)
|
|
125
102
|
|
|
126
103
|
def post_delete_retweet(
|
|
127
104
|
self,
|
|
128
105
|
source_tweet_id: str,
|
|
129
106
|
) -> ResponseType[models.DeleteRetweetResponse]:
|
|
130
107
|
variables = non_nullable(
|
|
131
|
-
twitter.PostDeleteRetweetRequestVariables.from_dict(
|
|
132
|
-
self.flag["DeleteRetweet"]["variables"]
|
|
133
|
-
)
|
|
108
|
+
twitter.PostDeleteRetweetRequestVariables.from_dict(self.flag["DeleteRetweet"]["variables"])
|
|
134
109
|
)
|
|
135
110
|
variables.source_tweet_id = source_tweet_id
|
|
136
111
|
|
|
@@ -141,18 +116,15 @@ class PostApiUtils:
|
|
|
141
116
|
variables=variables,
|
|
142
117
|
),
|
|
143
118
|
)
|
|
144
|
-
|
|
145
|
-
return self.builder(res=res, type=models.DeleteRetweetResponse)
|
|
119
|
+
return build_response(res, res.data)
|
|
146
120
|
|
|
147
121
|
# postFavoriteTweet
|
|
148
122
|
def post_favorite_tweet(
|
|
149
123
|
self,
|
|
150
124
|
tweet_id: str,
|
|
151
|
-
) -> ResponseType[models.
|
|
125
|
+
) -> ResponseType[models.FavoriteTweetResponse]:
|
|
152
126
|
variables = non_nullable(
|
|
153
|
-
twitter.PostCreateBookmarkRequestVariables.from_dict(
|
|
154
|
-
self.flag["FavoriteTweet"]["variables"]
|
|
155
|
-
)
|
|
127
|
+
twitter.PostCreateBookmarkRequestVariables.from_dict(self.flag["FavoriteTweet"]["variables"])
|
|
156
128
|
)
|
|
157
129
|
variables.tweet_id = tweet_id
|
|
158
130
|
|
|
@@ -163,17 +135,15 @@ class PostApiUtils:
|
|
|
163
135
|
variables=variables,
|
|
164
136
|
),
|
|
165
137
|
)
|
|
166
|
-
return
|
|
138
|
+
return build_response(res, res.data)
|
|
167
139
|
|
|
168
140
|
# postUnfavoriteTweet
|
|
169
141
|
def post_unfavorite_tweet(
|
|
170
142
|
self,
|
|
171
143
|
tweet_id: str,
|
|
172
|
-
) -> ResponseType[models.
|
|
144
|
+
) -> ResponseType[models.UnfavoriteTweetResponse]:
|
|
173
145
|
variables = non_nullable(
|
|
174
|
-
twitter.PostCreateRetweetRequestVariables.from_dict(
|
|
175
|
-
self.flag["UnfavoriteTweet"]["variables"]
|
|
176
|
-
)
|
|
146
|
+
twitter.PostCreateRetweetRequestVariables.from_dict(self.flag["UnfavoriteTweet"]["variables"])
|
|
177
147
|
)
|
|
178
148
|
variables.tweet_id = tweet_id
|
|
179
149
|
|
|
@@ -184,4 +154,4 @@ class PostApiUtils:
|
|
|
184
154
|
variables=variables,
|
|
185
155
|
),
|
|
186
156
|
)
|
|
187
|
-
return
|
|
157
|
+
return build_response(res, res.data)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Callable, List, Optional,
|
|
1
|
+
from typing import Any, Callable, List, Optional, TypeVar
|
|
2
2
|
|
|
3
3
|
import twitter_openapi_python_generated as twitter
|
|
4
4
|
import twitter_openapi_python_generated.models as models
|
|
@@ -11,17 +11,16 @@ from twitter_openapi_python.models import (
|
|
|
11
11
|
)
|
|
12
12
|
from twitter_openapi_python.utils import (
|
|
13
13
|
build_response,
|
|
14
|
-
check_error,
|
|
15
14
|
entries_cursor,
|
|
15
|
+
error_check,
|
|
16
16
|
get_kwargs,
|
|
17
17
|
instruction_to_entry,
|
|
18
18
|
tweet_entries_converter,
|
|
19
19
|
)
|
|
20
|
-
from twitter_openapi_python.utils.api import non_nullable
|
|
21
20
|
|
|
22
21
|
T = TypeVar("T")
|
|
23
22
|
ResponseType = TwitterApiUtilsResponse[TimelineApiUtilsResponse[TweetApiUtilsData]]
|
|
24
|
-
ApiFnType = Callable[..., twitter.ApiResponse]
|
|
23
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
25
24
|
ParamType = dict[str, Any]
|
|
26
25
|
|
|
27
26
|
|
|
@@ -35,17 +34,14 @@ class TweetApiUtils:
|
|
|
35
34
|
|
|
36
35
|
def request(
|
|
37
36
|
self,
|
|
38
|
-
apiFn: ApiFnType,
|
|
37
|
+
apiFn: "ApiFnType[T]",
|
|
39
38
|
convertFn: Callable[[T], List[models.InstructionUnion]],
|
|
40
|
-
type: Type[T],
|
|
41
39
|
key: str,
|
|
42
40
|
param: ParamType,
|
|
43
41
|
) -> ResponseType:
|
|
44
42
|
args = get_kwargs(flag=self.flag[key], additional=param)
|
|
45
43
|
res = apiFn(**args)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
instruction = convertFn(checked)
|
|
44
|
+
instruction = convertFn(res.data)
|
|
49
45
|
entry = instruction_to_entry(instruction)
|
|
50
46
|
tweet_list = tweet_entries_converter(entry)
|
|
51
47
|
raw = ApiUtilsRaw(
|
|
@@ -57,8 +53,7 @@ class TweetApiUtils:
|
|
|
57
53
|
cursor=entries_cursor(entry),
|
|
58
54
|
data=tweet_list,
|
|
59
55
|
)
|
|
60
|
-
|
|
61
|
-
return build_response(response=res, data=data)
|
|
56
|
+
return build_response(res, data)
|
|
62
57
|
|
|
63
58
|
def get_tweet_detail(
|
|
64
59
|
self,
|
|
@@ -77,8 +72,7 @@ class TweetApiUtils:
|
|
|
77
72
|
|
|
78
73
|
response = self.request(
|
|
79
74
|
apiFn=self.api.get_tweet_detail_with_http_info,
|
|
80
|
-
convertFn=lambda e: e.data.threaded_conversation_with_injections_v2.instructions,
|
|
81
|
-
type=models.TweetDetailResponse,
|
|
75
|
+
convertFn=lambda e: error_check(e.data.threaded_conversation_with_injections_v2, e.errors).instructions,
|
|
82
76
|
key="TweetDetail",
|
|
83
77
|
param=param,
|
|
84
78
|
)
|
|
@@ -104,8 +98,7 @@ class TweetApiUtils:
|
|
|
104
98
|
|
|
105
99
|
response = self.request(
|
|
106
100
|
apiFn=self.api.get_search_timeline_with_http_info,
|
|
107
|
-
convertFn=lambda e: e.data.search_by_raw_query.search_timeline.timeline.instructions,
|
|
108
|
-
type=models.SearchTimelineResponse,
|
|
101
|
+
convertFn=lambda e: error_check(e.data.search_by_raw_query, e.errors).search_timeline.timeline.instructions,
|
|
109
102
|
key="SearchTimeline",
|
|
110
103
|
param=param,
|
|
111
104
|
)
|
|
@@ -127,8 +120,7 @@ class TweetApiUtils:
|
|
|
127
120
|
|
|
128
121
|
response = self.request(
|
|
129
122
|
apiFn=self.api.get_home_timeline_with_http_info,
|
|
130
|
-
convertFn=lambda e: e.data.home.home_timeline_urt.instructions,
|
|
131
|
-
type=models.TimelineResponse,
|
|
123
|
+
convertFn=lambda e: error_check(e.data.home, e.errors).home_timeline_urt.instructions,
|
|
132
124
|
key="HomeTimeline",
|
|
133
125
|
param=param,
|
|
134
126
|
)
|
|
@@ -150,8 +142,7 @@ class TweetApiUtils:
|
|
|
150
142
|
|
|
151
143
|
response = self.request(
|
|
152
144
|
apiFn=self.api.get_home_latest_timeline_with_http_info,
|
|
153
|
-
convertFn=lambda e: e.data.home.home_timeline_urt.instructions,
|
|
154
|
-
type=models.TimelineResponse,
|
|
145
|
+
convertFn=lambda e: error_check(e.data.home, e.errors).home_timeline_urt.instructions,
|
|
155
146
|
key="HomeLatestTimeline",
|
|
156
147
|
param=param,
|
|
157
148
|
)
|
|
@@ -174,10 +165,9 @@ class TweetApiUtils:
|
|
|
174
165
|
|
|
175
166
|
response = self.request(
|
|
176
167
|
apiFn=self.api.get_list_latest_tweets_timeline_with_http_info,
|
|
177
|
-
convertFn=lambda e:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
type=models.ListLatestTweetsTimelineResponse,
|
|
168
|
+
convertFn=lambda e: error_check(
|
|
169
|
+
error_check(e.data.list, e.errors).tweets_timeline.timeline, e.errors
|
|
170
|
+
).instructions,
|
|
181
171
|
key="ListLatestTweetsTimeline",
|
|
182
172
|
param=param,
|
|
183
173
|
)
|
|
@@ -200,10 +190,9 @@ class TweetApiUtils:
|
|
|
200
190
|
|
|
201
191
|
response = self.request(
|
|
202
192
|
apiFn=self.api.get_user_tweets_with_http_info,
|
|
203
|
-
convertFn=lambda e:
|
|
204
|
-
e.data.user.result.timeline_v2.timeline
|
|
193
|
+
convertFn=lambda e: error_check(
|
|
194
|
+
error_check(e.data.user, e.errors).result.timeline_v2.timeline, e.errors
|
|
205
195
|
).instructions,
|
|
206
|
-
type=models.UserTweetsResponse,
|
|
207
196
|
key="UserTweets",
|
|
208
197
|
param=param,
|
|
209
198
|
)
|
|
@@ -226,10 +215,9 @@ class TweetApiUtils:
|
|
|
226
215
|
|
|
227
216
|
response = self.request(
|
|
228
217
|
apiFn=self.api.get_user_tweets_and_replies_with_http_info,
|
|
229
|
-
convertFn=lambda e:
|
|
230
|
-
e.data.user.result.timeline_v2.timeline
|
|
218
|
+
convertFn=lambda e: error_check(
|
|
219
|
+
error_check(e.data.user, e.errors).result.timeline_v2.timeline, e.errors
|
|
231
220
|
).instructions,
|
|
232
|
-
type=models.UserTweetsResponse,
|
|
233
221
|
key="UserTweetsAndReplies",
|
|
234
222
|
param=param,
|
|
235
223
|
)
|
|
@@ -252,10 +240,9 @@ class TweetApiUtils:
|
|
|
252
240
|
|
|
253
241
|
response = self.request(
|
|
254
242
|
apiFn=self.api.get_user_media_with_http_info,
|
|
255
|
-
convertFn=lambda e:
|
|
256
|
-
e.data.user.result.timeline_v2.timeline
|
|
243
|
+
convertFn=lambda e: error_check(
|
|
244
|
+
error_check(e.data.user, e.errors).result.timeline_v2.timeline, e.errors
|
|
257
245
|
).instructions,
|
|
258
|
-
type=models.UserTweetsResponse,
|
|
259
246
|
key="UserMedia",
|
|
260
247
|
param=param,
|
|
261
248
|
)
|
|
@@ -278,10 +265,9 @@ class TweetApiUtils:
|
|
|
278
265
|
|
|
279
266
|
response = self.request(
|
|
280
267
|
apiFn=self.api.get_likes_with_http_info,
|
|
281
|
-
convertFn=lambda e:
|
|
282
|
-
e.data.user.result.timeline_v2.timeline
|
|
268
|
+
convertFn=lambda e: error_check(
|
|
269
|
+
error_check(e.data.user, e.errors).result.timeline_v2.timeline, e.errors
|
|
283
270
|
).instructions,
|
|
284
|
-
type=models.UserTweetsResponse,
|
|
285
271
|
key="Likes",
|
|
286
272
|
param=param,
|
|
287
273
|
)
|
|
@@ -303,8 +289,9 @@ class TweetApiUtils:
|
|
|
303
289
|
|
|
304
290
|
response = self.request(
|
|
305
291
|
apiFn=self.api.get_bookmarks_with_http_info,
|
|
306
|
-
convertFn=lambda e:
|
|
307
|
-
|
|
292
|
+
convertFn=lambda e: error_check(
|
|
293
|
+
error_check(e.data, e.errors).bookmark_timeline_v2.timeline, e.errors
|
|
294
|
+
).instructions,
|
|
308
295
|
key="Bookmarks",
|
|
309
296
|
param=param,
|
|
310
297
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Callable, Optional,
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
2
2
|
|
|
3
3
|
import twitter_openapi_python_generated as twitter
|
|
4
4
|
import twitter_openapi_python_generated.models as models
|
|
@@ -6,14 +6,14 @@ import twitter_openapi_python_generated.models as models
|
|
|
6
6
|
from twitter_openapi_python.models import TwitterApiUtilsResponse, UserApiUtilsData
|
|
7
7
|
from twitter_openapi_python.utils import (
|
|
8
8
|
build_response,
|
|
9
|
-
|
|
9
|
+
error_check,
|
|
10
10
|
get_kwargs,
|
|
11
11
|
user_or_null_converter,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
T = TypeVar("T")
|
|
15
15
|
ResponseType = TwitterApiUtilsResponse[UserApiUtilsData]
|
|
16
|
-
ApiFnType = Callable[..., twitter.ApiResponse]
|
|
16
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
17
17
|
ParamType = dict[str, Any]
|
|
18
18
|
|
|
19
19
|
|
|
@@ -27,27 +27,19 @@ class UserApiUtils:
|
|
|
27
27
|
|
|
28
28
|
def request(
|
|
29
29
|
self,
|
|
30
|
-
apiFn: ApiFnType,
|
|
30
|
+
apiFn: "ApiFnType[T]",
|
|
31
31
|
convertFn: Callable[[T], models.UserResults],
|
|
32
|
-
type: Type[T],
|
|
33
32
|
key: str,
|
|
34
33
|
param: ParamType,
|
|
35
34
|
) -> ResponseType:
|
|
36
35
|
args = get_kwargs(flag=self.flag[key], additional=param)
|
|
37
36
|
res = apiFn(**args)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
result = convertFn(checked)
|
|
37
|
+
result = convertFn(res.data)
|
|
41
38
|
if result.result is None:
|
|
42
|
-
# never reach this point.
|
|
43
39
|
raise Exception("No user")
|
|
44
|
-
|
|
45
40
|
user = user_or_null_converter(result.result)
|
|
46
|
-
|
|
47
41
|
if user is None:
|
|
48
|
-
# never reach this point.
|
|
49
42
|
raise Exception("No user")
|
|
50
|
-
|
|
51
43
|
data = UserApiUtilsData(
|
|
52
44
|
raw=result,
|
|
53
45
|
user=user,
|
|
@@ -65,8 +57,7 @@ class UserApiUtils:
|
|
|
65
57
|
param.update(extra_param)
|
|
66
58
|
return self.request(
|
|
67
59
|
apiFn=self.api.get_user_by_screen_name_with_http_info,
|
|
68
|
-
convertFn=lambda e: e.data.user,
|
|
69
|
-
type=models.UserResponse,
|
|
60
|
+
convertFn=lambda e: error_check(e.data.user, e.errors),
|
|
70
61
|
key="UserByScreenName",
|
|
71
62
|
param=param,
|
|
72
63
|
)
|
|
@@ -81,8 +72,7 @@ class UserApiUtils:
|
|
|
81
72
|
param.update(extra_param)
|
|
82
73
|
return self.request(
|
|
83
74
|
apiFn=self.api.get_user_by_rest_id_with_http_info,
|
|
84
|
-
convertFn=lambda e: e.data.user,
|
|
85
|
-
type=models.UserResponse,
|
|
75
|
+
convertFn=lambda e: error_check(e.data.user, e.errors),
|
|
86
76
|
key="UserByRestId",
|
|
87
77
|
param=param,
|
|
88
78
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Callable, List, Optional,
|
|
1
|
+
from typing import Any, Callable, List, Optional, TypeVar
|
|
2
2
|
|
|
3
3
|
import twitter_openapi_python_generated as twitter
|
|
4
4
|
import twitter_openapi_python_generated.models as models
|
|
@@ -11,18 +11,17 @@ from twitter_openapi_python.models import (
|
|
|
11
11
|
)
|
|
12
12
|
from twitter_openapi_python.utils import (
|
|
13
13
|
build_response,
|
|
14
|
-
check_error,
|
|
15
14
|
entries_cursor,
|
|
15
|
+
error_check,
|
|
16
16
|
get_kwargs,
|
|
17
17
|
instruction_to_entry,
|
|
18
|
-
non_nullable,
|
|
19
18
|
user_entries_converter,
|
|
20
19
|
user_result_converter,
|
|
21
20
|
)
|
|
22
21
|
|
|
23
22
|
T = TypeVar("T")
|
|
24
23
|
ResponseType = TwitterApiUtilsResponse[TimelineApiUtilsResponse[UserApiUtilsData]]
|
|
25
|
-
ApiFnType = Callable[..., twitter.ApiResponse]
|
|
24
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
26
25
|
ParamType = dict[str, Any]
|
|
27
26
|
|
|
28
27
|
|
|
@@ -36,17 +35,14 @@ class UserListApiUtils:
|
|
|
36
35
|
|
|
37
36
|
def request(
|
|
38
37
|
self,
|
|
39
|
-
apiFn: ApiFnType,
|
|
38
|
+
apiFn: "ApiFnType[T]",
|
|
40
39
|
convertFn: Callable[[T], List[models.InstructionUnion]],
|
|
41
|
-
type: Type[T],
|
|
42
40
|
key: str,
|
|
43
41
|
param: ParamType,
|
|
44
42
|
) -> ResponseType:
|
|
45
43
|
args = get_kwargs(flag=self.flag[key], additional=param)
|
|
46
44
|
res = apiFn(**args)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
instruction = convertFn(checked)
|
|
45
|
+
instruction = convertFn(res.data)
|
|
50
46
|
entry = instruction_to_entry(instruction)
|
|
51
47
|
user_list = user_entries_converter(entry)
|
|
52
48
|
user = user_result_converter(user_list)
|
|
@@ -79,8 +75,7 @@ class UserListApiUtils:
|
|
|
79
75
|
param.update(extra_param)
|
|
80
76
|
return self.request(
|
|
81
77
|
apiFn=self.api.get_followers_with_http_info,
|
|
82
|
-
convertFn=lambda e: e.data.user.result.timeline.timeline.instructions,
|
|
83
|
-
type=models.FollowResponse,
|
|
78
|
+
convertFn=lambda e: error_check(e.data.user, e.errors).result.timeline.timeline.instructions,
|
|
84
79
|
key="Followers",
|
|
85
80
|
param=param,
|
|
86
81
|
)
|
|
@@ -101,8 +96,7 @@ class UserListApiUtils:
|
|
|
101
96
|
param.update(extra_param)
|
|
102
97
|
return self.request(
|
|
103
98
|
apiFn=self.api.get_following_with_http_info,
|
|
104
|
-
convertFn=lambda e: e.data.user.result.timeline.timeline.instructions,
|
|
105
|
-
type=models.FollowResponse,
|
|
99
|
+
convertFn=lambda e: error_check(e.data.user, e.errors).result.timeline.timeline.instructions,
|
|
106
100
|
key="Following",
|
|
107
101
|
param=param,
|
|
108
102
|
)
|
|
@@ -123,8 +117,7 @@ class UserListApiUtils:
|
|
|
123
117
|
param.update(extra_param)
|
|
124
118
|
return self.request(
|
|
125
119
|
apiFn=self.api.get_followers_you_know_with_http_info,
|
|
126
|
-
convertFn=lambda e: e.data.user.result.timeline.timeline.instructions,
|
|
127
|
-
type=models.FollowResponse,
|
|
120
|
+
convertFn=lambda e: error_check(e.data.user, e.errors).result.timeline.timeline.instructions,
|
|
128
121
|
key="FollowersYouKnow",
|
|
129
122
|
param=param,
|
|
130
123
|
)
|
|
@@ -145,10 +138,9 @@ class UserListApiUtils:
|
|
|
145
138
|
param.update(extra_param)
|
|
146
139
|
return self.request(
|
|
147
140
|
apiFn=self.api.get_favoriters_with_http_info,
|
|
148
|
-
convertFn=lambda e:
|
|
149
|
-
e.data.favoriters_timeline.timeline
|
|
141
|
+
convertFn=lambda e: error_check(
|
|
142
|
+
error_check(e.data.favoriters_timeline, e.errors).timeline, e.errors
|
|
150
143
|
).instructions,
|
|
151
|
-
type=models.TweetFavoritersResponse,
|
|
152
144
|
key="Favoriters",
|
|
153
145
|
param=param,
|
|
154
146
|
)
|
|
@@ -169,10 +161,9 @@ class UserListApiUtils:
|
|
|
169
161
|
param.update(extra_param)
|
|
170
162
|
return self.request(
|
|
171
163
|
apiFn=self.api.get_retweeters_with_http_info,
|
|
172
|
-
convertFn=lambda e:
|
|
173
|
-
e.data.retweeters_timeline.timeline
|
|
164
|
+
convertFn=lambda e: error_check(
|
|
165
|
+
error_check(e.data.retweeters_timeline, e.errors).timeline, e.errors
|
|
174
166
|
).instructions,
|
|
175
|
-
type=models.TweetRetweetersResponse,
|
|
176
167
|
key="Retweeters",
|
|
177
168
|
param=param,
|
|
178
169
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Callable, Optional,
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
2
2
|
|
|
3
3
|
import twitter_openapi_python_generated as twitter
|
|
4
4
|
import twitter_openapi_python_generated.models as models
|
|
@@ -6,14 +6,14 @@ import twitter_openapi_python_generated.models as models
|
|
|
6
6
|
from twitter_openapi_python.models import TwitterApiUtilsResponse, UserApiUtilsData
|
|
7
7
|
from twitter_openapi_python.utils import (
|
|
8
8
|
build_response,
|
|
9
|
-
|
|
9
|
+
error_check,
|
|
10
10
|
get_kwargs,
|
|
11
11
|
user_result_converter,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
T = TypeVar("T")
|
|
15
15
|
ResponseType = TwitterApiUtilsResponse[list[UserApiUtilsData]]
|
|
16
|
-
ApiFnType = Callable[..., twitter.ApiResponse]
|
|
16
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
17
17
|
ParamType = dict[str, Any]
|
|
18
18
|
|
|
19
19
|
|
|
@@ -27,17 +27,14 @@ class UsersApiUtils:
|
|
|
27
27
|
|
|
28
28
|
def request(
|
|
29
29
|
self,
|
|
30
|
-
apiFn: ApiFnType,
|
|
30
|
+
apiFn: "ApiFnType[T]",
|
|
31
31
|
convertFn: Callable[[T], list[models.UserResults]],
|
|
32
|
-
type: Type[T],
|
|
33
32
|
key: str,
|
|
34
33
|
param: ParamType,
|
|
35
34
|
) -> ResponseType:
|
|
36
35
|
args = get_kwargs(flag=self.flag[key], additional=param)
|
|
37
36
|
res = apiFn(**args)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
user_result = convertFn(checked)
|
|
37
|
+
user_result = convertFn(res.data)
|
|
41
38
|
user = user_result_converter(user_result)
|
|
42
39
|
return build_response(response=res, data=user)
|
|
43
40
|
|
|
@@ -51,8 +48,7 @@ class UsersApiUtils:
|
|
|
51
48
|
param.update(extra_param)
|
|
52
49
|
return self.request(
|
|
53
50
|
apiFn=self.api.get_users_by_rest_ids_with_http_info,
|
|
54
|
-
convertFn=lambda e: e.data.users,
|
|
55
|
-
type=models.UsersResponse,
|
|
51
|
+
convertFn=lambda e: error_check(e.data.users, e.errors),
|
|
56
52
|
key="UsersByRestIds",
|
|
57
53
|
param=param,
|
|
58
54
|
)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import Any, Callable, Optional, Type, TypeVar
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
3
2
|
|
|
4
3
|
import twitter_openapi_python_generated as twitter
|
|
5
4
|
|
|
@@ -7,7 +6,7 @@ from twitter_openapi_python.models import TwitterApiUtilsResponse
|
|
|
7
6
|
from twitter_openapi_python.utils import build_response, get_legacy_kwargs
|
|
8
7
|
|
|
9
8
|
T = TypeVar("T")
|
|
10
|
-
ApiFnType = Callable[..., twitter.ApiResponse]
|
|
9
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
11
10
|
ParamType = dict[str, Any]
|
|
12
11
|
|
|
13
12
|
|
|
@@ -21,18 +20,15 @@ class V11GetApiUtils:
|
|
|
21
20
|
|
|
22
21
|
def request(
|
|
23
22
|
self,
|
|
24
|
-
apiFn: ApiFnType,
|
|
25
|
-
type: Type[T],
|
|
23
|
+
apiFn: "ApiFnType[T]",
|
|
26
24
|
key: str,
|
|
27
25
|
param: ParamType,
|
|
28
26
|
) -> TwitterApiUtilsResponse[T]:
|
|
29
27
|
args = get_legacy_kwargs(flag=self.flag[key], additional=param)
|
|
30
28
|
res = apiFn(**args)
|
|
31
29
|
data = res.data
|
|
32
|
-
|
|
33
30
|
if not isinstance(data, type):
|
|
34
31
|
raise Exception("Error")
|
|
35
|
-
|
|
36
32
|
return build_response(response=res, data=data)
|
|
37
33
|
|
|
38
34
|
def get_friends_following_list(
|
|
@@ -52,7 +48,6 @@ class V11GetApiUtils:
|
|
|
52
48
|
|
|
53
49
|
response = self.request(
|
|
54
50
|
apiFn=self.api.get_friends_following_list_with_http_info,
|
|
55
|
-
type=NoneType,
|
|
56
51
|
param=param,
|
|
57
52
|
key="friends/following/list.json",
|
|
58
53
|
)
|
|
@@ -69,7 +64,6 @@ class V11GetApiUtils:
|
|
|
69
64
|
|
|
70
65
|
response = self.request(
|
|
71
66
|
apiFn=self.api.get_search_typeahead_with_http_info,
|
|
72
|
-
type=NoneType,
|
|
73
67
|
param=param,
|
|
74
68
|
key="search/typeahead.json",
|
|
75
69
|
)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import Any, Callable, Optional, Type, TypeVar
|
|
1
|
+
from typing import Any, Callable, Optional, TypeVar
|
|
3
2
|
|
|
4
3
|
import twitter_openapi_python_generated as twitter
|
|
5
4
|
|
|
@@ -7,7 +6,7 @@ from twitter_openapi_python.models import TwitterApiUtilsResponse
|
|
|
7
6
|
from twitter_openapi_python.utils import build_response, get_legacy_kwargs
|
|
8
7
|
|
|
9
8
|
T = TypeVar("T")
|
|
10
|
-
ApiFnType = Callable[..., twitter.ApiResponse]
|
|
9
|
+
ApiFnType = Callable[..., twitter.ApiResponse[T]]
|
|
11
10
|
ParamType = dict[str, Any]
|
|
12
11
|
|
|
13
12
|
|
|
@@ -21,8 +20,7 @@ class V11PostApiUtils:
|
|
|
21
20
|
|
|
22
21
|
def request(
|
|
23
22
|
self,
|
|
24
|
-
apiFn: ApiFnType,
|
|
25
|
-
type: Type[T],
|
|
23
|
+
apiFn: "ApiFnType[T]",
|
|
26
24
|
key: str,
|
|
27
25
|
param: ParamType,
|
|
28
26
|
) -> TwitterApiUtilsResponse[T]:
|
|
@@ -46,7 +44,6 @@ class V11PostApiUtils:
|
|
|
46
44
|
|
|
47
45
|
response = self.request(
|
|
48
46
|
apiFn=self.api.post_create_friendships_with_http_info,
|
|
49
|
-
type=NoneType,
|
|
50
47
|
param=param,
|
|
51
48
|
key="friendships/create.json",
|
|
52
49
|
)
|
|
@@ -63,7 +60,6 @@ class V11PostApiUtils:
|
|
|
63
60
|
|
|
64
61
|
response = self.request(
|
|
65
62
|
apiFn=self.api.post_destroy_friendships_with_http_info,
|
|
66
|
-
type=NoneType,
|
|
67
63
|
param=param,
|
|
68
64
|
key="friendships/destroy.json",
|
|
69
65
|
)
|
{twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/client.py
RENAMED
|
@@ -58,7 +58,7 @@ class TwitterOpenapiPythonClient:
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class TwitterOpenapiPython:
|
|
61
|
-
hash: str = "
|
|
61
|
+
hash: str = "4b27150977e1bdd807de8969dc0ddd7918ac9923"
|
|
62
62
|
placeholder_url = "https://raw.githubusercontent.com/fa0311/twitter-openapi/{hash}/src/config/placeholder.json"
|
|
63
63
|
header = "https://raw.githubusercontent.com/fa0311/latest-user-agent/refs/heads/main/header.json"
|
|
64
64
|
access_token: str = "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
|
|
@@ -81,7 +81,6 @@ class TwitterOpenapiPython:
|
|
|
81
81
|
{
|
|
82
82
|
**getHader("chrome-fetch"),
|
|
83
83
|
"accept-encoding": "identity",
|
|
84
|
-
"pragma": "no-cache",
|
|
85
84
|
"referer": self.twitter_url,
|
|
86
85
|
"priority": "u=1, i",
|
|
87
86
|
"x-twitter-client-language": "en",
|
|
@@ -98,11 +97,18 @@ class TwitterOpenapiPython:
|
|
|
98
97
|
},
|
|
99
98
|
)
|
|
100
99
|
|
|
100
|
+
def remove_prefix(self, text: str) -> str:
|
|
101
|
+
if text.startswith("x-twitter-"):
|
|
102
|
+
return text[10:]
|
|
103
|
+
if text.startswith("x-"):
|
|
104
|
+
return text[2:]
|
|
105
|
+
return text
|
|
106
|
+
|
|
101
107
|
def kebab_to_upper_camel(self, text: dict[str, Any]) -> dict[str, Any]:
|
|
102
108
|
res = {}
|
|
103
109
|
for key, value in text.items():
|
|
104
110
|
new_key = "".join(
|
|
105
|
-
[x.capitalize() for x in
|
|
111
|
+
[x.capitalize() for x in self.remove_prefix(key).split("-")]
|
|
106
112
|
)
|
|
107
113
|
res[new_key] = value
|
|
108
114
|
return res
|
{twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/utils/api.py
RENAMED
|
@@ -28,6 +28,7 @@ def non_nullable(x: Optional[T]) -> T:
|
|
|
28
28
|
raise Exception("No data")
|
|
29
29
|
return x
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
def non_nullable_list(x: List[Optional[T]]) -> List[T]:
|
|
32
33
|
def filter_fn(x: Optional[T]) -> TypeGuard[T]:
|
|
33
34
|
return x is not None
|
|
@@ -58,15 +59,13 @@ def get_legacy_kwargs(flag: ParamType, additional: ParamType) -> ParamType:
|
|
|
58
59
|
return res | additional
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
def
|
|
62
|
-
if data
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return oneOf
|
|
69
|
-
raise Exception("Error")
|
|
62
|
+
def error_check(data: Optional[T], error: Optional[List[models.ErrorResponse]]) -> T:
|
|
63
|
+
if data is None:
|
|
64
|
+
if error is None:
|
|
65
|
+
raise Exception("No data")
|
|
66
|
+
else:
|
|
67
|
+
raise Exception(", ".join([x.message for x in error]))
|
|
68
|
+
return data
|
|
70
69
|
|
|
71
70
|
|
|
72
71
|
def instruction_to_entry(
|
|
@@ -263,10 +262,7 @@ def build_header(headers: Dict[str, str]) -> ApiUtilsHeader:
|
|
|
263
262
|
)
|
|
264
263
|
|
|
265
264
|
|
|
266
|
-
def build_response(
|
|
267
|
-
response: twitter.ApiResponse,
|
|
268
|
-
data: T1,
|
|
269
|
-
) -> TwitterApiUtilsResponse[T1]:
|
|
265
|
+
def build_response(response: twitter.ApiResponse, data: T1) -> TwitterApiUtilsResponse[T1]:
|
|
270
266
|
if response.headers is None:
|
|
271
267
|
raise Exception("headers is None")
|
|
272
268
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: twitter-openapi-python
|
|
3
|
+
Version: 0.0.27
|
|
4
|
+
Summary: Twitter OpenAPI
|
|
5
|
+
Home-page: https://github.com/fa0311/twitter_openapi_python
|
|
6
|
+
Author: fa0311
|
|
7
|
+
Author-email: yuki@yuki0311.com
|
|
8
|
+
License: proprietary or AGPL-3.0-or-later
|
|
9
|
+
Keywords: OpenAPI,OpenAPI-Generator,Twitter OpenAPI
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: LICENSE.AGPL
|
|
13
|
+
|
|
14
|
+
# twitter_openapi_python
|
|
15
|
+
|
|
16
|
+
Twitter scraping with data validation by pydantic.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
```shell
|
|
23
|
+
pip install twitter-openapi-python
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import json
|
|
30
|
+
import datetime
|
|
31
|
+
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from tweepy_authlib import CookieSessionUserHandler
|
|
34
|
+
from twitter_openapi_python import TwitterOpenapiPython
|
|
35
|
+
|
|
36
|
+
# login by tweepy_authlib
|
|
37
|
+
if Path("cookie.json").exists():
|
|
38
|
+
with open("cookie.json", "r") as f:
|
|
39
|
+
cookies_dict = json.load(f)
|
|
40
|
+
if isinstance(cookies_dict, list):
|
|
41
|
+
cookies_dict = {k["name"]: k["value"] for k in cookies_dict}
|
|
42
|
+
else:
|
|
43
|
+
auth_handler = CookieSessionUserHandler(
|
|
44
|
+
screen_name=input("screen_name: "),
|
|
45
|
+
password=input("password: "),
|
|
46
|
+
)
|
|
47
|
+
cookies_dict = auth_handler.get_cookies().get_dict()
|
|
48
|
+
|
|
49
|
+
# To extract cookies from Windows (Linux by default)
|
|
50
|
+
# If you use tweepy_authlib, you must be on Windows
|
|
51
|
+
client = TwitterOpenapiPython()
|
|
52
|
+
client.additional_api_headers = {
|
|
53
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
54
|
+
}
|
|
55
|
+
client.additional_browser_headers = {
|
|
56
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# get client from cookies
|
|
60
|
+
撫 = client.get_client_from_cookies(cookies=cookies_dict)
|
|
61
|
+
|
|
62
|
+
# tweet "Hello World!!" with current time
|
|
63
|
+
time = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
64
|
+
撫.get_post_api().post_create_tweet(tweet_text=f"Hello World!!{time}")
|
|
65
|
+
|
|
66
|
+
# get user info
|
|
67
|
+
response = 撫.get_user_api().get_user_by_screen_name("elonmusk")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Login
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from twitter_openapi_python import TwitterOpenapiPython
|
|
74
|
+
|
|
75
|
+
# guest client
|
|
76
|
+
撫 = TwitterOpenapiPython().get_guest_client()
|
|
77
|
+
tweet = 撫.get_default_api().get_tweet_result_by_rest_id("1349129669258448897")
|
|
78
|
+
assert tweet.data is not None
|
|
79
|
+
assert tweet.data.tweet.legacy is not None
|
|
80
|
+
print(tweet.data.tweet.legacy.full_text)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Multiple OS
|
|
84
|
+
|
|
85
|
+
The Token can only be used on the same OS that issued the Token
|
|
86
|
+
In other words, if the sec-ch-ua-platform does not match, the Token cannot be used.
|
|
87
|
+
This library uses the Linux Chrome header by default.
|
|
88
|
+
To use Token issued by Windows, do the following.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client = TwitterOpenapiPython()
|
|
92
|
+
client.additional_api_headers = {
|
|
93
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
94
|
+
};
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
This project is dual licensed. You can choose one of the following licenses:
|
|
100
|
+
|
|
101
|
+
- [Custom License](./LICENSE)
|
|
102
|
+
- [GNU Affero General Public License v3.0](./LICENSE.AGPL)
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: twitter_openapi_python
|
|
3
|
-
Version: 0.0.24
|
|
4
|
-
Summary: Twitter OpenAPI
|
|
5
|
-
Home-page: https://github.com/fa0311/twitter_openapi_python
|
|
6
|
-
Author: fa0311
|
|
7
|
-
Author-email: yuki@yuki0311.com
|
|
8
|
-
License: proprietary or AGPL-3.0-or-later
|
|
9
|
-
Keywords: OpenAPI,OpenAPI-Generator,Twitter OpenAPI
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
License-File: LICENSE.AGPL
|
|
13
|
-
|
|
14
|
-
Twitter scraping with data validation by pydantic.
|
|
15
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: twitter-openapi-python
|
|
3
|
-
Version: 0.0.24
|
|
4
|
-
Summary: Twitter OpenAPI
|
|
5
|
-
Home-page: https://github.com/fa0311/twitter_openapi_python
|
|
6
|
-
Author: fa0311
|
|
7
|
-
Author-email: yuki@yuki0311.com
|
|
8
|
-
License: proprietary or AGPL-3.0-or-later
|
|
9
|
-
Keywords: OpenAPI,OpenAPI-Generator,Twitter OpenAPI
|
|
10
|
-
Description-Content-Type: text/markdown
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
License-File: LICENSE.AGPL
|
|
13
|
-
|
|
14
|
-
Twitter scraping with data validation by pydantic.
|
|
15
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{twitter_openapi_python-0.0.24 → twitter_openapi_python-0.0.27}/twitter_openapi_python/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|