forked from FOSS/Steam-Panel
v 3.0.3
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user