tweepy-self 1.4.0__py3-none-any.whl → 1.5.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- {tweepy_self-1.4.0.dist-info → tweepy_self-1.5.1.dist-info}/METADATA +2 -2
- tweepy_self-1.5.1.dist-info/RECORD +15 -0
- twitter/base/session.py +7 -4
- twitter/client.py +591 -374
- twitter/errors.py +45 -35
- twitter/models.py +29 -15
- twitter/utils/file.py +1 -1
- twitter/utils/html.py +26 -10
- twitter/utils/other.py +1 -1
- tweepy_self-1.4.0.dist-info/RECORD +0 -15
- {tweepy_self-1.4.0.dist-info → tweepy_self-1.5.1.dist-info}/WHEEL +0 -0
twitter/errors.py
CHANGED
@@ -24,14 +24,16 @@ class TwitterException(Exception):
|
|
24
24
|
|
25
25
|
|
26
26
|
def _http_exception_message(
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
response: requests.Response,
|
28
|
+
api_errors: list[dict],
|
29
|
+
detail: str | None,
|
30
|
+
custom_exception_message: str = None,
|
31
31
|
):
|
32
32
|
exception_message = f"(response status: {response.status_code})"
|
33
|
-
if custom_exception_message:
|
34
|
-
|
33
|
+
if custom_exception_message:
|
34
|
+
exception_message += f" {custom_exception_message}"
|
35
|
+
if detail:
|
36
|
+
exception_message += f"\n(detail) {detail}"
|
35
37
|
for error in api_errors:
|
36
38
|
if "code" in error and "message" in error:
|
37
39
|
exception_message += f"\n(code {error['code']}) {error['message']}"
|
@@ -41,14 +43,13 @@ def _http_exception_message(
|
|
41
43
|
|
42
44
|
|
43
45
|
class HTTPException(TwitterException):
|
44
|
-
"""Exception raised when an HTTP request fails.
|
45
|
-
"""
|
46
|
+
"""Exception raised when an HTTP request fails."""
|
46
47
|
|
47
48
|
def __init__(
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
self,
|
50
|
+
response: requests.Response,
|
51
|
+
data: dict | str,
|
52
|
+
custom_exception_message: str = None,
|
52
53
|
):
|
53
54
|
self.response = response
|
54
55
|
self.api_errors: list[dict] = []
|
@@ -58,10 +59,14 @@ class HTTPException(TwitterException):
|
|
58
59
|
|
59
60
|
# Если ответ — строка, то это html
|
60
61
|
if isinstance(data, str):
|
61
|
-
exception_message =
|
62
|
+
exception_message = (
|
63
|
+
f"(response status: {response.status_code}) HTML Response:\n{data}"
|
64
|
+
)
|
62
65
|
if response.status_code == 429:
|
63
|
-
exception_message = (
|
64
|
-
|
66
|
+
exception_message = (
|
67
|
+
f"(response status: {response.status_code}) Rate limit exceeded."
|
68
|
+
f" Set wait_on_rate_limit=True to ignore this exception."
|
69
|
+
)
|
65
70
|
super().__init__(exception_message)
|
66
71
|
return
|
67
72
|
|
@@ -74,52 +79,54 @@ class HTTPException(TwitterException):
|
|
74
79
|
if "message" in error:
|
75
80
|
self.api_messages.append(error["message"])
|
76
81
|
|
77
|
-
exception_message = _http_exception_message(
|
82
|
+
exception_message = _http_exception_message(
|
83
|
+
response, self.api_errors, self.detail, custom_exception_message
|
84
|
+
)
|
78
85
|
super().__init__(exception_message)
|
79
86
|
|
80
87
|
|
81
88
|
class BadRequest(HTTPException):
|
82
|
-
"""Exception raised for a 400 HTTP status code.
|
83
|
-
|
89
|
+
"""Exception raised for a 400 HTTP status code."""
|
90
|
+
|
84
91
|
pass
|
85
92
|
|
86
93
|
|
87
94
|
class Unauthorized(HTTPException):
|
88
|
-
"""Exception raised for a 401 HTTP status code.
|
89
|
-
|
95
|
+
"""Exception raised for a 401 HTTP status code."""
|
96
|
+
|
90
97
|
pass
|
91
98
|
|
92
99
|
|
93
100
|
class Forbidden(HTTPException):
|
94
|
-
"""Exception raised for a 403 HTTP status code.
|
95
|
-
|
101
|
+
"""Exception raised for a 403 HTTP status code."""
|
102
|
+
|
96
103
|
pass
|
97
104
|
|
98
105
|
|
99
106
|
class NotFound(HTTPException):
|
100
|
-
"""Exception raised for a 404 HTTP status code.
|
101
|
-
|
107
|
+
"""Exception raised for a 404 HTTP status code."""
|
108
|
+
|
102
109
|
pass
|
103
110
|
|
104
111
|
|
105
112
|
class RateLimited(HTTPException):
|
106
|
-
"""Exception raised for a 429 HTTP status code.
|
107
|
-
|
113
|
+
"""Exception raised for a 429 HTTP status code."""
|
114
|
+
|
108
115
|
pass
|
109
116
|
|
110
117
|
|
111
118
|
class ServerError(HTTPException):
|
112
|
-
"""Exception raised for a 5xx HTTP status code.
|
113
|
-
|
119
|
+
"""Exception raised for a 5xx HTTP status code."""
|
120
|
+
|
114
121
|
pass
|
115
122
|
|
116
123
|
|
117
124
|
class BadAccount(TwitterException):
|
118
125
|
def __init__(
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
126
|
+
self,
|
127
|
+
http_exception: "HTTPException",
|
128
|
+
account: Account,
|
129
|
+
custom_exception_message: str = None,
|
123
130
|
):
|
124
131
|
self.http_exception = http_exception
|
125
132
|
self.account = account
|
@@ -127,7 +134,8 @@ class BadAccount(TwitterException):
|
|
127
134
|
http_exception.response,
|
128
135
|
http_exception.api_errors,
|
129
136
|
http_exception.detail,
|
130
|
-
custom_exception_message or "Bad Twitter account."
|
137
|
+
custom_exception_message or "Bad Twitter account.",
|
138
|
+
)
|
131
139
|
super().__init__(exception_message)
|
132
140
|
|
133
141
|
|
@@ -139,8 +147,10 @@ class BadToken(BadAccount):
|
|
139
147
|
|
140
148
|
class Locked(BadAccount):
|
141
149
|
def __init__(self, http_exception: "HTTPException", account: Account):
|
142
|
-
exception_message = (
|
143
|
-
|
150
|
+
exception_message = (
|
151
|
+
f"Twitter account is locked."
|
152
|
+
f" Set CapSolver API key (capsolver_api_key) to autounlock."
|
153
|
+
)
|
144
154
|
super().__init__(http_exception, account, exception_message)
|
145
155
|
|
146
156
|
|
twitter/models.py
CHANGED
@@ -24,12 +24,14 @@ class UserData(BaseModel):
|
|
24
24
|
legacy = data["legacy"]
|
25
25
|
keys = ("name", "description", "location", "followers_count", "friends_count")
|
26
26
|
values = {key: legacy[key] for key in keys}
|
27
|
-
values.update(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
values.update(
|
28
|
+
{
|
29
|
+
"id": int(data["rest_id"]),
|
30
|
+
"username": legacy["screen_name"],
|
31
|
+
"created_at": to_datetime(legacy["created_at"]),
|
32
|
+
"raw_data": data,
|
33
|
+
}
|
34
|
+
)
|
33
35
|
return cls(**values)
|
34
36
|
|
35
37
|
|
@@ -47,18 +49,30 @@ class Tweet(BaseModel):
|
|
47
49
|
raw_data: dict
|
48
50
|
|
49
51
|
def __str__(self):
|
50
|
-
short_text =
|
52
|
+
short_text = (
|
53
|
+
f"{self.full_text[:32]}..." if len(self.full_text) > 16 else self.full_text
|
54
|
+
)
|
51
55
|
return f"({self.id}) {short_text}"
|
52
56
|
|
53
57
|
@classmethod
|
54
58
|
def from_raw_data(cls, data: dict):
|
55
|
-
legacy = data[
|
56
|
-
keys = (
|
59
|
+
legacy = data["legacy"]
|
60
|
+
keys = (
|
61
|
+
"full_text",
|
62
|
+
"lang",
|
63
|
+
"favorite_count",
|
64
|
+
"quote_count",
|
65
|
+
"reply_count",
|
66
|
+
"retweet_count",
|
67
|
+
"retweeted",
|
68
|
+
)
|
57
69
|
values = {key: legacy[key] for key in keys}
|
58
|
-
values.update(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
70
|
+
values.update(
|
71
|
+
{
|
72
|
+
"user_id": int(legacy["user_id_str"]),
|
73
|
+
"id": int(legacy["id_str"]),
|
74
|
+
"created_at": to_datetime(legacy["created_at"]),
|
75
|
+
"raw_data": data,
|
76
|
+
}
|
77
|
+
)
|
64
78
|
return cls(**values)
|
twitter/utils/file.py
CHANGED
twitter/utils/html.py
CHANGED
@@ -7,25 +7,41 @@ def parse_oauth_html(html: str) -> tuple[str | None, str | None, str | None]:
|
|
7
7
|
"""
|
8
8
|
soup = BeautifulSoup(html, "lxml")
|
9
9
|
authenticity_token_element = soup.find("input", {"name": "authenticity_token"})
|
10
|
-
authenticity_token =
|
10
|
+
authenticity_token = (
|
11
|
+
authenticity_token_element.get("value") if authenticity_token_element else None
|
12
|
+
)
|
11
13
|
redirect_url_element = soup.find("a", text="click here to continue")
|
12
14
|
redirect_url = redirect_url_element.get("href") if redirect_url_element else None
|
13
15
|
redirect_after_login_element = soup.find("input", {"name": "redirect_after_login"})
|
14
|
-
redirect_after_login_url =
|
16
|
+
redirect_after_login_url = (
|
17
|
+
redirect_after_login_element.get("value")
|
18
|
+
if redirect_after_login_element
|
19
|
+
else None
|
20
|
+
)
|
15
21
|
return authenticity_token, redirect_url, redirect_after_login_url
|
16
22
|
|
17
23
|
|
18
|
-
def parse_unlock_html(html: str) -> tuple[str | None, str | None, bool, bool]:
|
24
|
+
def parse_unlock_html(html: str) -> tuple[str | None, str | None, bool, bool, bool]:
|
19
25
|
"""
|
20
|
-
:return: authenticity_token, assignment_token, needs_unlock,
|
26
|
+
:return: authenticity_token, assignment_token, needs_unlock, start_button, finish_button
|
21
27
|
"""
|
22
28
|
soup = BeautifulSoup(html, "lxml")
|
23
29
|
authenticity_token_element = soup.find("input", {"name": "authenticity_token"})
|
24
|
-
authenticity_token =
|
30
|
+
authenticity_token = (
|
31
|
+
authenticity_token_element.get("value") if authenticity_token_element else None
|
32
|
+
)
|
25
33
|
assignment_token_element = soup.find("input", {"name": "assignment_token"})
|
26
|
-
assignment_token =
|
27
|
-
|
34
|
+
assignment_token = (
|
35
|
+
assignment_token_element.get("value") if assignment_token_element else None
|
36
|
+
)
|
37
|
+
verification_string = soup.find("input", id="verification_string")
|
28
38
|
needs_unlock = bool(verification_string)
|
29
|
-
|
30
|
-
|
31
|
-
return
|
39
|
+
start_button = bool(soup.find("input", value="Start"))
|
40
|
+
finish_button = bool(soup.find("input", value="Continue to X"))
|
41
|
+
return (
|
42
|
+
authenticity_token,
|
43
|
+
assignment_token,
|
44
|
+
needs_unlock,
|
45
|
+
start_button,
|
46
|
+
finish_button,
|
47
|
+
)
|
twitter/utils/other.py
CHANGED
@@ -15,7 +15,7 @@ def tweet_url(username: str, tweet_id: int) -> str:
|
|
15
15
|
|
16
16
|
|
17
17
|
def to_datetime(twitter_datetime: str):
|
18
|
-
return datetime.strptime(twitter_datetime,
|
18
|
+
return datetime.strptime(twitter_datetime, "%a %b %d %H:%M:%S +0000 %Y")
|
19
19
|
|
20
20
|
|
21
21
|
def hidden_value(value: str) -> str:
|
@@ -1,15 +0,0 @@
|
|
1
|
-
twitter/__init__.py,sha256=hdrsdbH_qFhx6ro1ct79qF9SpkgFhxgbYUw9A4RVuec,684
|
2
|
-
twitter/account.py,sha256=7bfV-vvN_-Sj7PjDQHSrWu0dv7lviANDPIby06pfqCA,3291
|
3
|
-
twitter/base/__init__.py,sha256=x0EHKv4q_FI6xEq2nL4V9s8P6VWr6IaHTqdH9sXB5d8,133
|
4
|
-
twitter/base/client.py,sha256=7byb0Psai-dvg_ww6Y7uyE2hV1pfTU653hFgVdRiqXo,478
|
5
|
-
twitter/base/session.py,sha256=5aMjCytV_cu-uouccJzjMUPIgXZwMElPkqb7FCawmfg,2055
|
6
|
-
twitter/client.py,sha256=H3kh957TgwDKyecBIjCeVBm-6mMo1nX36IS30qm3mg0,54724
|
7
|
-
twitter/errors.py,sha256=qzvxKDb1lkIe69KAoPkdQEnBAq0gZJ_1TPSryY75C70,4868
|
8
|
-
twitter/models.py,sha256=3-Lft160msCqOjRPubOmxMqWUkmjlTSzHSGsvZK91nU,1817
|
9
|
-
twitter/utils/__init__.py,sha256=pyhQXwTdp0HFwV_UNF4dTyklLD9RtaefA16SrQXeNlg,589
|
10
|
-
twitter/utils/file.py,sha256=-6n8I8KWDlntfciJJsfIeOi0gmqoHRIe1ldIx1ynGUE,1118
|
11
|
-
twitter/utils/html.py,sha256=Abs7iB5qgl0CLFuum4eS0gdiwDXCwcudzWDovroABBo,1813
|
12
|
-
twitter/utils/other.py,sha256=4NaGd2CIJVrDiW17shcrDlJRqFkQNbBSTiiH7kNWcww,559
|
13
|
-
tweepy_self-1.4.0.dist-info/METADATA,sha256=Qd-F5_VpywMW3MkR3R1Bx4t0wT3ZQdl8tAEXmcRnMRM,9139
|
14
|
-
tweepy_self-1.4.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
15
|
-
tweepy_self-1.4.0.dist-info/RECORD,,
|
File without changes
|