- hexi core: message handling, rate limiting, cooldown, plugin manager - Custom plugins: BF stats, daily check-in, quotes, persona cards, etc. - Community plugins vendored under hexi/plugins with local fixes - Web admin frontends (learning-chat, persona-admin), unified hexi/web - Tests for rate_limit/cooldown/memes/persona; poetry.lock Co-Authored-By: Claude <noreply@anthropic.com>
110 lines
3.3 KiB
Python
110 lines
3.3 KiB
Python
import os
|
|
from typing import List, Dict, Optional
|
|
|
|
from nonebot import logger
|
|
from playwright.async_api import async_playwright, Response
|
|
|
|
|
|
async def fetch_tracker_search(
|
|
search_url: str,
|
|
cookies_txt: str,
|
|
wait_seconds: int = 2,
|
|
) -> Optional[Dict]:
|
|
"""
|
|
使用 Playwright + cookies 访问 tracker.gg API 并截获响应数据
|
|
"""
|
|
cookies = parse_netscape_cookies(cookies_txt)
|
|
if not cookies:
|
|
logger.error("cookies.txt 解析失败或为空")
|
|
return None
|
|
|
|
result: Optional[Dict] = None
|
|
|
|
async with async_playwright() as p:
|
|
browser = await p.chromium.launch(
|
|
executable_path="C:/Program Files/Google/Chrome/Application/chrome.exe",
|
|
headless=True, # False 可以看到浏览器操作
|
|
args=[
|
|
"--autoplay-policy=no-user-gesture-required",
|
|
"--disable-features=AutoplayDisableSuppression",
|
|
"--use-fake-ui-for-media-stream",
|
|
]
|
|
)
|
|
|
|
context = await browser.new_context(
|
|
viewport={"width": 640, "height": 320},
|
|
user_agent=(
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
"Chrome/143.0.0.0 Safari/537.36"
|
|
),
|
|
)
|
|
|
|
await context.add_cookies(cookies)
|
|
page = await context.new_page()
|
|
|
|
# 响应监听
|
|
async def handle_response(response: Response):
|
|
nonlocal result
|
|
try:
|
|
if search_url in response.url:
|
|
# 尝试解析 JSON
|
|
data = await response.json()
|
|
if data and "data" in data:
|
|
result = data
|
|
logger.info(f"成功截获响应数据: {data}")
|
|
except Exception as e:
|
|
logger.warning(f"解析响应失败: {e}")
|
|
|
|
page.on("response", handle_response)
|
|
|
|
try:
|
|
# 直接访问 API 链接
|
|
await page.goto(search_url, wait_until="domcontentloaded", timeout=60000)
|
|
# 等待响应截获
|
|
await page.wait_for_timeout(wait_seconds * 1000)
|
|
except Exception as e:
|
|
logger.error(f"访问页面出错: {e}")
|
|
finally:
|
|
await browser.close()
|
|
|
|
return result
|
|
|
|
|
|
def parse_netscape_cookies(cookies_txt_path: str) -> List[Dict]:
|
|
"""
|
|
解析 Netscape HTTP Cookie File -> Playwright cookies
|
|
"""
|
|
cookies: List[Dict] = []
|
|
|
|
if not os.path.exists(cookies_txt_path):
|
|
return cookies
|
|
|
|
with open(cookies_txt_path, "r", encoding="utf-8") as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line or line.startswith("#"):
|
|
continue
|
|
|
|
parts = line.split("\t")
|
|
if len(parts) != 7:
|
|
continue
|
|
|
|
domain, include_sub, path, secure, expiry, name, value = parts
|
|
|
|
cookie = {
|
|
"name": name,
|
|
"value": value,
|
|
"domain": domain,
|
|
"path": path,
|
|
"secure": secure.upper() == "TRUE",
|
|
"httpOnly": False,
|
|
}
|
|
|
|
if expiry.isdigit() and int(expiry) > 0:
|
|
cookie["expires"] = int(expiry)
|
|
|
|
cookies.append(cookie)
|
|
|
|
return cookies
|