fix: filter telegram native command names (#1558) (thanks @Glucksberg)
This commit is contained in:
parent
c2940adc80
commit
185ffca274
@ -551,6 +551,7 @@ Notes:
|
||||
- Commands are registered globally and work across all channels
|
||||
- Command names are case-insensitive (`/MyStatus` matches `/mystatus`)
|
||||
- Command names must start with a letter and contain only letters, numbers, hyphens, and underscores
|
||||
- Telegram native commands only allow `a-z0-9_` (max 32 chars). Use underscores (not hyphens) if you want a plugin command to appear in Telegram’s native command list.
|
||||
- Reserved command names (like `help`, `status`, `reset`, etc.) cannot be overridden by plugins
|
||||
- Duplicate command registration across plugins will fail with a diagnostic error
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
listChatCommandsForConfig,
|
||||
listNativeCommandSpecs,
|
||||
listNativeCommandSpecsForConfig,
|
||||
normalizeNativeCommandSpecsForSurface,
|
||||
normalizeCommandBody,
|
||||
parseCommandArgs,
|
||||
resolveCommandArgMenu,
|
||||
@ -45,6 +46,20 @@ describe("commands registry", () => {
|
||||
expect(specs.find((spec) => spec.name === "compact")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("normalizes telegram native command specs", () => {
|
||||
const specs = [
|
||||
{ name: "OK", description: "Ok", acceptsArgs: false },
|
||||
{ name: "bad-name", description: "Bad", acceptsArgs: false },
|
||||
{ name: "fine_name", description: "Fine", acceptsArgs: false },
|
||||
{ name: "ok", description: "Dup", acceptsArgs: false },
|
||||
];
|
||||
const normalized = normalizeNativeCommandSpecsForSurface({
|
||||
surface: "telegram",
|
||||
specs,
|
||||
});
|
||||
expect(normalized.map((spec) => spec.name)).toEqual(["ok", "fine_name"]);
|
||||
});
|
||||
|
||||
it("filters commands based on config flags", () => {
|
||||
const disabled = listChatCommandsForConfig({
|
||||
commands: { config: false, debug: false },
|
||||
|
||||
@ -4,6 +4,10 @@ import { getChatCommands, getNativeCommandSurfaces } from "./commands-registry.d
|
||||
import { getPluginCommandSpecs } from "../plugins/commands.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
|
||||
import { resolveConfiguredModelRef } from "../agents/model-selection.js";
|
||||
import {
|
||||
normalizeTelegramCommandName,
|
||||
TELEGRAM_COMMAND_NAME_PATTERN,
|
||||
} from "../config/telegram-custom-commands.js";
|
||||
import type {
|
||||
ChatCommandDefinition,
|
||||
CommandArgChoiceContext,
|
||||
@ -143,6 +147,37 @@ export function listNativeCommandSpecsForConfig(
|
||||
return extras.length > 0 ? [...base, ...extras] : base;
|
||||
}
|
||||
|
||||
function normalizeNativeCommandNameForSurface(name: string, surface: string): string | null {
|
||||
const trimmed = name.trim();
|
||||
if (!trimmed) return null;
|
||||
if (surface === "telegram") {
|
||||
const normalized = normalizeTelegramCommandName(trimmed);
|
||||
if (!normalized) return null;
|
||||
if (!TELEGRAM_COMMAND_NAME_PATTERN.test(normalized)) return null;
|
||||
return normalized;
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
export function normalizeNativeCommandSpecsForSurface(params: {
|
||||
surface: string;
|
||||
specs: NativeCommandSpec[];
|
||||
}): NativeCommandSpec[] {
|
||||
const surface = params.surface.toLowerCase();
|
||||
if (!surface) return params.specs;
|
||||
const normalized: NativeCommandSpec[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const spec of params.specs) {
|
||||
const normalizedName = normalizeNativeCommandNameForSurface(spec.name, surface);
|
||||
if (!normalizedName) continue;
|
||||
const key = normalizedName.toLowerCase();
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
normalized.push(normalizedName === spec.name ? spec : { ...spec, name: normalizedName });
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function findCommandByNativeName(name: string): ChatCommandDefinition | undefined {
|
||||
const normalized = name.trim().toLowerCase();
|
||||
return getChatCommands().find(
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
findCommandByNativeName,
|
||||
listNativeCommandSpecs,
|
||||
listNativeCommandSpecsForConfig,
|
||||
normalizeNativeCommandSpecsForSurface,
|
||||
parseCommandArgs,
|
||||
resolveCommandArgMenu,
|
||||
} from "../auto-reply/commands-registry.js";
|
||||
@ -84,13 +85,28 @@ export const registerTelegramNativeCommands = ({
|
||||
}: RegisterTelegramNativeCommandsParams) => {
|
||||
const skillCommands =
|
||||
nativeEnabled && nativeSkillsEnabled ? listSkillCommandsForAgents({ cfg }) : [];
|
||||
const nativeCommands = nativeEnabled
|
||||
const rawNativeCommands = nativeEnabled
|
||||
? listNativeCommandSpecsForConfig(cfg, { skillCommands })
|
||||
: [];
|
||||
const nativeCommands = normalizeNativeCommandSpecsForSurface({
|
||||
surface: "telegram",
|
||||
specs: rawNativeCommands,
|
||||
});
|
||||
const reservedCommands = new Set(
|
||||
listNativeCommandSpecs().map((command) => command.name.toLowerCase()),
|
||||
normalizeNativeCommandSpecsForSurface({
|
||||
surface: "telegram",
|
||||
specs: listNativeCommandSpecs(),
|
||||
}).map((command) => command.name.toLowerCase()),
|
||||
);
|
||||
for (const command of skillCommands) {
|
||||
const reservedSkillSpecs = normalizeNativeCommandSpecsForSurface({
|
||||
surface: "telegram",
|
||||
specs: skillCommands.map((command) => ({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
acceptsArgs: true,
|
||||
})),
|
||||
});
|
||||
for (const command of reservedSkillSpecs) {
|
||||
reservedCommands.add(command.name.toLowerCase());
|
||||
}
|
||||
const customResolution = resolveTelegramCustomCommands({
|
||||
|
||||
Loading…
Reference in New Issue
Block a user