зеркало из
https://github.com/viginum-datalab/twscrape.git
synced 2025-10-29 21:16:25 +02:00
Коммит
ec240e7468
@ -23,6 +23,7 @@ dependencies = [
|
|||||||
"fake-useragent>=1.4.0",
|
"fake-useragent>=1.4.0",
|
||||||
"httpx>=0.26.0",
|
"httpx>=0.26.0",
|
||||||
"loguru>=0.7.0",
|
"loguru>=0.7.0",
|
||||||
|
"pyotp>=2.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class Account(JSONTrait):
|
|||||||
stats: dict[str, int] = field(default_factory=dict) # queue: requests
|
stats: dict[str, int] = field(default_factory=dict) # queue: requests
|
||||||
headers: dict[str, str] = field(default_factory=dict)
|
headers: dict[str, str] = field(default_factory=dict)
|
||||||
cookies: dict[str, str] = field(default_factory=dict)
|
cookies: dict[str, str] = field(default_factory=dict)
|
||||||
|
mfa_code: str | None = None
|
||||||
proxy: str | None = None
|
proxy: str | None = None
|
||||||
error_msg: str | None = None
|
error_msg: str | None = None
|
||||||
last_used: datetime | None = None
|
last_used: datetime | None = None
|
||||||
|
|||||||
@ -80,6 +80,7 @@ class AccountsPool:
|
|||||||
user_agent: str | None = None,
|
user_agent: str | None = None,
|
||||||
proxy: str | None = None,
|
proxy: str | None = None,
|
||||||
cookies: str | None = None,
|
cookies: str | None = None,
|
||||||
|
mfa_code: str | None = None
|
||||||
):
|
):
|
||||||
qs = "SELECT * FROM accounts WHERE username = :username"
|
qs = "SELECT * FROM accounts WHERE username = :username"
|
||||||
rs = await fetchone(self._db_file, qs, {"username": username})
|
rs = await fetchone(self._db_file, qs, {"username": username})
|
||||||
@ -99,6 +100,7 @@ class AccountsPool:
|
|||||||
headers={},
|
headers={},
|
||||||
cookies=parse_cookies(cookies) if cookies else {},
|
cookies=parse_cookies(cookies) if cookies else {},
|
||||||
proxy=proxy,
|
proxy=proxy,
|
||||||
|
mfa_code=mfa_code,
|
||||||
)
|
)
|
||||||
|
|
||||||
if "ct0" in account.cookies:
|
if "ct0" in account.cookies:
|
||||||
|
|||||||
@ -81,10 +81,14 @@ async def migrate(db: aiosqlite.Connection):
|
|||||||
async def v3():
|
async def v3():
|
||||||
await db.execute("ALTER TABLE accounts ADD COLUMN _tx TEXT DEFAULT NULL")
|
await db.execute("ALTER TABLE accounts ADD COLUMN _tx TEXT DEFAULT NULL")
|
||||||
|
|
||||||
|
async def v4():
|
||||||
|
await db.execute("ALTER TABLE accounts ADD COLUMN mfa_code TEXT DEFAULT NULL")
|
||||||
|
|
||||||
migrations = {
|
migrations = {
|
||||||
1: v1,
|
1: v1,
|
||||||
2: v2,
|
2: v2,
|
||||||
3: v3,
|
3: v3,
|
||||||
|
4: v4,
|
||||||
}
|
}
|
||||||
|
|
||||||
# logger.debug(f"Current migration v{uv} (latest v{len(migrations)})")
|
# logger.debug(f"Current migration v{uv} (latest v{len(migrations)})")
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import imaplib
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
import pyotp
|
||||||
|
|
||||||
from httpx import AsyncClient, Response
|
from httpx import AsyncClient, Response
|
||||||
|
|
||||||
@ -119,6 +120,23 @@ async def login_enter_password(ctx: TaskCtx) -> Response:
|
|||||||
return rep
|
return rep
|
||||||
|
|
||||||
|
|
||||||
|
async def login_two_factor_auth_challenge(ctx: TaskCtx) -> Response:
|
||||||
|
totp = pyotp.TOTP(ctx.acc.mfa_code)
|
||||||
|
payload = {
|
||||||
|
"flow_token": ctx.prev["flow_token"],
|
||||||
|
"subtask_inputs": [
|
||||||
|
{
|
||||||
|
"subtask_id": "LoginTwoFactorAuthChallenge",
|
||||||
|
"enter_text": {"text": totp.now(), "link": "next_link"},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
rep = await ctx.client.post(LOGIN_URL, json=payload)
|
||||||
|
raise_for_status(rep, "login_two_factor_auth_challenge")
|
||||||
|
return rep
|
||||||
|
|
||||||
|
|
||||||
async def login_duplication_check(ctx: TaskCtx) -> Response:
|
async def login_duplication_check(ctx: TaskCtx) -> Response:
|
||||||
payload = {
|
payload = {
|
||||||
"flow_token": ctx.prev["flow_token"],
|
"flow_token": ctx.prev["flow_token"],
|
||||||
@ -212,6 +230,8 @@ async def next_login_task(ctx: TaskCtx, rep: Response):
|
|||||||
return await login_duplication_check(ctx)
|
return await login_duplication_check(ctx)
|
||||||
if task_id == "LoginEnterPassword":
|
if task_id == "LoginEnterPassword":
|
||||||
return await login_enter_password(ctx)
|
return await login_enter_password(ctx)
|
||||||
|
if task_id == "LoginTwoFactorAuthChallenge":
|
||||||
|
return await login_two_factor_auth_challenge(ctx)
|
||||||
if task_id == "LoginEnterUserIdentifierSSO":
|
if task_id == "LoginEnterUserIdentifierSSO":
|
||||||
return await login_enter_username(ctx)
|
return await login_enter_username(ctx)
|
||||||
if task_id == "LoginJsInstrumentationSubtask":
|
if task_id == "LoginJsInstrumentationSubtask":
|
||||||
|
|||||||
Загрузка…
x
Ссылка в новой задаче
Block a user