"""Ownership map for Kernel commands and reviewed local extensions.""" from __future__ import annotations from dataclasses import dataclass LOCAL_COMMAND_ALIASES = { "功能列表": "feature_list", "帮助": "feature_list", "help": "feature_list", "丢骰子": "dice", "骰子": "dice", "丢骰子挑战": "dice_challenge", "骰子挑战": "dice_challenge", "翻硬币": "coin", "随机数": "random_number", "抽签": "draw_lot", "今日运势": "daily_fortune", "猜数字": "guess_number", "猜": "guess_number", "石头剪刀布": "rps", "记住": "remember", "我的记忆": "my_memory", "忘记": "forget_memory", "人物记忆": "person_memory", "记忆帮助": "memory_help", "扫码排行": "scan_ranking", "扫码榜": "scan_ranking", "今日扫码": "scan_ranking", "复读榜": "repeat_ranking", "复读": "repeat_ranking", "查记录": "history_search", "历史搜索": "history_search", "状态": "local_status", "status": "server_status", "version": "jwebot_version", "debug": "telemetry_debug", "余额": "deepseek_balance", "steam": "steam_friends", "上下文": "context_summary", "context": "context_summary", "金币": "wallet", "wallet": "wallet", "抽奖": "prize_draw", "draw": "prize_draw", "彩票": "lottery", "买彩票": "lottery", "lottery": "lottery", "21点": "blackjack", "二十一点": "blackjack", "blackjack": "blackjack", "游戏记录": "game_history", "个人记录": "game_history", "gamehistory": "game_history", "游戏帮助": "game_rules", "games": "game_rules", "人物列表": "person_directory", "别名列表": "person_directory", "people": "person_directory", "人物": "person_info", "个人属性": "person_info", "person": "person_info", "自走棋": "cs_autochess", "cs自走棋": "cs_autochess", "autochess": "cs_autochess", "选": "cs_autochess", "刷新": "cs_autochess", "锁定": "cs_autochess", "战术": "cs_autochess", "升级教练": "cs_autochess", "上阵": "cs_autochess", "下阵": "cs_autochess", "出售": "cs_autochess", "阵容": "cs_autochess", "赛程": "cs_autochess", "结束回合": "cs_autochess", } @dataclass(frozen=True) class LocalExtensionDescriptor: extension_id: str author: str description: str help_entries: tuple[str, ...] command_kinds: tuple[str, ...] @property def directory(self) -> str: return self.author.casefold() @property def commands(self) -> tuple[str, ...]: owned = set(self.command_kinds) return tuple( f"/{alias}" for alias, kind in LOCAL_COMMAND_ALIASES.items() if kind in owned ) @dataclass(frozen=True) class KernelCommandDescriptor: description: str help_entries: tuple[str, ...] command_kinds: tuple[str, ...] OFFICIAL_AUTHOR = "official" JWEBOT_AUTHOR = "jwebot" DIRECT_KERNEL_COMMANDS = ( KernelCommandDescriptor( description="查看由插件元数据自动生成的功能列表", help_entries=("/功能列表(别名 /帮助、/help)",), command_kinds=("feature_list",), ), KernelCommandDescriptor( description="查看 DeepSeek 账户余额", help_entries=("/余额",), command_kinds=("deepseek_balance",), ), ) DIRECT_KERNEL_COMMAND_KINDS = frozenset( kind for descriptor in DIRECT_KERNEL_COMMANDS for kind in descriptor.command_kinds ) LOCAL_EXTENSIONS = ( LocalExtensionDescriptor( extension_id="context-summary", author=OFFICIAL_AUTHOR, description="提取当前群最近100、200或300条本地文字的上下文", help_entries=("/上下文 [100|200|300];/context [100|200|300]",), command_kinds=("context_summary",), ), LocalExtensionDescriptor( extension_id="single-player-games", author=JWEBOT_AUTHOR, description="虚拟金币账户、抽奖、群内多人彩票和单人21点", help_entries=( "/金币 /wallet;/抽奖 [金额] /draw;/游戏记录 /gamehistory", "/彩票 开始|报名|当前状态|开奖|取消;/lottery", "/21点 开始 [1~500](金额=本局下注,默认50,空格可省略)", "/21点 要牌|停牌|状态|结束;/blackjack hit|stand|status|end", "/游戏帮助;/games", ), command_kinds=( "wallet", "prize_draw", "lottery", "blackjack", "game_history", "game_rules", ), ), LocalExtensionDescriptor( extension_id="cs-major-autochess", author=JWEBOT_AUTHOR, description="CS自走棋", help_entries=("/自走棋开始|报名",), command_kinds=("cs_autochess",), ), LocalExtensionDescriptor( extension_id="person-directory", author=OFFICIAL_AUTHOR, description="查询当前群已配置人物、基本属性和别名", help_entries=("/人物列表 [页码] /people;/人物 [名称或别名] /person",), command_kinds=("person_directory", "person_info"), ), LocalExtensionDescriptor( extension_id="person-memory", author=OFFICIAL_AUTHOR, description="管理个人和同群人物记忆", help_entries=( "/记住 内容;/我的记忆;/忘记 关键词或全部;" "/人物记忆 [显示名]", ), command_kinds=( "remember", "my_memory", "forget_memory", "person_memory", "memory_help", ), ), LocalExtensionDescriptor( extension_id="server-status", author=OFFICIAL_AUTHOR, description="服务器状态", help_entries=("/状态;/status",), command_kinds=("local_status", "server_status"), ), LocalExtensionDescriptor( extension_id="jwebot-version", author=OFFICIAL_AUTHOR, description="JWeBot 版本", help_entries=("/version",), command_kinds=("jwebot_version",), ), LocalExtensionDescriptor( extension_id="debug-telemetry", author=OFFICIAL_AUTHOR, description="埋点 p50/p95", help_entries=("/debug",), command_kinds=("telemetry_debug",), ), LocalExtensionDescriptor( extension_id="dice", author=JWEBOT_AUTHOR, description="骰子和多人骰子挑战", help_entries=( "/丢骰子 [NdM]", "/丢骰子挑战 [5d6|5d20|状态|结算|战绩|结束]", ), command_kinds=("dice", "dice_challenge"), ), LocalExtensionDescriptor( extension_id="coin", author=JWEBOT_AUTHOR, description="抛一枚硬币", help_entries=("/翻硬币",), command_kinds=("coin",), ), LocalExtensionDescriptor( extension_id="random-number", author=JWEBOT_AUTHOR, description="在指定整数范围内取随机数", help_entries=("/随机数 最小值 最大值",), command_kinds=("random_number",), ), LocalExtensionDescriptor( extension_id="draw-lot", author=JWEBOT_AUTHOR, description="抽取一条本地签文", help_entries=("/抽签",), command_kinds=("draw_lot",), ), LocalExtensionDescriptor( extension_id="daily-fortune", author=JWEBOT_AUTHOR, description="生成当天稳定一致的运势", help_entries=("/今日运势",), command_kinds=("daily_fortune",), ), LocalExtensionDescriptor( extension_id="guess-number", author=JWEBOT_AUTHOR, description="群内持久化猜数字游戏", help_entries=( "/猜数字 开始 [上限];/猜 数字;/猜数字 [状态|结束]", ), command_kinds=("guess_number",), ), LocalExtensionDescriptor( extension_id="rock-paper-scissors", author=JWEBOT_AUTHOR, description="多人石头剪刀布游戏", help_entries=( "/石头剪刀布 [开始|加入|出 选择|状态|结算|结束]", ), command_kinds=("rps",), ), LocalExtensionDescriptor( extension_id="scan-ranking", author=JWEBOT_AUTHOR, description="查看北京时间今日扫码排行", help_entries=("/扫码排行",), command_kinds=("scan_ranking",), ), LocalExtensionDescriptor( extension_id="repeat-ranking", author=JWEBOT_AUTHOR, description="查看北京时间今日复读排行", help_entries=("/复读榜",), command_kinds=("repeat_ranking",), ), LocalExtensionDescriptor( extension_id="history-search", author=JWEBOT_AUTHOR, description="搜索本地群聊文字记录", help_entries=("/查记录 关键词",), command_kinds=("history_search",), ), LocalExtensionDescriptor( extension_id="steam-presence", author=JWEBOT_AUTHOR, description="Steam 状态与时长", help_entries=( "/steam;/steam时长 游戏[、玩家]", ), command_kinds=("steam_friends",), ), ) def extension_for_kind(kind: str) -> LocalExtensionDescriptor | None: return next( ( extension for extension in LOCAL_EXTENSIONS if kind in extension.command_kinds ), None, ) def render_feature_list() -> str: lines = ["功能列表(由内核和插件描述自动生成)"] for descriptor in DIRECT_KERNEL_COMMANDS: lines.append( f"[内核] {descriptor.description}:" + ";".join(descriptor.help_entries) ) for descriptor in LOCAL_EXTENSIONS: lines.append( f"{descriptor.description}:" + ";".join(descriptor.help_entries) ) lines.extend( ( "[DeepSeek] 普通 @JWeBot 消息走本地直连 DeepSeek", "[OpenClaw] @JWeBot #任务进入 OpenClaw", ) ) return "\n".join(lines) def _validate_inventory() -> None: extension_kinds = [ kind for extension in LOCAL_EXTENSIONS for kind in extension.command_kinds ] if len(extension_kinds) != len(set(extension_kinds)): raise RuntimeError("local extension command ownership overlaps") known_kinds = set(LOCAL_COMMAND_ALIASES.values()) if set(extension_kinds) | DIRECT_KERNEL_COMMAND_KINDS != known_kinds: raise RuntimeError("local command inventory is incomplete") if set(extension_kinds) & DIRECT_KERNEL_COMMAND_KINDS: raise RuntimeError("Kernel commands cannot be extension-owned") if any( not extension.description or not extension.help_entries for extension in LOCAL_EXTENSIONS ): raise RuntimeError("local extensions must provide help metadata") _validate_inventory()