tweepy-self 1.6.3__py3-none-any.whl → 1.10.0b1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
twitter/errors.py CHANGED
@@ -61,12 +61,17 @@ class HTTPException(TwitterException):
61
61
  self.api_codes: list[int] = []
62
62
  self.api_messages: list[str] = []
63
63
  self.detail: str | None = None
64
+ self.html: str | None = None
64
65
 
65
66
  # Если ответ — строка, то это html
66
67
  if isinstance(data, str):
67
- exception_message = (
68
- f"(response status: {response.status_code}) HTML Response:\n{data}"
69
- )
68
+ if not data:
69
+ exception_message = (
70
+ f"(response status: {response.status_code}) Empty response body."
71
+ )
72
+ else:
73
+ self.html = data
74
+ exception_message = f"(response status: {response.status_code}) HTML Response:\n{self.html}"
70
75
  if response.status_code == 429:
71
76
  exception_message = (
72
77
  f"(response status: {response.status_code}) Rate limit exceeded."
@@ -75,7 +80,7 @@ class HTTPException(TwitterException):
75
80
  super().__init__(exception_message)
76
81
  return
77
82
 
78
- self.api_errors = data.get("errors", [])
83
+ self.api_errors = data.get("errors", [data])
79
84
  self.detail = data.get("detail")
80
85
 
81
86
  for error in self.api_errors:
@@ -146,7 +151,9 @@ class BadAccount(TwitterException):
146
151
 
147
152
  class BadToken(BadAccount):
148
153
  def __init__(self, http_exception: "HTTPException", account: Account):
149
- exception_message = "Bad Twitter account's auth_token."
154
+ exception_message = (
155
+ "Bad Twitter account's auth_token. Relogin to get new token."
156
+ )
150
157
  super().__init__(http_exception, account, exception_message)
151
158
 
152
159
 
@@ -154,14 +161,14 @@ class Locked(BadAccount):
154
161
  def __init__(self, http_exception: "HTTPException", account: Account):
155
162
  exception_message = (
156
163
  f"Twitter account is locked."
157
- f" Set CapSolver API key (capsolver_api_key) to autounlock."
164
+ f" Set CapSolver API key (capsolver_api_key) to auto-unlock."
158
165
  )
159
166
  super().__init__(http_exception, account, exception_message)
160
167
 
161
168
 
162
169
  class ConsentLocked(BadAccount):
163
170
  def __init__(self, http_exception: "HTTPException", account: Account):
164
- exception_message = f"Twitter account is consent locked. Relogin to unlock."
171
+ exception_message = f"Twitter account is locked."
165
172
  super().__init__(http_exception, account, exception_message)
166
173
 
167
174
 
twitter/models.py CHANGED
@@ -1,26 +1,53 @@
1
- from datetime import datetime
1
+ from typing import Optional
2
+ from datetime import datetime, timedelta
2
3
 
3
- from pydantic import BaseModel
4
+ from pydantic import BaseModel, Field, field_validator
4
5
 
5
- from .utils import to_datetime
6
+ from .utils import to_datetime, tweet_url
6
7
 
7
8
 
8
- class UserData(BaseModel):
9
- id: int
10
- username: str
11
- name: str
12
- created_at: datetime
13
- description: str
14
- location: str
15
- followers_count: int
16
- friends_count: int
17
- raw_data: dict
9
+ class Image(BaseModel):
10
+ type: str = Field(..., alias="image_type")
11
+ width: int = Field(..., alias="w")
12
+ height: int = Field(..., alias="h")
13
+
14
+
15
+ class Media(BaseModel):
16
+ id: int = Field(..., alias="media_id")
17
+ image: Image
18
+ size: int
19
+ expires_at: datetime = Field(..., alias="expires_after_secs")
20
+
21
+ @field_validator("expires_at", mode="before")
22
+ @classmethod
23
+ def set_expires_at(cls, v):
24
+ return datetime.now() + timedelta(seconds=v)
18
25
 
19
26
  def __str__(self):
20
- return f"({self.id}) @{self.username}"
27
+ return str(self.id)
28
+
29
+
30
+ class User(BaseModel):
31
+ # fmt: off
32
+ id: int | None = None
33
+ username: str | None = None
34
+ name: str | None = None # 50
35
+ created_at: datetime | None = None
36
+ description: str | None = None # 160
37
+ location: str | None = None # 30
38
+ followers_count: int | None = None
39
+ friends_count: int | None = None
40
+ raw_data: dict | None = None
41
+ # fmt: on
42
+
43
+ def __str__(self):
44
+ return str(self.id)
45
+
46
+ def __repr__(self):
47
+ return f"{self.__class__.__name__}(id={self.id}, username={self.username})"
21
48
 
22
49
  @classmethod
23
- def from_raw_user_data(cls, data: dict):
50
+ def from_raw_data(cls, data: dict):
24
51
  legacy = data["legacy"]
25
52
  keys = ("name", "description", "location", "followers_count", "friends_count")
26
53
  values = {key: legacy[key] for key in keys}
@@ -36,44 +63,93 @@ class UserData(BaseModel):
36
63
 
37
64
 
38
65
  class Tweet(BaseModel):
39
- user_id: int
40
- id: int
41
- created_at: datetime
42
- full_text: str
43
- lang: str
44
- favorite_count: int
45
- quote_count: int
46
- reply_count: int
47
- retweet_count: int
48
- retweeted: bool
49
- raw_data: dict
50
- url: str | None = None
66
+ # fmt: off
67
+ id: int
68
+ text: str
69
+ language: str
70
+ created_at: datetime
71
+
72
+ conversation_id: int
73
+
74
+ quoted: bool
75
+ retweeted: bool
76
+ bookmarked: bool
77
+ favorited: bool
78
+
79
+ quote_count: int
80
+ retweet_count: int
81
+ bookmark_count: int
82
+ favorite_count: int
83
+ reply_count: int
84
+
85
+ quoted_tweet: Optional["Tweet"] = None
86
+ retweeted_tweet: Optional["Tweet"] = None
87
+
88
+ user: User
89
+ url: str
90
+
91
+ raw_data: dict
92
+
93
+ # TODO hashtags
94
+ # TODO media
95
+ # TODO symbols
96
+ # TODO timestamps
97
+ # TODO urls
98
+ # TODO user_mentions
99
+ # TODO views
100
+ # fmt: on
51
101
 
52
102
  def __str__(self):
53
- short_text = (
54
- f"{self.full_text[:32]}..." if len(self.full_text) > 16 else self.full_text
55
- )
56
- return f"({self.id}) {short_text}"
103
+ return str(self.id)
104
+
105
+ def __repr__(self):
106
+ return f"{self.__class__.__name__}(id={self.id}, user_id={self.user.id})"
107
+
108
+ @property
109
+ def short_text(self) -> str:
110
+ return f"{self.text[:32]}..." if len(self.text) > 16 else self.text
57
111
 
58
112
  @classmethod
59
113
  def from_raw_data(cls, data: dict):
60
- legacy = data["legacy"]
61
- keys = (
62
- "full_text",
63
- "lang",
64
- "favorite_count",
65
- "quote_count",
66
- "reply_count",
67
- "retweet_count",
68
- "retweeted",
69
- )
70
- values = {key: legacy[key] for key in keys}
71
- values.update(
72
- {
73
- "user_id": int(legacy["user_id_str"]),
74
- "id": int(legacy["id_str"]),
75
- "created_at": to_datetime(legacy["created_at"]),
76
- "raw_data": data,
77
- }
78
- )
114
+ legacy_data = data["legacy"]
115
+
116
+ user_data = data["core"]["user_results"]["result"]
117
+ user = User.from_raw_data(user_data)
118
+
119
+ id = int(legacy_data["id_str"])
120
+ url = tweet_url(user.username, id)
121
+
122
+ retweeted_tweet = None
123
+ if "retweeted_status_result" in legacy_data:
124
+ retweeted_tweet_data = legacy_data["retweeted_status_result"]["result"]
125
+ retweeted_tweet = cls.from_raw_data(retweeted_tweet_data)
126
+
127
+ quoted_tweet = None
128
+ if "quoted_status_result" in data:
129
+ quoted_tweet_data = data["quoted_status_result"]["result"]
130
+ quoted_tweet = cls.from_raw_data(quoted_tweet_data)
131
+
132
+ values = {
133
+ "id": id,
134
+ "text": legacy_data["full_text"],
135
+ "language": legacy_data["lang"],
136
+ "created_at": to_datetime(legacy_data["created_at"]),
137
+ "conversation_id": int(legacy_data["conversation_id_str"]),
138
+ "quoted": legacy_data["is_quote_status"],
139
+ "retweeted": legacy_data["retweeted"],
140
+ "bookmarked": legacy_data["bookmarked"],
141
+ "favorited": legacy_data["favorited"],
142
+ "quote_count": legacy_data["quote_count"],
143
+ "retweet_count": legacy_data["retweet_count"],
144
+ "bookmark_count": legacy_data["bookmark_count"],
145
+ "favorite_count": legacy_data["favorite_count"],
146
+ "reply_count": legacy_data["reply_count"],
147
+ "user": user.model_dump(),
148
+ "quoted_tweet": quoted_tweet.model_dump() if quoted_tweet else None,
149
+ "retweeted_tweet": (
150
+ retweeted_tweet.model_dump() if retweeted_tweet else None
151
+ ),
152
+ "url": url,
153
+ "raw_data": data,
154
+ }
79
155
  return cls(**values)
twitter/utils/__init__.py CHANGED
@@ -16,6 +16,7 @@ from .other import (
16
16
  tweet_url,
17
17
  to_datetime,
18
18
  hidden_value,
19
+ tweets_data_from_instructions,
19
20
  )
20
21
 
21
22
 
@@ -33,4 +34,5 @@ __all__ = [
33
34
  "tweet_url",
34
35
  "to_datetime",
35
36
  "hidden_value",
37
+ "tweets_data_from_instructions",
36
38
  ]
twitter/utils/other.py CHANGED
@@ -14,6 +14,19 @@ def tweet_url(username: str, tweet_id: int) -> str:
14
14
  return f"https://x.com/{username}/status/{tweet_id}"
15
15
 
16
16
 
17
+ def tweets_data_from_instructions(instructions: dict) -> list[dict]:
18
+ tweets = []
19
+ for instruction in instructions:
20
+ if instruction["type"] == "TimelineAddEntries":
21
+ for entry in instruction["entries"]:
22
+ if entry["entryId"].startswith("tweet-"):
23
+ tweet_data = entry["content"]["itemContent"]["tweet_results"][
24
+ "result"
25
+ ]
26
+ tweets.append(tweet_data)
27
+ return tweets
28
+
29
+
17
30
  def to_datetime(twitter_datetime: str):
18
31
  return datetime.strptime(twitter_datetime, "%a %b %d %H:%M:%S +0000 %Y")
19
32
 
@@ -1,16 +0,0 @@
1
- twitter/__init__.py,sha256=hdrsdbH_qFhx6ro1ct79qF9SpkgFhxgbYUw9A4RVuec,684
2
- twitter/account.py,sha256=tHzBdc34pEJI2SRbjLcmKtwAzjhzUFsa4vIQqtXAc1s,3015
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=JFPS-9Qae1iY3NfNcywxvWWmRDijaU_Rjs3WaQ00iFA,2071
6
- twitter/client.py,sha256=TXcIr-HnuqEtYOAiS5fmvY2P7KumlLE0lOAytMq89N8,61346
7
- twitter/enums.py,sha256=-OH6Ibxarq5qt4E2AhkProVawcEyIf5YG_h_G5xiV9Y,270
8
- twitter/errors.py,sha256=PsvGP3hps1HNWdKbUPhEkuDqLUmmqzhpAkRtVrJr5jc,5007
9
- twitter/models.py,sha256=ttSNuhY1knTdI-Yty54OCJF5YzARc8IaA11zKNHV9p0,2063
10
- twitter/utils/__init__.py,sha256=pyhQXwTdp0HFwV_UNF4dTyklLD9RtaefA16SrQXeNlg,589
11
- twitter/utils/file.py,sha256=Sz2KEF9DnL04aOP1XabuMYMMF4VR8dJ_KWMEVvQ666Y,1120
12
- twitter/utils/html.py,sha256=hVtIRFI2yRAdWEaShFNBG-_ZWxd16og8i8OVDnFy5Hc,1971
13
- twitter/utils/other.py,sha256=UnUxS3uDR4eggbNN16xiw96VC_MNt9tOgnBlNWvRBoY,559
14
- tweepy_self-1.6.3.dist-info/METADATA,sha256=Nl46Hi1yQY6s47FPLnT0bMjhWTYR7gM18SJsZaDxhJQ,9304
15
- tweepy_self-1.6.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
16
- tweepy_self-1.6.3.dist-info/RECORD,,