chore: fork @llblab/[email protected].0 as @evandrodevbr/oh-my-pi-telegram
Fork of upstream llblab/[email protected].0 (SHA afe09c5) repackaged as a drop-in extension for can1357/oh-my-pi (omp >= 16.5.2). Changes vs upstream: - Rename package: @llblab/pi-telegram -> @evandrodevbr/oh-my-pi-telegram - Bump version: 0.22.0 -> 0.22.0-evandro.1 - Update repository/homepage/bugs URLs to evandrodevbr - Add omp field in package.json (displayName + compatibleWith) - README: prepend 'Oh My Pi (omp) Compatibility' section with install commands, verified-components table, upstream lineage, and rationale - CHANGELOG: add '0.22.0-evandro.1' entry under Unreleased - docs/: rename @llblab/pi-telegram imports to @evandrodevbr/oh-my-pi-telegram - tests/public-api.test.ts: rename package import paths Gates re-run on the fork: - npm run typecheck -> tsc --noEmit (success) - npm test -> 1229 pass / 0 fail / 1 platform-only skip - npm run audit -> found 0 vulnerabilities - npm run pack:check -> evandrodevbr-oh-my-pi-telegram-0.22.0-evandro.1.tgz Verified: - omp install --link ./pi-telegram -> Linked OK - omp plugin list -> @evandrodevbr/[email protected].0-evandro.1 Upstream lineage: badlogic/pi-telegram -> llblab/pi-telegram (0.22.0) -> evandrodevbr/oh-my-pi-telegram (this fork)
This commit is contained in:
commit
d1d04001d0
155 files changed
+107848
No files matched your search
@@ -0,0 +1,274 @@
|
||||
/**
|
||||
* Telegram status menu UI helpers
|
||||
* Zones: telegram ui, status controls, menu composition
|
||||
* Owns status-menu payloads, status callback handling, and status-menu message rendering
|
||||
*/
|
||||
|
||||
import { formatTelegramCommandEmojiPrefix } from "./commands.ts";
|
||||
import {
|
||||
getTelegramSectionMainMenuRows,
|
||||
type TelegramSectionRegistry,
|
||||
} from "./sections.ts";
|
||||
import {
|
||||
formatStatusButtonLabel,
|
||||
type TelegramMenuMessageRuntimeDeps,
|
||||
type TelegramMenuRenderPayload,
|
||||
type TelegramModelMenuState,
|
||||
type TelegramReplyMarkup,
|
||||
} from "./menu-model.ts";
|
||||
import {
|
||||
getCanonicalModelId,
|
||||
type MenuModel,
|
||||
type ThinkingLevel,
|
||||
} from "./model.ts";
|
||||
|
||||
export interface TelegramStatusMenuCallbackDeps {
|
||||
updateModelMenuMessage: () => Promise<void>;
|
||||
updateThinkingMenuMessage: () => Promise<void>;
|
||||
updateSettingsMenuMessage?: () => Promise<void>;
|
||||
answerCallbackQuery: (
|
||||
callbackQueryId: string,
|
||||
text?: string,
|
||||
) => Promise<void>;
|
||||
isVoiceReplyActive?: () => boolean;
|
||||
}
|
||||
|
||||
export interface TelegramStatusMenuOpenDeps<
|
||||
TModel extends MenuModel = MenuModel,
|
||||
> {
|
||||
isIdle: () => boolean;
|
||||
sendBusyMessage: () => Promise<void>;
|
||||
getModelMenuState: () => Promise<TelegramModelMenuState<TModel>>;
|
||||
buildStatusHtml: () => string;
|
||||
getActiveModel: () => TModel | undefined;
|
||||
getThinkingLevel: () => ThinkingLevel;
|
||||
getQueueItemCount?: () => number;
|
||||
sendStatusMenu: (
|
||||
state: TelegramModelMenuState<TModel>,
|
||||
statusHtml: string,
|
||||
activeModel: TModel | undefined,
|
||||
thinkingLevel: ThinkingLevel,
|
||||
queueItemCount: number,
|
||||
) => Promise<number | undefined>;
|
||||
storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
|
||||
}
|
||||
|
||||
function isTelegramStatusMenuCallbackAction(
|
||||
data: string | undefined,
|
||||
action: "model" | "thinking" | "settings",
|
||||
): boolean {
|
||||
return data === `menu:${action}` || data === `status:${action}`;
|
||||
}
|
||||
|
||||
function applyTelegramMenuRenderPayload(
|
||||
state: TelegramModelMenuState,
|
||||
payload: TelegramMenuRenderPayload,
|
||||
): TelegramMenuRenderPayload {
|
||||
state.mode = payload.nextMode;
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function editTelegramMenuMessage(
|
||||
state: TelegramModelMenuState,
|
||||
payload: TelegramMenuRenderPayload,
|
||||
deps: TelegramMenuMessageRuntimeDeps,
|
||||
): Promise<void> {
|
||||
const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
|
||||
await deps.editInteractiveMessage(
|
||||
state.chatId,
|
||||
state.messageId,
|
||||
appliedPayload.text,
|
||||
appliedPayload.mode,
|
||||
appliedPayload.replyMarkup,
|
||||
);
|
||||
}
|
||||
|
||||
function sendTelegramMenuMessage(
|
||||
state: TelegramModelMenuState,
|
||||
payload: TelegramMenuRenderPayload,
|
||||
deps: TelegramMenuMessageRuntimeDeps,
|
||||
): Promise<number | undefined> {
|
||||
const appliedPayload = applyTelegramMenuRenderPayload(state, payload);
|
||||
return deps.sendInteractiveMessage(
|
||||
state.chatId,
|
||||
appliedPayload.text,
|
||||
appliedPayload.mode,
|
||||
appliedPayload.replyMarkup,
|
||||
state.threadId !== undefined
|
||||
? { target: { chatId: state.chatId, threadId: state.threadId } }
|
||||
: undefined,
|
||||
);
|
||||
}
|
||||
|
||||
export async function openTelegramStatusMenu<
|
||||
TModel extends MenuModel = MenuModel,
|
||||
>(deps: TelegramStatusMenuOpenDeps<TModel>): Promise<void> {
|
||||
const state = await deps.getModelMenuState();
|
||||
const messageId = await deps.sendStatusMenu(
|
||||
state,
|
||||
deps.buildStatusHtml(),
|
||||
deps.getActiveModel(),
|
||||
deps.getThinkingLevel(),
|
||||
deps.getQueueItemCount?.() ?? 0,
|
||||
);
|
||||
if (messageId === undefined) return;
|
||||
state.messageId = messageId;
|
||||
state.mode = "status";
|
||||
deps.storeModelMenuState(state);
|
||||
}
|
||||
|
||||
export async function handleTelegramStatusMenuCallbackAction(
|
||||
callbackQueryId: string,
|
||||
data: string | undefined,
|
||||
activeModel: MenuModel | undefined,
|
||||
deps: TelegramStatusMenuCallbackDeps,
|
||||
): Promise<boolean> {
|
||||
if (isTelegramStatusMenuCallbackAction(data, "model")) {
|
||||
await deps.updateModelMenuMessage();
|
||||
await deps.answerCallbackQuery(callbackQueryId);
|
||||
return true;
|
||||
}
|
||||
if (isTelegramStatusMenuCallbackAction(data, "settings")) {
|
||||
if (!deps.updateSettingsMenuMessage) return false;
|
||||
await deps.updateSettingsMenuMessage();
|
||||
await deps.answerCallbackQuery(callbackQueryId);
|
||||
return true;
|
||||
}
|
||||
if (!isTelegramStatusMenuCallbackAction(data, "thinking")) return false;
|
||||
if (deps.isVoiceReplyActive?.()) {
|
||||
await deps.answerCallbackQuery(
|
||||
callbackQueryId,
|
||||
"Thinking controls are disabled during voice replies.",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
if (!activeModel?.reasoning) {
|
||||
await deps.answerCallbackQuery(
|
||||
callbackQueryId,
|
||||
"This model has no reasoning controls.",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
await deps.updateThinkingMenuMessage();
|
||||
await deps.answerCallbackQuery(callbackQueryId);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function buildStatusReplyMarkup(
|
||||
activeModel: MenuModel | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
queueItemCount = 0,
|
||||
sectionRegistry?: TelegramSectionRegistry,
|
||||
isVoiceReplyActive?: boolean,
|
||||
): TelegramReplyMarkup {
|
||||
const rows: Array<Array<{ text: string; callback_data: string }>> = [];
|
||||
rows.push([
|
||||
{
|
||||
text: formatStatusButtonLabel(
|
||||
`${formatTelegramCommandEmojiPrefix("model")}Model`,
|
||||
activeModel ? getCanonicalModelId(activeModel) : "unknown",
|
||||
),
|
||||
callback_data: "menu:model",
|
||||
},
|
||||
]);
|
||||
if (activeModel?.reasoning && !isVoiceReplyActive) {
|
||||
rows.push([
|
||||
{
|
||||
text: formatStatusButtonLabel(
|
||||
`${formatTelegramCommandEmojiPrefix("thinking")}Thinking`,
|
||||
currentThinkingLevel,
|
||||
),
|
||||
callback_data: "menu:thinking",
|
||||
},
|
||||
]);
|
||||
}
|
||||
rows.push([
|
||||
{
|
||||
text: `${queueItemCount === 0 ? "⌛" : "⏳"} Queue: ${queueItemCount}`,
|
||||
callback_data: "menu:queue",
|
||||
},
|
||||
]);
|
||||
if (sectionRegistry) {
|
||||
const sectionRows = getTelegramSectionMainMenuRows(sectionRegistry);
|
||||
for (const row of sectionRows) {
|
||||
rows.push([row]);
|
||||
}
|
||||
}
|
||||
rows.push([
|
||||
{
|
||||
text: "⚙️ Settings",
|
||||
callback_data: "menu:settings",
|
||||
},
|
||||
]);
|
||||
return { inline_keyboard: rows };
|
||||
}
|
||||
|
||||
export function buildTelegramStatusMenuRenderPayload(
|
||||
statusText: string,
|
||||
activeModel: MenuModel | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
queueItemCount = 0,
|
||||
sectionRegistry?: TelegramSectionRegistry,
|
||||
isVoiceReplyActive?: boolean,
|
||||
): TelegramMenuRenderPayload {
|
||||
return {
|
||||
nextMode: "status",
|
||||
text: statusText,
|
||||
mode: "html",
|
||||
replyMarkup: buildStatusReplyMarkup(
|
||||
activeModel,
|
||||
currentThinkingLevel,
|
||||
queueItemCount,
|
||||
sectionRegistry,
|
||||
isVoiceReplyActive,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export async function updateTelegramStatusMessage(
|
||||
state: TelegramModelMenuState,
|
||||
statusText: string,
|
||||
activeModel: MenuModel | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
deps: TelegramMenuMessageRuntimeDeps,
|
||||
queueItemCount = 0,
|
||||
sectionRegistry?: TelegramSectionRegistry,
|
||||
isVoiceReplyActive?: boolean,
|
||||
): Promise<void> {
|
||||
await editTelegramMenuMessage(
|
||||
state,
|
||||
buildTelegramStatusMenuRenderPayload(
|
||||
statusText,
|
||||
activeModel,
|
||||
currentThinkingLevel,
|
||||
queueItemCount,
|
||||
sectionRegistry,
|
||||
isVoiceReplyActive,
|
||||
),
|
||||
deps,
|
||||
);
|
||||
}
|
||||
|
||||
export function sendTelegramStatusMessage(
|
||||
state: TelegramModelMenuState,
|
||||
statusText: string,
|
||||
activeModel: MenuModel | undefined,
|
||||
currentThinkingLevel: ThinkingLevel,
|
||||
deps: TelegramMenuMessageRuntimeDeps,
|
||||
queueItemCount = 0,
|
||||
sectionRegistry?: TelegramSectionRegistry,
|
||||
isVoiceReplyActive?: boolean,
|
||||
): Promise<number | undefined> {
|
||||
return sendTelegramMenuMessage(
|
||||
state,
|
||||
buildTelegramStatusMenuRenderPayload(
|
||||
statusText,
|
||||
activeModel,
|
||||
currentThinkingLevel,
|
||||
queueItemCount,
|
||||
sectionRegistry,
|
||||
isVoiceReplyActive,
|
||||
),
|
||||
deps,
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user