tweepy-self 1.10.0b7__tar.gz → 1.10.0b8__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/PKG-INFO +1 -1
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/pyproject.toml +1 -1
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/client.py +128 -101
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/models.py +22 -1
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/README.md +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/__init__.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/_capsolver/__init__.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/_capsolver/core/__init__.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/_capsolver/core/base.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/_capsolver/core/config.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/_capsolver/core/enum.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/_capsolver/core/serializer.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/_capsolver/fun_captcha.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/account.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/base/__init__.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/base/client.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/base/session.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/enums.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/errors.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/utils/__init__.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/utils/file.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/utils/html.py +0 -0
- {tweepy_self-1.10.0b7 → tweepy_self-1.10.0b8}/twitter/utils/other.py +0 -0
@@ -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()
|
@@ -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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|