tweepy-self 1.10.0b6__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.
@@ -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=
|
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=
|
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.
|
22
|
-
tweepy_self-1.10.
|
23
|
-
tweepy_self-1.10.
|
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,
|
@@ -190,7 +190,6 @@ class Client(BaseHTTPClient):
|
|
190
190
|
raise Locked(exc, self.account)
|
191
191
|
raise exc
|
192
192
|
|
193
|
-
self.account.status = AccountStatus.GOOD
|
194
193
|
return response, data
|
195
194
|
|
196
195
|
if response.status_code == 400:
|
@@ -1184,6 +1183,7 @@ class Client(BaseHTTPClient):
|
|
1184
1183
|
url = "https://twitter.com/i/api/1.1/account/update_profile.json"
|
1185
1184
|
try:
|
1186
1185
|
await self.request("POST", url, auto_unlock=False, auto_relogin=False)
|
1186
|
+
self.account.status = AccountStatus.GOOD
|
1187
1187
|
except BadAccount:
|
1188
1188
|
pass
|
1189
1189
|
|
@@ -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}
|
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}"
|
@@ -1422,18 +1423,51 @@ class Client(BaseHTTPClient):
|
|
1422
1423
|
|
1423
1424
|
await self.establish_status()
|
1424
1425
|
|
1425
|
-
async def
|
1426
|
+
async def update_backup_code(self):
|
1427
|
+
url = "https://api.twitter.com/1.1/account/backup_code.json"
|
1428
|
+
response, response_json = await self.request("GET", url)
|
1429
|
+
self.account.backup_code = response_json["codes"][0]
|
1430
|
+
|
1431
|
+
async def _send_raw_subtask(self, **request_kwargs) -> tuple[str, list[Subtask]]:
|
1426
1432
|
"""
|
1427
|
-
:return: flow_token
|
1433
|
+
:return: flow_token and subtasks
|
1428
1434
|
"""
|
1429
1435
|
url = "https://api.twitter.com/1.1/onboarding/task.json"
|
1430
|
-
response,
|
1431
|
-
|
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
|
1432
1454
|
|
1433
|
-
async def
|
1434
|
-
|
1435
|
-
:
|
1436
|
-
|
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]]:
|
1437
1471
|
params = {
|
1438
1472
|
"flow_name": "login",
|
1439
1473
|
}
|
@@ -1488,30 +1522,14 @@ class Client(BaseHTTPClient):
|
|
1488
1522
|
"web_modal": 1,
|
1489
1523
|
},
|
1490
1524
|
}
|
1491
|
-
return await self.
|
1525
|
+
return await self._send_raw_subtask(params=params, json=payload, auth=False)
|
1492
1526
|
|
1493
|
-
async def
|
1494
|
-
|
1495
|
-
payload.update(
|
1496
|
-
{
|
1497
|
-
"flow_token": flow_token,
|
1498
|
-
"subtask_inputs": subtask_inputs,
|
1499
|
-
}
|
1500
|
-
)
|
1501
|
-
return await self._task(**kwargs)
|
1502
|
-
|
1503
|
-
async def _finish_task(self, flow_token):
|
1504
|
-
payload = {
|
1505
|
-
"flow_token": flow_token,
|
1506
|
-
"subtask_inputs": [],
|
1507
|
-
}
|
1508
|
-
return await self._task(json=payload)
|
1509
|
-
|
1510
|
-
async def _login_enter_user_identifier(self, flow_token):
|
1511
|
-
subtask_inputs = [
|
1527
|
+
async def _login_enter_user_identifier(self, flow_token: str):
|
1528
|
+
inputs = [
|
1512
1529
|
{
|
1513
1530
|
"subtask_id": "LoginEnterUserIdentifierSSO",
|
1514
1531
|
"settings_list": {
|
1532
|
+
"link": "next_link",
|
1515
1533
|
"setting_responses": [
|
1516
1534
|
{
|
1517
1535
|
"key": "user_identifier",
|
@@ -1523,49 +1541,70 @@ class Client(BaseHTTPClient):
|
|
1523
1541
|
},
|
1524
1542
|
}
|
1525
1543
|
],
|
1526
|
-
"link": "next_link",
|
1527
1544
|
},
|
1528
1545
|
}
|
1529
1546
|
]
|
1530
|
-
return await self.
|
1547
|
+
return await self._complete_subtask(flow_token, inputs, auth=False)
|
1531
1548
|
|
1532
|
-
async def _login_enter_password(self, flow_token):
|
1533
|
-
|
1549
|
+
async def _login_enter_password(self, flow_token: str):
|
1550
|
+
inputs = [
|
1534
1551
|
{
|
1535
1552
|
"subtask_id": "LoginEnterPassword",
|
1536
1553
|
"enter_password": {
|
1537
|
-
"password": self.account.password,
|
1538
1554
|
"link": "next_link",
|
1555
|
+
"password": self.account.password,
|
1539
1556
|
},
|
1540
1557
|
}
|
1541
1558
|
]
|
1542
|
-
return await self.
|
1559
|
+
return await self._complete_subtask(flow_token, inputs, auth=False)
|
1543
1560
|
|
1544
1561
|
async def _account_duplication_check(self, flow_token):
|
1545
|
-
|
1562
|
+
inputs = [
|
1546
1563
|
{
|
1547
1564
|
"subtask_id": "AccountDuplicationCheck",
|
1548
1565
|
"check_logged_in_account": {"link": "AccountDuplicationCheck_false"},
|
1549
1566
|
}
|
1550
1567
|
]
|
1551
|
-
return await self.
|
1552
|
-
|
1553
|
-
async def _login_two_factor_auth_challenge(self, flow_token):
|
1554
|
-
if not self.account.totp_secret:
|
1555
|
-
raise TwitterException(
|
1556
|
-
f"Failed to login. Task id: LoginTwoFactorAuthChallenge"
|
1557
|
-
)
|
1568
|
+
return await self._complete_subtask(flow_token, inputs, auth=False)
|
1558
1569
|
|
1559
|
-
|
1570
|
+
async def _login_two_factor_auth_challenge(self, flow_token, value: str):
|
1571
|
+
inputs = [
|
1560
1572
|
{
|
1561
1573
|
"subtask_id": "LoginTwoFactorAuthChallenge",
|
1562
1574
|
"enter_text": {
|
1563
|
-
"text": self.account.get_totp_code(),
|
1564
1575
|
"link": "next_link",
|
1576
|
+
"text": value,
|
1565
1577
|
},
|
1566
1578
|
}
|
1567
1579
|
]
|
1568
|
-
return await self.
|
1580
|
+
return await self._complete_subtask(flow_token, inputs, auth=False)
|
1581
|
+
|
1582
|
+
async def _login_two_factor_auth_choose_method(
|
1583
|
+
self, flow_token: str, choices: Iterable[int] = (0,)
|
1584
|
+
):
|
1585
|
+
inputs = [
|
1586
|
+
{
|
1587
|
+
"subtask_id": "LoginTwoFactorAuthChooseMethod",
|
1588
|
+
"choice_selection": {
|
1589
|
+
"link": "next_link",
|
1590
|
+
"selected_choices": [str(choice) for choice in choices],
|
1591
|
+
},
|
1592
|
+
}
|
1593
|
+
]
|
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)
|
1569
1608
|
|
1570
1609
|
async def _viewer(self):
|
1571
1610
|
url, query_id = self._action_to_url("Viewer")
|
@@ -1582,9 +1621,9 @@ class Client(BaseHTTPClient):
|
|
1582
1621
|
}
|
1583
1622
|
variables = {"withCommunitiesMemberships": True}
|
1584
1623
|
params = {
|
1585
|
-
"features":
|
1586
|
-
"fieldToggles":
|
1587
|
-
"variables":
|
1624
|
+
"features": features,
|
1625
|
+
"fieldToggles": field_toggles,
|
1626
|
+
"variables": variables,
|
1588
1627
|
}
|
1589
1628
|
return await self.request("GET", url, params=params)
|
1590
1629
|
|
@@ -1601,38 +1640,92 @@ class Client(BaseHTTPClient):
|
|
1601
1640
|
guest_token = re.search(r"gt\s?=\s?\d+", response.text)[0].split("=")[1]
|
1602
1641
|
return guest_token
|
1603
1642
|
|
1604
|
-
async def _login(self):
|
1643
|
+
async def _login(self) -> bool:
|
1644
|
+
update_backup_code = False
|
1645
|
+
|
1605
1646
|
guest_token = await self._request_guest_token()
|
1606
1647
|
self._session.headers["X-Guest-Token"] = guest_token
|
1607
1648
|
|
1608
|
-
# Можно не устанавливать, так как твиттер сам вернет этот токен
|
1609
|
-
# self._session.cookies["gt"] = guest_token
|
1610
|
-
|
1611
1649
|
flow_token, subtasks = await self._request_login_tasks()
|
1612
1650
|
for _ in range(2):
|
1613
1651
|
flow_token, subtasks = await self._login_enter_user_identifier(flow_token)
|
1614
1652
|
|
1615
|
-
subtask_ids =
|
1616
|
-
|
1653
|
+
subtask_ids = {subtask.id for subtask in subtasks}
|
1617
1654
|
if "LoginEnterAlternateIdentifierSubtask" in subtask_ids:
|
1618
1655
|
if not self.account.username:
|
1619
|
-
raise
|
1656
|
+
raise TwitterException("Failed to login: no username to relogin")
|
1620
1657
|
|
1621
1658
|
flow_token, subtasks = await self._login_enter_password(flow_token)
|
1622
1659
|
flow_token, subtasks = await self._account_duplication_check(flow_token)
|
1623
1660
|
|
1624
|
-
subtask_ids =
|
1625
|
-
|
1626
|
-
# TODO IMAP Обработчик
|
1661
|
+
subtask_ids = {subtask.id for subtask in subtasks}
|
1627
1662
|
if "LoginAcid" in subtask_ids:
|
1628
|
-
|
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
|
1629
1683
|
|
1630
1684
|
if "LoginTwoFactorAuthChallenge" in subtask_ids:
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1685
|
+
if not self.account.totp_secret:
|
1686
|
+
raise TwitterException(
|
1687
|
+
f"Failed to login. Task id: LoginTwoFactorAuthChallenge. No totp_secret!"
|
1688
|
+
)
|
1634
1689
|
|
1635
|
-
|
1690
|
+
try:
|
1691
|
+
# fmt: off
|
1692
|
+
flow_token, subtasks = await self._login_two_factor_auth_challenge(flow_token, self.account.get_totp_code())
|
1693
|
+
# fmt: on
|
1694
|
+
except HTTPException as exc:
|
1695
|
+
if 399 in exc.api_codes:
|
1696
|
+
logger.warning(
|
1697
|
+
f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
|
1698
|
+
f" Bad TOTP secret!"
|
1699
|
+
)
|
1700
|
+
if not self.account.backup_code:
|
1701
|
+
raise TwitterException(
|
1702
|
+
f"Failed to login. Task id: LoginTwoFactorAuthChallenge. No backup code!"
|
1703
|
+
)
|
1704
|
+
|
1705
|
+
# Enter backup code
|
1706
|
+
# fmt: off
|
1707
|
+
flow_token, subtasks = await self._login_two_factor_auth_choose_method(flow_token)
|
1708
|
+
try:
|
1709
|
+
flow_token, subtasks = await self._login_two_factor_auth_challenge(flow_token, self.account.backup_code)
|
1710
|
+
except HTTPException as exc:
|
1711
|
+
if 399 in exc.api_codes:
|
1712
|
+
logger.warning(
|
1713
|
+
f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
|
1714
|
+
f" Bad backup code!"
|
1715
|
+
)
|
1716
|
+
raise TwitterException(
|
1717
|
+
f"Failed to login. Task id: LoginTwoFactorAuthChallenge. Bad backup_code!"
|
1718
|
+
)
|
1719
|
+
else:
|
1720
|
+
raise
|
1721
|
+
|
1722
|
+
update_backup_code = True
|
1723
|
+
# fmt: on
|
1724
|
+
else:
|
1725
|
+
raise
|
1726
|
+
|
1727
|
+
await self._complete_subtask(flow_token, [])
|
1728
|
+
return update_backup_code
|
1636
1729
|
|
1637
1730
|
async def relogin(self):
|
1638
1731
|
"""
|
@@ -1647,8 +1740,17 @@ class Client(BaseHTTPClient):
|
|
1647
1740
|
if not self.account.password:
|
1648
1741
|
raise ValueError("No password")
|
1649
1742
|
|
1650
|
-
await self._login()
|
1743
|
+
update_backup_code = await self._login()
|
1651
1744
|
await self._viewer()
|
1745
|
+
|
1746
|
+
if update_backup_code:
|
1747
|
+
await self.update_backup_code()
|
1748
|
+
logger.warning(
|
1749
|
+
f"(auth_token={self.account.hidden_auth_token}, id={self.account.id}, username={self.account.username})"
|
1750
|
+
f" Requested new backup code!"
|
1751
|
+
)
|
1752
|
+
# TODO Также обновлять totp_secret
|
1753
|
+
|
1652
1754
|
await self.establish_status()
|
1653
1755
|
|
1654
1756
|
async def login(self):
|
@@ -1665,13 +1767,13 @@ class Client(BaseHTTPClient):
|
|
1665
1767
|
|
1666
1768
|
url = f"https://twitter.com/i/api/1.1/strato/column/User/{self.account.id}/account-security/twoFactorAuthSettings2"
|
1667
1769
|
response, data = await self.request("GET", url)
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1770
|
+
# fmt: off
|
1771
|
+
return "Totp" in [method_data["twoFactorType"] for method_data in data["methods"]]
|
1772
|
+
# fmt: on
|
1671
1773
|
|
1672
|
-
async def _request_2fa_tasks(self):
|
1774
|
+
async def _request_2fa_tasks(self) -> tuple[str, list[Subtask]]:
|
1673
1775
|
"""
|
1674
|
-
:return: flow_token,
|
1776
|
+
:return: flow_token, tasks
|
1675
1777
|
"""
|
1676
1778
|
query = {
|
1677
1779
|
"flow_name": "two-factor-auth-app-enrollment",
|
@@ -1727,34 +1829,37 @@ class Client(BaseHTTPClient):
|
|
1727
1829
|
"web_modal": 1,
|
1728
1830
|
},
|
1729
1831
|
}
|
1730
|
-
return await self.
|
1832
|
+
return await self._send_raw_subtask(params=query, json=payload)
|
1731
1833
|
|
1732
|
-
async def _two_factor_enrollment_verify_password_subtask(
|
1733
|
-
|
1834
|
+
async def _two_factor_enrollment_verify_password_subtask(
|
1835
|
+
self, flow_token: str
|
1836
|
+
) -> tuple[str, list[Subtask]]:
|
1837
|
+
inputs = [
|
1734
1838
|
{
|
1735
1839
|
"subtask_id": "TwoFactorEnrollmentVerifyPasswordSubtask",
|
1736
1840
|
"enter_password": {
|
1737
|
-
"password": self.account.password,
|
1738
1841
|
"link": "next_link",
|
1842
|
+
"password": self.account.password,
|
1739
1843
|
},
|
1740
1844
|
}
|
1741
1845
|
]
|
1742
|
-
return await self.
|
1846
|
+
return await self._complete_subtask(flow_token, inputs)
|
1743
1847
|
|
1744
1848
|
async def _two_factor_enrollment_authentication_app_begin_subtask(
|
1745
1849
|
self, flow_token: str
|
1746
|
-
):
|
1747
|
-
|
1850
|
+
) -> tuple[str, list[Subtask]]:
|
1851
|
+
inputs = [
|
1748
1852
|
{
|
1749
1853
|
"subtask_id": "TwoFactorEnrollmentAuthenticationAppBeginSubtask",
|
1750
1854
|
"action_list": {"link": "next_link"},
|
1751
1855
|
}
|
1752
1856
|
]
|
1753
|
-
return await self.
|
1857
|
+
return await self._complete_subtask(flow_token, inputs)
|
1754
1858
|
|
1755
1859
|
async def _two_factor_enrollment_authentication_app_plain_code_subtask(
|
1756
|
-
self,
|
1757
|
-
|
1860
|
+
self,
|
1861
|
+
flow_token: str,
|
1862
|
+
) -> tuple[str, list[Subtask]]:
|
1758
1863
|
subtask_inputs = [
|
1759
1864
|
{
|
1760
1865
|
"subtask_id": "TwoFactorEnrollmentAuthenticationAppPlainCodeSubtask",
|
@@ -1763,12 +1868,12 @@ class Client(BaseHTTPClient):
|
|
1763
1868
|
{
|
1764
1869
|
"subtask_id": "TwoFactorEnrollmentAuthenticationAppEnterCodeSubtask",
|
1765
1870
|
"enter_text": {
|
1766
|
-
"text": self.account.get_totp_code(),
|
1767
1871
|
"link": "next_link",
|
1872
|
+
"text": self.account.get_totp_code(),
|
1768
1873
|
},
|
1769
1874
|
},
|
1770
1875
|
]
|
1771
|
-
return await self.
|
1876
|
+
return await self._complete_subtask(flow_token, subtask_inputs)
|
1772
1877
|
|
1773
1878
|
async def _finish_2fa_task(self, flow_token: str):
|
1774
1879
|
subtask_inputs = [
|
@@ -1777,52 +1882,38 @@ class Client(BaseHTTPClient):
|
|
1777
1882
|
"cta": {"link": "finish_link"},
|
1778
1883
|
}
|
1779
1884
|
]
|
1780
|
-
|
1885
|
+
await self._complete_subtask(flow_token, subtask_inputs)
|
1781
1886
|
|
1782
1887
|
async def _enable_totp(self):
|
1888
|
+
# fmt: off
|
1783
1889
|
flow_token, subtasks = await self._request_2fa_tasks()
|
1784
|
-
flow_token, subtasks = (
|
1785
|
-
|
1786
|
-
)
|
1787
|
-
flow_token, subtasks = (
|
1788
|
-
await self._two_factor_enrollment_authentication_app_begin_subtask(
|
1789
|
-
flow_token
|
1790
|
-
)
|
1890
|
+
flow_token, subtasks = await self._two_factor_enrollment_verify_password_subtask(
|
1891
|
+
flow_token
|
1791
1892
|
)
|
1893
|
+
flow_token, subtasks = (await self._two_factor_enrollment_authentication_app_begin_subtask(flow_token))
|
1792
1894
|
|
1793
1895
|
for subtask in subtasks:
|
1794
|
-
if
|
1795
|
-
subtask["
|
1796
|
-
== "TwoFactorEnrollmentAuthenticationAppPlainCodeSubtask"
|
1797
|
-
):
|
1798
|
-
self.account.totp_secret = subtask["show_code"]["code"]
|
1896
|
+
if subtask.id == "TwoFactorEnrollmentAuthenticationAppPlainCodeSubtask":
|
1897
|
+
self.account.totp_secret = subtask.raw_data["show_code"]["code"]
|
1799
1898
|
break
|
1800
1899
|
|
1801
|
-
flow_token, subtasks = (
|
1802
|
-
await self._two_factor_enrollment_authentication_app_plain_code_subtask(
|
1803
|
-
flow_token
|
1804
|
-
)
|
1805
|
-
)
|
1900
|
+
flow_token, subtasks = await self._two_factor_enrollment_authentication_app_plain_code_subtask(flow_token)
|
1806
1901
|
|
1807
1902
|
for subtask in subtasks:
|
1808
|
-
if
|
1809
|
-
subtask["
|
1810
|
-
== "TwoFactorEnrollmentAuthenticationAppCompleteSubtask"
|
1811
|
-
):
|
1812
|
-
result = re.search(
|
1813
|
-
r"\n[a-z0-9]{12}\n", subtask["cta"]["secondary_text"]["text"]
|
1814
|
-
)
|
1903
|
+
if subtask.id == "TwoFactorEnrollmentAuthenticationAppCompleteSubtask":
|
1904
|
+
result = re.search(r"\n[a-z0-9]{12}\n", subtask.raw_data["cta"]["secondary_text"]["text"])
|
1815
1905
|
backup_code = result[0].strip() if result else None
|
1816
1906
|
self.account.backup_code = backup_code
|
1817
1907
|
break
|
1818
1908
|
|
1909
|
+
# fmt: on
|
1819
1910
|
await self._finish_2fa_task(flow_token)
|
1820
1911
|
|
1821
1912
|
async def enable_totp(self):
|
1822
|
-
if not self.account.password:
|
1823
|
-
raise ValueError("Password is required for this action")
|
1824
|
-
|
1825
1913
|
if await self.totp_is_enabled():
|
1826
1914
|
return
|
1827
1915
|
|
1916
|
+
if not self.account.password:
|
1917
|
+
raise ValueError("Password required to enable TOTP")
|
1918
|
+
|
1828
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)
|
File without changes
|