mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-30 01:06:11 +00:00
* fix: enforce Telegram 100-command limit with warning (#5787) Telegram's setMyCommands API rejects requests with more than 100 commands. When skills + custom + plugin commands exceed the limit, truncate to 100 and warn the user instead of silently failing on every startup. * fix: enforce Telegram menu cap + keep hidden commands callable (#15844) (thanks @battman21) --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Agents/Heartbeat: stop auto-creating `HEARTBEAT.md` during workspace bootstrap so missing files continue to run heartbeat as documented. (#11766) Thanks @shadril238.
|
||||
- Telegram: cap bot menu registration to Telegram's 100-command limit with an overflow warning while keeping typed hidden commands available. (#15844) Thanks @battman21.
|
||||
- CLI: lazily load outbound provider dependencies and remove forced success-path exits so commands terminate naturally without killing intentional long-running foreground actions. (#12906) Thanks @DrCrinkle.
|
||||
- Auto-reply/Heartbeat: strip sentence-ending `HEARTBEAT_OK` tokens even when followed by up to 4 punctuation characters, while preserving surrounding sentence punctuation. (#15847) Thanks @Spacefish.
|
||||
- Clawdock: avoid Zsh readonly variable collisions in helper scripts. (#15501) Thanks @nkelner.
|
||||
|
||||
@@ -23,6 +23,67 @@ vi.mock("../pairing/pairing-store.js", () => ({
|
||||
}));
|
||||
|
||||
describe("registerTelegramNativeCommands (plugin auth)", () => {
|
||||
it("caps menu registration at 100 while leaving hidden plugin handlers available", () => {
|
||||
const specs = Array.from({ length: 101 }, (_, i) => ({
|
||||
name: `cmd_${i}`,
|
||||
description: `Command ${i}`,
|
||||
}));
|
||||
getPluginCommandSpecs.mockReturnValue(specs);
|
||||
matchPluginCommand.mockReset();
|
||||
executePluginCommand.mockReset();
|
||||
deliverReplies.mockReset();
|
||||
|
||||
const handlers: Record<string, (ctx: unknown) => Promise<void>> = {};
|
||||
const setMyCommands = vi.fn().mockResolvedValue(undefined);
|
||||
const log = vi.fn();
|
||||
const bot = {
|
||||
api: {
|
||||
setMyCommands,
|
||||
sendMessage: vi.fn(),
|
||||
},
|
||||
command: (name: string, handler: (ctx: unknown) => Promise<void>) => {
|
||||
handlers[name] = handler;
|
||||
},
|
||||
} as const;
|
||||
|
||||
registerTelegramNativeCommands({
|
||||
bot: bot as unknown as Parameters<typeof registerTelegramNativeCommands>[0]["bot"],
|
||||
cfg: {} as OpenClawConfig,
|
||||
runtime: { log } as RuntimeEnv,
|
||||
accountId: "default",
|
||||
telegramCfg: {} as TelegramAccountConfig,
|
||||
allowFrom: [],
|
||||
groupAllowFrom: [],
|
||||
replyToMode: "off",
|
||||
textLimit: 4000,
|
||||
useAccessGroups: false,
|
||||
nativeEnabled: false,
|
||||
nativeSkillsEnabled: false,
|
||||
nativeDisabledExplicit: false,
|
||||
resolveGroupPolicy: () =>
|
||||
({
|
||||
allowlistEnabled: false,
|
||||
allowed: true,
|
||||
}) as ChannelGroupPolicy,
|
||||
resolveTelegramGroupConfig: () => ({
|
||||
groupConfig: undefined,
|
||||
topicConfig: undefined,
|
||||
}),
|
||||
shouldSkipUpdate: () => false,
|
||||
opts: { token: "token" },
|
||||
});
|
||||
|
||||
const registered = setMyCommands.mock.calls[0]?.[0] as Array<{
|
||||
command: string;
|
||||
description: string;
|
||||
}>;
|
||||
expect(registered).toHaveLength(100);
|
||||
expect(registered[0]).toEqual({ command: "cmd_0", description: "Command 0" });
|
||||
expect(registered[99]).toEqual({ command: "cmd_99", description: "Command 99" });
|
||||
expect(log).toHaveBeenCalledWith(expect.stringContaining("registering first 100"));
|
||||
expect(Object.keys(handlers)).toHaveLength(101);
|
||||
});
|
||||
|
||||
it("allows requireAuth:false plugin command even when sender is unauthorized", async () => {
|
||||
const command = {
|
||||
name: "plugin",
|
||||
|
||||
@@ -112,7 +112,7 @@ describe("registerTelegramNativeCommands", () => {
|
||||
expect(registeredCommands).toHaveLength(100);
|
||||
expect(registeredCommands).toEqual(customCommands.slice(0, 100));
|
||||
expect(runtimeLog).toHaveBeenCalledWith(
|
||||
"telegram: truncating 120 commands to 100 (Telegram Bot API limit)",
|
||||
"Telegram limits bots to 100 commands. 120 configured; registering first 100. Use channels.telegram.commands.native: false to disable, or reduce skill/custom commands.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -366,25 +366,26 @@ export const registerTelegramNativeCommands = ({
|
||||
...pluginCommands,
|
||||
...customCommands,
|
||||
];
|
||||
// Telegram Bot API limits commands to 100 per scope.
|
||||
// Truncate with a warning rather than failing with BOT_COMMANDS_TOO_MUCH.
|
||||
const TELEGRAM_MAX_COMMANDS = 100;
|
||||
if (allCommandsFull.length > TELEGRAM_MAX_COMMANDS) {
|
||||
runtime.log?.(
|
||||
`telegram: truncating ${allCommandsFull.length} commands to ${TELEGRAM_MAX_COMMANDS} (Telegram Bot API limit)`,
|
||||
`Telegram limits bots to ${TELEGRAM_MAX_COMMANDS} commands. ` +
|
||||
`${allCommandsFull.length} configured; registering first ${TELEGRAM_MAX_COMMANDS}. ` +
|
||||
`Use channels.telegram.commands.native: false to disable, or reduce skill/custom commands.`,
|
||||
);
|
||||
}
|
||||
const allCommands = allCommandsFull.slice(0, TELEGRAM_MAX_COMMANDS);
|
||||
// Telegram only limits the setMyCommands payload (menu entries).
|
||||
const commandsToRegister = allCommandsFull.slice(0, TELEGRAM_MAX_COMMANDS);
|
||||
|
||||
// Clear stale commands before registering new ones to prevent
|
||||
// leftover commands from deleted skills persisting across restarts (#5717).
|
||||
// Chain delete → set so a late-resolving delete cannot wipe newly registered commands.
|
||||
const registerCommands = () => {
|
||||
if (allCommands.length > 0) {
|
||||
if (commandsToRegister.length > 0) {
|
||||
withTelegramApiErrorLogging({
|
||||
operation: "setMyCommands",
|
||||
runtime,
|
||||
fn: () => bot.api.setMyCommands(allCommands),
|
||||
fn: () => bot.api.setMyCommands(commandsToRegister),
|
||||
}).catch(() => {});
|
||||
}
|
||||
};
|
||||
@@ -401,7 +402,7 @@ export const registerTelegramNativeCommands = ({
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
if (allCommands.length > 0) {
|
||||
if (commandsToRegister.length > 0) {
|
||||
if (typeof (bot as unknown as { command?: unknown }).command !== "function") {
|
||||
logVerbose("telegram: bot.command unavailable; skipping native handlers");
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user