@llblab/pi-telegram 0.9.9 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +19 -2
- package/BACKLOG.md +0 -2
- package/CHANGELOG.md +24 -1
- package/README.md +8 -3
- package/docs/README.md +1 -1
- package/docs/callback-namespaces.md +1 -1
- package/docs/extension-sections.md +304 -163
- package/index.ts +25 -12
- package/lib/commands.ts +0 -36
- package/lib/extension-sections.ts +627 -0
- package/lib/menu-model.ts +1 -1
- package/lib/menu-settings.ts +68 -33
- package/lib/menu-status.ts +18 -0
- package/lib/menu.ts +130 -1
- package/lib/replies.ts +1 -2
- package/lib/routing.ts +63 -16
- package/package.json +2 -2
package/lib/menu-settings.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Owns hidden settings-menu rendering, settings callbacks, and persisted toggle wiring
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
getTelegramExtensionSettingsRows,
|
|
9
|
+
type TelegramSectionRegistry,
|
|
10
|
+
} from "./extension-sections.ts";
|
|
7
11
|
import type { TelegramInlineKeyboardMarkup } from "./keyboard.ts";
|
|
8
12
|
import type { TelegramModelMenuState } from "./menu-model.ts";
|
|
9
13
|
import type { MenuModel } from "./model.ts";
|
|
@@ -39,6 +43,7 @@ export interface TelegramSettingsMenuCallbackDeps extends TelegramSettingsMutati
|
|
|
39
43
|
callbackQueryId: string,
|
|
40
44
|
text?: string,
|
|
41
45
|
) => Promise<void>;
|
|
46
|
+
sectionRegistry?: TelegramSectionRegistry;
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
export interface TelegramSettingsMenuRuntime<TContext> {
|
|
@@ -116,28 +121,41 @@ export function buildProactivePushSettingsText(): string {
|
|
|
116
121
|
|
|
117
122
|
export function buildTelegramSettingsMenuReplyMarkup(
|
|
118
123
|
proactivePushEnabled: boolean,
|
|
124
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
119
125
|
): TelegramSettingsMenuReplyMarkup {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
const rows: Array<Array<{ text: string; callback_data: string }>> = [
|
|
127
|
+
[{ text: "⬆️ Main menu", callback_data: "menu:back" }],
|
|
128
|
+
];
|
|
129
|
+
// Extension settings rows before built-in controls
|
|
130
|
+
if (sectionRegistry) {
|
|
131
|
+
const settingsRows = getTelegramExtensionSettingsRows(sectionRegistry);
|
|
132
|
+
for (const row of settingsRows) {
|
|
133
|
+
rows.push([{ text: row.label, callback_data: row.callback_data }]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
rows.push([
|
|
137
|
+
{
|
|
138
|
+
text: `${proactivePushEnabled ? "🟢" : "⚫️"} Proactive push`,
|
|
139
|
+
callback_data: "settings:open:proactive",
|
|
140
|
+
},
|
|
141
|
+
]);
|
|
142
|
+
return { inline_keyboard: rows };
|
|
131
143
|
}
|
|
132
144
|
|
|
133
145
|
export async function openTelegramSettingsMenu<
|
|
134
146
|
TModel extends MenuModel = MenuModel,
|
|
135
|
-
>(
|
|
147
|
+
>(
|
|
148
|
+
deps: TelegramSettingsMenuOpenDeps<TModel>,
|
|
149
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
150
|
+
): Promise<void> {
|
|
136
151
|
const state = await deps.getModelMenuState();
|
|
137
152
|
const messageId = await deps.sendSettingsMenu(
|
|
138
153
|
state,
|
|
139
154
|
buildTelegramSettingsMenuText(),
|
|
140
|
-
buildTelegramSettingsMenuReplyMarkup(
|
|
155
|
+
buildTelegramSettingsMenuReplyMarkup(
|
|
156
|
+
deps.isProactivePushEnabled(),
|
|
157
|
+
sectionRegistry,
|
|
158
|
+
),
|
|
141
159
|
);
|
|
142
160
|
if (messageId === undefined) return;
|
|
143
161
|
state.messageId = messageId;
|
|
@@ -167,10 +185,14 @@ export function buildProactivePushSettingsReplyMarkup(
|
|
|
167
185
|
|
|
168
186
|
export async function updateTelegramSettingsMenuMessage(
|
|
169
187
|
deps: TelegramSettingsMenuMessageUpdateDeps,
|
|
188
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
170
189
|
): Promise<void> {
|
|
171
190
|
await deps.updateSettingsMessage(
|
|
172
191
|
buildTelegramSettingsMenuText(),
|
|
173
|
-
buildTelegramSettingsMenuReplyMarkup(
|
|
192
|
+
buildTelegramSettingsMenuReplyMarkup(
|
|
193
|
+
deps.isProactivePushEnabled(),
|
|
194
|
+
sectionRegistry,
|
|
195
|
+
),
|
|
174
196
|
);
|
|
175
197
|
}
|
|
176
198
|
|
|
@@ -190,7 +212,7 @@ export async function handleTelegramSettingsMenuCallbackAction(
|
|
|
190
212
|
): Promise<boolean> {
|
|
191
213
|
if (!data?.startsWith("settings:")) return false;
|
|
192
214
|
if (data === "settings:list") {
|
|
193
|
-
await updateTelegramSettingsMenuMessage(deps);
|
|
215
|
+
await updateTelegramSettingsMenuMessage(deps, deps.sectionRegistry);
|
|
194
216
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
195
217
|
return true;
|
|
196
218
|
}
|
|
@@ -221,28 +243,40 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
221
243
|
TModel extends MenuModel = MenuModel,
|
|
222
244
|
>(
|
|
223
245
|
deps: TelegramSettingsMenuRuntimeDeps<TContext, TModel>,
|
|
246
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
224
247
|
): TelegramSettingsMenuRuntime<TContext> {
|
|
225
248
|
return {
|
|
226
249
|
openSettingsMenu: (chatId, _replyToMessageId, ctx) =>
|
|
227
|
-
openTelegramSettingsMenu(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
250
|
+
openTelegramSettingsMenu(
|
|
251
|
+
{
|
|
252
|
+
getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
|
|
253
|
+
isProactivePushEnabled: deps.isProactivePushEnabled,
|
|
254
|
+
sendSettingsMenu: (state, text, replyMarkup) =>
|
|
255
|
+
deps.sendInteractiveMessage(
|
|
256
|
+
state.chatId,
|
|
257
|
+
text,
|
|
258
|
+
"html",
|
|
259
|
+
replyMarkup,
|
|
260
|
+
),
|
|
261
|
+
storeModelMenuState: deps.storeModelMenuState,
|
|
262
|
+
},
|
|
263
|
+
sectionRegistry,
|
|
264
|
+
),
|
|
234
265
|
updateSettingsMenuMessage: (state) =>
|
|
235
|
-
updateTelegramSettingsMenuMessage(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
266
|
+
updateTelegramSettingsMenuMessage(
|
|
267
|
+
{
|
|
268
|
+
isProactivePushEnabled: deps.isProactivePushEnabled,
|
|
269
|
+
updateSettingsMessage: (text, replyMarkup) =>
|
|
270
|
+
deps.editInteractiveMessage(
|
|
271
|
+
state.chatId,
|
|
272
|
+
state.messageId,
|
|
273
|
+
text,
|
|
274
|
+
"html",
|
|
275
|
+
replyMarkup,
|
|
276
|
+
),
|
|
277
|
+
},
|
|
278
|
+
sectionRegistry,
|
|
279
|
+
),
|
|
246
280
|
handleCallbackQuery: async (query) => {
|
|
247
281
|
if (!query.data?.startsWith("settings:")) return false;
|
|
248
282
|
const state = deps.getStoredModelMenuState(query.message?.message_id);
|
|
@@ -265,6 +299,7 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
265
299
|
replyMarkup,
|
|
266
300
|
),
|
|
267
301
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
302
|
+
sectionRegistry,
|
|
268
303
|
});
|
|
269
304
|
},
|
|
270
305
|
};
|
package/lib/menu-status.ts
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { formatTelegramCommandEmojiPrefix } from "./commands.ts";
|
|
8
|
+
import {
|
|
9
|
+
getTelegramSectionMainMenuRows,
|
|
10
|
+
type TelegramSectionRegistry,
|
|
11
|
+
} from "./extension-sections.ts";
|
|
8
12
|
import {
|
|
9
13
|
formatStatusButtonLabel,
|
|
10
14
|
type TelegramMenuMessageRuntimeDeps,
|
|
@@ -143,6 +147,7 @@ export function buildStatusReplyMarkup(
|
|
|
143
147
|
activeModel: MenuModel | undefined,
|
|
144
148
|
currentThinkingLevel: ThinkingLevel,
|
|
145
149
|
queueItemCount = 0,
|
|
150
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
146
151
|
): TelegramReplyMarkup {
|
|
147
152
|
const rows: Array<Array<{ text: string; callback_data: string }>> = [];
|
|
148
153
|
rows.push([
|
|
@@ -171,6 +176,13 @@ export function buildStatusReplyMarkup(
|
|
|
171
176
|
callback_data: "menu:queue",
|
|
172
177
|
},
|
|
173
178
|
]);
|
|
179
|
+
// Extension section rows before Settings
|
|
180
|
+
if (sectionRegistry) {
|
|
181
|
+
const sectionRows = getTelegramSectionMainMenuRows(sectionRegistry);
|
|
182
|
+
for (const row of sectionRows) {
|
|
183
|
+
rows.push([{ text: row.text, callback_data: row.callback_data }]);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
174
186
|
rows.push([
|
|
175
187
|
{
|
|
176
188
|
text: "⚙️ Settings",
|
|
@@ -185,6 +197,7 @@ export function buildTelegramStatusMenuRenderPayload(
|
|
|
185
197
|
activeModel: MenuModel | undefined,
|
|
186
198
|
currentThinkingLevel: ThinkingLevel,
|
|
187
199
|
queueItemCount = 0,
|
|
200
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
188
201
|
): TelegramMenuRenderPayload {
|
|
189
202
|
return {
|
|
190
203
|
nextMode: "status",
|
|
@@ -194,6 +207,7 @@ export function buildTelegramStatusMenuRenderPayload(
|
|
|
194
207
|
activeModel,
|
|
195
208
|
currentThinkingLevel,
|
|
196
209
|
queueItemCount,
|
|
210
|
+
sectionRegistry,
|
|
197
211
|
),
|
|
198
212
|
};
|
|
199
213
|
}
|
|
@@ -205,6 +219,7 @@ export async function updateTelegramStatusMessage(
|
|
|
205
219
|
currentThinkingLevel: ThinkingLevel,
|
|
206
220
|
deps: TelegramMenuMessageRuntimeDeps,
|
|
207
221
|
queueItemCount = 0,
|
|
222
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
208
223
|
): Promise<void> {
|
|
209
224
|
await editTelegramMenuMessage(
|
|
210
225
|
state,
|
|
@@ -213,6 +228,7 @@ export async function updateTelegramStatusMessage(
|
|
|
213
228
|
activeModel,
|
|
214
229
|
currentThinkingLevel,
|
|
215
230
|
queueItemCount,
|
|
231
|
+
sectionRegistry,
|
|
216
232
|
),
|
|
217
233
|
deps,
|
|
218
234
|
);
|
|
@@ -225,6 +241,7 @@ export function sendTelegramStatusMessage(
|
|
|
225
241
|
currentThinkingLevel: ThinkingLevel,
|
|
226
242
|
deps: TelegramMenuMessageRuntimeDeps,
|
|
227
243
|
queueItemCount = 0,
|
|
244
|
+
sectionRegistry?: TelegramSectionRegistry,
|
|
228
245
|
): Promise<number | undefined> {
|
|
229
246
|
return sendTelegramMenuMessage(
|
|
230
247
|
state,
|
|
@@ -233,6 +250,7 @@ export function sendTelegramStatusMessage(
|
|
|
233
250
|
activeModel,
|
|
234
251
|
currentThinkingLevel,
|
|
235
252
|
queueItemCount,
|
|
253
|
+
sectionRegistry,
|
|
236
254
|
),
|
|
237
255
|
deps,
|
|
238
256
|
);
|
package/lib/menu.ts
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
* Owns app-menu/status state, inline UI text, and callback composition while model/thinking/queue menu details live in dedicated domains
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
handleTelegramSectionCallback,
|
|
9
|
+
handleTelegramSectionOpen,
|
|
10
|
+
handleTelegramSectionSettingsOpen,
|
|
11
|
+
parseTelegramSectionCallback,
|
|
12
|
+
type TelegramSectionRegistry,
|
|
13
|
+
} from "./extension-sections.ts";
|
|
7
14
|
import {
|
|
8
15
|
createTelegramModelMenuStateBuilder,
|
|
9
16
|
handleTelegramModelMenuCallbackAction,
|
|
@@ -14,6 +21,7 @@ import {
|
|
|
14
21
|
type TelegramModelMenuState,
|
|
15
22
|
type TelegramModelMenuStateBuilderContext,
|
|
16
23
|
type TelegramModelMenuStateBuilderDeps,
|
|
24
|
+
type TelegramReplyMarkup,
|
|
17
25
|
} from "./menu-model.ts";
|
|
18
26
|
import {
|
|
19
27
|
handleTelegramStatusMenuCallbackAction,
|
|
@@ -39,9 +47,9 @@ export {
|
|
|
39
47
|
buildModelPageMenuReplyMarkup,
|
|
40
48
|
buildTelegramModelCallbackPlan,
|
|
41
49
|
buildTelegramModelMenuRenderPayload,
|
|
42
|
-
buildTelegramModelPageMenuRenderPayload,
|
|
43
50
|
buildTelegramModelMenuState,
|
|
44
51
|
buildTelegramModelMenuStateRuntime,
|
|
52
|
+
buildTelegramModelPageMenuRenderPayload,
|
|
45
53
|
createTelegramModelMenuRuntime,
|
|
46
54
|
createTelegramModelMenuStateBuilder,
|
|
47
55
|
formatScopedModelButtonText,
|
|
@@ -215,6 +223,21 @@ export interface TelegramMenuCallbackRuntimeDeps<
|
|
|
215
223
|
selection: ScopedTelegramModel<TModel>,
|
|
216
224
|
ctx: TContext,
|
|
217
225
|
) => Promise<boolean> | boolean;
|
|
226
|
+
sectionRegistry?: TelegramSectionRegistry;
|
|
227
|
+
editInteractiveMessage?: (
|
|
228
|
+
chatId: number,
|
|
229
|
+
messageId: number,
|
|
230
|
+
text: string,
|
|
231
|
+
mode: "html" | "plain",
|
|
232
|
+
replyMarkup: TelegramReplyMarkup,
|
|
233
|
+
) => Promise<void>;
|
|
234
|
+
sendInteractiveMessage?: (
|
|
235
|
+
chatId: number,
|
|
236
|
+
text: string,
|
|
237
|
+
mode: "html" | "plain",
|
|
238
|
+
replyMarkup: TelegramReplyMarkup,
|
|
239
|
+
) => Promise<number | undefined>;
|
|
240
|
+
enqueueSectionPrompt?: (prompt: string, ctx: TContext) => Promise<void>;
|
|
218
241
|
}
|
|
219
242
|
|
|
220
243
|
export interface TelegramMenuActionRuntimeDeps<
|
|
@@ -237,6 +260,7 @@ export interface TelegramMenuActionRuntimeDeps<
|
|
|
237
260
|
replyToMessageId: number,
|
|
238
261
|
text: string,
|
|
239
262
|
) => Promise<unknown>;
|
|
263
|
+
sectionRegistry?: TelegramSectionRegistry;
|
|
240
264
|
}
|
|
241
265
|
|
|
242
266
|
export interface TelegramMenuActionRuntime<
|
|
@@ -432,6 +456,21 @@ export interface TelegramMenuCallbackRuntimeAdapterDeps<
|
|
|
432
456
|
selection: ScopedTelegramModel<TModel>,
|
|
433
457
|
ctx: TContext,
|
|
434
458
|
) => Promise<boolean> | boolean;
|
|
459
|
+
sectionRegistry?: TelegramSectionRegistry;
|
|
460
|
+
editInteractiveMessage?: (
|
|
461
|
+
chatId: number,
|
|
462
|
+
messageId: number,
|
|
463
|
+
text: string,
|
|
464
|
+
mode: "html" | "plain",
|
|
465
|
+
replyMarkup: TelegramReplyMarkup,
|
|
466
|
+
) => Promise<void>;
|
|
467
|
+
sendInteractiveMessage?: (
|
|
468
|
+
chatId: number,
|
|
469
|
+
text: string,
|
|
470
|
+
mode: "html" | "plain",
|
|
471
|
+
replyMarkup: TelegramReplyMarkup,
|
|
472
|
+
) => Promise<number | undefined>;
|
|
473
|
+
enqueueSectionPrompt?: (prompt: string, ctx: TContext) => Promise<void>;
|
|
435
474
|
}
|
|
436
475
|
|
|
437
476
|
export function createTelegramMenuCallbackHandler<
|
|
@@ -471,6 +510,10 @@ export function createTelegramMenuCallbackHandlerForContext<
|
|
|
471
510
|
setCurrentModel: deps.setCurrentModel,
|
|
472
511
|
stagePendingModelSwitch: deps.stagePendingModelSwitch,
|
|
473
512
|
restartInterruptedTelegramTurn: deps.restartInterruptedTelegramTurn,
|
|
513
|
+
sectionRegistry: deps.sectionRegistry,
|
|
514
|
+
editInteractiveMessage: deps.editInteractiveMessage,
|
|
515
|
+
sendInteractiveMessage: deps.sendInteractiveMessage,
|
|
516
|
+
enqueueSectionPrompt: deps.enqueueSectionPrompt,
|
|
474
517
|
});
|
|
475
518
|
}
|
|
476
519
|
|
|
@@ -493,6 +536,89 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
493
536
|
await deps.answerCallbackQuery(query.id);
|
|
494
537
|
return;
|
|
495
538
|
}
|
|
539
|
+
// Section callbacks: dispatch before built-in menu handling
|
|
540
|
+
if (deps.sectionRegistry && query.data?.startsWith("section:")) {
|
|
541
|
+
const parsed = parseTelegramSectionCallback(query.data);
|
|
542
|
+
if (parsed) {
|
|
543
|
+
const chatId = (query as { message?: { chat?: { id?: number } } }).message
|
|
544
|
+
?.chat?.id;
|
|
545
|
+
const messageId = (query as { message?: { message_id?: number } }).message
|
|
546
|
+
?.message_id;
|
|
547
|
+
if (typeof chatId === "number" && typeof messageId === "number") {
|
|
548
|
+
const { token, action, payload } = parsed;
|
|
549
|
+
if (action === "open") {
|
|
550
|
+
const state = deps.getStoredModelMenuState(messageId);
|
|
551
|
+
if (!state) {
|
|
552
|
+
await deps.answerCallbackQuery(
|
|
553
|
+
query.id,
|
|
554
|
+
"Interactive message expired.",
|
|
555
|
+
);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const handled = await handleTelegramSectionOpen(
|
|
559
|
+
deps.sectionRegistry,
|
|
560
|
+
token,
|
|
561
|
+
chatId,
|
|
562
|
+
messageId,
|
|
563
|
+
query.id,
|
|
564
|
+
{
|
|
565
|
+
answerCallbackQuery: deps.answerCallbackQuery,
|
|
566
|
+
editInteractiveMessage:
|
|
567
|
+
deps.editInteractiveMessage ?? (async () => {}),
|
|
568
|
+
sendInteractiveMessage:
|
|
569
|
+
deps.sendInteractiveMessage ?? (async () => undefined),
|
|
570
|
+
enqueuePrompt: deps.enqueueSectionPrompt
|
|
571
|
+
? (prompt: string) => deps.enqueueSectionPrompt!(prompt, ctx)
|
|
572
|
+
: async () => {},
|
|
573
|
+
},
|
|
574
|
+
);
|
|
575
|
+
if (handled) return;
|
|
576
|
+
} else if (action === "settings") {
|
|
577
|
+
if (typeof chatId === "number" && typeof messageId === "number") {
|
|
578
|
+
const handled = await handleTelegramSectionSettingsOpen(
|
|
579
|
+
deps.sectionRegistry,
|
|
580
|
+
token,
|
|
581
|
+
chatId,
|
|
582
|
+
messageId,
|
|
583
|
+
query.id,
|
|
584
|
+
{
|
|
585
|
+
answerCallbackQuery: deps.answerCallbackQuery,
|
|
586
|
+
editInteractiveMessage:
|
|
587
|
+
deps.editInteractiveMessage ?? (async () => {}),
|
|
588
|
+
sendInteractiveMessage:
|
|
589
|
+
deps.sendInteractiveMessage ?? (async () => undefined),
|
|
590
|
+
enqueuePrompt: deps.enqueueSectionPrompt
|
|
591
|
+
? (prompt: string) => deps.enqueueSectionPrompt!(prompt, ctx)
|
|
592
|
+
: async () => {},
|
|
593
|
+
},
|
|
594
|
+
);
|
|
595
|
+
if (handled) return;
|
|
596
|
+
}
|
|
597
|
+
} else {
|
|
598
|
+
const handled = await handleTelegramSectionCallback(
|
|
599
|
+
deps.sectionRegistry,
|
|
600
|
+
token,
|
|
601
|
+
action,
|
|
602
|
+
payload,
|
|
603
|
+
chatId,
|
|
604
|
+
messageId,
|
|
605
|
+
query.id,
|
|
606
|
+
{
|
|
607
|
+
answerCallbackQuery: deps.answerCallbackQuery,
|
|
608
|
+
editInteractiveMessage:
|
|
609
|
+
deps.editInteractiveMessage ?? (async () => {}),
|
|
610
|
+
sendInteractiveMessage:
|
|
611
|
+
deps.sendInteractiveMessage ?? (async () => undefined),
|
|
612
|
+
enqueuePrompt: deps.enqueueSectionPrompt
|
|
613
|
+
? (prompt: string) => deps.enqueueSectionPrompt!(prompt, ctx)
|
|
614
|
+
: async () => {},
|
|
615
|
+
},
|
|
616
|
+
);
|
|
617
|
+
if (handled) return;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
496
622
|
await handleStoredTelegramMenuCallback(query, {
|
|
497
623
|
getStoredModelMenuState: deps.getStoredModelMenuState,
|
|
498
624
|
handleStatusAction: async (state) =>
|
|
@@ -601,6 +727,7 @@ export function createTelegramMenuActionRuntimeWithStateBuilder<
|
|
|
601
727
|
sendTextReply: deps.sendTextReply,
|
|
602
728
|
editInteractiveMessage: deps.editInteractiveMessage,
|
|
603
729
|
sendInteractiveMessage: deps.sendInteractiveMessage,
|
|
730
|
+
sectionRegistry: deps.sectionRegistry,
|
|
604
731
|
});
|
|
605
732
|
}
|
|
606
733
|
|
|
@@ -628,6 +755,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
628
755
|
deps.getThinkingLevel(),
|
|
629
756
|
deps,
|
|
630
757
|
deps.getQueueItemCount?.() ?? 0,
|
|
758
|
+
deps.sectionRegistry,
|
|
631
759
|
),
|
|
632
760
|
sendStatusMessage: (chatId, replyToMessageId, ctx) =>
|
|
633
761
|
openTelegramStatusMenu({
|
|
@@ -658,6 +786,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
658
786
|
thinkingLevel,
|
|
659
787
|
deps,
|
|
660
788
|
queueItemCount,
|
|
789
|
+
deps.sectionRegistry,
|
|
661
790
|
),
|
|
662
791
|
storeModelMenuState: deps.storeModelMenuState,
|
|
663
792
|
}),
|
package/lib/replies.ts
CHANGED
|
@@ -430,8 +430,7 @@ export function dedupSendMarkdownReply<TReplyMarkup = unknown>(
|
|
|
430
430
|
/**
|
|
431
431
|
* Guest reply sender: renders Markdown → HTML, sends via answerGuestQuery.
|
|
432
432
|
* Keeps guest rendering inside the replies domain so the orchestration layer
|
|
433
|
-
* (index.ts) does not import from rendering.ts directly.
|
|
434
|
-
*/
|
|
433
|
+
* (index.ts) does not import from rendering.ts directly. */
|
|
435
434
|
export function createGuestMarkdownReplySender(deps: {
|
|
436
435
|
renderTelegramMessage: (
|
|
437
436
|
text: string,
|
package/lib/routing.ts
CHANGED
|
@@ -4,21 +4,22 @@
|
|
|
4
4
|
* Wires authorized updates into menus, commands, media grouping, and prompt queueing
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import * as OutboundHandlers from "./outbound-handlers.ts";
|
|
8
|
-
import * as Commands from "./commands.ts";
|
|
9
7
|
import { readFile } from "node:fs/promises";
|
|
8
|
+
import * as Commands from "./commands.ts";
|
|
10
9
|
import type { TelegramConfigStore } from "./config.ts";
|
|
10
|
+
import type { TelegramSectionRegistry } from "./extension-sections.ts";
|
|
11
11
|
import type { TelegramInboundHandlerRuntime } from "./inbound-handlers.ts";
|
|
12
12
|
import * as Media from "./media.ts";
|
|
13
13
|
import * as Menu from "./menu.ts";
|
|
14
14
|
import * as Model from "./model.ts";
|
|
15
|
-
import * as
|
|
15
|
+
import * as OutboundHandlers from "./outbound-handlers.ts";
|
|
16
16
|
import * as PromptTemplates from "./prompt-templates.ts";
|
|
17
|
+
import * as Queue from "./queue.ts";
|
|
17
18
|
import type { TelegramBridgeRuntime } from "./runtime.ts";
|
|
18
19
|
import * as TextGroups from "./text-groups.ts";
|
|
19
20
|
import * as Turns from "./turns.ts";
|
|
20
|
-
import * as Updates from "./updates.ts";
|
|
21
21
|
import type { TelegramUser } from "./updates.ts";
|
|
22
|
+
import * as Updates from "./updates.ts";
|
|
22
23
|
|
|
23
24
|
export type TelegramRoutedMessage = Updates.TelegramUpdateMessage &
|
|
24
25
|
Media.TelegramMediaMessage &
|
|
@@ -82,6 +83,19 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
82
83
|
callbackQueryId: string,
|
|
83
84
|
text?: string,
|
|
84
85
|
) => Promise<void>;
|
|
86
|
+
editInteractiveMessage?: (
|
|
87
|
+
chatId: number,
|
|
88
|
+
messageId: number,
|
|
89
|
+
text: string,
|
|
90
|
+
mode: "html" | "plain",
|
|
91
|
+
replyMarkup: Menu.TelegramReplyMarkup,
|
|
92
|
+
) => Promise<void>;
|
|
93
|
+
sendInteractiveMessage?: (
|
|
94
|
+
chatId: number,
|
|
95
|
+
text: string,
|
|
96
|
+
mode: "html" | "plain",
|
|
97
|
+
replyMarkup: Menu.TelegramReplyMarkup,
|
|
98
|
+
) => Promise<number | undefined>;
|
|
85
99
|
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
86
100
|
sendTextReply: (
|
|
87
101
|
chatId: number,
|
|
@@ -112,12 +126,14 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
112
126
|
error: unknown,
|
|
113
127
|
details?: Record<string, unknown>,
|
|
114
128
|
) => void;
|
|
129
|
+
sectionRegistry?: TelegramSectionRegistry;
|
|
115
130
|
}
|
|
116
131
|
|
|
117
132
|
const TELEGRAM_OWNED_CALLBACK_PREFIXES = [
|
|
118
133
|
"menu:",
|
|
119
134
|
"model:",
|
|
120
135
|
"queue:",
|
|
136
|
+
"section:",
|
|
121
137
|
"settings:",
|
|
122
138
|
"status:",
|
|
123
139
|
"tgbtn:",
|
|
@@ -174,6 +190,35 @@ export function createTelegramInboundRouteRuntime<
|
|
|
174
190
|
stagePendingModelSwitch: deps.modelSwitchController.stagePendingSwitch,
|
|
175
191
|
restartInterruptedTelegramTurn:
|
|
176
192
|
deps.modelSwitchController.restartInterruptedTurn,
|
|
193
|
+
sectionRegistry: deps.sectionRegistry,
|
|
194
|
+
editInteractiveMessage: deps.editInteractiveMessage,
|
|
195
|
+
sendInteractiveMessage: deps.sendInteractiveMessage,
|
|
196
|
+
enqueueSectionPrompt: async (prompt: string, ctx: TContext) => {
|
|
197
|
+
const chatId = deps.configStore.getAllowedUserId();
|
|
198
|
+
if (typeof chatId !== "number") return;
|
|
199
|
+
const order = deps.bridgeRuntime.queue.allocateItemOrder();
|
|
200
|
+
const turn: Queue.PendingTelegramTurn = {
|
|
201
|
+
kind: "prompt",
|
|
202
|
+
chatId,
|
|
203
|
+
replyToMessageId: 0,
|
|
204
|
+
sourceMessageIds: [],
|
|
205
|
+
queueOrder: order,
|
|
206
|
+
queueLane: "default",
|
|
207
|
+
laneOrder: order,
|
|
208
|
+
queuedAttachments: [],
|
|
209
|
+
content: [
|
|
210
|
+
{
|
|
211
|
+
type: "text",
|
|
212
|
+
text: `[telegram] ${prompt}`,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
historyText: Turns.truncateTelegramQueueSummary(prompt),
|
|
216
|
+
statusSummary: Turns.truncateTelegramQueueSummary(prompt),
|
|
217
|
+
};
|
|
218
|
+
deps.queueMutationRuntime.append(turn, ctx);
|
|
219
|
+
deps.updateStatus(ctx);
|
|
220
|
+
deps.dispatchNextQueuedTelegramTurn(ctx);
|
|
221
|
+
},
|
|
177
222
|
});
|
|
178
223
|
const callbackHandler = async (
|
|
179
224
|
query: TCallbackQuery,
|
|
@@ -376,9 +421,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
376
421
|
// Build telegram prefix with guest context
|
|
377
422
|
const fromRaw = gm.from as Record<string, unknown> | undefined;
|
|
378
423
|
const fromName =
|
|
379
|
-
(fromRaw?.username as string) ||
|
|
380
|
-
(fromRaw?.first_name as string) ||
|
|
381
|
-
"";
|
|
424
|
+
(fromRaw?.username as string) || (fromRaw?.first_name as string) || "";
|
|
382
425
|
const chatRaw = gm.chat as Record<string, unknown>;
|
|
383
426
|
const chatTitle = chatRaw?.title as string | undefined;
|
|
384
427
|
const chatType = chatRaw?.type as string;
|
|
@@ -390,20 +433,24 @@ export function createTelegramInboundRouteRuntime<
|
|
|
390
433
|
const telegramPrefix = `[${prefixParts.join("|")}]`;
|
|
391
434
|
// Extract reply context
|
|
392
435
|
const replyMsg = gm.reply_to_message as Record<string, unknown> | undefined;
|
|
393
|
-
const replyText =
|
|
394
|
-
replyMsg
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
436
|
+
const replyText = replyMsg
|
|
437
|
+
? ((replyMsg.text as string) || (replyMsg.caption as string) || "").trim()
|
|
438
|
+
: "";
|
|
439
|
+
const replyFrom = replyMsg
|
|
440
|
+
? ((replyMsg.from as Record<string, unknown> | undefined)?.username as
|
|
441
|
+
| string
|
|
442
|
+
| undefined)
|
|
443
|
+
: undefined;
|
|
401
444
|
// Download files, run inbound handlers
|
|
402
445
|
const guestMsg = guestMessage as unknown as Media.TelegramMediaMessage;
|
|
403
446
|
const files = await Media.downloadTelegramMessageFiles([guestMsg], {
|
|
404
447
|
downloadFile: deps.downloadFile,
|
|
405
448
|
});
|
|
406
|
-
const processed = await deps.inboundHandlerRuntime.process(
|
|
449
|
+
const processed = await deps.inboundHandlerRuntime.process(
|
|
450
|
+
files,
|
|
451
|
+
text,
|
|
452
|
+
ctx,
|
|
453
|
+
);
|
|
407
454
|
let rawText = processed.rawText || text;
|
|
408
455
|
// Append reply context after handler processing
|
|
409
456
|
if (replyText) {
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-telegram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
-
"description": "Telegram
|
|
8
|
+
"description": "Telegram runtime adapter for π",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"pi-package",
|