tweepy-self 1.10.0b7__py3-none-any.whl → 1.10.0b8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tweepy-self
3
- Version: 1.10.0b7
3
+ Version: 1.10.0b8
4
4
  Summary: Twitter (selfbot) for Python!
5
5
  Home-page: https://github.com/alenkimov/tweepy-self
6
6
  Author: Alen
@@ -10,14 +10,14 @@ twitter/account.py,sha256=joAB5Zw-Le5E3kOZ-1nb4DPGlTqWYv2Vs6gJ3cwu7is,3175
10
10
  twitter/base/__init__.py,sha256=Q2ko0HeOS5tiBnDVKxxaZYetwRR3YXJ67ujL3oThGd4,141
11
11
  twitter/base/client.py,sha256=J_iL4ZGfwTbZ2gpjtFCbBxNgt7weJ55EeMGzYsLtjf4,500
12
12
  twitter/base/session.py,sha256=JFPS-9Qae1iY3NfNcywxvWWmRDijaU_Rjs3WaQ00iFA,2071
13
- twitter/client.py,sha256=69QQb-ZnbjqpIhhozxsboruuy3MeJDUGM7nPEH-cBdg,73559
13
+ twitter/client.py,sha256=3eCld11wiZPOKF561RlTcJ_Re-lupvGSPz7MPKrJl1k,75103
14
14
  twitter/enums.py,sha256=-OH6Ibxarq5qt4E2AhkProVawcEyIf5YG_h_G5xiV9Y,270
15
15
  twitter/errors.py,sha256=oNa0Neos80ZK4-0FBzqgxXonH564qFnoN-kavHalfR4,5274
16
- twitter/models.py,sha256=7yObMPUUEwJEbraHzFwmUKd91UhR2-zyfJTm4xIqrSQ,4834
16
+ twitter/models.py,sha256=CrGb3dvA0U4PfPTkUtuprPKXpqkLpM8AR_-De4D3efM,5677
17
17
  twitter/utils/__init__.py,sha256=usxpfcRQ7zxTTgZ-i425tT7hIz73Pwh9FDj4t6O3dYg,663
18
18
  twitter/utils/file.py,sha256=Sz2KEF9DnL04aOP1XabuMYMMF4VR8dJ_KWMEVvQ666Y,1120
19
19
  twitter/utils/html.py,sha256=nrOJw0vUKfBaHgFaQSQIdXfvfZ8mdu84MU_s46kJTJ4,2087
20
20
  twitter/utils/other.py,sha256=9RIYF2AMdmNKIwClG3jBP7zlvxZPEgYfuHaIiOhURzM,1061
21
- tweepy_self-1.10.0b7.dist-info/METADATA,sha256=NpC2w-frRkmdX851z52xcAnsXX33NJ49pNuo21TIFUY,13153
22
- tweepy_self-1.10.0b7.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
23
- tweepy_self-1.10.0b7.dist-info/RECORD,,
21
+ tweepy_self-1.10.0b8.dist-info/METADATA,sha256=gghI1xBSOt1w8QajiIFg6H93LAydql3haQ3tk0YghoU,13153
22
+ tweepy_self-1.10.0b8.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
23
+ tweepy_self-1.10.0b8.dist-info/RECORD,,
twitter/client.py CHANGED
@@ -30,7 +30,7 @@ from .errors import (
30
30
  from .utils import to_json
31
31
  from .base import BaseHTTPClient
32
32
  from .account import Account, AccountStatus
33
- from .models import User, Tweet, Media
33
+ from .models import User, Tweet, Media, Subtask
34
34
  from .utils import (
35
35
  parse_oauth_html,
36
36
  parse_unlock_html,
@@ -1377,7 +1377,8 @@ class Client(BaseHTTPClient):
1377
1377
  solution = await FunCaptcha(**funcaptcha).aio_captcha_handler()
1378
1378
  if solution.errorId:
1379
1379
  logger.warning(
1380
- f"{self.account} Failed to solve funcaptcha:"
1380
+ f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
1381
+ f"Failed to solve funcaptcha:"
1381
1382
  f"\n\tUnlock attempt: {attempt}/{self.max_unlock_attempts}"
1382
1383
  f"\n\tError ID: {solution.errorId}"
1383
1384
  f"\n\tError code: {solution.errorCode}"
@@ -1427,18 +1428,46 @@ class Client(BaseHTTPClient):
1427
1428
  response, response_json = await self.request("GET", url)
1428
1429
  self.account.backup_code = response_json["codes"][0]
1429
1430
 
1430
- async def _task(self, **kwargs):
1431
+ async def _send_raw_subtask(self, **request_kwargs) -> tuple[str, list[Subtask]]:
1431
1432
  """
1432
- :return: flow_token, subtasks
1433
+ :return: flow_token and subtasks
1433
1434
  """
1434
1435
  url = "https://api.twitter.com/1.1/onboarding/task.json"
1435
- response, response_json = await self.request("POST", url, **kwargs)
1436
- return response_json["flow_token"], response_json["subtasks"]
1436
+ response, data = await self.request("POST", url, **request_kwargs)
1437
+ subtasks = [
1438
+ Subtask.from_raw_data(subtask_data) for subtask_data in data["subtasks"]
1439
+ ]
1440
+ log_message = (
1441
+ f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
1442
+ f" Requested subtasks:"
1443
+ )
1444
+ for subtask in subtasks:
1445
+ log_message += f"\n\t{subtask.id}"
1446
+ if subtask.primary_text:
1447
+ log_message += f"\n\tPrimary text: {subtask.primary_text}"
1448
+ if subtask.secondary_text:
1449
+ log_message += f"\n\tSecondary text: {subtask.secondary_text}"
1450
+ if subtask.detail_text:
1451
+ log_message += f"\n\tDetail text: {subtask.detail_text}"
1452
+ logger.debug(log_message)
1453
+ return data["flow_token"], subtasks
1437
1454
 
1438
- async def _request_login_tasks(self):
1439
- """
1440
- :return: flow_token, subtask_ids
1441
- """
1455
+ async def _complete_subtask(
1456
+ self,
1457
+ flow_token: str,
1458
+ inputs: list[dict],
1459
+ **request_kwargs,
1460
+ ) -> tuple[str, list[Subtask]]:
1461
+ payload = request_kwargs["json"] = request_kwargs.get("json") or {}
1462
+ payload.update(
1463
+ {
1464
+ "flow_token": flow_token,
1465
+ "subtask_inputs": inputs,
1466
+ }
1467
+ )
1468
+ return await self._send_raw_subtask(**request_kwargs)
1469
+
1470
+ async def _request_login_tasks(self) -> tuple[str, list[Subtask]]:
1442
1471
  params = {
1443
1472
  "flow_name": "login",
1444
1473
  }
@@ -1493,30 +1522,14 @@ class Client(BaseHTTPClient):
1493
1522
  "web_modal": 1,
1494
1523
  },
1495
1524
  }
1496
- return await self._task(params=params, json=payload, auth=False)
1497
-
1498
- async def _send_task(self, flow_token: str, subtask_inputs: list[dict], **kwargs):
1499
- payload = kwargs["json"] = kwargs.get("json") or {}
1500
- payload.update(
1501
- {
1502
- "flow_token": flow_token,
1503
- "subtask_inputs": subtask_inputs,
1504
- }
1505
- )
1506
- return await self._task(**kwargs)
1507
-
1508
- async def _finish_task(self, flow_token):
1509
- payload = {
1510
- "flow_token": flow_token,
1511
- "subtask_inputs": [],
1512
- }
1513
- return await self._task(json=payload)
1525
+ return await self._send_raw_subtask(params=params, json=payload, auth=False)
1514
1526
 
1515
- async def _login_enter_user_identifier(self, flow_token):
1516
- subtask_inputs = [
1527
+ async def _login_enter_user_identifier(self, flow_token: str):
1528
+ inputs = [
1517
1529
  {
1518
1530
  "subtask_id": "LoginEnterUserIdentifierSSO",
1519
1531
  "settings_list": {
1532
+ "link": "next_link",
1520
1533
  "setting_responses": [
1521
1534
  {
1522
1535
  "key": "user_identifier",
@@ -1528,49 +1541,48 @@ class Client(BaseHTTPClient):
1528
1541
  },
1529
1542
  }
1530
1543
  ],
1531
- "link": "next_link",
1532
1544
  },
1533
1545
  }
1534
1546
  ]
1535
- return await self._send_task(flow_token, subtask_inputs, auth=False)
1547
+ return await self._complete_subtask(flow_token, inputs, auth=False)
1536
1548
 
1537
- async def _login_enter_password(self, flow_token):
1538
- subtask_inputs = [
1549
+ async def _login_enter_password(self, flow_token: str):
1550
+ inputs = [
1539
1551
  {
1540
1552
  "subtask_id": "LoginEnterPassword",
1541
1553
  "enter_password": {
1542
- "password": self.account.password,
1543
1554
  "link": "next_link",
1555
+ "password": self.account.password,
1544
1556
  },
1545
1557
  }
1546
1558
  ]
1547
- return await self._send_task(flow_token, subtask_inputs, auth=False)
1559
+ return await self._complete_subtask(flow_token, inputs, auth=False)
1548
1560
 
1549
1561
  async def _account_duplication_check(self, flow_token):
1550
- subtask_inputs = [
1562
+ inputs = [
1551
1563
  {
1552
1564
  "subtask_id": "AccountDuplicationCheck",
1553
1565
  "check_logged_in_account": {"link": "AccountDuplicationCheck_false"},
1554
1566
  }
1555
1567
  ]
1556
- return await self._send_task(flow_token, subtask_inputs, auth=False)
1568
+ return await self._complete_subtask(flow_token, inputs, auth=False)
1557
1569
 
1558
1570
  async def _login_two_factor_auth_challenge(self, flow_token, value: str):
1559
- subtask_inputs = [
1571
+ inputs = [
1560
1572
  {
1561
1573
  "subtask_id": "LoginTwoFactorAuthChallenge",
1562
1574
  "enter_text": {
1563
- "text": value,
1564
1575
  "link": "next_link",
1576
+ "text": value,
1565
1577
  },
1566
1578
  }
1567
1579
  ]
1568
- return await self._send_task(flow_token, subtask_inputs, auth=False)
1580
+ return await self._complete_subtask(flow_token, inputs, auth=False)
1569
1581
 
1570
1582
  async def _login_two_factor_auth_choose_method(
1571
1583
  self, flow_token: str, choices: Iterable[int] = (0,)
1572
1584
  ):
1573
- subtask_inputs = [
1585
+ inputs = [
1574
1586
  {
1575
1587
  "subtask_id": "LoginTwoFactorAuthChooseMethod",
1576
1588
  "choice_selection": {
@@ -1579,7 +1591,20 @@ class Client(BaseHTTPClient):
1579
1591
  },
1580
1592
  }
1581
1593
  ]
1582
- return await self._send_task(flow_token, subtask_inputs, auth=False)
1594
+ return await self._complete_subtask(flow_token, inputs, auth=False)
1595
+
1596
+ async def _login_acid(
1597
+ self,
1598
+ flow_token: str,
1599
+ value: str,
1600
+ ):
1601
+ inputs = [
1602
+ {
1603
+ "subtask_id": "LoginAcid",
1604
+ "enter_text": {"text": value, "link": "next_link"},
1605
+ }
1606
+ ]
1607
+ return await self._complete_subtask(flow_token, inputs, auth=False)
1583
1608
 
1584
1609
  async def _viewer(self):
1585
1610
  url, query_id = self._action_to_url("Viewer")
@@ -1596,9 +1621,9 @@ class Client(BaseHTTPClient):
1596
1621
  }
1597
1622
  variables = {"withCommunitiesMemberships": True}
1598
1623
  params = {
1599
- "features": to_json(features),
1600
- "fieldToggles": to_json(field_toggles),
1601
- "variables": to_json(variables),
1624
+ "features": features,
1625
+ "fieldToggles": field_toggles,
1626
+ "variables": variables,
1602
1627
  }
1603
1628
  return await self.request("GET", url, params=params)
1604
1629
 
@@ -1621,15 +1646,11 @@ class Client(BaseHTTPClient):
1621
1646
  guest_token = await self._request_guest_token()
1622
1647
  self._session.headers["X-Guest-Token"] = guest_token
1623
1648
 
1624
- # Можно не устанавливать, так как твиттер сам вернет этот токен
1625
- # self._session.cookies["gt"] = guest_token
1626
-
1627
1649
  flow_token, subtasks = await self._request_login_tasks()
1628
1650
  for _ in range(2):
1629
1651
  flow_token, subtasks = await self._login_enter_user_identifier(flow_token)
1630
1652
 
1631
- subtask_ids = [subtask["subtask_id"] for subtask in subtasks]
1632
-
1653
+ subtask_ids = {subtask.id for subtask in subtasks}
1633
1654
  if "LoginEnterAlternateIdentifierSubtask" in subtask_ids:
1634
1655
  if not self.account.username:
1635
1656
  raise TwitterException("Failed to login: no username to relogin")
@@ -1637,11 +1658,28 @@ class Client(BaseHTTPClient):
1637
1658
  flow_token, subtasks = await self._login_enter_password(flow_token)
1638
1659
  flow_token, subtasks = await self._account_duplication_check(flow_token)
1639
1660
 
1640
- subtask_ids = [subtask["subtask_id"] for subtask in subtasks]
1641
-
1642
- # TODO IMAP Обработчик
1661
+ subtask_ids = {subtask.id for subtask in subtasks}
1643
1662
  if "LoginAcid" in subtask_ids:
1644
- raise TwitterException(f"Failed to login: email verification!")
1663
+ if not self.account.email:
1664
+ raise TwitterException(
1665
+ f"Failed to login. Task id: LoginAcid. No email!"
1666
+ )
1667
+
1668
+ try:
1669
+ # fmt: off
1670
+ flow_token, subtasks = await self._login_acid(flow_token, self.account.email)
1671
+ # fmt: on
1672
+ except HTTPException as exc:
1673
+ if 399 in exc.api_codes:
1674
+ logger.warning(
1675
+ f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
1676
+ f" Bad email!"
1677
+ )
1678
+ raise TwitterException(
1679
+ f"Failed to login. Task id: LoginAcid. Bad email!"
1680
+ )
1681
+ else:
1682
+ raise
1645
1683
 
1646
1684
  if "LoginTwoFactorAuthChallenge" in subtask_ids:
1647
1685
  if not self.account.totp_secret:
@@ -1686,7 +1724,7 @@ class Client(BaseHTTPClient):
1686
1724
  else:
1687
1725
  raise
1688
1726
 
1689
- await self._finish_task(flow_token)
1727
+ await self._complete_subtask(flow_token, [])
1690
1728
  return update_backup_code
1691
1729
 
1692
1730
  async def relogin(self):
@@ -1729,13 +1767,13 @@ class Client(BaseHTTPClient):
1729
1767
 
1730
1768
  url = f"https://twitter.com/i/api/1.1/strato/column/User/{self.account.id}/account-security/twoFactorAuthSettings2"
1731
1769
  response, data = await self.request("GET", url)
1732
- return "Totp" in [
1733
- method_data["twoFactorType"] for method_data in data["methods"]
1734
- ]
1770
+ # fmt: off
1771
+ return "Totp" in [method_data["twoFactorType"] for method_data in data["methods"]]
1772
+ # fmt: on
1735
1773
 
1736
- async def _request_2fa_tasks(self):
1774
+ async def _request_2fa_tasks(self) -> tuple[str, list[Subtask]]:
1737
1775
  """
1738
- :return: flow_token, subtask_ids
1776
+ :return: flow_token, tasks
1739
1777
  """
1740
1778
  query = {
1741
1779
  "flow_name": "two-factor-auth-app-enrollment",
@@ -1791,34 +1829,37 @@ class Client(BaseHTTPClient):
1791
1829
  "web_modal": 1,
1792
1830
  },
1793
1831
  }
1794
- return await self._task(params=query, json=payload)
1832
+ return await self._send_raw_subtask(params=query, json=payload)
1795
1833
 
1796
- async def _two_factor_enrollment_verify_password_subtask(self, flow_token: str):
1797
- subtask_inputs = [
1834
+ async def _two_factor_enrollment_verify_password_subtask(
1835
+ self, flow_token: str
1836
+ ) -> tuple[str, list[Subtask]]:
1837
+ inputs = [
1798
1838
  {
1799
1839
  "subtask_id": "TwoFactorEnrollmentVerifyPasswordSubtask",
1800
1840
  "enter_password": {
1801
- "password": self.account.password,
1802
1841
  "link": "next_link",
1842
+ "password": self.account.password,
1803
1843
  },
1804
1844
  }
1805
1845
  ]
1806
- return await self._send_task(flow_token, subtask_inputs)
1846
+ return await self._complete_subtask(flow_token, inputs)
1807
1847
 
1808
1848
  async def _two_factor_enrollment_authentication_app_begin_subtask(
1809
1849
  self, flow_token: str
1810
- ):
1811
- subtask_inputs = [
1850
+ ) -> tuple[str, list[Subtask]]:
1851
+ inputs = [
1812
1852
  {
1813
1853
  "subtask_id": "TwoFactorEnrollmentAuthenticationAppBeginSubtask",
1814
1854
  "action_list": {"link": "next_link"},
1815
1855
  }
1816
1856
  ]
1817
- return await self._send_task(flow_token, subtask_inputs)
1857
+ return await self._complete_subtask(flow_token, inputs)
1818
1858
 
1819
1859
  async def _two_factor_enrollment_authentication_app_plain_code_subtask(
1820
- self, flow_token: str
1821
- ):
1860
+ self,
1861
+ flow_token: str,
1862
+ ) -> tuple[str, list[Subtask]]:
1822
1863
  subtask_inputs = [
1823
1864
  {
1824
1865
  "subtask_id": "TwoFactorEnrollmentAuthenticationAppPlainCodeSubtask",
@@ -1827,12 +1868,12 @@ class Client(BaseHTTPClient):
1827
1868
  {
1828
1869
  "subtask_id": "TwoFactorEnrollmentAuthenticationAppEnterCodeSubtask",
1829
1870
  "enter_text": {
1830
- "text": self.account.get_totp_code(),
1831
1871
  "link": "next_link",
1872
+ "text": self.account.get_totp_code(),
1832
1873
  },
1833
1874
  },
1834
1875
  ]
1835
- return await self._send_task(flow_token, subtask_inputs)
1876
+ return await self._complete_subtask(flow_token, subtask_inputs)
1836
1877
 
1837
1878
  async def _finish_2fa_task(self, flow_token: str):
1838
1879
  subtask_inputs = [
@@ -1841,52 +1882,38 @@ class Client(BaseHTTPClient):
1841
1882
  "cta": {"link": "finish_link"},
1842
1883
  }
1843
1884
  ]
1844
- return await self._send_task(flow_token, subtask_inputs)
1885
+ await self._complete_subtask(flow_token, subtask_inputs)
1845
1886
 
1846
1887
  async def _enable_totp(self):
1888
+ # fmt: off
1847
1889
  flow_token, subtasks = await self._request_2fa_tasks()
1848
- flow_token, subtasks = (
1849
- await self._two_factor_enrollment_verify_password_subtask(flow_token)
1850
- )
1851
- flow_token, subtasks = (
1852
- await self._two_factor_enrollment_authentication_app_begin_subtask(
1853
- flow_token
1854
- )
1890
+ flow_token, subtasks = await self._two_factor_enrollment_verify_password_subtask(
1891
+ flow_token
1855
1892
  )
1893
+ flow_token, subtasks = (await self._two_factor_enrollment_authentication_app_begin_subtask(flow_token))
1856
1894
 
1857
1895
  for subtask in subtasks:
1858
- if (
1859
- subtask["subtask_id"]
1860
- == "TwoFactorEnrollmentAuthenticationAppPlainCodeSubtask"
1861
- ):
1862
- self.account.totp_secret = subtask["show_code"]["code"]
1896
+ if subtask.id == "TwoFactorEnrollmentAuthenticationAppPlainCodeSubtask":
1897
+ self.account.totp_secret = subtask.raw_data["show_code"]["code"]
1863
1898
  break
1864
1899
 
1865
- flow_token, subtasks = (
1866
- await self._two_factor_enrollment_authentication_app_plain_code_subtask(
1867
- flow_token
1868
- )
1869
- )
1900
+ flow_token, subtasks = await self._two_factor_enrollment_authentication_app_plain_code_subtask(flow_token)
1870
1901
 
1871
1902
  for subtask in subtasks:
1872
- if (
1873
- subtask["subtask_id"]
1874
- == "TwoFactorEnrollmentAuthenticationAppCompleteSubtask"
1875
- ):
1876
- result = re.search(
1877
- r"\n[a-z0-9]{12}\n", subtask["cta"]["secondary_text"]["text"]
1878
- )
1903
+ if subtask.id == "TwoFactorEnrollmentAuthenticationAppCompleteSubtask":
1904
+ result = re.search(r"\n[a-z0-9]{12}\n", subtask.raw_data["cta"]["secondary_text"]["text"])
1879
1905
  backup_code = result[0].strip() if result else None
1880
1906
  self.account.backup_code = backup_code
1881
1907
  break
1882
1908
 
1909
+ # fmt: on
1883
1910
  await self._finish_2fa_task(flow_token)
1884
1911
 
1885
1912
  async def enable_totp(self):
1886
- if not self.account.password:
1887
- raise ValueError("Password is required for this action")
1888
-
1889
1913
  if await self.totp_is_enabled():
1890
1914
  return
1891
1915
 
1916
+ if not self.account.password:
1917
+ raise ValueError("Password required to enable TOTP")
1918
+
1892
1919
  await self._enable_totp()
twitter/models.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from typing import Optional, Any
2
2
  from datetime import datetime, timedelta
3
3
 
4
4
  from pydantic import BaseModel, Field, field_validator
@@ -153,3 +153,24 @@ class Tweet(BaseModel):
153
153
  "raw_data": data,
154
154
  }
155
155
  return cls(**values)
156
+
157
+
158
+ class Subtask(BaseModel):
159
+ id: str
160
+ primary_text: Optional[str] = None
161
+ secondary_text: Optional[str] = None
162
+ detail_text: Optional[str] = None
163
+ raw_data: dict
164
+
165
+ @classmethod
166
+ def from_raw_data(cls, data: dict) -> "Subtask":
167
+ task = {"id": data["subtask_id"]}
168
+ if enter_text := data.get("enter_text"):
169
+ if header := enter_text.get("header"):
170
+ if primary_text := header.get("primary_text"):
171
+ task["primary_text"] = primary_text["text"]
172
+ if secondary_text := header.get("secondary_text"):
173
+ task["secondary_text"] = secondary_text["text"]
174
+ if detail_text := header.get("detail_text"):
175
+ task["detail_text"] = detail_text["text"]
176
+ return cls(**task, raw_data=data)