@llblab/pi-telegram 0.10.8 → 0.11.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 +11 -7
- package/BACKLOG.md +5 -0
- package/CHANGELOG.md +29 -1
- package/README.md +62 -23
- package/docs/README.md +3 -2
- package/docs/architecture.md +7 -6
- package/docs/extension-sections.md +6 -4
- package/docs/inbound-handlers.md +41 -1
- package/docs/outbound-handlers.md +32 -49
- package/docs/voice.md +210 -0
- package/index.ts +87 -1
- package/lib/api.ts +35 -2
- package/lib/config.ts +134 -0
- package/lib/extension-sections.ts +39 -20
- package/lib/external-handlers.ts +3 -4
- package/lib/inbound-handlers.ts +197 -0
- package/lib/media.ts +3 -0
- package/lib/menu-settings.ts +233 -9
- package/lib/menu-status.ts +17 -3
- package/lib/menu-thinking.ts +12 -1
- package/lib/menu.ts +10 -1
- package/lib/outbound-handlers.ts +719 -277
- package/lib/preview.ts +9 -0
- package/lib/prompts.ts +3 -1
- package/lib/queue.ts +84 -5
- package/lib/routing.ts +12 -1
- package/lib/time-injection.ts +78 -0
- package/lib/turns.ts +97 -20
- package/lib/voice.ts +295 -0
- package/package.json +1 -1
package/lib/menu-settings.ts
CHANGED
|
@@ -8,18 +8,27 @@ import {
|
|
|
8
8
|
getTelegramExtensionSettingsRows,
|
|
9
9
|
type TelegramSectionRegistry,
|
|
10
10
|
} from "./extension-sections.ts";
|
|
11
|
+
import type { TelegramTimeMode } from "./config.ts";
|
|
11
12
|
import type { TelegramInlineKeyboardMarkup } from "./keyboard.ts";
|
|
12
13
|
import type { TelegramModelMenuState } from "./menu-model.ts";
|
|
13
14
|
import type { MenuModel } from "./model.ts";
|
|
15
|
+
import type { TelegramVoiceReplyMode } from "./voice.ts";
|
|
14
16
|
|
|
15
17
|
export type TelegramSettingsMenuReplyMarkup = TelegramInlineKeyboardMarkup;
|
|
16
18
|
|
|
17
19
|
export interface TelegramSettingsStateDeps {
|
|
18
20
|
isProactivePushEnabled: () => boolean;
|
|
21
|
+
getTimeInjectionMode: () => TelegramTimeMode;
|
|
22
|
+
getVoiceReplyMode: () => TelegramVoiceReplyMode;
|
|
23
|
+
isVoiceReplyModeConfigured: () => boolean;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
export interface TelegramSettingsMutationDeps extends TelegramSettingsStateDeps {
|
|
22
27
|
setProactivePushEnabled: (enabled: boolean) => Promise<void>;
|
|
28
|
+
setVoiceReplyMode: (
|
|
29
|
+
mode: TelegramVoiceReplyMode | undefined,
|
|
30
|
+
) => Promise<void>;
|
|
31
|
+
setTimeInjectionMode: (mode: TelegramTimeMode) => Promise<void>;
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
export interface TelegramSettingsMenuOpenDeps<
|
|
@@ -105,7 +114,22 @@ export interface TelegramSettingsMenuRuntimeDeps<
|
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
export const SETTINGS_MENU_TITLE = "<b>⚙️ Settings:</b>";
|
|
108
|
-
export const PROACTIVE_PUSH_SETTINGS_TITLE = "<b
|
|
117
|
+
export const PROACTIVE_PUSH_SETTINGS_TITLE = "<b>📌 Proactive push:</b>";
|
|
118
|
+
export const TIME_INJECTION_MODE_SETTINGS_TITLE = "<b>🕒 Time injection mode:</b>";
|
|
119
|
+
export const VOICE_REPLY_MODE_SETTINGS_TITLE = "<b>👄 Voice reply mode:</b>";
|
|
120
|
+
|
|
121
|
+
type TelegramVoiceReplyModeSetting = TelegramVoiceReplyMode | "hidden";
|
|
122
|
+
|
|
123
|
+
function getVoiceReplyModeLabel(mode: TelegramVoiceReplyModeSetting): string {
|
|
124
|
+
return mode;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function getVoiceReplyModeSetting(
|
|
128
|
+
mode: TelegramVoiceReplyMode,
|
|
129
|
+
configured: boolean,
|
|
130
|
+
): TelegramVoiceReplyModeSetting {
|
|
131
|
+
return configured ? mode : "hidden";
|
|
132
|
+
}
|
|
109
133
|
|
|
110
134
|
export function buildTelegramSettingsMenuText(): string {
|
|
111
135
|
return SETTINGS_MENU_TITLE;
|
|
@@ -119,9 +143,37 @@ export function buildProactivePushSettingsText(): string {
|
|
|
119
143
|
].join("\n");
|
|
120
144
|
}
|
|
121
145
|
|
|
146
|
+
export function buildVoiceReplyModeSettingsText(): string {
|
|
147
|
+
return [
|
|
148
|
+
VOICE_REPLY_MODE_SETTINGS_TITLE,
|
|
149
|
+
"",
|
|
150
|
+
"Controls when pi-telegram converts assistant text replies into Telegram voice messages.",
|
|
151
|
+
"",
|
|
152
|
+
"<code>-</code> <code>hidden</code> (default): same behavior as 'manual', but no voice policy is added to prompt context.",
|
|
153
|
+
"<code>-</code> <code>manual</code>: agent decides; explicit 'telegram_voice' markup still works and reply mode is visible in prompt context.",
|
|
154
|
+
"<code>-</code> <code>mirror</code>: voice input prefers a voice reply; text input gracefully follows 'manual' behavior.",
|
|
155
|
+
"<code>-</code> <code>always</code>: every reply is converted to voice when delivery succeeds.",
|
|
156
|
+
].join("\n");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function buildTimeInjectionModeSettingsText(): string {
|
|
160
|
+
return [
|
|
161
|
+
TIME_INJECTION_MODE_SETTINGS_TITLE,
|
|
162
|
+
"",
|
|
163
|
+
"Controls whether Telegram-originated prompts include a compact wall-clock [time] line.",
|
|
164
|
+
"",
|
|
165
|
+
"<code>-</code> <code>off</code> (default): no time line is added.",
|
|
166
|
+
"<code>-</code> <code>always</code>: add time to every Telegram turn.",
|
|
167
|
+
"<code>-</code> <code>interval</code>: add time at most once per chat interval (default: 1 hour).",
|
|
168
|
+
].join("\n");
|
|
169
|
+
}
|
|
170
|
+
|
|
122
171
|
export function buildTelegramSettingsMenuReplyMarkup(
|
|
123
172
|
proactivePushEnabled: boolean,
|
|
173
|
+
voiceReplyMode: TelegramVoiceReplyMode,
|
|
174
|
+
timeInjectionMode: TelegramTimeMode,
|
|
124
175
|
sectionRegistry?: TelegramSectionRegistry,
|
|
176
|
+
voiceReplyModeConfigured = true,
|
|
125
177
|
): TelegramSettingsMenuReplyMarkup {
|
|
126
178
|
const rows: Array<Array<{ text: string; callback_data: string }>> = [
|
|
127
179
|
[{ text: "⬆️ Main menu", callback_data: "menu:back" }],
|
|
@@ -133,12 +185,28 @@ export function buildTelegramSettingsMenuReplyMarkup(
|
|
|
133
185
|
rows.push([{ text: row.label, callback_data: row.callback_data }]);
|
|
134
186
|
}
|
|
135
187
|
}
|
|
136
|
-
rows.push(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
188
|
+
rows.push(
|
|
189
|
+
[
|
|
190
|
+
{
|
|
191
|
+
text: `👄 Voice reply: ${getVoiceReplyModeLabel(
|
|
192
|
+
getVoiceReplyModeSetting(voiceReplyMode, voiceReplyModeConfigured),
|
|
193
|
+
)}`,
|
|
194
|
+
callback_data: "settings:open:voice-reply",
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
[
|
|
198
|
+
{
|
|
199
|
+
text: `🕒 Time injection: ${timeInjectionMode}`,
|
|
200
|
+
callback_data: "settings:open:time-injection",
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
[
|
|
204
|
+
{
|
|
205
|
+
text: `📌 Proactive push: ${proactivePushEnabled ? "on" : "off"}`,
|
|
206
|
+
callback_data: "settings:open:proactive",
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
);
|
|
142
210
|
return { inline_keyboard: rows };
|
|
143
211
|
}
|
|
144
212
|
|
|
@@ -154,7 +222,10 @@ export async function openTelegramSettingsMenu<
|
|
|
154
222
|
buildTelegramSettingsMenuText(),
|
|
155
223
|
buildTelegramSettingsMenuReplyMarkup(
|
|
156
224
|
deps.isProactivePushEnabled(),
|
|
225
|
+
deps.getVoiceReplyMode(),
|
|
226
|
+
deps.getTimeInjectionMode(),
|
|
157
227
|
sectionRegistry,
|
|
228
|
+
deps.isVoiceReplyModeConfigured(),
|
|
158
229
|
),
|
|
159
230
|
);
|
|
160
231
|
if (messageId === undefined) return;
|
|
@@ -171,11 +242,11 @@ export function buildProactivePushSettingsReplyMarkup(
|
|
|
171
242
|
[{ text: "⬆️ Back", callback_data: "settings:list" }],
|
|
172
243
|
[
|
|
173
244
|
{
|
|
174
|
-
text: proactivePushEnabled ? "🟢
|
|
245
|
+
text: proactivePushEnabled ? "🟢 on" : "⚫️ on",
|
|
175
246
|
callback_data: "settings:set:proactive:on",
|
|
176
247
|
},
|
|
177
248
|
{
|
|
178
|
-
text: proactivePushEnabled ? "⚫️
|
|
249
|
+
text: proactivePushEnabled ? "⚫️ off" : "🟡 off",
|
|
179
250
|
callback_data: "settings:set:proactive:off",
|
|
180
251
|
},
|
|
181
252
|
],
|
|
@@ -183,6 +254,47 @@ export function buildProactivePushSettingsReplyMarkup(
|
|
|
183
254
|
};
|
|
184
255
|
}
|
|
185
256
|
|
|
257
|
+
export function buildTimeInjectionModeSettingsReplyMarkup(
|
|
258
|
+
mode: TelegramTimeMode,
|
|
259
|
+
): TelegramSettingsMenuReplyMarkup {
|
|
260
|
+
const modes: TelegramTimeMode[] = ["off", "always", "interval"];
|
|
261
|
+
return {
|
|
262
|
+
inline_keyboard: [
|
|
263
|
+
[{ text: "⬆️ Back", callback_data: "settings:list" }],
|
|
264
|
+
...modes.map((value) => [
|
|
265
|
+
{
|
|
266
|
+
text: `${value === mode ? "🟢 " : ""}${value}`,
|
|
267
|
+
callback_data: `settings:set:time-injection:${value}`,
|
|
268
|
+
},
|
|
269
|
+
]),
|
|
270
|
+
],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function buildVoiceReplyModeSettingsReplyMarkup(
|
|
275
|
+
mode: TelegramVoiceReplyMode,
|
|
276
|
+
configured = true,
|
|
277
|
+
): TelegramSettingsMenuReplyMarkup {
|
|
278
|
+
const activeMode = getVoiceReplyModeSetting(mode, configured);
|
|
279
|
+
const modes: TelegramVoiceReplyModeSetting[] = [
|
|
280
|
+
"hidden",
|
|
281
|
+
"manual",
|
|
282
|
+
"mirror",
|
|
283
|
+
"always",
|
|
284
|
+
];
|
|
285
|
+
return {
|
|
286
|
+
inline_keyboard: [
|
|
287
|
+
[{ text: "⬆️ Back", callback_data: "settings:list" }],
|
|
288
|
+
...modes.map((value) => [
|
|
289
|
+
{
|
|
290
|
+
text: `${value === activeMode ? "🟢 " : ""}${getVoiceReplyModeLabel(value)}`,
|
|
291
|
+
callback_data: `settings:set:voice-reply:${value}`,
|
|
292
|
+
},
|
|
293
|
+
]),
|
|
294
|
+
],
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
186
298
|
export async function updateTelegramSettingsMenuMessage(
|
|
187
299
|
deps: TelegramSettingsMenuMessageUpdateDeps,
|
|
188
300
|
sectionRegistry?: TelegramSectionRegistry,
|
|
@@ -191,7 +303,10 @@ export async function updateTelegramSettingsMenuMessage(
|
|
|
191
303
|
buildTelegramSettingsMenuText(),
|
|
192
304
|
buildTelegramSettingsMenuReplyMarkup(
|
|
193
305
|
deps.isProactivePushEnabled(),
|
|
306
|
+
deps.getVoiceReplyMode(),
|
|
307
|
+
deps.getTimeInjectionMode(),
|
|
194
308
|
sectionRegistry,
|
|
309
|
+
deps.isVoiceReplyModeConfigured(),
|
|
195
310
|
),
|
|
196
311
|
);
|
|
197
312
|
}
|
|
@@ -205,6 +320,27 @@ export async function updateProactivePushSettingsMessage(
|
|
|
205
320
|
);
|
|
206
321
|
}
|
|
207
322
|
|
|
323
|
+
export async function updateTimeInjectionModeSettingsMessage(
|
|
324
|
+
deps: TelegramSettingsMenuCallbackDeps,
|
|
325
|
+
): Promise<void> {
|
|
326
|
+
await deps.updateSettingsMessage(
|
|
327
|
+
buildTimeInjectionModeSettingsText(),
|
|
328
|
+
buildTimeInjectionModeSettingsReplyMarkup(deps.getTimeInjectionMode()),
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export async function updateVoiceReplyModeSettingsMessage(
|
|
333
|
+
deps: TelegramSettingsMenuCallbackDeps,
|
|
334
|
+
): Promise<void> {
|
|
335
|
+
await deps.updateSettingsMessage(
|
|
336
|
+
buildVoiceReplyModeSettingsText(),
|
|
337
|
+
buildVoiceReplyModeSettingsReplyMarkup(
|
|
338
|
+
deps.getVoiceReplyMode(),
|
|
339
|
+
deps.isVoiceReplyModeConfigured(),
|
|
340
|
+
),
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
208
344
|
export async function handleTelegramSettingsMenuCallbackAction(
|
|
209
345
|
callbackQueryId: string,
|
|
210
346
|
data: string | undefined,
|
|
@@ -221,6 +357,47 @@ export async function handleTelegramSettingsMenuCallbackAction(
|
|
|
221
357
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
222
358
|
return true;
|
|
223
359
|
}
|
|
360
|
+
if (data === "settings:open:voice-reply") {
|
|
361
|
+
await updateVoiceReplyModeSettingsMessage(deps);
|
|
362
|
+
await deps.answerCallbackQuery(callbackQueryId);
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
if (data === "settings:open:time-injection" || data === "settings:open:time") {
|
|
366
|
+
await updateTimeInjectionModeSettingsMessage(deps);
|
|
367
|
+
await deps.answerCallbackQuery(callbackQueryId);
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
if (data.startsWith("settings:set:voice-reply:")) {
|
|
371
|
+
const mode = data.slice("settings:set:voice-reply:".length);
|
|
372
|
+
if (
|
|
373
|
+
mode === "hidden" ||
|
|
374
|
+
mode === "manual" ||
|
|
375
|
+
mode === "mirror" ||
|
|
376
|
+
mode === "always"
|
|
377
|
+
) {
|
|
378
|
+
await deps.setVoiceReplyMode(mode === "hidden" ? undefined : mode);
|
|
379
|
+
await updateVoiceReplyModeSettingsMessage(deps);
|
|
380
|
+
await deps.answerCallbackQuery(
|
|
381
|
+
callbackQueryId,
|
|
382
|
+
`Voice reply mode: ${mode}`,
|
|
383
|
+
);
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (
|
|
388
|
+
data.startsWith("settings:set:time-injection:") ||
|
|
389
|
+
data.startsWith("settings:set:time:")
|
|
390
|
+
) {
|
|
391
|
+
const mode = data.startsWith("settings:set:time-injection:")
|
|
392
|
+
? data.slice("settings:set:time-injection:".length)
|
|
393
|
+
: data.slice("settings:set:time:".length);
|
|
394
|
+
if (mode === "off" || mode === "always" || mode === "interval") {
|
|
395
|
+
await deps.setTimeInjectionMode(mode);
|
|
396
|
+
await updateTimeInjectionModeSettingsMessage(deps);
|
|
397
|
+
await deps.answerCallbackQuery(callbackQueryId, `Time injection: ${mode}`);
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
224
401
|
if (
|
|
225
402
|
data === "settings:set:proactive:on" ||
|
|
226
403
|
data === "settings:set:proactive:off"
|
|
@@ -251,6 +428,9 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
251
428
|
{
|
|
252
429
|
getModelMenuState: () => deps.getModelMenuState(chatId, ctx),
|
|
253
430
|
isProactivePushEnabled: deps.isProactivePushEnabled,
|
|
431
|
+
getVoiceReplyMode: deps.getVoiceReplyMode,
|
|
432
|
+
isVoiceReplyModeConfigured: deps.isVoiceReplyModeConfigured,
|
|
433
|
+
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
254
434
|
sendSettingsMenu: (state, text, replyMarkup) =>
|
|
255
435
|
deps.sendInteractiveMessage(
|
|
256
436
|
state.chatId,
|
|
@@ -266,6 +446,9 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
266
446
|
updateTelegramSettingsMenuMessage(
|
|
267
447
|
{
|
|
268
448
|
isProactivePushEnabled: deps.isProactivePushEnabled,
|
|
449
|
+
getVoiceReplyMode: deps.getVoiceReplyMode,
|
|
450
|
+
isVoiceReplyModeConfigured: deps.isVoiceReplyModeConfigured,
|
|
451
|
+
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
269
452
|
updateSettingsMessage: (text, replyMarkup) =>
|
|
270
453
|
deps.editInteractiveMessage(
|
|
271
454
|
state.chatId,
|
|
@@ -281,6 +464,42 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
281
464
|
if (!query.data?.startsWith("settings:")) return false;
|
|
282
465
|
const state = deps.getStoredModelMenuState(query.message?.message_id);
|
|
283
466
|
if (!state) {
|
|
467
|
+
const voiceMode = query.data.slice("settings:set:voice-reply:".length);
|
|
468
|
+
if (
|
|
469
|
+
query.data.startsWith("settings:set:voice-reply:") &&
|
|
470
|
+
(voiceMode === "hidden" ||
|
|
471
|
+
voiceMode === "manual" ||
|
|
472
|
+
voiceMode === "mirror" ||
|
|
473
|
+
voiceMode === "always")
|
|
474
|
+
) {
|
|
475
|
+
await deps.setVoiceReplyMode(
|
|
476
|
+
voiceMode === "hidden" ? undefined : voiceMode,
|
|
477
|
+
);
|
|
478
|
+
await deps.answerCallbackQuery(
|
|
479
|
+
query.id,
|
|
480
|
+
`Voice reply mode: ${voiceMode}`,
|
|
481
|
+
);
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
const hasTimeInjectionPrefix = query.data.startsWith(
|
|
485
|
+
"settings:set:time-injection:",
|
|
486
|
+
);
|
|
487
|
+
const timeMode = hasTimeInjectionPrefix
|
|
488
|
+
? query.data.slice("settings:set:time-injection:".length)
|
|
489
|
+
: query.data.slice("settings:set:time:".length);
|
|
490
|
+
if (
|
|
491
|
+
(hasTimeInjectionPrefix || query.data.startsWith("settings:set:time:")) &&
|
|
492
|
+
(timeMode === "off" ||
|
|
493
|
+
timeMode === "always" ||
|
|
494
|
+
timeMode === "interval")
|
|
495
|
+
) {
|
|
496
|
+
await deps.setTimeInjectionMode(timeMode);
|
|
497
|
+
await deps.answerCallbackQuery(
|
|
498
|
+
query.id,
|
|
499
|
+
`Time injection: ${timeMode}`,
|
|
500
|
+
);
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
284
503
|
await deps.answerCallbackQuery(
|
|
285
504
|
query.id,
|
|
286
505
|
"Interactive message expired.",
|
|
@@ -289,7 +508,12 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
289
508
|
}
|
|
290
509
|
return handleTelegramSettingsMenuCallbackAction(query.id, query.data, {
|
|
291
510
|
isProactivePushEnabled: deps.isProactivePushEnabled,
|
|
511
|
+
getVoiceReplyMode: deps.getVoiceReplyMode,
|
|
512
|
+
isVoiceReplyModeConfigured: deps.isVoiceReplyModeConfigured,
|
|
513
|
+
getTimeInjectionMode: deps.getTimeInjectionMode,
|
|
292
514
|
setProactivePushEnabled: deps.setProactivePushEnabled,
|
|
515
|
+
setVoiceReplyMode: deps.setVoiceReplyMode,
|
|
516
|
+
setTimeInjectionMode: deps.setTimeInjectionMode,
|
|
293
517
|
updateSettingsMessage: (text, replyMarkup) =>
|
|
294
518
|
deps.editInteractiveMessage(
|
|
295
519
|
state.chatId,
|
package/lib/menu-status.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface TelegramStatusMenuCallbackDeps {
|
|
|
30
30
|
callbackQueryId: string,
|
|
31
31
|
text?: string,
|
|
32
32
|
) => Promise<void>;
|
|
33
|
+
isVoiceReplyActive?: () => boolean;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export interface TelegramStatusMenuOpenDeps<
|
|
@@ -131,6 +132,13 @@ export async function handleTelegramStatusMenuCallbackAction(
|
|
|
131
132
|
return true;
|
|
132
133
|
}
|
|
133
134
|
if (!isTelegramStatusMenuCallbackAction(data, "thinking")) return false;
|
|
135
|
+
if (deps.isVoiceReplyActive?.()) {
|
|
136
|
+
await deps.answerCallbackQuery(
|
|
137
|
+
callbackQueryId,
|
|
138
|
+
"Thinking controls are disabled during voice replies.",
|
|
139
|
+
);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
134
142
|
if (!activeModel?.reasoning) {
|
|
135
143
|
await deps.answerCallbackQuery(
|
|
136
144
|
callbackQueryId,
|
|
@@ -148,6 +156,7 @@ export function buildStatusReplyMarkup(
|
|
|
148
156
|
currentThinkingLevel: ThinkingLevel,
|
|
149
157
|
queueItemCount = 0,
|
|
150
158
|
sectionRegistry?: TelegramSectionRegistry,
|
|
159
|
+
isVoiceReplyActive?: boolean,
|
|
151
160
|
): TelegramReplyMarkup {
|
|
152
161
|
const rows: Array<Array<{ text: string; callback_data: string }>> = [];
|
|
153
162
|
rows.push([
|
|
@@ -159,7 +168,7 @@ export function buildStatusReplyMarkup(
|
|
|
159
168
|
callback_data: "menu:model",
|
|
160
169
|
},
|
|
161
170
|
]);
|
|
162
|
-
if (activeModel?.reasoning) {
|
|
171
|
+
if (activeModel?.reasoning && !isVoiceReplyActive) {
|
|
163
172
|
rows.push([
|
|
164
173
|
{
|
|
165
174
|
text: formatStatusButtonLabel(
|
|
@@ -176,11 +185,10 @@ export function buildStatusReplyMarkup(
|
|
|
176
185
|
callback_data: "menu:queue",
|
|
177
186
|
},
|
|
178
187
|
]);
|
|
179
|
-
// Extension section rows before Settings
|
|
180
188
|
if (sectionRegistry) {
|
|
181
189
|
const sectionRows = getTelegramSectionMainMenuRows(sectionRegistry);
|
|
182
190
|
for (const row of sectionRows) {
|
|
183
|
-
rows.push([
|
|
191
|
+
rows.push([row]);
|
|
184
192
|
}
|
|
185
193
|
}
|
|
186
194
|
rows.push([
|
|
@@ -198,6 +206,7 @@ export function buildTelegramStatusMenuRenderPayload(
|
|
|
198
206
|
currentThinkingLevel: ThinkingLevel,
|
|
199
207
|
queueItemCount = 0,
|
|
200
208
|
sectionRegistry?: TelegramSectionRegistry,
|
|
209
|
+
isVoiceReplyActive?: boolean,
|
|
201
210
|
): TelegramMenuRenderPayload {
|
|
202
211
|
return {
|
|
203
212
|
nextMode: "status",
|
|
@@ -208,6 +217,7 @@ export function buildTelegramStatusMenuRenderPayload(
|
|
|
208
217
|
currentThinkingLevel,
|
|
209
218
|
queueItemCount,
|
|
210
219
|
sectionRegistry,
|
|
220
|
+
isVoiceReplyActive,
|
|
211
221
|
),
|
|
212
222
|
};
|
|
213
223
|
}
|
|
@@ -220,6 +230,7 @@ export async function updateTelegramStatusMessage(
|
|
|
220
230
|
deps: TelegramMenuMessageRuntimeDeps,
|
|
221
231
|
queueItemCount = 0,
|
|
222
232
|
sectionRegistry?: TelegramSectionRegistry,
|
|
233
|
+
isVoiceReplyActive?: boolean,
|
|
223
234
|
): Promise<void> {
|
|
224
235
|
await editTelegramMenuMessage(
|
|
225
236
|
state,
|
|
@@ -229,6 +240,7 @@ export async function updateTelegramStatusMessage(
|
|
|
229
240
|
currentThinkingLevel,
|
|
230
241
|
queueItemCount,
|
|
231
242
|
sectionRegistry,
|
|
243
|
+
isVoiceReplyActive,
|
|
232
244
|
),
|
|
233
245
|
deps,
|
|
234
246
|
);
|
|
@@ -242,6 +254,7 @@ export function sendTelegramStatusMessage(
|
|
|
242
254
|
deps: TelegramMenuMessageRuntimeDeps,
|
|
243
255
|
queueItemCount = 0,
|
|
244
256
|
sectionRegistry?: TelegramSectionRegistry,
|
|
257
|
+
isVoiceReplyActive?: boolean,
|
|
245
258
|
): Promise<number | undefined> {
|
|
246
259
|
return sendTelegramMenuMessage(
|
|
247
260
|
state,
|
|
@@ -251,6 +264,7 @@ export function sendTelegramStatusMessage(
|
|
|
251
264
|
currentThinkingLevel,
|
|
252
265
|
queueItemCount,
|
|
253
266
|
sectionRegistry,
|
|
267
|
+
isVoiceReplyActive,
|
|
254
268
|
),
|
|
255
269
|
deps,
|
|
256
270
|
);
|
package/lib/menu-thinking.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface TelegramThinkingMenuCallbackDeps {
|
|
|
25
25
|
callbackQueryId: string,
|
|
26
26
|
text?: string,
|
|
27
27
|
) => Promise<void>;
|
|
28
|
+
isVoiceReplyActive?: () => boolean;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export interface TelegramThinkingMenuOpenDeps<
|
|
@@ -34,6 +35,7 @@ export interface TelegramThinkingMenuOpenDeps<
|
|
|
34
35
|
getActiveModel: () => TModel | undefined;
|
|
35
36
|
getThinkingLevel: () => ThinkingLevel;
|
|
36
37
|
storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
|
|
38
|
+
isVoiceReplyActive?: () => boolean;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
function parseTelegramThinkingMenuCallbackAction(
|
|
@@ -92,6 +94,13 @@ export async function handleTelegramThinkingMenuCallbackAction(
|
|
|
92
94
|
await deps.answerCallbackQuery(callbackQueryId, "Invalid thinking level.");
|
|
93
95
|
return true;
|
|
94
96
|
}
|
|
97
|
+
if (deps.isVoiceReplyActive?.()) {
|
|
98
|
+
await deps.answerCallbackQuery(
|
|
99
|
+
callbackQueryId,
|
|
100
|
+
"Thinking controls are disabled during voice replies.",
|
|
101
|
+
);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
95
104
|
if (!activeModel?.reasoning) {
|
|
96
105
|
await deps.answerCallbackQuery(
|
|
97
106
|
callbackQueryId,
|
|
@@ -142,6 +151,7 @@ export function buildTelegramThinkingMenuRenderPayload(
|
|
|
142
151
|
export async function openTelegramThinkingMenu<
|
|
143
152
|
TModel extends MenuModel = MenuModel,
|
|
144
153
|
>(deps: TelegramThinkingMenuOpenDeps<TModel>): Promise<void> {
|
|
154
|
+
if (deps.isVoiceReplyActive?.()) return;
|
|
145
155
|
const state = await deps.getModelMenuState();
|
|
146
156
|
const messageId = await sendTelegramMenuMessage(
|
|
147
157
|
state,
|
|
@@ -161,8 +171,9 @@ export async function updateTelegramThinkingMenuMessage(
|
|
|
161
171
|
state: TelegramModelMenuState,
|
|
162
172
|
activeModel: MenuModel | undefined,
|
|
163
173
|
currentThinkingLevel: ThinkingLevel,
|
|
164
|
-
deps: TelegramMenuMessageRuntimeDeps,
|
|
174
|
+
deps: TelegramMenuMessageRuntimeDeps & { isVoiceReplyActive?: () => boolean },
|
|
165
175
|
): Promise<void> {
|
|
176
|
+
if (deps.isVoiceReplyActive?.()) return;
|
|
166
177
|
await editTelegramMenuMessage(
|
|
167
178
|
state,
|
|
168
179
|
buildTelegramThinkingMenuRenderPayload(activeModel, currentThinkingLevel),
|
package/lib/menu.ts
CHANGED
|
@@ -239,6 +239,7 @@ export interface TelegramMenuCallbackRuntimeDeps<
|
|
|
239
239
|
) => Promise<number | undefined>;
|
|
240
240
|
enqueueSectionPrompt?: (prompt: string, ctx: TContext) => Promise<void>;
|
|
241
241
|
deleteMessage?: (chatId: number, messageId: number) => Promise<void>;
|
|
242
|
+
isVoiceReplyActive?: () => boolean;
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
export interface TelegramMenuActionRuntimeDeps<
|
|
@@ -262,6 +263,7 @@ export interface TelegramMenuActionRuntimeDeps<
|
|
|
262
263
|
text: string,
|
|
263
264
|
) => Promise<unknown>;
|
|
264
265
|
sectionRegistry?: TelegramSectionRegistry;
|
|
266
|
+
isVoiceReplyActive?: () => boolean;
|
|
265
267
|
}
|
|
266
268
|
|
|
267
269
|
export interface TelegramMenuActionRuntime<
|
|
@@ -639,6 +641,7 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
639
641
|
updateSettingsMenuMessage: () =>
|
|
640
642
|
deps.updateSettingsMenuMessage?.(state, ctx) ?? Promise.resolve(),
|
|
641
643
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
644
|
+
isVoiceReplyActive: deps.isVoiceReplyActive,
|
|
642
645
|
},
|
|
643
646
|
),
|
|
644
647
|
handleThinkingAction: async (state) =>
|
|
@@ -654,6 +657,7 @@ export async function handleTelegramMenuCallbackRuntime<
|
|
|
654
657
|
getCurrentThinkingLevel: deps.getThinkingLevel,
|
|
655
658
|
updateStatusMessage: () => deps.updateStatusMessage(state, ctx),
|
|
656
659
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
660
|
+
isVoiceReplyActive: deps.isVoiceReplyActive,
|
|
657
661
|
},
|
|
658
662
|
),
|
|
659
663
|
handleModelAction: async (state) => {
|
|
@@ -708,7 +712,9 @@ export interface TelegramMenuActionRuntimeWithStateBuilderDeps<
|
|
|
708
712
|
>
|
|
709
713
|
extends
|
|
710
714
|
Omit<TelegramMenuActionRuntimeDeps<TContext, TModel>, "getModelMenuState">,
|
|
711
|
-
TelegramModelMenuStateBuilderDeps<TModel, TContext> {
|
|
715
|
+
TelegramModelMenuStateBuilderDeps<TModel, TContext> {
|
|
716
|
+
isVoiceReplyActive?: () => boolean;
|
|
717
|
+
}
|
|
712
718
|
|
|
713
719
|
export function createTelegramMenuActionRuntimeWithStateBuilder<
|
|
714
720
|
TModel extends MenuModel = MenuModel,
|
|
@@ -762,6 +768,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
762
768
|
deps,
|
|
763
769
|
deps.getQueueItemCount?.() ?? 0,
|
|
764
770
|
deps.sectionRegistry,
|
|
771
|
+
deps.isVoiceReplyActive?.(),
|
|
765
772
|
),
|
|
766
773
|
sendStatusMessage: (chatId, replyToMessageId, ctx) =>
|
|
767
774
|
openTelegramStatusMenu({
|
|
@@ -793,6 +800,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
793
800
|
deps,
|
|
794
801
|
queueItemCount,
|
|
795
802
|
deps.sectionRegistry,
|
|
803
|
+
deps.isVoiceReplyActive?.(),
|
|
796
804
|
),
|
|
797
805
|
storeModelMenuState: deps.storeModelMenuState,
|
|
798
806
|
}),
|
|
@@ -829,6 +837,7 @@ export function createTelegramMenuActionRuntime<
|
|
|
829
837
|
storeModelMenuState: deps.storeModelMenuState,
|
|
830
838
|
editInteractiveMessage: deps.editInteractiveMessage,
|
|
831
839
|
sendInteractiveMessage: deps.sendInteractiveMessage,
|
|
840
|
+
isVoiceReplyActive: deps.isVoiceReplyActive,
|
|
832
841
|
}),
|
|
833
842
|
};
|
|
834
843
|
}
|