Telegram: add inline button model selection for /models and /model commands

This commit is contained in:
Ermenegildo Fiorito
2026-02-03 19:03:00 +01:00
committed by Ayaan Zaidi
parent efb4a34be4
commit 16349b6e93
8 changed files with 757 additions and 86 deletions

View File

@@ -7,6 +7,7 @@ import {
resolveInboundDebounceMs,
} from "../auto-reply/inbound-debounce.js";
import { buildCommandsPaginationKeyboard } from "../auto-reply/reply/commands-info.js";
import { buildModelsProviderData } from "../auto-reply/reply/commands-models.js";
import { listSkillCommandsForAgents } from "../auto-reply/skill-commands.js";
import { buildCommandsMessagePaginated } from "../auto-reply/status.js";
import { resolveChannelConfigWrites } from "../channels/plugins/config-writes.js";
@@ -22,6 +23,14 @@ import { resolveMedia } from "./bot/delivery.js";
import { resolveTelegramForumThreadId } from "./bot/helpers.js";
import { migrateTelegramGroupConfig } from "./group-migration.js";
import { resolveTelegramInlineButtonsScope } from "./inline-buttons.js";
import {
buildModelsKeyboard,
buildProviderKeyboard,
calculateTotalPages,
getModelsPageSize,
parseModelCallbackData,
type ProviderInfo,
} from "./model-buttons.js";
import { buildInlineKeyboard } from "./send.js";
export const registerTelegramHandlers = ({
@@ -404,6 +413,107 @@ export const registerTelegramHandlers = ({
return;
}
// Model selection callback handler (mdl_prov, mdl_list_*, mdl_sel_*, mdl_back)
const modelCallback = parseModelCallbackData(data);
if (modelCallback) {
const modelData = await buildModelsProviderData(cfg);
const { byProvider, providers } = modelData;
const editMessageWithButtons = async (
text: string,
buttons: ReturnType<typeof buildProviderKeyboard>,
) => {
const keyboard = buildInlineKeyboard(buttons);
try {
await bot.api.editMessageText(
callbackMessage.chat.id,
callbackMessage.message_id,
text,
keyboard ? { reply_markup: keyboard } : undefined,
);
} catch (editErr) {
const errStr = String(editErr);
if (!errStr.includes("message is not modified")) {
throw editErr;
}
}
};
if (modelCallback.type === "providers" || modelCallback.type === "back") {
if (providers.length === 0) {
await editMessageWithButtons("No providers available.", []);
return;
}
const providerInfos: ProviderInfo[] = providers.map((p) => ({
id: p,
count: byProvider.get(p)?.size ?? 0,
}));
const buttons = buildProviderKeyboard(providerInfos);
await editMessageWithButtons("Select a provider:", buttons);
return;
}
if (modelCallback.type === "list") {
const { provider, page } = modelCallback;
const modelSet = byProvider.get(provider);
if (!modelSet || modelSet.size === 0) {
// Provider not found or no models - show providers list
const providerInfos: ProviderInfo[] = providers.map((p) => ({
id: p,
count: byProvider.get(p)?.size ?? 0,
}));
const buttons = buildProviderKeyboard(providerInfos);
await editMessageWithButtons(
`Unknown provider: ${provider}\n\nSelect a provider:`,
buttons,
);
return;
}
const models = [...modelSet].toSorted();
const pageSize = getModelsPageSize();
const totalPages = calculateTotalPages(models.length, pageSize);
const safePage = Math.max(1, Math.min(page, totalPages));
const buttons = buildModelsKeyboard({
provider,
models,
currentPage: safePage,
totalPages,
pageSize,
});
const text = `Models (${provider}) — ${models.length} available`;
await editMessageWithButtons(text, buttons);
return;
}
if (modelCallback.type === "select") {
const { provider, model } = modelCallback;
// Process model selection as a synthetic message with /model command
const syntheticMessage: TelegramMessage = {
...callbackMessage,
from: callback.from,
text: `/model ${provider}/${model}`,
caption: undefined,
caption_entities: undefined,
entities: undefined,
};
const getFile =
typeof ctx.getFile === "function" ? ctx.getFile.bind(ctx) : async () => ({});
await processMessage(
{ message: syntheticMessage, me: ctx.me, getFile },
[],
storeAllowFrom,
{
forceWasMentioned: true,
messageIdOverride: callback.id,
},
);
return;
}
return;
}
const syntheticMessage: TelegramMessage = {
...callbackMessage,
from: callback.from,