feat(telegram): add customFirst option for command menu ordering

Adds a new `commands.customFirst` config option for Telegram that allows
custom commands to appear before native commands in the bot's menu.

When `channels.telegram.commands.customFirst: true`, the Telegram command
menu shows user-defined custom commands first, followed by native commands.
Default behavior (false) keeps native commands first for backwards compat.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
zorrohk 2026-01-29 16:30:34 +08:00
parent 6859e1e6a6
commit 84066dc0d5
5 changed files with 16 additions and 7 deletions

View File

@ -604,6 +604,8 @@ const FIELD_HELP: Record<string, string> = {
"channels.telegram.commands.native": 'Override native commands for Telegram (bool or "auto").', "channels.telegram.commands.native": 'Override native commands for Telegram (bool or "auto").',
"channels.telegram.commands.nativeSkills": "channels.telegram.commands.nativeSkills":
'Override native skill commands for Telegram (bool or "auto").', 'Override native skill commands for Telegram (bool or "auto").',
"channels.telegram.commands.customFirst":
"If true, custom commands appear before native commands in the Telegram menu (default: false).",
"channels.slack.commands.native": 'Override native commands for Slack (bool or "auto").', "channels.slack.commands.native": 'Override native commands for Slack (bool or "auto").',
"channels.slack.commands.nativeSkills": "channels.slack.commands.nativeSkills":
'Override native skill commands for Slack (bool or "auto").', 'Override native skill commands for Slack (bool or "auto").',

View File

@ -114,4 +114,6 @@ export type ProviderCommandsConfig = {
native?: NativeCommandsSetting; native?: NativeCommandsSetting;
/** Override native skill command registration for this provider (bool or "auto"). */ /** Override native skill command registration for this provider (bool or "auto"). */
nativeSkills?: NativeCommandsSetting; nativeSkills?: NativeCommandsSetting;
/** If true, custom commands appear before native commands in the menu. Default: false (native first). */
customFirst?: boolean;
}; };

View File

@ -497,6 +497,7 @@ export const ProviderCommandsSchema = z
.object({ .object({
native: NativeCommandsSettingSchema.optional(), native: NativeCommandsSettingSchema.optional(),
nativeSkills: NativeCommandsSettingSchema.optional(), nativeSkills: NativeCommandsSettingSchema.optional(),
customFirst: z.boolean().optional(),
}) })
.strict() .strict()
.optional(); .optional();

View File

@ -56,6 +56,7 @@ type RegisterTelegramNativeCommandsParams = {
nativeEnabled: boolean; nativeEnabled: boolean;
nativeSkillsEnabled: boolean; nativeSkillsEnabled: boolean;
nativeDisabledExplicit: boolean; nativeDisabledExplicit: boolean;
customFirst: boolean;
resolveGroupPolicy: (chatId: string | number) => ChannelGroupPolicy; resolveGroupPolicy: (chatId: string | number) => ChannelGroupPolicy;
resolveTelegramGroupConfig: ( resolveTelegramGroupConfig: (
chatId: string | number, chatId: string | number,
@ -79,6 +80,7 @@ export const registerTelegramNativeCommands = ({
nativeEnabled, nativeEnabled,
nativeSkillsEnabled, nativeSkillsEnabled,
nativeDisabledExplicit, nativeDisabledExplicit,
customFirst,
resolveGroupPolicy, resolveGroupPolicy,
resolveTelegramGroupConfig, resolveTelegramGroupConfig,
shouldSkipUpdate, shouldSkipUpdate,
@ -103,13 +105,13 @@ export const registerTelegramNativeCommands = ({
runtime.error?.(danger(issue.message)); runtime.error?.(danger(issue.message));
} }
const customCommands = customResolution.commands; const customCommands = customResolution.commands;
const allCommands: Array<{ command: string; description: string }> = [ const nativeCommandItems = nativeCommands.map((command) => ({
...nativeCommands.map((command) => ({ command: command.name,
command: command.name, description: command.description,
description: command.description, }));
})), const allCommands: Array<{ command: string; description: string }> = customFirst
...customCommands, ? [...customCommands, ...nativeCommandItems]
]; : [...nativeCommandItems, ...customCommands];
if (allCommands.length > 0) { if (allCommands.length > 0) {
bot.api.setMyCommands(allCommands).catch((err) => { bot.api.setMyCommands(allCommands).catch((err) => {

View File

@ -236,6 +236,7 @@ export function createTelegramBot(opts: TelegramBotOptions) {
providerSetting: telegramCfg.commands?.native, providerSetting: telegramCfg.commands?.native,
globalSetting: cfg.commands?.native, globalSetting: cfg.commands?.native,
}); });
const customFirst = telegramCfg.commands?.customFirst === true;
const useAccessGroups = cfg.commands?.useAccessGroups !== false; const useAccessGroups = cfg.commands?.useAccessGroups !== false;
const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions"; const ackReactionScope = cfg.messages?.ackReactionScope ?? "group-mentions";
const mediaMaxBytes = (opts.mediaMaxMb ?? telegramCfg.mediaMaxMb ?? 5) * 1024 * 1024; const mediaMaxBytes = (opts.mediaMaxMb ?? telegramCfg.mediaMaxMb ?? 5) * 1024 * 1024;
@ -341,6 +342,7 @@ export function createTelegramBot(opts: TelegramBotOptions) {
nativeEnabled, nativeEnabled,
nativeSkillsEnabled, nativeSkillsEnabled,
nativeDisabledExplicit, nativeDisabledExplicit,
customFirst,
resolveGroupPolicy, resolveGroupPolicy,
resolveTelegramGroupConfig, resolveTelegramGroupConfig,
shouldSkipUpdate, shouldSkipUpdate,