tweepy-self 1.10.0b1__tar.gz → 1.10.0b3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (23) hide show
  1. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/PKG-INFO +2 -2
  2. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/pyproject.toml +2 -2
  3. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/client.py +89 -47
  4. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/README.md +0 -0
  5. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/__init__.py +0 -0
  6. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/_capsolver/__init__.py +0 -0
  7. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/_capsolver/core/__init__.py +0 -0
  8. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/_capsolver/core/base.py +0 -0
  9. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/_capsolver/core/config.py +0 -0
  10. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/_capsolver/core/enum.py +0 -0
  11. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/_capsolver/core/serializer.py +0 -0
  12. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/_capsolver/fun_captcha.py +0 -0
  13. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/account.py +0 -0
  14. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/base/__init__.py +0 -0
  15. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/base/client.py +0 -0
  16. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/base/session.py +0 -0
  17. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/enums.py +0 -0
  18. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/errors.py +0 -0
  19. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/models.py +0 -0
  20. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/utils/__init__.py +0 -0
  21. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/utils/file.py +0 -0
  22. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/utils/html.py +0 -0
  23. {tweepy_self-1.10.0b1 → tweepy_self-1.10.0b3}/twitter/utils/other.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tweepy-self
3
- Version: 1.10.0b1
3
+ Version: 1.10.0b3
4
4
  Summary: Twitter (selfbot) for Python!
5
5
  Home-page: https://github.com/alenkimov/tweepy-self
6
6
  Author: Alen
@@ -15,7 +15,7 @@ Requires-Dist: better-proxy (>=1.1,<2.0)
15
15
  Requires-Dist: curl_cffi (==0.6.2)
16
16
  Requires-Dist: loguru (>=0.7,<0.8)
17
17
  Requires-Dist: lxml (>=5,<6)
18
- Requires-Dist: pydantic (>=1)
18
+ Requires-Dist: pydantic (>=2)
19
19
  Requires-Dist: pyotp (>=2,<3)
20
20
  Requires-Dist: requests (>=2,<3)
21
21
  Requires-Dist: tenacity (>=8,<9)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "tweepy-self"
3
- version = "1.10.0.b1"
3
+ version = "1.10.0.b3"
4
4
  description = "Twitter (selfbot) for Python!"
5
5
  authors = ["Alen <alen.kimov@gmail.com>"]
6
6
  readme = "README.md"
@@ -15,7 +15,7 @@ python = "^3.11"
15
15
  curl_cffi = "0.6.2"
16
16
  better-proxy = "^1.1"
17
17
  beautifulsoup4 = "^4"
18
- pydantic = ">=1"
18
+ pydantic = ">=2"
19
19
  lxml = "^5"
20
20
  pyotp = "^2"
21
21
  yarl = "^1"
@@ -52,7 +52,7 @@ class Client(BaseHTTPClient):
52
52
  "CreateRetweet": "ojPdsZsimiJrUGLR1sjUtA",
53
53
  "FavoriteTweet": "lI07N6Otwv1PhnEgXILM7A",
54
54
  "UnfavoriteTweet": "ZYKSe-w7KEslx3JhSIk5LA",
55
- "CreateTweet": "SoVnbfCycZ7fERGCwpZkYA",
55
+ "CreateTweet": "v0en1yVV-Ybeek8ClmXwYw",
56
56
  "TweetResultByRestId": "V3vfsYzNEyD9tsf4xoFRgw",
57
57
  "ModerateTweet": "p'jF:GVqCjTcZol0xcBJjw",
58
58
  "DeleteTweet": "VaenaVgh5q5ih7kvyVjgtg",
@@ -159,7 +159,7 @@ class Client(BaseHTTPClient):
159
159
  auth_token = self._session.cookies.get("auth_token")
160
160
  if auth_token and auth_token != self.account.auth_token:
161
161
  self.account.auth_token = auth_token
162
- logger.info(
162
+ logger.warning(
163
163
  f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
164
164
  f" Requested new auth_token!"
165
165
  )
@@ -239,7 +239,7 @@ class Client(BaseHTTPClient):
239
239
  reset_time = int(response.headers["x-rate-limit-reset"])
240
240
  sleep_time = reset_time - int(time()) + 1
241
241
  if sleep_time > 0:
242
- logger.info(
242
+ logger.warning(
243
243
  f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
244
244
  f"Rate limited! Sleep time: {sleep_time} sec."
245
245
  )
@@ -290,7 +290,7 @@ class Client(BaseHTTPClient):
290
290
 
291
291
  except Forbidden as exc:
292
292
  if 353 in exc.api_codes and "ct0" in exc.response.cookies:
293
- return await self._request(method, url, **kwargs)
293
+ return await self.request(method, url, **kwargs)
294
294
  else:
295
295
  raise
296
296
 
@@ -426,7 +426,7 @@ class Client(BaseHTTPClient):
426
426
  response, response_json = await self.request("POST", url)
427
427
  self.account.username = response_json["screen_name"]
428
428
 
429
- async def _request_user(self, username: str) -> User:
429
+ async def _request_user(self, username: str) -> User | None:
430
430
  url, query_id = self._action_to_url("UserByScreenName")
431
431
  username = remove_at_sign(username)
432
432
  variables = {
@@ -454,26 +454,39 @@ class Client(BaseHTTPClient):
454
454
  "fieldToggles": to_json(field_toggles),
455
455
  }
456
456
  response, data = await self.request("GET", url, params=params)
457
+ if not data["data"]:
458
+ return None
457
459
  return User.from_raw_data(data["data"]["user"]["result"])
458
460
 
459
461
  async def request_user(
460
462
  self, *, username: str = None, user_id: int | str = None
461
- ) -> User | Account:
463
+ ) -> User | Account | None:
464
+ """
465
+ Возвращает None, если задано несуществующее имя пользователя
466
+ """
462
467
  if username and user_id:
463
468
  raise ValueError("Specify username or user_id, not both.")
464
469
 
465
470
  if user_id:
466
471
  users = await self.request_users((user_id,))
467
472
  user = users[user_id]
473
+ elif username:
474
+ user = await self._request_user(username)
468
475
  else:
469
- if not username:
470
- if not self.account.username:
471
- await self.request_and_set_username()
472
- username = self.account.username
476
+ if not self.account.username:
477
+ await self.request_and_set_username()
473
478
 
474
- user = await self._request_user(username)
479
+ user = await self._request_user(self.account.username)
480
+
481
+ if not user:
482
+ bad_username = self.account.username
483
+ await self.request_and_set_username()
484
+ user = await self._request_user(self.account.username)
485
+ logger.warning(
486
+ f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
487
+ f" Bad username: {bad_username}. Requested a real username."
488
+ )
475
489
 
476
- if self.account.username == user.username:
477
490
  self.account.update(**user.model_dump())
478
491
  user = self.account
479
492
 
@@ -606,6 +619,8 @@ class Client(BaseHTTPClient):
606
619
  """
607
620
  Repost (retweet)
608
621
 
622
+ Иногда может вернуть ошибку 404 (Not Found), если плохой прокси или по другим неизвестным причинам
623
+
609
624
  :return: Tweet
610
625
  """
611
626
  return await self._repost_or_search_duplicate(
@@ -613,9 +628,18 @@ class Client(BaseHTTPClient):
613
628
  )
614
629
 
615
630
  async def like(self, tweet_id: int) -> bool:
616
- response_json = await self._interact_with_tweet("FavoriteTweet", tweet_id)
617
- is_liked = response_json["data"]["favorite_tweet"] == "Done"
618
- return is_liked
631
+ """
632
+ :return: Liked or not
633
+ """
634
+ try:
635
+ response_json = await self._interact_with_tweet("FavoriteTweet", tweet_id)
636
+ except HTTPException as exc:
637
+ if 139 in exc.api_codes:
638
+ # Already liked
639
+ return True
640
+ else:
641
+ raise
642
+ return response_json["data"]["favorite_tweet"] == "Done"
619
643
 
620
644
  async def unlike(self, tweet_id: int) -> dict:
621
645
  response_json = await self._interact_with_tweet("UnfavoriteTweet", tweet_id)
@@ -662,47 +686,50 @@ class Client(BaseHTTPClient):
662
686
  attachment_url: str = None,
663
687
  ) -> Tweet:
664
688
  url, query_id = self._action_to_url("CreateTweet")
665
- payload = {
666
- "variables": {
667
- "tweet_text": text if text is not None else "",
668
- "dark_request": False,
669
- "media": {"media_entities": [], "possibly_sensitive": False},
670
- "semantic_annotation_ids": [],
671
- },
672
- "features": {
673
- "tweetypie_unmention_optimization_enabled": True,
674
- "responsive_web_edit_tweet_api_enabled": True,
675
- "graphql_is_translatable_rweb_tweet_is_translatable_enabled": True,
676
- "view_counts_everywhere_api_enabled": True,
677
- "longform_notetweets_consumption_enabled": True,
678
- "tweet_awards_web_tipping_enabled": False,
679
- "longform_notetweets_rich_text_read_enabled": True,
680
- "longform_notetweets_inline_media_enabled": True,
681
- "responsive_web_graphql_exclude_directive_enabled": True,
682
- "verified_phone_label_enabled": False,
683
- "freedom_of_speech_not_reach_fetch_enabled": True,
684
- "standardized_nudges_misinfo": True,
685
- "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": False,
686
- "responsive_web_graphql_skip_user_profile_image_extensions_enabled": False,
687
- "responsive_web_graphql_timeline_navigation_enabled": True,
688
- "responsive_web_enhance_cards_enabled": False,
689
- "responsive_web_twitter_article_tweet_consumption_enabled": False,
690
- "responsive_web_media_download_video_enabled": False,
691
- },
692
- "queryId": query_id,
689
+ variables = {
690
+ "tweet_text": text if text is not None else "",
691
+ "dark_request": False,
692
+ "media": {"media_entities": [], "possibly_sensitive": False},
693
+ "semantic_annotation_ids": [],
693
694
  }
694
695
  if attachment_url:
695
- payload["variables"]["attachment_url"] = attachment_url
696
+ variables["attachment_url"] = attachment_url
696
697
  if tweet_id_to_reply:
697
- payload["variables"]["reply"] = {
698
+ variables["reply"] = {
698
699
  "in_reply_to_tweet_id": str(tweet_id_to_reply),
699
700
  "exclude_reply_user_ids": [],
700
701
  }
701
702
  if media_id:
702
- payload["variables"]["media"]["media_entities"].append(
703
+ variables["media"]["media_entities"].append(
703
704
  {"media_id": str(media_id), "tagged_users": []}
704
705
  )
705
-
706
+ features = {
707
+ "communities_web_enable_tweet_community_results_fetch": True,
708
+ "c9s_tweet_anatomy_moderator_badge_enabled": True,
709
+ "tweetypie_unmention_optimization_enabled": True,
710
+ "responsive_web_edit_tweet_api_enabled": True,
711
+ "graphql_is_translatable_rweb_tweet_is_translatable_enabled": True,
712
+ "view_counts_everywhere_api_enabled": True,
713
+ "longform_notetweets_consumption_enabled": True,
714
+ "responsive_web_twitter_article_tweet_consumption_enabled": True,
715
+ "tweet_awards_web_tipping_enabled": False,
716
+ "longform_notetweets_rich_text_read_enabled": True,
717
+ "longform_notetweets_inline_media_enabled": True,
718
+ "rweb_video_timestamps_enabled": True,
719
+ "responsive_web_graphql_exclude_directive_enabled": True,
720
+ "verified_phone_label_enabled": False,
721
+ "freedom_of_speech_not_reach_fetch_enabled": True,
722
+ "standardized_nudges_misinfo": True,
723
+ "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": True,
724
+ "responsive_web_graphql_skip_user_profile_image_extensions_enabled": False,
725
+ "responsive_web_graphql_timeline_navigation_enabled": True,
726
+ "responsive_web_enhance_cards_enabled": False,
727
+ }
728
+ payload = {
729
+ "variables": variables,
730
+ "features": features,
731
+ "queryId": query_id,
732
+ }
706
733
  response, response_json = await self.request("POST", url, json=payload)
707
734
  tweet = Tweet.from_raw_data(
708
735
  response_json["data"]["create_tweet"]["tweet_results"]["result"]
@@ -754,6 +781,11 @@ class Client(BaseHTTPClient):
754
781
  media_id: int | str = None,
755
782
  search_duplicate: bool = True,
756
783
  ) -> Tweet:
784
+ """
785
+ Иногда может вернуть ошибку 404 (Not Found), если плохой прокси или по другим неизвестным причинам
786
+
787
+ :return: Tweet
788
+ """
757
789
  return await self._tweet_or_search_duplicate(
758
790
  text,
759
791
  media_id=media_id,
@@ -768,6 +800,11 @@ class Client(BaseHTTPClient):
768
800
  media_id: int | str = None,
769
801
  search_duplicate: bool = True,
770
802
  ) -> Tweet:
803
+ """
804
+ Иногда может вернуть ошибку 404 (Not Found), если плохой прокси или по другим неизвестным причинам
805
+
806
+ :return: Tweet
807
+ """
771
808
  return await self._tweet_or_search_duplicate(
772
809
  text,
773
810
  media_id=media_id,
@@ -783,6 +820,11 @@ class Client(BaseHTTPClient):
783
820
  media_id: int | str = None,
784
821
  search_duplicate: bool = True,
785
822
  ) -> Tweet:
823
+ """
824
+ Иногда может вернуть ошибку 404 (Not Found), если плохой прокси или по другим неизвестным причинам
825
+
826
+ :return: Tweet
827
+ """
786
828
  return await self._tweet_or_search_duplicate(
787
829
  text,
788
830
  media_id=media_id,
File without changes