mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-01 20:42:50 +08:00
feat(commands): add canRunCommand gate helper + admin-set source of truth
This commit is contained in:
1 parent
0c7f7e128b
commit
f98ce47c52
2 files changed
+61
-7
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { parseCommand } from "./commands.js";
|
||||
import { canRunCommand, isAdminCommand } from "./commands.js";
|
||||
|
||||
describe("Command Parser", () => {
|
||||
it("parses simple command", () => {
|
||||
@@ -60,3 +61,36 @@ describe("Command Parser", () => {
|
||||
expect(result!.args).toBe("3");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isAdminCommand classification", () => {
|
||||
it("treats stop/clear/remove/move/vol/mode as admin", () => {
|
||||
for (const c of ["stop", "clear", "remove", "move", "vol", "mode"]) {
|
||||
expect(isAdminCommand(c)).toBe(true);
|
||||
}
|
||||
});
|
||||
it("treats follow and play as NOT admin", () => {
|
||||
expect(isAdminCommand("follow")).toBe(false);
|
||||
expect(isAdminCommand("play")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("canRunCommand", () => {
|
||||
it("allows any public command regardless of groups", () => {
|
||||
expect(canRunCommand("play", [], [6])).toBe(true);
|
||||
expect(canRunCommand("follow", [], [6])).toBe(true);
|
||||
});
|
||||
it("allows admin command when enforcement is off (empty adminGroups)", () => {
|
||||
expect(canRunCommand("stop", [], [])).toBe(true);
|
||||
});
|
||||
it("allows admin command when an invoker group matches (string vs number)", () => {
|
||||
expect(canRunCommand("stop", ["6"], [6])).toBe(true);
|
||||
expect(canRunCommand("stop", [6], [6])).toBe(true);
|
||||
expect(canRunCommand("vol", ["8", "6"], [6])).toBe(true);
|
||||
});
|
||||
it("denies admin command when no invoker group matches", () => {
|
||||
expect(canRunCommand("stop", ["8"], [6])).toBe(false);
|
||||
});
|
||||
it("denies admin command when invoker has no groups and enforcement is on", () => {
|
||||
expect(canRunCommand("clear", [], [6])).toBe(false);
|
||||
});
|
||||
});
|
||||
+27
-7
@@ -5,14 +5,13 @@ export interface ParsedCommand {
|
||||
flags: Set<string>;
|
||||
}
|
||||
|
||||
export const PUBLIC_COMMANDS = new Set([
|
||||
"play", "add", "queue", "list", "now", "lyrics", "vote", "help",
|
||||
"playlist", "album", "fm", "prev", "next", "skip", "pause", "resume",
|
||||
"artist",
|
||||
]);
|
||||
|
||||
/**
|
||||
* The fixed set of "admin" chat commands. This is the SINGLE source of truth
|
||||
* for which commands the permission gate restricts; reclassifying a command is
|
||||
* a one-line edit here. Everything not in this set is public.
|
||||
*/
|
||||
export const ADMIN_COMMANDS = new Set([
|
||||
"stop", "clear", "move", "vol", "mode", "follow", "remove",
|
||||
"stop", "clear", "remove", "move", "vol", "mode",
|
||||
]);
|
||||
|
||||
export function parseCommand(
|
||||
@@ -59,3 +58,24 @@ export function parseCommand(
|
||||
export function isAdminCommand(commandName: string): boolean {
|
||||
return ADMIN_COMMANDS.has(commandName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether a chat command may run, given the invoker's TS server groups
|
||||
* and the configured admin groups. Pure + synchronous so it is trivially unit
|
||||
* tested and reused by the async gate in BotInstance.
|
||||
*
|
||||
* Allowed iff: (1) it is a public command, OR (2) enforcement is off
|
||||
* (adminGroups empty), OR (3) some invoker group is in adminGroups.
|
||||
* invokerGroups (strings from TS) and adminGroups (numbers) are normalized to
|
||||
* strings before comparison so "6" matches 6.
|
||||
*/
|
||||
export function canRunCommand(
|
||||
commandName: string,
|
||||
invokerGroups: readonly (string | number)[],
|
||||
adminGroups: readonly number[],
|
||||
): boolean {
|
||||
if (!isAdminCommand(commandName)) return true;
|
||||
if (adminGroups.length === 0) return true;
|
||||
const admin = new Set(adminGroups.map((g) => String(g)));
|
||||
return invokerGroups.some((g) => admin.has(String(g)));
|
||||
}
|
||||
Reference in new issue
Block a user