tweepy-self 1.2.1__tar.gz → 1.3.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/PKG-INFO +1 -1
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/pyproject.toml +1 -1
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/account.py +8 -8
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/client.py +22 -14
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/errors.py +1 -1
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/README.md +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/__init__.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/base/__init__.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/base/client.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/base/session.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/models.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/utils/__init__.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/utils/file.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/utils/html.py +0 -0
- {tweepy_self-1.2.1 → tweepy_self-1.3.0}/twitter/utils/other.py +0 -0
@@ -21,14 +21,14 @@ class AccountStatus(enum.StrEnum):
|
|
21
21
|
|
22
22
|
class Account(BaseModel):
|
23
23
|
auth_token: str | None = Field(default=None, pattern=r"^[a-f0-9]{40}$")
|
24
|
-
ct0: str | None
|
25
|
-
id: int | None
|
26
|
-
name: str | None
|
27
|
-
username: str | None
|
28
|
-
password: str | None
|
29
|
-
email: str | None
|
30
|
-
totp_secret: str | None =
|
31
|
-
backup_code: str | None =
|
24
|
+
ct0: str | None = None
|
25
|
+
id: int | None = None
|
26
|
+
name: str | None = None
|
27
|
+
username: str | None = None
|
28
|
+
password: str | None = None
|
29
|
+
email: str | None = None
|
30
|
+
totp_secret: str | None = None
|
31
|
+
backup_code: str | None = None
|
32
32
|
status: AccountStatus = AccountStatus.UNKNOWN
|
33
33
|
|
34
34
|
@property
|
@@ -36,7 +36,7 @@ class Client(BaseClient):
|
|
36
36
|
'authority': 'twitter.com',
|
37
37
|
'origin': 'https://twitter.com',
|
38
38
|
'x-twitter-active-user': 'yes',
|
39
|
-
|
39
|
+
'x-twitter-auth-type': 'OAuth2Session',
|
40
40
|
'x-twitter-client-language': 'en',
|
41
41
|
}
|
42
42
|
_GRAPHQL_URL = 'https://twitter.com/i/api/graphql'
|
@@ -73,11 +73,15 @@ class Client(BaseClient):
|
|
73
73
|
account: Account,
|
74
74
|
*,
|
75
75
|
wait_on_rate_limit: bool = True,
|
76
|
+
capsolver_api_key: str = None,
|
77
|
+
max_unlock_attempts: int = 4,
|
76
78
|
**session_kwargs,
|
77
79
|
):
|
78
80
|
super().__init__(**session_kwargs)
|
79
81
|
self.account = account
|
80
82
|
self.wait_on_rate_limit = wait_on_rate_limit
|
83
|
+
self.capsolver_api_key = capsolver_api_key
|
84
|
+
self.max_unlock_attempts = max_unlock_attempts
|
81
85
|
|
82
86
|
async def request(
|
83
87
|
self,
|
@@ -148,7 +152,11 @@ class Client(BaseClient):
|
|
148
152
|
|
149
153
|
if 326 in exc.api_codes:
|
150
154
|
self.account.status = AccountStatus.LOCKED
|
151
|
-
|
155
|
+
if not self.capsolver_api_key:
|
156
|
+
raise Locked(self.account)
|
157
|
+
|
158
|
+
await self.unlock()
|
159
|
+
return await self.request(method, url, auth, bearer, **kwargs)
|
152
160
|
|
153
161
|
raise exc
|
154
162
|
|
@@ -170,7 +178,11 @@ class Client(BaseClient):
|
|
170
178
|
|
171
179
|
if 326 in exc.api_codes:
|
172
180
|
self.account.status = AccountStatus.LOCKED
|
173
|
-
|
181
|
+
if not self.capsolver_api_key:
|
182
|
+
raise Locked(self.account)
|
183
|
+
|
184
|
+
await self.unlock()
|
185
|
+
return await self.request(method, url, auth, bearer, **kwargs)
|
174
186
|
|
175
187
|
raise exc
|
176
188
|
|
@@ -722,6 +734,8 @@ class Client(BaseClient):
|
|
722
734
|
is_updated = all(response_json.get(key) == value for key, value in data.items() if key != "url")
|
723
735
|
if website: is_updated &= URL(website) == URL(response_json["entities"]["url"]["urls"][0]["expanded_url"])
|
724
736
|
await self.establish_status() # Изменение данных профиля часто замораживает аккаунт
|
737
|
+
await self.unlock()
|
738
|
+
await self.request_user_data()
|
725
739
|
return is_updated
|
726
740
|
|
727
741
|
async def establish_status(self):
|
@@ -888,11 +902,7 @@ class Client(BaseClient):
|
|
888
902
|
|
889
903
|
return await self.request("POST", self._CAPTCHA_URL, data=payload, bearer=False)
|
890
904
|
|
891
|
-
async def unlock(
|
892
|
-
self,
|
893
|
-
capsolver_api_key: str,
|
894
|
-
attempts: int = 4):
|
895
|
-
await self.establish_status()
|
905
|
+
async def unlock(self):
|
896
906
|
if not self.account.status == "LOCKED":
|
897
907
|
return
|
898
908
|
|
@@ -901,7 +911,7 @@ class Client(BaseClient):
|
|
901
911
|
attempt = 1
|
902
912
|
|
903
913
|
funcaptcha = {
|
904
|
-
"api_key": capsolver_api_key,
|
914
|
+
"api_key": self.capsolver_api_key,
|
905
915
|
"websiteURL": self._CAPTCHA_URL,
|
906
916
|
"websitePublicKey": self._CAPTCHA_SITE_KEY,
|
907
917
|
}
|
@@ -921,7 +931,7 @@ class Client(BaseClient):
|
|
921
931
|
response, html = await self._confirm_unlock(authenticity_token, assignment_token,
|
922
932
|
verification_string=token)
|
923
933
|
|
924
|
-
if attempt >
|
934
|
+
if attempt > self.max_unlock_attempts or response.url == "https://twitter.com/?lang=en":
|
925
935
|
await self.establish_status()
|
926
936
|
return
|
927
937
|
|
@@ -1034,10 +1044,6 @@ class Client(BaseClient):
|
|
1034
1044
|
return await self._send_task(flow_token, subtask_inputs, auth=False)
|
1035
1045
|
|
1036
1046
|
async def _viewer(self):
|
1037
|
-
"""
|
1038
|
-
Здесь нужно забрать ct0
|
1039
|
-
:return:
|
1040
|
-
"""
|
1041
1047
|
url, query_id = self._action_to_url("Viewer")
|
1042
1048
|
features = {
|
1043
1049
|
'responsive_web_graphql_exclude_directive_enabled': True,
|
@@ -1066,6 +1072,8 @@ class Client(BaseClient):
|
|
1066
1072
|
"""
|
1067
1073
|
url = 'https://twitter.com'
|
1068
1074
|
response = await self._session.request("GET", url)
|
1075
|
+
# TODO Если в сессии есть рабочий auth_token, то не вернет нужную страницу.
|
1076
|
+
# Поэтому нужно очищать сессию перед вызовом этого метода.
|
1069
1077
|
guest_token = re.search(r'gt\s?=\s?\d+', response.text)[0].split('=')[1]
|
1070
1078
|
return guest_token
|
1071
1079
|
|
@@ -41,7 +41,7 @@ class BadToken(BadAccount):
|
|
41
41
|
|
42
42
|
class Locked(BadAccount):
|
43
43
|
def __init__(self, account: Account):
|
44
|
-
exception_message = f"Twitter account is locked.
|
44
|
+
exception_message = (f"Twitter account is locked. Paste CapSolver API key into Client instance to autounlock.")
|
45
45
|
super().__init__(account, custom_exception_message=exception_message)
|
46
46
|
|
47
47
|
|
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
|