api.3 / app /gs_code.py
Celeskry's picture
Update app/gs_code.py
1bb2377 verified
# app/gs_code.py
import httpx
import aiohttp
from bs4 import BeautifulSoup
from typing import Dict, Any, List
ROLE_URL = "https://api-os-takumi.hoyoverse.com/binding/api/getUserGameRolesByCookie"
REDEEM_URL = "https://public-operation-hk4e.hoyoverse.com/common/apicdkey/api/webExchangeCdkey"
GENSHIN_API = "https://genshin-impact.fandom.com/api.php?action=parse&page=Promotional_Code&prop=text&format=json"
class GSCodeApp:
def __init__(self):
self.client: httpx.AsyncClient | None = None
self.wiki_session: aiohttp.ClientSession | None = None
async def start(self):
self.client = httpx.AsyncClient(timeout=15)
self.wiki_session = aiohttp.ClientSession(
headers={"User-Agent": "Mozilla/5.0 (GenshinCodeBot)"}
)
async def stop(self):
if self.client:
await self.client.aclose()
if self.wiki_session:
await self.wiki_session.close()
def _headers(self, cookie: str):
return {
"Cookie": cookie,
"User-Agent": "Mozilla/5.0",
"Accept": "application/json"
}
async def _get_roles(self, cookie: str):
r = await self.client.get(
ROLE_URL,
headers=self._headers(cookie),
params={"game_biz": "hk4e_global"}
)
return r.json()
async def _get_code_info(self, code: str) -> Dict[str, Any]:
async with self.wiki_session.get(GENSHIN_API) as resp:
if resp.status != 200:
return {}
data = await resp.json()
html = data.get("parse", {}).get("text", {}).get("*")
if not html:
return {}
soup = BeautifulSoup(html, "html.parser")
for row in soup.select("table.wikitable tbody tr"):
tds = row.find_all("td")
if len(tds) < 3:
continue
code_els = tds[0].find_all("code")
codes = [c.get_text(strip=True) for c in code_els]
if code not in codes:
continue
rewards: List[str] = []
for item in tds[2].select(".item-text"):
text = item.get_text(" ", strip=True)
if text:
text = text.replace("×", "×")
rewards.append(text)
validity = (
tds[3].get_text(" ", strip=True).replace("×", "×")
if len(tds) >= 4 else None
)
return {
"rewards": rewards,
"validity": validity
}
return {}
async def redeem_code(
self,
cookie: str,
server_input: str,
code: str
) -> Dict[str, Any]:
try:
role_data = await self._get_roles(cookie)
if role_data.get("retcode") != 0:
return {"ok": False, "error": "Cookie không hợp lệ"}
roles = role_data.get("data", {}).get("list", [])
if not roles:
return {"ok": False, "error": "Không có tài khoản Genshin"}
region_map = {
"asia": "os_asia",
"america": "os_usa",
"usa": "os_usa",
"na": "os_usa",
"europe": "os_euro",
"eu": "os_euro",
"tw": "os_cht",
"hk": "os_cht"
}
target_region = region_map.get(server_input.lower())
if not target_region:
return {"ok": False, "error": "Server không hợp lệ"}
selected_role = next(
(r for r in roles if r.get("region") == target_region),
None
)
if not selected_role:
return {"ok": False, "error": f"Không tìm thấy tài khoản ở server {server_input}"}
uid = selected_role.get("game_uid")
region = selected_role.get("region")
params = {
"uid": uid,
"region": region,
"lang": "en",
"cdkey": code,
"game_biz": "hk4e_global",
"sLangKey": "en-us"
}
r = await self.client.get(
REDEEM_URL,
headers=self._headers(cookie),
params=params
)
data = r.json()
if data.get("retcode") != 0:
return {
"ok": False,
"error": data.get("message", "Redeem thất bại"),
"retcode": data.get("retcode")
}
code_info = await self._get_code_info(code)
return {
"ok": True,
"message": data.get("data", {}).get("msg"),
"uid": uid,
"server": server_input.capitalize(),
"code": code,
"rewards": code_info.get("rewards", []),
"validity": code_info.get("validity")
}
except Exception as e:
return {"ok": False, "error": str(e)}