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.
@@ -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,
|
@@ -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}"
|
@@ -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
|
1431
|
+
async def _send_raw_subtask(self, **request_kwargs) -> tuple[str, list[Subtask]]:
|
1431
1432
|
"""
|
1432
|
-
:return: flow_token
|
1433
|
+
:return: flow_token and subtasks
|
1433
1434
|
"""
|
1434
1435
|
url = "https://api.twitter.com/1.1/onboarding/task.json"
|
1435
|
-
response,
|
1436
|
-
|
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
|
1439
|
-
|
1440
|
-
:
|
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.
|
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
|
-
|
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.
|
1547
|
+
return await self._complete_subtask(flow_token, inputs, auth=False)
|
1536
1548
|
|
1537
|
-
async def _login_enter_password(self, flow_token):
|
1538
|
-
|
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.
|
1559
|
+
return await self._complete_subtask(flow_token, inputs, auth=False)
|
1548
1560
|
|
1549
1561
|
async def _account_duplication_check(self, flow_token):
|
1550
|
-
|
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.
|
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
|
-
|
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.
|
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
|
-
|
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.
|
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":
|
1600
|
-
"fieldToggles":
|
1601
|
-
"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 =
|
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 =
|
1641
|
-
|
1642
|
-
# TODO IMAP Обработчик
|
1661
|
+
subtask_ids = {subtask.id for subtask in subtasks}
|
1643
1662
|
if "LoginAcid" in subtask_ids:
|
1644
|
-
|
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.
|
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
|
-
|
1733
|
-
|
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,
|
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.
|
1832
|
+
return await self._send_raw_subtask(params=query, json=payload)
|
1795
1833
|
|
1796
|
-
async def _two_factor_enrollment_verify_password_subtask(
|
1797
|
-
|
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.
|
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
|
-
|
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.
|
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,
|
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.
|
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
|
-
|
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
|
-
|
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["
|
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["
|
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)
|
File without changes
|