tweepy-self 1.11.1__tar.gz → 1.12.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/PKG-INFO +3 -3
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/pyproject.toml +3 -3
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/__init__.py +0 -5
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/client.py +31 -28
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/errors.py +8 -14
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/utils/__init__.py +2 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/utils/other.py +4 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/README.md +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/_capsolver/__init__.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/_capsolver/core/__init__.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/_capsolver/core/base.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/_capsolver/core/config.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/_capsolver/core/enum.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/_capsolver/core/serializer.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/_capsolver/fun_captcha.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/account.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/base/__init__.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/base/client.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/base/session.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/enums.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/models.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/utils/file.py +0 -0
- {tweepy_self-1.11.1 → tweepy_self-1.12.0}/twitter/utils/html.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tweepy-self
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.12.0
|
4
4
|
Summary: Twitter (selfbot) for Python!
|
5
5
|
Home-page: https://github.com/alenkimov/tweepy-self
|
6
6
|
Author: Alen
|
@@ -12,13 +12,13 @@ Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Requires-Dist: aiohttp (>=3.9,<4.0)
|
13
13
|
Requires-Dist: beautifulsoup4 (>=4,<5)
|
14
14
|
Requires-Dist: better-proxy (>=1.1,<2.0)
|
15
|
-
Requires-Dist: curl_cffi (==0.
|
15
|
+
Requires-Dist: curl_cffi (==0.9.0b2)
|
16
16
|
Requires-Dist: loguru (>=0.7,<0.8)
|
17
17
|
Requires-Dist: lxml (>=5,<6)
|
18
18
|
Requires-Dist: pydantic (>=2,<3)
|
19
19
|
Requires-Dist: pyotp (>=2,<3)
|
20
20
|
Requires-Dist: requests (>=2,<3)
|
21
|
-
Requires-Dist: tenacity (>=
|
21
|
+
Requires-Dist: tenacity (>=9,<10)
|
22
22
|
Requires-Dist: yarl (>=1,<2)
|
23
23
|
Project-URL: Repository, https://github.com/alenkimov/tweepy-self
|
24
24
|
Project-URL: Source, https://github.com/alenkimov/tweepy-self
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "tweepy-self"
|
3
|
-
version = "1.
|
3
|
+
version = "1.12.0"
|
4
4
|
description = "Twitter (selfbot) for Python!"
|
5
5
|
authors = ["Alen <alen.kimov@gmail.com>"]
|
6
6
|
readme = "README.md"
|
@@ -12,7 +12,7 @@ Source = "https://github.com/alenkimov/tweepy-self"
|
|
12
12
|
|
13
13
|
[tool.poetry.dependencies]
|
14
14
|
python = "^3.11"
|
15
|
-
curl_cffi = "0.
|
15
|
+
curl_cffi = "0.9.0b2"
|
16
16
|
better-proxy = "^1.1"
|
17
17
|
beautifulsoup4 = "^4"
|
18
18
|
pydantic = "^2"
|
@@ -20,7 +20,7 @@ lxml = "^5"
|
|
20
20
|
pyotp = "^2"
|
21
21
|
yarl = "^1"
|
22
22
|
aiohttp = "^3.9"
|
23
|
-
tenacity = "^
|
23
|
+
tenacity = "^9"
|
24
24
|
requests = "^2"
|
25
25
|
loguru = "^0.7"
|
26
26
|
|
@@ -31,11 +31,10 @@ from .errors import (
|
|
31
31
|
from .base import BaseHTTPClient
|
32
32
|
from .account import Account, AccountStatus
|
33
33
|
from .models import User, Tweet, Media, Subtask
|
34
|
-
from .utils import
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
)
|
34
|
+
from .utils import parse_oauth_html
|
35
|
+
from .utils import parse_unlock_html
|
36
|
+
from .utils import tweets_data_from_instructions
|
37
|
+
from .utils import encode_x_client_transaction_id
|
39
38
|
|
40
39
|
|
41
40
|
class Client(BaseHTTPClient):
|
@@ -103,8 +102,8 @@ class Client(BaseHTTPClient):
|
|
103
102
|
|
104
103
|
async def _request(
|
105
104
|
self,
|
106
|
-
method,
|
107
|
-
url,
|
105
|
+
method: str,
|
106
|
+
url: str | URL,
|
108
107
|
*,
|
109
108
|
auth: bool = True,
|
110
109
|
bearer: bool = True,
|
@@ -113,6 +112,10 @@ class Client(BaseHTTPClient):
|
|
113
112
|
) -> tuple[requests.Response, Any]:
|
114
113
|
cookies = kwargs["cookies"] = kwargs.get("cookies", {})
|
115
114
|
headers = kwargs["headers"] = kwargs.get("headers", {})
|
115
|
+
|
116
|
+
url = URL(url)
|
117
|
+
headers["x-client-transaction-id"] = encode_x_client_transaction_id(url.path)
|
118
|
+
|
116
119
|
if bearer:
|
117
120
|
headers["authorization"] = f"Bearer {self._BEARER_TOKEN}"
|
118
121
|
|
@@ -140,7 +143,7 @@ class Client(BaseHTTPClient):
|
|
140
143
|
# fmt: on
|
141
144
|
|
142
145
|
try:
|
143
|
-
response = await self._session.request(method, url, **kwargs)
|
146
|
+
response = await self._session.request(method, str(url), **kwargs)
|
144
147
|
except requests.errors.RequestsError as exc:
|
145
148
|
if exc.code == 35:
|
146
149
|
msg = (
|
@@ -178,12 +181,12 @@ class Client(BaseHTTPClient):
|
|
178
181
|
if isinstance(data, dict) and "errors" in data:
|
179
182
|
exc = HTTPException(response, data)
|
180
183
|
|
181
|
-
if 141 in exc.
|
184
|
+
if 141 in exc.error_codes or 37 in exc.error_codes:
|
182
185
|
self.account.status = AccountStatus.SUSPENDED
|
183
186
|
raise AccountSuspended(exc, self.account)
|
184
187
|
|
185
|
-
if 326 in exc.
|
186
|
-
for error_data in exc.
|
188
|
+
if 326 in exc.error_codes:
|
189
|
+
for error_data in exc.errors:
|
187
190
|
if (
|
188
191
|
error_data.get("code") == 326
|
189
192
|
and error_data.get("bounce_location")
|
@@ -201,7 +204,7 @@ class Client(BaseHTTPClient):
|
|
201
204
|
if response.status_code == 400:
|
202
205
|
exc = BadRequest(response, data)
|
203
206
|
|
204
|
-
if 399 in exc.
|
207
|
+
if 399 in exc.error_codes:
|
205
208
|
self.account.status = AccountStatus.NOT_FOUND
|
206
209
|
raise AccountNotFound(exc, self.account)
|
207
210
|
|
@@ -210,7 +213,7 @@ class Client(BaseHTTPClient):
|
|
210
213
|
if response.status_code == 401:
|
211
214
|
exc = Unauthorized(response, data)
|
212
215
|
|
213
|
-
if 32 in exc.
|
216
|
+
if 32 in exc.error_codes:
|
214
217
|
self.account.status = AccountStatus.BAD_TOKEN
|
215
218
|
raise BadAccountToken(exc, self.account)
|
216
219
|
|
@@ -219,12 +222,12 @@ class Client(BaseHTTPClient):
|
|
219
222
|
if response.status_code == 403:
|
220
223
|
exc = Forbidden(response, data)
|
221
224
|
|
222
|
-
if 64 in exc.
|
225
|
+
if 64 in exc.error_codes:
|
223
226
|
self.account.status = AccountStatus.SUSPENDED
|
224
227
|
raise AccountSuspended(exc, self.account)
|
225
228
|
|
226
|
-
if 326 in exc.
|
227
|
-
for error_data in exc.
|
229
|
+
if 326 in exc.error_codes:
|
230
|
+
for error_data in exc.errors:
|
228
231
|
if (
|
229
232
|
error_data.get("code") == 326
|
230
233
|
and error_data.get("bounce_location") == "/i/flow/consent_flow"
|
@@ -268,8 +271,8 @@ class Client(BaseHTTPClient):
|
|
268
271
|
|
269
272
|
async def request(
|
270
273
|
self,
|
271
|
-
method,
|
272
|
-
url,
|
274
|
+
method: str,
|
275
|
+
url: str | URL,
|
273
276
|
*,
|
274
277
|
auto_unlock: bool = True,
|
275
278
|
auto_relogin: bool = None,
|
@@ -302,7 +305,7 @@ class Client(BaseHTTPClient):
|
|
302
305
|
except Forbidden as exc:
|
303
306
|
if (
|
304
307
|
rerequest_on_bad_ct0
|
305
|
-
and 353 in exc.
|
308
|
+
and 353 in exc.error_codes
|
306
309
|
and "ct0" in exc.response.cookies
|
307
310
|
):
|
308
311
|
return await self.request(
|
@@ -440,7 +443,7 @@ class Client(BaseHTTPClient):
|
|
440
443
|
return authenticity_token, redirect_url
|
441
444
|
|
442
445
|
async def _update_account_username(self):
|
443
|
-
url = "https://x.com/
|
446
|
+
url = "https://api.x.com/1.1/account/settings.json"
|
444
447
|
response, response_json = await self.request("POST", url)
|
445
448
|
self.account.username = response_json["screen_name"]
|
446
449
|
|
@@ -637,7 +640,7 @@ class Client(BaseHTTPClient):
|
|
637
640
|
if (
|
638
641
|
search_duplicate
|
639
642
|
and 327
|
640
|
-
in exc.
|
643
|
+
in exc.error_codes # duplicate retweet (You have already retweeted this Tweet)
|
641
644
|
):
|
642
645
|
tweets = await self.request_tweets(self.account.id)
|
643
646
|
duplicate_tweet = None
|
@@ -681,7 +684,7 @@ class Client(BaseHTTPClient):
|
|
681
684
|
try:
|
682
685
|
response_json = await self._interact_with_tweet("FavoriteTweet", tweet_id)
|
683
686
|
except HTTPException as exc:
|
684
|
-
if 139 in exc.
|
687
|
+
if 139 in exc.error_codes:
|
685
688
|
# Already liked
|
686
689
|
return True
|
687
690
|
else:
|
@@ -805,7 +808,7 @@ class Client(BaseHTTPClient):
|
|
805
808
|
except HTTPException as exc:
|
806
809
|
if (
|
807
810
|
search_duplicate
|
808
|
-
and 187 in exc.
|
811
|
+
and 187 in exc.error_codes # duplicate tweet (Status is a duplicate)
|
809
812
|
):
|
810
813
|
tweets = await self.request_tweets()
|
811
814
|
duplicate_tweet = None
|
@@ -1202,9 +1205,9 @@ class Client(BaseHTTPClient):
|
|
1202
1205
|
return updated
|
1203
1206
|
|
1204
1207
|
async def establish_status(self):
|
1205
|
-
url = "https://x.com/
|
1208
|
+
url = "https://api.x.com/1.1/account/personalization/p13n_preferences.json"
|
1206
1209
|
try:
|
1207
|
-
await self.request("
|
1210
|
+
await self.request("GET", url, auto_unlock=False, auto_relogin=False)
|
1208
1211
|
self.account.status = AccountStatus.GOOD
|
1209
1212
|
except BadAccount:
|
1210
1213
|
pass
|
@@ -1700,7 +1703,7 @@ class Client(BaseHTTPClient):
|
|
1700
1703
|
flow_token, subtasks = await self._login_acid(flow_token, self.account.email)
|
1701
1704
|
# fmt: on
|
1702
1705
|
except HTTPException as exc:
|
1703
|
-
if 399 in exc.
|
1706
|
+
if 399 in exc.error_codes:
|
1704
1707
|
logger.warning(
|
1705
1708
|
f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
|
1706
1709
|
f" Bad email!"
|
@@ -1724,7 +1727,7 @@ class Client(BaseHTTPClient):
|
|
1724
1727
|
flow_token, subtasks = await self._login_two_factor_auth_challenge(flow_token, self.account.get_totp_code())
|
1725
1728
|
# fmt: on
|
1726
1729
|
except HTTPException as exc:
|
1727
|
-
if 399 in exc.
|
1730
|
+
if 399 in exc.error_codes:
|
1728
1731
|
logger.warning(
|
1729
1732
|
f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
|
1730
1733
|
f" Bad TOTP secret!"
|
@@ -1740,7 +1743,7 @@ class Client(BaseHTTPClient):
|
|
1740
1743
|
try:
|
1741
1744
|
flow_token, subtasks = await self._login_two_factor_auth_challenge(flow_token, self.account.backup_code)
|
1742
1745
|
except HTTPException as exc:
|
1743
|
-
if 399 in exc.
|
1746
|
+
if 399 in exc.error_codes:
|
1744
1747
|
logger.warning(
|
1745
1748
|
f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
|
1746
1749
|
f" Bad backup code!"
|
@@ -41,10 +41,7 @@ def _http_exception_message(
|
|
41
41
|
if detail:
|
42
42
|
exception_message += f"\n(detail) {detail}"
|
43
43
|
for error in api_errors:
|
44
|
-
|
45
|
-
exception_message += f"\n(code {error['code']}) {error['message']}"
|
46
|
-
elif "message" in error:
|
47
|
-
exception_message += f"\n{error['message']}"
|
44
|
+
exception_message += f"\n{error}"
|
48
45
|
return exception_message
|
49
46
|
|
50
47
|
|
@@ -58,9 +55,8 @@ class HTTPException(TwitterException):
|
|
58
55
|
custom_exception_message: str = None,
|
59
56
|
):
|
60
57
|
self.response = response
|
61
|
-
self.
|
62
|
-
self.
|
63
|
-
self.api_messages: list[str] = []
|
58
|
+
self.errors: list[dict] = []
|
59
|
+
self.error_codes: list[int] = []
|
64
60
|
self.detail: str | None = None
|
65
61
|
self.html: str | None = None
|
66
62
|
|
@@ -81,17 +77,15 @@ class HTTPException(TwitterException):
|
|
81
77
|
super().__init__(exception_message)
|
82
78
|
return
|
83
79
|
|
84
|
-
self.
|
80
|
+
self.errors = data.get("errors", [data])
|
85
81
|
self.detail = data.get("detail")
|
86
82
|
|
87
|
-
for error in self.
|
83
|
+
for error in self.errors:
|
88
84
|
if "code" in error:
|
89
|
-
self.
|
90
|
-
if "message" in error:
|
91
|
-
self.api_messages.append(error["message"])
|
85
|
+
self.error_codes.append(error["code"])
|
92
86
|
|
93
87
|
exception_message = _http_exception_message(
|
94
|
-
response, self.
|
88
|
+
response, self.errors, self.detail, custom_exception_message
|
95
89
|
)
|
96
90
|
super().__init__(exception_message)
|
97
91
|
|
@@ -143,7 +137,7 @@ class BadAccount(TwitterException):
|
|
143
137
|
self.account = account
|
144
138
|
exception_message = _http_exception_message(
|
145
139
|
http_exception.response,
|
146
|
-
http_exception.
|
140
|
+
http_exception.errors,
|
147
141
|
http_exception.detail,
|
148
142
|
custom_exception_message or "Bad Twitter account.",
|
149
143
|
)
|
@@ -17,6 +17,7 @@ from .other import (
|
|
17
17
|
to_datetime,
|
18
18
|
hidden_value,
|
19
19
|
tweets_data_from_instructions,
|
20
|
+
encode_x_client_transaction_id,
|
20
21
|
)
|
21
22
|
|
22
23
|
|
@@ -35,4 +36,5 @@ __all__ = [
|
|
35
36
|
"to_datetime",
|
36
37
|
"hidden_value",
|
37
38
|
"tweets_data_from_instructions",
|
39
|
+
"encode_x_client_transaction_id",
|
38
40
|
]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|