This commit is contained in:
Manchik
2026-04-17 22:22:40 +03:00
commit c82866b58a
135 changed files with 18493 additions and 0 deletions
+216
View File
@@ -0,0 +1,216 @@
import json
import re
from loguru import logger
from app.services.steam_auth import create_steam_session, close_steam
from app.services.steam_wizard import AJAX_HEADERS, run_common_wizard
_PHONE_RE = re.compile(r"^\+\d{7,15}$")
def _get_fresh_sessionid(steam) -> str:
"""Get sessionid from the live aiohttp cookie jar (not from stale storage)."""
return steam._requests.cookies("help.steampowered.com").get("sessionid", "")
def _validate_phone(phone: str) -> str:
"""Strip spaces/dashes and verify E.164-like format."""
cleaned = re.sub(r"[\s\-()]", "", phone)
if cleaned and not cleaned.startswith("+"):
cleaned = "+" + cleaned
if not _PHONE_RE.match(cleaned):
raise ValueError(
f"Неверный формат номера: '{phone}'. "
"Ожидается международный формат, например +79123456789"
)
return cleaned
async def change_phone(account: dict, params: dict, task_id: str = "") -> None:
"""Change phone number on a Steam account via recovery wizard + interactive prompts."""
from app.core.task_manager import task_manager
login = account["login"]
new_phone = params.get("new_phone", "")
if new_phone:
new_phone = _validate_phone(new_phone)
await task_manager.set_step(task_id, 1, 5, "Авторизация", acc_id=account["id"])
logger.info(f"[change_phone] {login}: logging in to Steam...")
steam = await create_steam_session(account)
try:
password = account["password"]
await task_manager.set_step(task_id, 2, 5, "Wizard", acc_id=account["id"])
logger.info(f"[change_phone] {login}: auth ok, starting phone change wizard...")
wizard = await run_common_wizard(
steam,
entry_url="https://help.steampowered.com/en/wizard/HelpRemovePhoneNumber?redir=store/account",
login=login,
password=password,
)
logger.info(f"[change_phone] {login}: wizard done")
if not new_phone:
new_phone = await task_manager.prompt_user(task_id, f"Введите новый номер телефона для {login}", login=login)
if not new_phone:
raise ValueError("new_phone is required")
new_phone = _validate_phone(new_phone)
await task_manager.set_step(task_id, 3, 5, "Запрос смены", acc_id=account["id"])
logger.info(f"[change_phone] {login}: requesting phone change -> {new_phone}...")
fresh_sid = _get_fresh_sessionid(steam)
text = await steam.request(
url="https://help.steampowered.com/en/wizard/AjaxAccountRecoveryChangePhone/",
method="POST",
data={
"s": wizard.s,
"account": wizard.account,
"sessionid": fresh_sid,
"wizard_ajax": 1,
"gamepad": 0,
"phone_number": new_phone,
},
headers=AJAX_HEADERS,
)
data = json.loads(text) if isinstance(text, str) else text
logger.info(f"[change_phone] {login}: change request response: {data}")
if data.get("errorMsg"):
err = data["errorMsg"]
hint = ""
if "112" in err:
hint = " (номер уже привязан к другому аккаунту, виртуальный/VoIP, или не поддерживается Steam)"
raise RuntimeError(f"Phone change request failed: {err}{hint}")
await task_manager.set_step(task_id, 4, 5, "SMS код", acc_id=account["id"])
sms_code = await task_manager.prompt_user(task_id, f"Введите SMS код с номера {new_phone}", login=login)
if not sms_code:
raise ValueError("sms_code is required")
sms_code = sms_code.strip()
logger.info(f"[change_phone] {login}: confirming with SMS code...")
fresh_sid = _get_fresh_sessionid(steam)
confirm_text = await steam.request(
url="https://help.steampowered.com/en/wizard/AjaxAccountRecoveryConfirmChangePhone/",
method="POST",
data={
"s": wizard.s,
"account": wizard.account,
"sessionid": fresh_sid,
"wizard_ajax": 1,
"gamepad": 0,
"phone_number": new_phone,
"phone_change_code": sms_code,
},
headers=AJAX_HEADERS,
)
confirm_data = json.loads(confirm_text) if isinstance(confirm_text, str) else confirm_text
logger.info(f"[change_phone] {login}: confirm response: {confirm_data}")
if confirm_data.get("errorMsg"):
raise RuntimeError(f"Phone confirmation failed: {confirm_data['errorMsg']}")
await task_manager.set_step(task_id, 5, 5, "Сохранение", acc_id=account["id"])
from app.database import get_db
db = await get_db()
await db.execute(
"UPDATE accounts SET phone = ?, updated_at = datetime('now') WHERE id = ?",
(new_phone, account["id"]),
)
await db.commit()
logger.success(f"[change_phone] {login}: phone changed -> {new_phone}")
finally:
await close_steam(steam)
async def remove_phone(account: dict, params: dict, task_id: str = "") -> None:
"""Remove phone by changing it to a disposable number + SMS confirmation."""
from app.core.task_manager import task_manager
login = account["login"]
new_phone = params.get("new_phone", "")
if new_phone:
new_phone = _validate_phone(new_phone)
await task_manager.set_step(task_id, 1, 5, "Авторизация", acc_id=account["id"])
logger.info(f"[remove_phone] {login}: logging in to Steam...")
steam = await create_steam_session(account)
try:
password = account["password"]
await task_manager.set_step(task_id, 2, 5, "Wizard", acc_id=account["id"])
logger.info(f"[remove_phone] {login}: starting phone removal wizard...")
wizard = await run_common_wizard(
steam,
entry_url="https://help.steampowered.com/en/wizard/HelpRemovePhoneNumber?redir=store/account",
login=login,
password=password,
)
if not new_phone:
new_phone = await task_manager.prompt_user(task_id, "Введите новый номер телефона (на который переставится старый)", login=login)
if not new_phone:
raise ValueError("new_phone is required for phone removal")
new_phone = _validate_phone(new_phone)
await task_manager.set_step(task_id, 3, 5, "Запрос смены", acc_id=account["id"])
logger.info(f"[remove_phone] {login}: requesting phone change -> {new_phone}...")
fresh_sid = _get_fresh_sessionid(steam)
text = await steam.request(
url="https://help.steampowered.com/en/wizard/AjaxAccountRecoveryChangePhone/",
method="POST",
data={
"s": wizard.s,
"account": wizard.account,
"sessionid": fresh_sid,
"wizard_ajax": 1,
"gamepad": 0,
"phone_number": new_phone,
},
headers=AJAX_HEADERS,
)
data = json.loads(text) if isinstance(text, str) else text
logger.info(f"[remove_phone] {login}: change request response: {data}")
if data.get("errorMsg"):
raise RuntimeError(f"Phone change request failed: {data['errorMsg']}")
await task_manager.set_step(task_id, 4, 5, "SMS код", acc_id=account["id"])
sms_code = await task_manager.prompt_user(task_id, f"Введите SMS код с номера {new_phone}", login=login)
if not sms_code:
raise ValueError("sms_code is required")
sms_code = sms_code.strip()
logger.info(f"[remove_phone] {login}: confirming with SMS code...")
fresh_sid = _get_fresh_sessionid(steam)
confirm_text = await steam.request(
url="https://help.steampowered.com/en/wizard/AjaxAccountRecoveryConfirmChangePhone/",
method="POST",
data={
"s": wizard.s,
"account": wizard.account,
"sessionid": fresh_sid,
"wizard_ajax": 1,
"gamepad": 0,
"phone_number": new_phone,
"phone_change_code": sms_code,
},
headers=AJAX_HEADERS,
)
confirm_data = json.loads(confirm_text) if isinstance(confirm_text, str) else confirm_text
logger.info(f"[remove_phone] {login}: confirm response: {confirm_data}")
if confirm_data.get("errorMsg"):
raise RuntimeError(f"Phone confirmation failed: {confirm_data['errorMsg']}")
await task_manager.set_step(task_id, 5, 5, "Сохранение", acc_id=account["id"])
from app.database import get_db
db = await get_db()
await db.execute(
"UPDATE accounts SET phone = NULL, updated_at = datetime('now') WHERE id = ?",
(account["id"],),
)
await db.commit()
logger.success(f"[remove_phone] {login}: phone removed (changed to {new_phone})")
finally:
await close_steam(steam)