feat(command): 黑白名单开关、自动保存与管理界面重构

- 新增 list_enabled 开关控制是否启用名单过滤
- 表单变更后 800ms 自动保存,去掉手动保存按钮
- Header 显示"未保存"指示器,保存中 toast 提示
- 内容区限制最大宽度 900px,优化宽屏显示
- 侧边栏增加圆角选中态,运行状态带脉冲动画
- 白名单模式灰掉黑名单输入,关闭名单时显示遮罩
- 命令测试结果增加成功/失败颜色反馈
- 回调格式改用等宽字体代码块
This commit is contained in:
2026-05-03 21:56:48 +08:00
parent f82363f45f
commit 58e53c8aec
3 changed files with 470 additions and 376 deletions

View File

@@ -43,6 +43,7 @@ class CommandConfig:
length_min: int = 2
length_max: int = 4
scope: str = "all" # all / group / private
list_enabled: bool = False # 是否启用黑白名单过滤
list_mode: str = "allow" # allow / deny
allowed_groups: frozenset[str] = frozenset()
denied_groups: frozenset[str] = frozenset()
@@ -62,6 +63,7 @@ _ENV_DEFAULTS: dict = {
"length_min": int(os.environ.get("COMMAND_LENGTH_MIN", "2")),
"length_max": int(os.environ.get("COMMAND_LENGTH_MAX", "4")),
"scope": os.environ.get("COMMAND_SCOPE", "all"),
"list_enabled": os.environ.get("COMMAND_LIST_ENABLED", "false").lower() in ("true", "1", "yes"),
"list_mode": os.environ.get("COMMAND_LIST_MODE", "allow"),
"allowed_groups": os.environ.get("COMMAND_ALLOWED_GROUPS", ""),
"denied_groups": os.environ.get("COMMAND_DENIED_GROUPS", ""),
@@ -87,6 +89,7 @@ def _yaml_defaults() -> dict:
"length_min": 2,
"length_max": 4,
"scope": "all",
"list_enabled": False,
"list_mode": "allow",
"allowed_groups": [],
"denied_groups": [],
@@ -127,6 +130,7 @@ def ensure_settings_yaml() -> None:
"length_max": ("COMMAND_LENGTH_MAX", int),
"scope": ("COMMAND_SCOPE", str),
"list_mode": ("COMMAND_LIST_MODE", str),
"list_enabled": ("COMMAND_LIST_ENABLED", "bool"),
"allowed_groups": ("COMMAND_ALLOWED_GROUPS", "csv_list"),
"denied_groups": ("COMMAND_DENIED_GROUPS", "csv_list"),
"allowed_users": ("COMMAND_ALLOWED_USERS", "csv_list"),
@@ -158,6 +162,14 @@ def _apply_yaml_to_command(data: dict) -> None:
command.length_max = int(cmd_data.get("length_max", _ENV_DEFAULTS["length_max"]))
command.scope = str(cmd_data.get("scope", _ENV_DEFAULTS["scope"]))
command.list_mode = str(cmd_data.get("list_mode", _ENV_DEFAULTS["list_mode"]))
# list_enabled: YAML bool → Python bool
list_enabled_val = cmd_data.get("list_enabled", _ENV_DEFAULTS["list_enabled"])
if isinstance(list_enabled_val, str):
command.list_enabled = list_enabled_val.lower() in ("true", "1", "yes")
else:
command.list_enabled = bool(list_enabled_val)
command.callback_url = str(cmd_data.get("callback_url", _ENV_DEFAULTS["callback_url"]))
command.callback_timeout = int(cmd_data.get("callback_timeout", _ENV_DEFAULTS["callback_timeout"]))
@@ -210,6 +222,7 @@ def get_settings_flat() -> dict[str, str]:
"command_length_max": str(command.length_max),
"command_scope": command.scope,
"command_list_mode": command.list_mode,
"command_list_enabled": str(command.list_enabled).lower(),
"command_allowed_groups": ",".join(sorted(command.allowed_groups)),
"command_denied_groups": ",".join(sorted(command.denied_groups)),
"command_allowed_users": ",".join(sorted(command.allowed_users)),
@@ -227,6 +240,7 @@ _API_KEY_MAP: dict[str, tuple[str, ...]] = {
"command_length_max": ("length_max", int),
"command_scope": ("scope", str),
"command_list_mode": ("list_mode", str),
"command_list_enabled": ("list_enabled", "bool"),
"command_at_sender": ("at_sender", "bool"),
"command_callback_url": ("callback_url", str),
"command_callback_timeout": ("callback_timeout", int),

File diff suppressed because it is too large Load Diff

View File

@@ -51,11 +51,12 @@ class WebHookPlugin(NcatBotPlugin):
"已配置" if os.environ.get("WEBHOOK_API_KEY") else "自动生成",
)
self.logger.info(
"命令监听: 前缀=%s 长度=%d~%d 范围=%s 名单=%s 回调=%s",
"命令监听: 前缀=%s 长度=%d~%d 范围=%s 名单=%s(%s) 回调=%s",
command.prefix,
command.length_min,
command.length_max,
command.scope,
"" if command.list_enabled else "",
command.list_mode,
command.callback_url or "未配置",
)
@@ -122,20 +123,21 @@ class WebHookPlugin(NcatBotPlugin):
continue
# 黑白名单过滤
if command.list_mode == "allow":
# 白名单模式:在名单内才放行
if command.allowed_groups and is_group:
if event.data.group_id not in command.allowed_groups:
if command.list_enabled:
if command.list_mode == "allow":
# 白名单模式:在名单内才放行
if command.allowed_groups and is_group:
if event.data.group_id not in command.allowed_groups:
continue
if command.allowed_users and event.data.user_id not in command.allowed_users:
continue
if command.allowed_users and event.data.user_id not in command.allowed_users:
continue
elif command.list_mode == "deny":
# 黑名单模式:在名单内则拒绝
if command.denied_groups and is_group:
if event.data.group_id in command.denied_groups:
elif command.list_mode == "deny":
# 黑名单模式:在名单内则拒绝
if command.denied_groups and is_group:
if event.data.group_id in command.denied_groups:
continue
if command.denied_users and event.data.user_id in command.denied_users:
continue
if command.denied_users and event.data.user_id in command.denied_users:
continue
# 构建回调数据
data = {