diff --git a/README.md b/README.md index f8b8cc7..df6f9f7 100644 --- a/README.md +++ b/README.md @@ -360,7 +360,7 @@ sudo systemctl start tsmusicbot - 网页端:设置 → 命令权限,填写允许的服务器组 ID(逗号分隔),保存即时生效。 - 或编辑 `config.json` 的 `adminGroups`(数字数组),例如 `"adminGroups": [6, 8]`。 -填入任意服务器组 ID 后,限制立即开启:只有属于这些组之一的用户才能运行受限命令,其他人会收到「⛔ 需要管理员权限」的提示。 +填入任意服务器组 ID 后,限制立即开启:只有属于这些组之一的用户才能运行受限命令,其他人会收到「⛔ 需要管理员权限(该命令仅限管理员服务器组)」的提示。 > 提示(fail-closed):当受限命令来自一个机器人当前看不到其服务器组的发送者(例如不在机器人所在频道的私聊),机器人会尝试查询其分组;若仍无法确定,则拒绝执行。 diff --git a/src/bot/commands.test.ts b/src/bot/commands.test.ts index 7f502f6..8a6fb41 100644 --- a/src/bot/commands.test.ts +++ b/src/bot/commands.test.ts @@ -1,6 +1,5 @@ import { describe, it, expect } from "vitest"; -import { parseCommand } from "./commands.js"; -import { canRunCommand, isAdminCommand } from "./commands.js"; +import { parseCommand, canRunCommand, isAdminCommand } from "./commands.js"; describe("Command Parser", () => { it("parses simple command", () => { diff --git a/src/data/config.test.ts b/src/data/config.test.ts index 692ab14..2f014e4 100644 --- a/src/data/config.test.ts +++ b/src/data/config.test.ts @@ -177,3 +177,29 @@ describe("guestMode config", () => { }); }); }); + +describe("adminGroups normalization", () => { + function loadAdminGroups(raw: unknown) { + const dir = mkdtempSync(join(tmpdir(), "tsmb-cfg-")); + const p = join(dir, "config.json"); + writeFileSync(p, JSON.stringify(raw)); + try { + return loadConfig(p).adminGroups; + } finally { + rmSync(dir, { recursive: true, force: true }); + } + } + + it("defaults to [] when absent", () => { + expect(loadAdminGroups({})).toEqual([]); + }); + it("keeps valid non-negative integers", () => { + expect(loadAdminGroups({ adminGroups: [6, 8] })).toEqual([6, 8]); + }); + it("filters out negatives, non-integers and non-numbers", () => { + expect(loadAdminGroups({ adminGroups: [6, -1, 2.5, "8", null] })).toEqual([6]); + }); + it("a non-array value falls back to the default [] (no crash)", () => { + expect(loadAdminGroups({ adminGroups: "6" })).toEqual([]); + }); +}); diff --git a/src/data/config.ts b/src/data/config.ts index 2c2291d..bd1b788 100755 --- a/src/data/config.ts +++ b/src/data/config.ts @@ -104,9 +104,20 @@ export function loadConfig(path: string): BotConfig { gm.permissions[f] = gm.permissions[f] === true; } + // Sanitize adminGroups on load too: the WebUI write path filters it, but a + // hand-edited / legacy / corrupt config.json reaches the command gate + // directly. Keep only non-negative integers; a non-array falls back to the + // default []. Mirrors the guestMode sanitization above. + const adminGroups = Array.isArray(partial.adminGroups) + ? partial.adminGroups.filter( + (g): g is number => typeof g === "number" && Number.isInteger(g) && g >= 0, + ) + : defaults.adminGroups; + return { ...defaults, ...partial, + adminGroups, guestMode: gm, }; } catch {