refactor(bf-bot): 插件重构为 handlers/services/utils 分层,资源归入 res/

- 旧平铺模块(data/data_utils/database_op/image_builder*/get_bf6_data/
  gametools_bf6/bf6_data/img_utils)拆分为 handlers(绑定/查询)、
  services(fetchers/processing/query)、utils(渲染/参数)分层;
  新增 repository.py / config.py / paths.py
- 字体/图片/模板资源迁入 res/,新增 BF2042 卡面素材(bg/object_icon 等)
- browser_ext/:BF2042 扩展捕获桥(WS 直连兜底通道)源码入库
- 新增 hexi/web/src/plugins/bf_bot 前端管理页
- 新增 tests:test_bf_bot_helpers / repository / tracker_api

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
sansenhoshiandClaude Code committed 2026-10-13 17:53:06 +08:00
1 parent 614e1d7e00
commit a31aab00e8
752 files changed
+6241 -2506

No files matched your search

@@ -0,0 +1,68 @@
"""指令参数解析与展示表(纯函数、无副作用,可单测)。"""
from dataclasses import dataclass
# 游戏代号 -> 中文展示名(回复文案用)
BF_GAME_NAMES = {
"bf3": "战地3",
"bf4": "战地4",
"bf1": "战地1",
"bfv": "战地5",
"bf2042": "战地2042",
"bf6": "战地6",
}
# BF2042 取数失败原因 -> 给用户看的话(detail 只写日志不刷屏)
BF2042_ERROR_HINT = {
"not_found": "未找到该玩家,请检查 EAID 拼写是否正确",
"ext_offline": "查询服务未就绪(取数浏览器未连接),请联系管理员",
"timeout": "访问 tracker.gg 超时,请稍后再试",
"auth": "查询服务登录态异常,请联系管理员",
"error": "tracker.gg 取数失败,请稍后再试",
}
# 子命令关键词 -> (视图, 榜单类型)。不打关键词就是生涯卡
_BF2042_VIEWS = {
"武器": ("list", "weapon"),
"枪械": ("list", "weapon"),
"weapon": ("list", "weapon"),
"weapons": ("list", "weapon"),
"载具": ("list", "vehicle"),
"vehicle": ("list", "vehicle"),
"vehicles": ("list", "vehicle"),
"装备": ("list", "gadget"),
"道具": ("list", "gadget"),
"gadget": ("list", "gadget"),
"gadgets": ("list", "gadget"),
"模式": ("gamemode", ""),
"gamemode": ("gamemode", ""),
}
@dataclass(frozen=True)
class Bf2042Target:
"""BF2042 指令解析结果。
view: career(生涯卡)/ list(榜单卡)/ gamemode(模式明细)
kind: 榜单类型 weapon/vehicle/gadget,仅 view=list 时非空
player: 玩家名;空串 = 未指定,用绑定记录
"""
view: str = "career"
kind: str = ""
player: str = ""
def parse_bf2042_args(input_text: str) -> Bf2042Target:
"""拆出 BF2042 子命令关键词,返回目标。
首个词不是关键词时整串都当玩家名(生涯卡),所以玩家名里有空格也不受影响。
"""
text = (input_text or "").strip()
if not text:
return Bf2042Target()
head, _, rest = text.partition(" ")
view = _BF2042_VIEWS.get(head.lower())
if view is None:
return Bf2042Target(view="career", kind="", player=text)
return Bf2042Target(view=view[0], kind=view[1], player=rest.strip())