forked from FOSS/Steam-Panel
v3.1.0
This commit is contained in:
@@ -8,6 +8,7 @@ from loguru import logger
|
||||
from app.services.steam_auth import _resolve_proxy, ProxyRequestStrategy, close_steam, extract_session_cookies
|
||||
from app.services.steam_ban import check_ban
|
||||
from app.services.steam_profile import fetch_profile
|
||||
from app.services.steam_vac_checker import check_vac_and_limit
|
||||
from app.core.proxy_manager import proxy_manager
|
||||
from app.core.task_manager import task_manager
|
||||
|
||||
@@ -66,16 +67,25 @@ async def check_logpass_account(account: dict, params: dict, *, task_id: str) ->
|
||||
|
||||
steam_id = str(steam.steamid)
|
||||
ban_status = ""
|
||||
vac_status = ""
|
||||
limit_status = ""
|
||||
vac_games: list[str] = []
|
||||
if val_settings.get("check_ban"):
|
||||
await task_manager.set_step(task_id, 2, 3, "Проверка бана", acc_id)
|
||||
await task_manager.set_step(task_id, 2, 4, "Проверка бана", acc_id)
|
||||
ban_status = await check_ban(steam)
|
||||
vac_status, limit_status, vac_games = await check_vac_and_limit(steam)
|
||||
|
||||
await task_manager.set_step(task_id, 3, 4, "Баланс / Страна", acc_id)
|
||||
from app.services.steam_store_checker import fetch_balance_and_country
|
||||
balance, country = await fetch_balance_and_country(steam)
|
||||
logger.info(f"[checker] {account['login']}: balance={balance or 'none'}, country={country or 'none'}")
|
||||
|
||||
nickname = None
|
||||
steam_level = None
|
||||
avatar_url = None
|
||||
last_online = None
|
||||
if steam_id and steam_id != "0" and val_settings.get("fetch_profile"):
|
||||
await task_manager.set_step(task_id, 3, 3, "Получение профиля", acc_id)
|
||||
await task_manager.set_step(task_id, 4, 4, "Получение профиля", acc_id)
|
||||
profile = await fetch_profile(steam_id, steam=steam)
|
||||
if profile:
|
||||
nickname = profile.get("nickname")
|
||||
@@ -83,14 +93,20 @@ async def check_logpass_account(account: dict, params: dict, *, task_id: str) ->
|
||||
avatar_url = profile.get("avatar_url")
|
||||
last_online = profile.get("last_online")
|
||||
|
||||
import json as _json
|
||||
from app.database import get_db
|
||||
db = await get_db()
|
||||
await db.execute(
|
||||
"""UPDATE logpass_accounts
|
||||
SET steam_id = ?, nickname = ?, steam_level = ?, avatar_url = ?, last_online = ?,
|
||||
ban_status = ?, status = 'valid', updated_at = datetime('now')
|
||||
ban_status = ?, vac_status = ?, limit_status = ?, vac_games = ?,
|
||||
balance = ?, country = ?,
|
||||
status = 'valid', updated_at = datetime('now')
|
||||
WHERE id = ?""",
|
||||
(steam_id, nickname, steam_level, avatar_url, last_online, ban_status, acc_id),
|
||||
(steam_id, nickname, steam_level, avatar_url, last_online,
|
||||
ban_status, vac_status, limit_status,
|
||||
_json.dumps(vac_games) if vac_games else None,
|
||||
balance or None, country or None, acc_id),
|
||||
)
|
||||
await db.commit()
|
||||
logger.success(f"[checker] {account['login']} → valid, ban={ban_status}")
|
||||
@@ -222,7 +238,7 @@ async def full_parse_logpass_account(account: dict, params: dict, *, task_id: st
|
||||
)
|
||||
|
||||
try:
|
||||
await task_manager.set_step(task_id, 1, 6, "Авторизация", acc_id)
|
||||
await task_manager.set_step(task_id, 1, 7, "Авторизация", acc_id)
|
||||
await steam.login_to_steam()
|
||||
|
||||
# Save session cookies
|
||||
@@ -241,13 +257,14 @@ async def full_parse_logpass_account(account: dict, params: dict, *, task_id: st
|
||||
|
||||
steam_id = str(steam.steamid)
|
||||
|
||||
await task_manager.set_step(task_id, 2, 6, "Проверка бана", acc_id)
|
||||
await task_manager.set_step(task_id, 2, 7, "Проверка бана", acc_id)
|
||||
ban_status = await check_ban(steam)
|
||||
vac_status, limit_status, vac_games = await check_vac_and_limit(steam)
|
||||
|
||||
nickname = avatar_url = last_online = None
|
||||
steam_level = None
|
||||
if steam_id and steam_id != "0":
|
||||
await task_manager.set_step(task_id, 3, 6, "Профиль", acc_id)
|
||||
await task_manager.set_step(task_id, 3, 7, "Профиль", acc_id)
|
||||
profile = await fetch_profile(steam_id, steam=steam)
|
||||
if profile:
|
||||
nickname = profile.get("nickname")
|
||||
@@ -255,29 +272,40 @@ async def full_parse_logpass_account(account: dict, params: dict, *, task_id: st
|
||||
avatar_url = profile.get("avatar_url")
|
||||
last_online = profile.get("last_online")
|
||||
|
||||
await task_manager.set_step(task_id, 4, 7, "Баланс / Страна", acc_id)
|
||||
from app.services.steam_store_checker import fetch_balance_and_country
|
||||
balance, country = await fetch_balance_and_country(steam)
|
||||
logger.info(f"[full_parse] {account['login']}: balance={balance or 'none'}, country={country or 'none'}")
|
||||
|
||||
prime = "Disabled"
|
||||
trophy = None
|
||||
behavior = None
|
||||
if steam_id and steam_id != "0":
|
||||
await task_manager.set_step(task_id, 4, 6, "Prime / Trophy", acc_id)
|
||||
await task_manager.set_step(task_id, 5, 7, "Prime / Trophy", acc_id)
|
||||
prime = await _check_cs2_prime(steam, steam_id)
|
||||
trophy = await _check_dota_trophy(steam, steam_id)
|
||||
behavior = await _check_dota_behavior(steam, steam_id)
|
||||
|
||||
await task_manager.set_step(task_id, 5, 6, "Лицензии", acc_id)
|
||||
await task_manager.set_step(task_id, 6, 7, "Лицензии", acc_id)
|
||||
license_str = await _check_licenses(steam)
|
||||
|
||||
await task_manager.set_step(task_id, 6, 6, "Сохранение", acc_id)
|
||||
import json as _json
|
||||
await task_manager.set_step(task_id, 7, 7, "Сохранение", acc_id)
|
||||
from app.database import get_db
|
||||
db = await get_db()
|
||||
await db.execute(
|
||||
"""UPDATE logpass_accounts
|
||||
SET steam_id = ?, nickname = ?, steam_level = ?, avatar_url = ?, last_online = ?,
|
||||
ban_status = ?, prime = ?, trophy = ?, behavior = ?, license = ?,
|
||||
ban_status = ?, vac_status = ?, limit_status = ?, vac_games = ?,
|
||||
balance = ?, country = ?,
|
||||
prime = ?, trophy = ?, behavior = ?, license = ?,
|
||||
status = 'valid', updated_at = datetime('now')
|
||||
WHERE id = ?""",
|
||||
(steam_id, nickname, steam_level, avatar_url, last_online,
|
||||
ban_status, prime, trophy, behavior, license_str, acc_id),
|
||||
ban_status, vac_status, limit_status,
|
||||
_json.dumps(vac_games) if vac_games else None,
|
||||
balance or None, country or None,
|
||||
prime, trophy, behavior, license_str, acc_id),
|
||||
)
|
||||
await db.commit()
|
||||
logger.success(f"[full_parse] {account['login']} → valid, ban={ban_status}, prime={prime}, trophy={trophy}, behavior={behavior}, licenses={len(license_str.split(', ')) if license_str else 0}")
|
||||
|
||||
@@ -123,13 +123,18 @@ async def validate_account(account: dict, params: dict, task_id: str = "") -> No
|
||||
steam = None
|
||||
error_msg = None
|
||||
ban_status = ""
|
||||
vac_status = ""
|
||||
limit_status = ""
|
||||
vac_games: list[str] = []
|
||||
balance = ""
|
||||
country = ""
|
||||
|
||||
val_settings = read_validation_settings()
|
||||
|
||||
login = account["login"]
|
||||
profile = None
|
||||
try:
|
||||
await task_manager.set_step(task_id, 1, 4, "Авторизация", acc_id=account["id"])
|
||||
await task_manager.set_step(task_id, 1, 6, "Авторизация", acc_id=account["id"])
|
||||
logger.info(f"[validate] {login}: logging in to Steam...")
|
||||
steam = await create_steam_session(account)
|
||||
status = "valid"
|
||||
@@ -137,12 +142,20 @@ async def validate_account(account: dict, params: dict, task_id: str = "") -> No
|
||||
|
||||
if status == "valid" and steam is not None:
|
||||
if val_settings.get("check_ban"):
|
||||
await task_manager.set_step(task_id, 2, 4, "Проверка бана", acc_id=account["id"])
|
||||
await task_manager.set_step(task_id, 2, 6, "Проверка бана", acc_id=account["id"])
|
||||
logger.info(f"[validate] {login}: checking ban status...")
|
||||
ban_status = await check_ban(steam)
|
||||
logger.info(f"[validate] {login}: ban = {ban_status or 'none'}")
|
||||
await task_manager.set_step(task_id, 3, 6, "Проверка VAC", acc_id=account["id"])
|
||||
from app.services.steam_vac_checker import check_vac_and_limit
|
||||
vac_status, limit_status, vac_games = await check_vac_and_limit(steam)
|
||||
logger.info(f"[validate] {login}: vac={vac_status or 'none'}, limit={limit_status or 'none'}, games={vac_games}")
|
||||
await task_manager.set_step(task_id, 4, 6, "Баланс / Страна", acc_id=account["id"])
|
||||
from app.services.steam_store_checker import fetch_balance_and_country
|
||||
balance, country = await fetch_balance_and_country(steam)
|
||||
logger.info(f"[validate] {login}: balance={balance or 'none'}, country={country or 'none'}")
|
||||
if account.get("steam_id") and val_settings.get("fetch_profile"):
|
||||
await task_manager.set_step(task_id, 3, 4, "Профиль", acc_id=account["id"])
|
||||
await task_manager.set_step(task_id, 5, 6, "Профиль", acc_id=account["id"])
|
||||
logger.info(f"[validate] {login}: fetching profile (id={account['steam_id']})...")
|
||||
profile = await fetch_profile(account["steam_id"], steam=steam)
|
||||
if profile:
|
||||
@@ -150,7 +163,7 @@ async def validate_account(account: dict, params: dict, task_id: str = "") -> No
|
||||
else:
|
||||
logger.warning(f"[validate] {login}: failed to fetch profile")
|
||||
|
||||
await task_manager.set_step(task_id, 4, 4, "Сохранение", acc_id=account["id"])
|
||||
await task_manager.set_step(task_id, 6, 6, "Сохранение", acc_id=account["id"])
|
||||
if steam is not None and status == "valid":
|
||||
try:
|
||||
session_cookies = extract_session_cookies(steam)
|
||||
@@ -177,10 +190,13 @@ async def validate_account(account: dict, params: dict, task_id: str = "") -> No
|
||||
if steam is not None:
|
||||
await close_steam(steam)
|
||||
|
||||
import json as _json
|
||||
db = await get_db()
|
||||
await db.execute(
|
||||
"UPDATE accounts SET status = ?, ban_status = ?, updated_at = datetime('now') WHERE id = ?",
|
||||
(status, ban_status or None, account["id"]),
|
||||
"UPDATE accounts SET status = ?, ban_status = ?, vac_status = ?, limit_status = ?, vac_games = ?, balance = ?, country = ?, updated_at = datetime('now') WHERE id = ?",
|
||||
(status, ban_status or None, vac_status or None, limit_status or None,
|
||||
_json.dumps(vac_games) if vac_games else None,
|
||||
balance or None, country or None, account["id"]),
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@ def generate_2fa_code(shared_secret: str) -> str:
|
||||
"""Generate a Steam Guard 2FA code from shared_secret."""
|
||||
timestamp = int(time.time()) // 30
|
||||
msg = struct.pack(">Q", timestamp)
|
||||
key = base64.b64decode(shared_secret)
|
||||
# Normalise: accept both standard (+/) and URL-safe (-_) base64, fix padding
|
||||
normalised = shared_secret.strip().replace("-", "+").replace("_", "/")
|
||||
normalised += "=" * (-len(normalised) % 4)
|
||||
key = base64.b64decode(normalised)
|
||||
auth = hmac.new(key, msg, hashlib.sha1).digest()
|
||||
|
||||
offset = auth[19] & 0xF
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
"""Parses wallet balance and country from store.steampowered.com/account/."""
|
||||
|
||||
import re
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from loguru import logger
|
||||
|
||||
# fmt: off
|
||||
_COUNTRY_NAME_TO_ISO2: dict[str, str] = {
|
||||
"afghanistan": "AF", "albania": "AL", "algeria": "DZ", "andorra": "AD",
|
||||
"angola": "AO", "argentina": "AR", "armenia": "AM", "australia": "AU",
|
||||
"austria": "AT", "azerbaijan": "AZ", "bahrain": "BH", "bangladesh": "BD",
|
||||
"belarus": "BY", "belgium": "BE", "bolivia": "BO", "bosnia and herzegovina": "BA",
|
||||
"brazil": "BR", "bulgaria": "BG", "cambodia": "KH", "canada": "CA",
|
||||
"chile": "CL", "china": "CN", "colombia": "CO", "costa rica": "CR",
|
||||
"croatia": "HR", "cyprus": "CY", "czech republic": "CZ", "czechia": "CZ",
|
||||
"denmark": "DK", "ecuador": "EC", "egypt": "EG", "estonia": "EE",
|
||||
"ethiopia": "ET", "finland": "FI", "france": "FR", "georgia": "GE",
|
||||
"germany": "DE", "ghana": "GH", "greece": "GR", "guatemala": "GT",
|
||||
"honduras": "HN", "hong kong": "HK", "hungary": "HU", "iceland": "IS",
|
||||
"india": "IN", "indonesia": "ID", "iran": "IR", "iraq": "IQ",
|
||||
"ireland": "IE", "israel": "IL", "italy": "IT", "jamaica": "JM",
|
||||
"japan": "JP", "jordan": "JO", "kazakhstan": "KZ", "kenya": "KE",
|
||||
"kuwait": "KW", "kyrgyzstan": "KG", "latvia": "LV", "lebanon": "LB",
|
||||
"liechtenstein": "LI", "lithuania": "LT", "luxembourg": "LU",
|
||||
"malaysia": "MY", "malta": "MT", "mexico": "MX", "moldova": "MD",
|
||||
"mongolia": "MN", "morocco": "MA", "netherlands": "NL", "new zealand": "NZ",
|
||||
"nicaragua": "NI", "nigeria": "NG", "north macedonia": "MK", "norway": "NO",
|
||||
"oman": "OM", "pakistan": "PK", "panama": "PA", "paraguay": "PY",
|
||||
"peru": "PE", "philippines": "PH", "poland": "PL", "portugal": "PT",
|
||||
"qatar": "QA", "romania": "RO", "russia": "RU", "russian federation": "RU",
|
||||
"saudi arabia": "SA", "serbia": "RS", "singapore": "SG", "slovakia": "SK",
|
||||
"slovenia": "SI", "south africa": "ZA", "south korea": "KR",
|
||||
"republic of korea": "KR", "spain": "ES", "sri lanka": "LK", "sweden": "SE",
|
||||
"switzerland": "CH", "taiwan": "TW", "tajikistan": "TJ", "thailand": "TH",
|
||||
"tunisia": "TN", "turkey": "TR", "turkmenistan": "TM", "ukraine": "UA",
|
||||
"united arab emirates": "AE", "united kingdom": "GB",
|
||||
"united states": "US", "uruguay": "UY", "uzbekistan": "UZ",
|
||||
"venezuela": "VE", "vietnam": "VN",
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
|
||||
def _name_to_iso2(name: str) -> str:
|
||||
"""Convert a country name to ISO 3166-1 alpha-2 code, or return the name unchanged."""
|
||||
return _COUNTRY_NAME_TO_ISO2.get(name.strip().lower(), name)
|
||||
|
||||
|
||||
def _parse_balance_and_country_html(html: str) -> tuple[str, str]:
|
||||
"""Parse balance and country from store.steampowered.com/account/ HTML."""
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
|
||||
balance = ""
|
||||
balance_row = soup.find("div", class_="accountBalance")
|
||||
if balance_row:
|
||||
price_div = balance_row.find("div", class_="price")
|
||||
if price_div:
|
||||
balance = price_div.get_text(strip=True)
|
||||
|
||||
country = ""
|
||||
# Look inside country_settings div first (most reliable)
|
||||
country_block = soup.find("div", class_="country_settings")
|
||||
if country_block:
|
||||
span = country_block.find("span", class_="account_data_field")
|
||||
if span:
|
||||
country = _name_to_iso2(span.get_text(strip=True))
|
||||
|
||||
# Fallback: any <p> containing "Country:" label
|
||||
if not country:
|
||||
for p in soup.find_all("p"):
|
||||
text = p.get_text()
|
||||
if "Country:" in text or "Страна:" in text:
|
||||
span = p.find("span", class_="account_data_field")
|
||||
if span:
|
||||
country = _name_to_iso2(span.get_text(strip=True))
|
||||
break
|
||||
|
||||
# Last resort: look for 2-letter code in embedded JS
|
||||
if not country:
|
||||
for pattern in [
|
||||
r'"userCountry"\s*:\s*"([A-Z]{2})"',
|
||||
r'g_strCountryCode\s*=\s*"([A-Z]{2})"',
|
||||
]:
|
||||
m = re.search(pattern, html)
|
||||
if m:
|
||||
country = m.group(1)
|
||||
break
|
||||
|
||||
return balance, country
|
||||
|
||||
|
||||
async def fetch_balance_and_country(steam) -> tuple[str, str]:
|
||||
"""Fetch wallet balance and country using a pysteamauth steam session."""
|
||||
try:
|
||||
html = await steam.request("https://store.steampowered.com/account/", method="GET")
|
||||
if isinstance(html, bytes):
|
||||
html = html.decode("utf-8", errors="replace")
|
||||
return _parse_balance_and_country_html(html)
|
||||
except Exception as exc:
|
||||
logger.warning(f"Balance/country fetch failed: {exc}")
|
||||
return "", ""
|
||||
|
||||
|
||||
async def fetch_balance_and_country_aiohttp(session, jar) -> tuple[str, str]: # noqa: ARG001
|
||||
"""Fetch wallet balance and country using an aiohttp ClientSession."""
|
||||
import aiohttp
|
||||
|
||||
try:
|
||||
async with session.get(
|
||||
"https://store.steampowered.com/account/",
|
||||
timeout=aiohttp.ClientTimeout(total=15),
|
||||
) as resp:
|
||||
html = await resp.text()
|
||||
return _parse_balance_and_country_html(html)
|
||||
except Exception as exc:
|
||||
logger.warning(f"Balance/country aiohttp fetch failed: {exc}")
|
||||
return "", ""
|
||||
@@ -31,7 +31,7 @@ async def check_token_account(account: dict, params: dict, *, task_id: str) -> N
|
||||
token = account["token"]
|
||||
|
||||
# Step 1: Decode JWT to get steam_id
|
||||
await task_manager.set_step(task_id, 1, 4, "Декодирование токена", acc_id)
|
||||
await task_manager.set_step(task_id, 1, 5, "Декодирование токена", acc_id)
|
||||
try:
|
||||
jwt_payload = _decode_jwt_payload(token)
|
||||
except Exception as exc:
|
||||
@@ -42,7 +42,7 @@ async def check_token_account(account: dict, params: dict, *, task_id: str) -> N
|
||||
steam_id = jwt_payload.get("sub")
|
||||
|
||||
# Step 2: GET steamcommunity.com for sessionid cookie
|
||||
await task_manager.set_step(task_id, 2, 4, "Получение sessionid", acc_id)
|
||||
await task_manager.set_step(task_id, 2, 5, "Получение sessionid", acc_id)
|
||||
proxy = await _resolve_proxy(account)
|
||||
connector = proxy_manager.get_connector(proxy) if proxy else aiohttp.TCPConnector()
|
||||
jar = aiohttp.CookieJar(unsafe=True)
|
||||
@@ -68,7 +68,7 @@ async def check_token_account(account: dict, params: dict, *, task_id: str) -> N
|
||||
raise RuntimeError("Failed to acquire sessionid cookie")
|
||||
|
||||
# Step 3: POST /jwt/finalizelogin with refresh_token as nonce
|
||||
await task_manager.set_step(task_id, 3, 4, "Получение куки", acc_id)
|
||||
await task_manager.set_step(task_id, 3, 5, "Получение куки", acc_id)
|
||||
form = FormData(fields=[
|
||||
("nonce", token),
|
||||
("sessionid", sessionid),
|
||||
@@ -125,12 +125,14 @@ async def check_token_account(account: dict, params: dict, *, task_id: str) -> N
|
||||
session_cookies_json = _json.dumps(all_cookies)
|
||||
logger.debug(f"[token_checker] {account.get('login', acc_id)}: got {len(all_cookies)} cookies")
|
||||
|
||||
# Step 4: Fetch profile using authenticated session (cookies set)
|
||||
await task_manager.set_step(task_id, 4, 4, "Получение профиля", acc_id)
|
||||
# Step 4: Fetch profile + VAC/limit using authenticated session
|
||||
await task_manager.set_step(task_id, 4, 5, "Получение профиля", acc_id)
|
||||
nickname = None
|
||||
steam_level = None
|
||||
avatar_url = None
|
||||
last_online = None
|
||||
vac_status = ""
|
||||
limit_status = ""
|
||||
if steam_id and steam_id != "0":
|
||||
profile_url = f"https://steamcommunity.com/profiles/{steam_id}/"
|
||||
try:
|
||||
@@ -146,16 +148,60 @@ async def check_token_account(account: dict, params: dict, *, task_id: str) -> N
|
||||
last_online = _parse_last_online(html)
|
||||
except Exception as exc:
|
||||
logger.warning(f"[token_checker] profile fetch error: {exc}")
|
||||
# Step 5: Balance / country
|
||||
await task_manager.set_step(task_id, 5, 5, "Баланс / Страна", acc_id)
|
||||
balance = ""
|
||||
country = ""
|
||||
try:
|
||||
from app.services.steam_store_checker import fetch_balance_and_country_aiohttp
|
||||
balance, country = await fetch_balance_and_country_aiohttp(session, jar)
|
||||
logger.debug(f"[token_checker] {account.get('login', acc_id)}: balance={balance or 'none'}, country={country or 'none'}")
|
||||
except Exception as bal_exc:
|
||||
logger.warning(f"[token_checker] balance/country error: {bal_exc}")
|
||||
|
||||
vac_games: list[str] = []
|
||||
try:
|
||||
from bs4 import BeautifulSoup
|
||||
async with session.get(
|
||||
"https://help.steampowered.com/en/wizard/VacBans",
|
||||
timeout=aiohttp.ClientTimeout(total=15),
|
||||
) as resp:
|
||||
vac_html = await resp.text()
|
||||
soup = BeautifulSoup(vac_html, "html.parser")
|
||||
limit_status = "Lim" if soup.find("div", class_="help_event_limiteduser") else "NoLim"
|
||||
vac_status = "CLEAN"
|
||||
vac_body = soup.find("div", class_="vac_body")
|
||||
if vac_body:
|
||||
ban_header = vac_body.find("div", class_="vac_ban_header")
|
||||
if ban_header:
|
||||
header_text = ban_header.get_text(strip=True).lower()
|
||||
if "game developer" in header_text or "game ban" in header_text:
|
||||
vac_status = "GAME BAN"
|
||||
for box in vac_body.find_all("div", class_="refund_info_box"):
|
||||
for span in box.find_all("span", class_="help_highlight_text"):
|
||||
name = span.get_text(strip=True)
|
||||
if name:
|
||||
vac_games.append(name)
|
||||
else:
|
||||
vac_status = "VAC"
|
||||
except Exception as exc:
|
||||
logger.warning(f"[token_checker] vac/limit check error: {exc}")
|
||||
|
||||
# Update DB
|
||||
import json as _json
|
||||
from app.database import get_db
|
||||
db = await get_db()
|
||||
await db.execute(
|
||||
"""UPDATE token_accounts
|
||||
SET steam_id = ?, nickname = ?, steam_level = ?, avatar_url = ?, last_online = ?,
|
||||
session_cookies = ?, status = 'valid', updated_at = datetime('now')
|
||||
session_cookies = ?, vac_status = ?, limit_status = ?, vac_games = ?,
|
||||
balance = ?, country = ?,
|
||||
status = 'valid', updated_at = datetime('now')
|
||||
WHERE id = ?""",
|
||||
(steam_id, nickname, steam_level, avatar_url, last_online, session_cookies_json, acc_id),
|
||||
(steam_id, nickname, steam_level, avatar_url, last_online,
|
||||
session_cookies_json, vac_status, limit_status,
|
||||
_json.dumps(vac_games) if vac_games else None,
|
||||
balance or None, country or None, acc_id),
|
||||
)
|
||||
await db.commit()
|
||||
logger.success(f"[token_checker] {account.get('login', acc_id)} → valid, steam_id={steam_id}")
|
||||
|
||||
@@ -222,6 +222,37 @@ async def _fetch_phone_digits(session: aiohttp.ClientSession) -> dict:
|
||||
return {"phone_digits": None}
|
||||
|
||||
|
||||
async def _fetch_vac_and_limit(session: aiohttp.ClientSession) -> dict:
|
||||
"""GET /wizard/VacBans → vac_status, limit_status, vac_games."""
|
||||
url = "https://help.steampowered.com/en/wizard/VacBans"
|
||||
try:
|
||||
async with session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as resp:
|
||||
html = await resp.text()
|
||||
from bs4 import BeautifulSoup
|
||||
soup = BeautifulSoup(html, "html.parser")
|
||||
limit_status = "Lim" if soup.find("div", class_="help_event_limiteduser") else "NoLim"
|
||||
vac_status = "CLEAN"
|
||||
vac_games: list[str] = []
|
||||
vac_body = soup.find("div", class_="vac_body")
|
||||
if vac_body:
|
||||
ban_header = vac_body.find("div", class_="vac_ban_header")
|
||||
if ban_header:
|
||||
header_text = ban_header.get_text(strip=True).lower()
|
||||
if "game developer" in header_text or "game ban" in header_text:
|
||||
vac_status = "GAME BAN"
|
||||
for box in vac_body.find_all("div", class_="refund_info_box"):
|
||||
for span in box.find_all("span", class_="help_highlight_text"):
|
||||
name = span.get_text(strip=True)
|
||||
if name:
|
||||
vac_games.append(name)
|
||||
else:
|
||||
vac_status = "VAC"
|
||||
return {"vac_status": vac_status, "limit_status": limit_status, "vac_games": vac_games}
|
||||
except Exception as exc:
|
||||
logger.debug(f"[full_check] vac/limit check error: {exc}")
|
||||
return {"vac_status": "", "limit_status": "", "vac_games": []}
|
||||
|
||||
|
||||
async def _fetch_alert_status(session: aiohttp.ClientSession) -> dict:
|
||||
"""GET /supportmessages/ → alert_status string."""
|
||||
url = "https://store.steampowered.com/supportmessages/"
|
||||
@@ -529,6 +560,7 @@ async def full_check_token_account(
|
||||
_fetch_phone_digits(session),
|
||||
_fetch_alert_status(session),
|
||||
_fetch_market_limited(session),
|
||||
_fetch_vac_and_limit(session),
|
||||
return_exceptions=True,
|
||||
)
|
||||
|
||||
@@ -562,6 +594,11 @@ async def full_check_token_account(
|
||||
if isinstance(page_results[4], dict)
|
||||
else {"market_limited": False}
|
||||
)
|
||||
vac_data = (
|
||||
page_results[5]
|
||||
if isinstance(page_results[5], dict)
|
||||
else {"vac_status": "", "limit_status": ""}
|
||||
)
|
||||
|
||||
# -------------------------------------------------------------- #
|
||||
# Step 5: Inventory checks #
|
||||
@@ -625,6 +662,10 @@ async def full_check_token_account(
|
||||
"phone_digits": phone_data.get("phone_digits"),
|
||||
"alert_status": alert_data.get("alert_status", "None"),
|
||||
"market_limited": market_data.get("market_limited", False),
|
||||
# VAC / limit status
|
||||
"vac_status": vac_data.get("vac_status", ""),
|
||||
"limit_status": vac_data.get("limit_status", ""),
|
||||
"vac_games": vac_data.get("vac_games", []),
|
||||
# Timestamp
|
||||
"checked_at": datetime.utcnow().isoformat(),
|
||||
}
|
||||
@@ -641,6 +682,12 @@ async def full_check_token_account(
|
||||
last_online = ?,
|
||||
session_cookies = ?,
|
||||
check_data = ?,
|
||||
ban_status = ?,
|
||||
vac_status = ?,
|
||||
limit_status = ?,
|
||||
vac_games = ?,
|
||||
balance = ?,
|
||||
country = ?,
|
||||
status = 'valid',
|
||||
updated_at = datetime('now')
|
||||
WHERE id = ?""",
|
||||
@@ -652,6 +699,12 @@ async def full_check_token_account(
|
||||
check_data.get("last_online"),
|
||||
session_cookies_json,
|
||||
_json.dumps(check_data),
|
||||
check_data.get("alert_status"),
|
||||
check_data.get("vac_status"),
|
||||
check_data.get("limit_status"),
|
||||
_json.dumps(check_data.get("vac_games", [])) if check_data.get("vac_games") else None,
|
||||
check_data.get("balance_raw") or None,
|
||||
check_data.get("user_country") or None,
|
||||
acc_id,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
"""VAC/Game Ban and Limited Account status checker via Steam Help Portal."""
|
||||
|
||||
import json
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from loguru import logger
|
||||
|
||||
|
||||
async def check_vac_and_limit(steam) -> tuple[str, str, list[str]]:
|
||||
"""Check VAC/Game ban and limited account status from /wizard/VacBans.
|
||||
|
||||
Single authenticated GET, parses both statuses and banned game names.
|
||||
|
||||
Returns:
|
||||
tuple[str, str, list[str]]:
|
||||
- vac_status: 'VAC' | 'GAME BAN' | 'CLEAN' | ''
|
||||
- limit_status: 'Lim' | 'NoLim' | ''
|
||||
- vac_games: list of banned game names (may be empty)
|
||||
"""
|
||||
try:
|
||||
response_html = await steam.request(
|
||||
"https://help.steampowered.com/en/wizard/VacBans",
|
||||
method="GET",
|
||||
)
|
||||
|
||||
if isinstance(response_html, bytes):
|
||||
response_html = response_html.decode("utf-8", errors="replace")
|
||||
|
||||
soup = BeautifulSoup(response_html, "html.parser")
|
||||
|
||||
limit_status = "Lim" if soup.find("div", class_="help_event_limiteduser") else "NoLim"
|
||||
|
||||
vac_status = "CLEAN"
|
||||
vac_games: list[str] = []
|
||||
|
||||
vac_body = soup.find("div", class_="vac_body")
|
||||
if vac_body:
|
||||
ban_header = vac_body.find("div", class_="vac_ban_header")
|
||||
if ban_header:
|
||||
header_text = ban_header.get_text(strip=True).lower()
|
||||
if "game developer" in header_text or "game ban" in header_text:
|
||||
vac_status = "GAME BAN"
|
||||
for box in vac_body.find_all("div", class_="refund_info_box"):
|
||||
for span in box.find_all("span", class_="help_highlight_text"):
|
||||
name = span.get_text(strip=True)
|
||||
if name:
|
||||
vac_games.append(name)
|
||||
else:
|
||||
vac_status = "VAC"
|
||||
|
||||
return vac_status, limit_status, vac_games
|
||||
|
||||
except Exception as exc:
|
||||
logger.warning(f"VAC/limit check failed: {exc}")
|
||||
return "", "", []
|
||||
Reference in New Issue
Block a user