tweepy-self 1.4.0__py3-none-any.whl → 1.5.1__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.
- {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
|