Add HeXi bot codebase: custom plugins, web frontends, tests

- 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>
This commit is contained in:
2026-09-01 13:13:40 +08:00
co-authored by Claude
parent 1783c60afa
commit b61d09f09f
3201 changed files with 160436 additions and 171 deletions
@@ -0,0 +1,109 @@
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