This commit is contained in:
Manchik
2026-05-19 18:28:43 +03:00
parent 48c16b8d3a
commit 68433b74c0
183 changed files with 18415 additions and 192 deletions
+40 -12
View File
@@ -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}")
+22 -6
View File
@@ -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()
+4 -1
View File
@@ -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
+117
View File
@@ -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 "", ""
+53 -7
View File
@@ -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}")
+53
View File
@@ -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,
),
)
+55
View File
@@ -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 "", "", []