@llblab/pi-telegram 0.11.2 → 0.13.0
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 +20 -15
- package/BACKLOG.md +1 -11
- package/CHANGELOG.md +41 -1
- package/README.md +15 -41
- package/api/inbound.ts +14 -0
- package/api/keyboard.ts +10 -0
- package/api/outbound.ts +11 -0
- package/api/sections.ts +17 -0
- package/api/updates.ts +11 -0
- package/api/voice.ts +24 -0
- package/docs/README.md +7 -5
- package/docs/architecture.md +162 -226
- package/docs/callback-namespaces.md +3 -3
- package/docs/command-templates.md +18 -16
- package/docs/{inbound-handlers.md → inbound.md} +14 -11
- package/docs/locks.md +3 -3
- package/docs/{outbound-handlers.md → outbound.md} +14 -11
- package/docs/public-api.md +420 -0
- package/docs/{extension-sections.md → sections.md} +34 -30
- package/docs/ui-style.md +165 -0
- package/docs/{external-handlers.md → updates.md} +33 -31
- package/docs/voice.md +27 -19
- package/index.ts +88 -242
- package/lib/bindings.ts +299 -0
- package/lib/command-templates.ts +249 -60
- package/lib/commands.ts +114 -1
- package/lib/config.ts +44 -4
- package/lib/{inbound-handlers.ts → inbound.ts} +31 -21
- package/lib/lifecycle.ts +41 -6
- package/lib/locks.ts +4 -1
- package/lib/menu-model.ts +3 -3
- package/lib/menu-queue.ts +1 -1
- package/lib/menu-settings.ts +21 -10
- package/lib/menu-status.ts +1 -1
- package/lib/menu.ts +1 -1
- package/lib/outbound-buttons.ts +226 -0
- package/lib/outbound-markup.ts +357 -0
- package/lib/outbound-voice.ts +263 -0
- package/lib/outbound.ts +908 -0
- package/lib/polling.ts +4 -3
- package/lib/preview.ts +2 -2
- package/lib/queue.ts +3 -0
- package/lib/replies.ts +4 -1
- package/lib/routing.ts +44 -3
- package/lib/{extension-sections.ts → sections.ts} +37 -8
- package/lib/status.ts +13 -0
- package/lib/{api.ts → telegram-api.ts} +4 -4
- package/lib/text-groups.ts +3 -2
- package/lib/updates.ts +121 -1
- package/lib/voice.ts +67 -21
- package/package.json +13 -3
- package/lib/external-handlers.ts +0 -166
- package/lib/outbound-handlers.ts +0 -1663
package/lib/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram bridge config and pairing helpers
|
|
3
3
|
* Zones: telegram config, pairing, filesystem
|
|
4
|
-
* Owns persisted bot/session pairing state, local config storage, authorization policy, and first-user pairing side effects
|
|
4
|
+
* Owns persisted bot/session pairing state, local config storage, live config controls, authorization policy, and first-user pairing side effects
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { existsSync } from "node:fs";
|
|
@@ -9,7 +9,7 @@ import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { join, resolve } from "node:path";
|
|
11
11
|
|
|
12
|
-
import type { TelegramInboundHandlerConfig } from "./inbound
|
|
12
|
+
import type { TelegramInboundHandlerConfig } from "./inbound.ts";
|
|
13
13
|
import type { CommandTemplateObjectConfig } from "./command-templates.ts";
|
|
14
14
|
|
|
15
15
|
const CONFIG_RUNTIME_KEY = "__piTelegramConfigRuntime__";
|
|
@@ -30,9 +30,8 @@ export type TelegramOutboundCommandTemplateConfig =
|
|
|
30
30
|
export interface TelegramOutboundHandlerConfig extends CommandTemplateObjectConfig {
|
|
31
31
|
type?: string;
|
|
32
32
|
match?: string | string[];
|
|
33
|
-
pipe?: TelegramOutboundCommandTemplateConfig[];
|
|
34
33
|
output?: string;
|
|
35
|
-
timeout?: number;
|
|
34
|
+
timeout?: number | string;
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
export type TelegramTimeMode = "hidden" | "always" | "interval";
|
|
@@ -110,6 +109,22 @@ export function updateTelegramVoiceConfig(
|
|
|
110
109
|
return true;
|
|
111
110
|
}
|
|
112
111
|
|
|
112
|
+
export function bindGlobalTelegramConfigRuntime(
|
|
113
|
+
configStore: Pick<TelegramConfigStore, "get" | "set" | "persist">,
|
|
114
|
+
): void {
|
|
115
|
+
setGlobalTelegramConfigRuntime({
|
|
116
|
+
updateVoiceConfig(voice) {
|
|
117
|
+
const current = configStore.get();
|
|
118
|
+
const next = {
|
|
119
|
+
...current,
|
|
120
|
+
voice: { ...(current.voice ?? {}), ...voice },
|
|
121
|
+
};
|
|
122
|
+
configStore.set(next);
|
|
123
|
+
void configStore.persist(next);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
113
128
|
export async function readTelegramConfig(
|
|
114
129
|
configPath: string,
|
|
115
130
|
): Promise<TelegramConfig> {
|
|
@@ -266,6 +281,16 @@ export function createTelegramTimeInjectionModeSetter(
|
|
|
266
281
|
): (injectionMode: TelegramTimeMode) => Promise<void> {
|
|
267
282
|
return async (injectionMode) => {
|
|
268
283
|
const current = configStore.get();
|
|
284
|
+
if (injectionMode === "hidden") {
|
|
285
|
+
const { injectionMode: _injectionMode, ...remainingTime } =
|
|
286
|
+
current.time ?? {};
|
|
287
|
+
const next = { ...current };
|
|
288
|
+
if (Object.keys(remainingTime).length > 0) next.time = remainingTime;
|
|
289
|
+
else delete next.time;
|
|
290
|
+
configStore.set(next);
|
|
291
|
+
await configStore.persist(next);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
269
294
|
const next = {
|
|
270
295
|
...current,
|
|
271
296
|
time: { ...(current.time ?? {}), injectionMode },
|
|
@@ -282,6 +307,21 @@ export function createTelegramProactivePushChatIdGetter(deps: {
|
|
|
282
307
|
return () => deps.getActiveTurnChatId() ?? deps.getAllowedUserId();
|
|
283
308
|
}
|
|
284
309
|
|
|
310
|
+
export function createTelegramConfigControls(
|
|
311
|
+
configStore: Pick<TelegramConfigStore, "get" | "set" | "persist">,
|
|
312
|
+
) {
|
|
313
|
+
return {
|
|
314
|
+
isProactivePushEnabled: createTelegramProactivePushChecker(configStore),
|
|
315
|
+
setProactivePushEnabled: createTelegramProactivePushSetter(configStore),
|
|
316
|
+
getVoiceReplyMode: createTelegramVoiceReplyModeGetter(configStore),
|
|
317
|
+
isVoiceReplyModeConfigured:
|
|
318
|
+
createTelegramVoiceReplyModeConfiguredChecker(configStore),
|
|
319
|
+
setVoiceReplyMode: createTelegramVoiceReplyModeSetter(configStore),
|
|
320
|
+
getTimeInjectionMode: createTelegramTimeInjectionModeGetter(configStore),
|
|
321
|
+
setTimeInjectionMode: createTelegramTimeInjectionModeSetter(configStore),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
285
325
|
export type TelegramAuthorizationState =
|
|
286
326
|
| { kind: "pair"; userId: number }
|
|
287
327
|
| { kind: "allow" }
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
buildCommandTemplateInvocation,
|
|
12
12
|
expandCommandTemplateConfigs,
|
|
13
13
|
normalizeCommandTemplateConfig,
|
|
14
|
+
substituteCommandTemplateToken,
|
|
14
15
|
type CommandTemplateConfig,
|
|
15
16
|
type CommandTemplateObjectConfig,
|
|
16
17
|
} from "./command-templates.ts";
|
|
@@ -28,10 +29,9 @@ export interface TelegramInboundHandlerConfig {
|
|
|
28
29
|
mime?: string | string[];
|
|
29
30
|
type?: string | string[];
|
|
30
31
|
template?: string | TelegramInboundCommandTemplateConfig[];
|
|
31
|
-
pipe?: TelegramInboundCommandTemplateConfig[];
|
|
32
32
|
args?: string[];
|
|
33
33
|
defaults?: Record<string, unknown>;
|
|
34
|
-
timeout?: number;
|
|
34
|
+
timeout?: number | string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export interface TelegramInboundHandlerFile {
|
|
@@ -62,6 +62,7 @@ export interface TelegramInboundHandlerExecOptions {
|
|
|
62
62
|
timeout?: number;
|
|
63
63
|
signal?: AbortSignal;
|
|
64
64
|
stdin?: string;
|
|
65
|
+
retry?: number;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export interface TelegramInboundHandlerExecResult {
|
|
@@ -216,9 +217,7 @@ function matchesWildcard(pattern: string, value: string | undefined): boolean {
|
|
|
216
217
|
return new RegExp(`^${escaped}$`).test(normalizedValue);
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
function handlerHasSelectors(
|
|
220
|
-
handler: TelegramInboundHandlerConfig,
|
|
221
|
-
): boolean {
|
|
220
|
+
function handlerHasSelectors(handler: TelegramInboundHandlerConfig): boolean {
|
|
222
221
|
return (
|
|
223
222
|
normalizeStringList(handler.match).length > 0 ||
|
|
224
223
|
normalizeStringList(handler.mime).length > 0 ||
|
|
@@ -324,13 +323,28 @@ export function buildTelegramInboundHandlerInvocation(
|
|
|
324
323
|
);
|
|
325
324
|
}
|
|
326
325
|
|
|
326
|
+
function resolveTelegramInboundNumericControlField(
|
|
327
|
+
value: number | string | undefined,
|
|
328
|
+
values: Record<string, unknown>,
|
|
329
|
+
label: string,
|
|
330
|
+
): number | undefined {
|
|
331
|
+
if (value === undefined) return undefined;
|
|
332
|
+
const resolved =
|
|
333
|
+
typeof value === "string"
|
|
334
|
+
? substituteCommandTemplateToken(value, values, label)
|
|
335
|
+
: value;
|
|
336
|
+
if (resolved === "") return undefined;
|
|
337
|
+
const numeric = Number(resolved);
|
|
338
|
+
if (!Number.isFinite(numeric) || numeric < 0)
|
|
339
|
+
throw new Error(`Command template ${label} must be a non-negative number.`);
|
|
340
|
+
return numeric;
|
|
341
|
+
}
|
|
342
|
+
|
|
327
343
|
function getTelegramInboundHandlerConfiguredTimeout(
|
|
328
344
|
handler: TelegramInboundCommandTemplateConfig,
|
|
329
345
|
): number | undefined {
|
|
330
346
|
const timeout = typeof handler === "string" ? undefined : handler.timeout;
|
|
331
|
-
return
|
|
332
|
-
? timeout
|
|
333
|
-
: undefined;
|
|
347
|
+
return resolveTelegramInboundNumericControlField(timeout, {}, "timeout");
|
|
334
348
|
}
|
|
335
349
|
|
|
336
350
|
function getTelegramInboundHandlerTimeout(
|
|
@@ -376,8 +390,7 @@ function getTelegramInboundCompositionStepTimeout(
|
|
|
376
390
|
function getTelegramInboundHandlerKind(
|
|
377
391
|
handler: TelegramInboundHandlerConfig,
|
|
378
392
|
): string {
|
|
379
|
-
if (Array.isArray(handler.template)
|
|
380
|
-
return "composition";
|
|
393
|
+
if (Array.isArray(handler.template)) return "composition";
|
|
381
394
|
if (handler.template) return "template";
|
|
382
395
|
return "unknown";
|
|
383
396
|
}
|
|
@@ -412,7 +425,7 @@ async function executeTelegramInboundHandlerInvocation(
|
|
|
412
425
|
cwd,
|
|
413
426
|
timeout,
|
|
414
427
|
...(typeof handler === "object" && handler.retry !== undefined
|
|
415
|
-
? { retry: handler.retry }
|
|
428
|
+
? { retry: resolveTelegramInboundNumericControlField(handler.retry, {}, "retry") }
|
|
416
429
|
: {}),
|
|
417
430
|
...(stdin !== undefined ? { stdin } : {}),
|
|
418
431
|
});
|
|
@@ -429,12 +442,6 @@ function getTelegramInboundHandlerCompositionSteps(
|
|
|
429
442
|
handler,
|
|
430
443
|
) as TelegramInboundCommandTemplateConfig[];
|
|
431
444
|
}
|
|
432
|
-
if (handler.pipe?.length) {
|
|
433
|
-
return expandCommandTemplateConfigs({
|
|
434
|
-
...handler,
|
|
435
|
-
template: handler.pipe,
|
|
436
|
-
}) as TelegramInboundCommandTemplateConfig[];
|
|
437
|
-
}
|
|
438
445
|
return [];
|
|
439
446
|
}
|
|
440
447
|
|
|
@@ -492,7 +499,7 @@ async function executeTelegramTextHandlerInvocation(
|
|
|
492
499
|
timeout,
|
|
493
500
|
stdin: text,
|
|
494
501
|
...(typeof handler === "object" && handler.retry !== undefined
|
|
495
|
-
? { retry: handler.retry }
|
|
502
|
+
? { retry: resolveTelegramInboundNumericControlField(handler.retry, {}, "retry") }
|
|
496
503
|
: {}),
|
|
497
504
|
});
|
|
498
505
|
if (result.code !== 0)
|
|
@@ -526,7 +533,7 @@ async function executeTelegramTextHandler(
|
|
|
526
533
|
: getTelegramInboundCompositionStepTimeout(handler, step, startedAt),
|
|
527
534
|
);
|
|
528
535
|
} catch (error) {
|
|
529
|
-
if (typeof step === "object" && step.
|
|
536
|
+
if (typeof step === "object" && step.failure === "root") throw error;
|
|
530
537
|
output = "";
|
|
531
538
|
}
|
|
532
539
|
if (index > 0 && !output) output = text;
|
|
@@ -642,7 +649,10 @@ async function readBuiltInTelegramTextAttachment(
|
|
|
642
649
|
if (!isTelegramTextMimeType(file.mimeType)) return undefined;
|
|
643
650
|
const content = await readFile(file.path, "utf8");
|
|
644
651
|
const normalized = content.trim();
|
|
645
|
-
if (
|
|
652
|
+
if (
|
|
653
|
+
!normalized ||
|
|
654
|
+
Buffer.byteLength(normalized, "utf8") > BUILT_IN_TEXT_ATTACHMENT_MAX_BYTES
|
|
655
|
+
) {
|
|
646
656
|
return undefined;
|
|
647
657
|
}
|
|
648
658
|
const name = file.fileName || basename(file.path);
|
|
@@ -681,7 +691,7 @@ async function executeTelegramInboundHandler(
|
|
|
681
691
|
index === 0 ? undefined : output,
|
|
682
692
|
);
|
|
683
693
|
} catch (error) {
|
|
684
|
-
if (typeof step === "object" && step.
|
|
694
|
+
if (typeof step === "object" && step.failure === "root") throw error;
|
|
685
695
|
output = "";
|
|
686
696
|
}
|
|
687
697
|
}
|
package/lib/lifecycle.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram lifecycle hook registration helpers
|
|
3
3
|
* Zones: pi agent lifecycle, telegram session
|
|
4
|
-
*
|
|
4
|
+
* Binds prepared Telegram lifecycle runtimes to pi extension lifecycle events
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type {
|
|
@@ -107,16 +107,15 @@ type TelegramLifecycleTimer = number | ReturnType<typeof setTimeout>;
|
|
|
107
107
|
export interface TelegramCompactionObserverRuntimeDeps<TContext> {
|
|
108
108
|
setCompactionInProgress: (inProgress: boolean) => void;
|
|
109
109
|
updateStatus: (ctx: TContext) => void;
|
|
110
|
+
startTypingLoop?: (ctx: TContext) => void;
|
|
111
|
+
stopTypingLoop?: () => void;
|
|
110
112
|
requestDeferredDispatchNextQueuedTelegramTurn: (
|
|
111
113
|
dispatch: (ctx: TContext) => void,
|
|
112
114
|
) => void;
|
|
113
115
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
114
116
|
recordRuntimeEvent?: (category: string, error: unknown) => void;
|
|
115
117
|
timeoutMs?: number;
|
|
116
|
-
setTimer?: (
|
|
117
|
-
callback: () => void,
|
|
118
|
-
ms: number,
|
|
119
|
-
) => TelegramLifecycleTimer;
|
|
118
|
+
setTimer?: (callback: () => void, ms: number) => TelegramLifecycleTimer;
|
|
120
119
|
clearTimer?: (timer: TelegramLifecycleTimer) => void;
|
|
121
120
|
}
|
|
122
121
|
|
|
@@ -149,11 +148,13 @@ export function createTelegramCompactionObserverRuntime<TContext>(
|
|
|
149
148
|
return {
|
|
150
149
|
onSessionBeforeCompact: (_event, ctx) => {
|
|
151
150
|
deps.setCompactionInProgress(true);
|
|
151
|
+
deps.startTypingLoop?.(ctx);
|
|
152
152
|
deps.updateStatus(ctx);
|
|
153
153
|
clearFallbackTimer();
|
|
154
154
|
fallbackTimer = setTimer(() => {
|
|
155
155
|
fallbackTimer = undefined;
|
|
156
156
|
deps.setCompactionInProgress(false);
|
|
157
|
+
deps.stopTypingLoop?.();
|
|
157
158
|
deps.updateStatus(ctx);
|
|
158
159
|
deps.recordRuntimeEvent?.(
|
|
159
160
|
"compact",
|
|
@@ -165,10 +166,44 @@ export function createTelegramCompactionObserverRuntime<TContext>(
|
|
|
165
166
|
onSessionCompact: (_event, ctx) => {
|
|
166
167
|
clearFallbackTimer();
|
|
167
168
|
deps.setCompactionInProgress(false);
|
|
169
|
+
deps.stopTypingLoop?.();
|
|
168
170
|
deps.updateStatus(ctx);
|
|
169
171
|
requestDispatch();
|
|
170
172
|
},
|
|
171
|
-
onSessionShutdown:
|
|
173
|
+
onSessionShutdown: () => {
|
|
174
|
+
clearFallbackTimer();
|
|
175
|
+
deps.stopTypingLoop?.();
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface TelegramMessageActivityTypingDeps<TContext> {
|
|
181
|
+
hasActiveTurn: () => boolean;
|
|
182
|
+
startTypingLoop: (ctx: TContext) => void;
|
|
183
|
+
onMessageStart: TelegramLifecycleRegistrationDeps["onMessageStart"];
|
|
184
|
+
onMessageUpdate: TelegramLifecycleRegistrationDeps["onMessageUpdate"];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function createTelegramMessageActivityTypingHooks<
|
|
188
|
+
TContext extends ExtensionContext,
|
|
189
|
+
>(
|
|
190
|
+
deps: TelegramMessageActivityTypingDeps<TContext>,
|
|
191
|
+
): Pick<
|
|
192
|
+
TelegramLifecycleRegistrationDeps,
|
|
193
|
+
"onMessageStart" | "onMessageUpdate"
|
|
194
|
+
> {
|
|
195
|
+
const ensureTyping = (ctx: TContext): void => {
|
|
196
|
+
if (deps.hasActiveTurn()) deps.startTypingLoop(ctx);
|
|
197
|
+
};
|
|
198
|
+
return {
|
|
199
|
+
onMessageStart: async (event, ctx) => {
|
|
200
|
+
ensureTyping(ctx as TContext);
|
|
201
|
+
await deps.onMessageStart(event, ctx);
|
|
202
|
+
},
|
|
203
|
+
onMessageUpdate: async (event, ctx) => {
|
|
204
|
+
ensureTyping(ctx as TContext);
|
|
205
|
+
await deps.onMessageUpdate(event, ctx);
|
|
206
|
+
},
|
|
172
207
|
};
|
|
173
208
|
}
|
|
174
209
|
|
package/lib/locks.ts
CHANGED
|
@@ -125,7 +125,10 @@ export function writeLocks(path: string, locks: Record<string, unknown>): void {
|
|
|
125
125
|
mkdirSync(dirname(path), { recursive: true });
|
|
126
126
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
127
127
|
try {
|
|
128
|
-
writeFileSync(tempPath, `${JSON.stringify(locks, null, 2)}\n`,
|
|
128
|
+
writeFileSync(tempPath, `${JSON.stringify(locks, null, 2)}\n`, {
|
|
129
|
+
encoding: "utf8",
|
|
130
|
+
mode: 0o600,
|
|
131
|
+
});
|
|
129
132
|
renameSync(tempPath, path);
|
|
130
133
|
} catch (error) {
|
|
131
134
|
try {
|
package/lib/menu-model.ts
CHANGED
|
@@ -983,7 +983,7 @@ export function buildModelMenuReplyMarkup(
|
|
|
983
983
|
callback_data: "model:scope:scoped",
|
|
984
984
|
},
|
|
985
985
|
{
|
|
986
|
-
text: state.scope === "all" ? "
|
|
986
|
+
text: state.scope === "all" ? "🟣 All" : "⚫️ All",
|
|
987
987
|
callback_data: "model:scope:all",
|
|
988
988
|
},
|
|
989
989
|
]);
|
|
@@ -1050,7 +1050,7 @@ export function buildModelDetailMenuReplyMarkup(
|
|
|
1050
1050
|
callback_data: "model:scope-enable",
|
|
1051
1051
|
},
|
|
1052
1052
|
{
|
|
1053
|
-
text: scoped ? "⚫️ All" : "
|
|
1053
|
+
text: scoped ? "⚫️ All" : "🟣 All",
|
|
1054
1054
|
callback_data: "model:scope-disable",
|
|
1055
1055
|
},
|
|
1056
1056
|
],
|
|
@@ -1091,7 +1091,7 @@ export function buildModelPageMenuReplyMarkup(
|
|
|
1091
1091
|
return {
|
|
1092
1092
|
text:
|
|
1093
1093
|
pageIndex === menuPage.page
|
|
1094
|
-
?
|
|
1094
|
+
? `🟣 ${pageIndex + 1}`
|
|
1095
1095
|
: String(pageIndex + 1),
|
|
1096
1096
|
callback_data: `model:page:${pageIndex}`,
|
|
1097
1097
|
};
|
package/lib/menu-queue.ts
CHANGED
|
@@ -162,7 +162,7 @@ function buildTelegramQueueItemSubmenuReplyMarkup(
|
|
|
162
162
|
callback_data: `queue:prio-set:${chatId}:${replyToMessageId}:priority`,
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
|
-
text: isPriority ? "⚫️ Normal" : "
|
|
165
|
+
text: isPriority ? "⚫️ Normal" : "🟣 Normal",
|
|
166
166
|
callback_data: `queue:prio-set:${chatId}:${replyToMessageId}:normal`,
|
|
167
167
|
},
|
|
168
168
|
],
|
package/lib/menu-settings.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import {
|
|
8
8
|
getTelegramExtensionSettingsRows,
|
|
9
9
|
type TelegramSectionRegistry,
|
|
10
|
-
} from "./
|
|
10
|
+
} from "./sections.ts";
|
|
11
11
|
import type { TelegramTimeMode } from "./config.ts";
|
|
12
12
|
import type { TelegramInlineKeyboardMarkup } from "./keyboard.ts";
|
|
13
13
|
import type { TelegramModelMenuState } from "./menu-model.ts";
|
|
@@ -115,7 +115,8 @@ export interface TelegramSettingsMenuRuntimeDeps<
|
|
|
115
115
|
|
|
116
116
|
export const SETTINGS_MENU_TITLE = "<b>⚙️ Settings:</b>";
|
|
117
117
|
export const PROACTIVE_PUSH_SETTINGS_TITLE = "<b>📌 Proactive push:</b>";
|
|
118
|
-
export const TIME_INJECTION_MODE_SETTINGS_TITLE =
|
|
118
|
+
export const TIME_INJECTION_MODE_SETTINGS_TITLE =
|
|
119
|
+
"<b>🕒 Time injection mode:</b>";
|
|
119
120
|
export const VOICE_REPLY_MODE_SETTINGS_TITLE = "<b>👄 Voice reply mode:</b>";
|
|
120
121
|
|
|
121
122
|
type TelegramVoiceReplyModeSetting = TelegramVoiceReplyMode | "hidden";
|
|
@@ -124,6 +125,10 @@ function getVoiceReplyModeLabel(mode: TelegramVoiceReplyModeSetting): string {
|
|
|
124
125
|
return mode;
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
function getTelegramSettingsStateValueLabel(value: string): string {
|
|
129
|
+
return value.length > 0 ? value[0]!.toUpperCase() + value.slice(1) : value;
|
|
130
|
+
}
|
|
131
|
+
|
|
127
132
|
function getVoiceReplyModeSetting(
|
|
128
133
|
mode: TelegramVoiceReplyMode,
|
|
129
134
|
configured: boolean,
|
|
@@ -197,21 +202,23 @@ export function buildTelegramSettingsMenuReplyMarkup(
|
|
|
197
202
|
rows.push(
|
|
198
203
|
[
|
|
199
204
|
{
|
|
200
|
-
text: `👄 Voice reply: ${
|
|
201
|
-
|
|
205
|
+
text: `👄 Voice reply: ${getTelegramSettingsStateValueLabel(
|
|
206
|
+
getVoiceReplyModeLabel(
|
|
207
|
+
getVoiceReplyModeSetting(voiceReplyMode, voiceReplyModeConfigured),
|
|
208
|
+
),
|
|
202
209
|
)}`,
|
|
203
210
|
callback_data: "settings:open:voice-reply",
|
|
204
211
|
},
|
|
205
212
|
],
|
|
206
213
|
[
|
|
207
214
|
{
|
|
208
|
-
text: `🕒 Time injection: ${timeInjectionMode}`,
|
|
215
|
+
text: `🕒 Time injection: ${getTelegramSettingsStateValueLabel(timeInjectionMode)}`,
|
|
209
216
|
callback_data: "settings:open:time-injection",
|
|
210
217
|
},
|
|
211
218
|
],
|
|
212
219
|
[
|
|
213
220
|
{
|
|
214
|
-
text: `📌 Proactive push: ${proactivePushEnabled ? "
|
|
221
|
+
text: `📌 Proactive push: ${proactivePushEnabled ? "On" : "Off"}`,
|
|
215
222
|
callback_data: "settings:open:proactive",
|
|
216
223
|
},
|
|
217
224
|
],
|
|
@@ -251,11 +258,11 @@ export function buildProactivePushSettingsReplyMarkup(
|
|
|
251
258
|
[{ text: "⬆️ Back", callback_data: "settings:list" }],
|
|
252
259
|
[
|
|
253
260
|
{
|
|
254
|
-
text: proactivePushEnabled ? "🟢
|
|
261
|
+
text: proactivePushEnabled ? "🟢 On" : "⚫️ On",
|
|
255
262
|
callback_data: "settings:set:proactive:on",
|
|
256
263
|
},
|
|
257
264
|
{
|
|
258
|
-
text: proactivePushEnabled ? "⚫️
|
|
265
|
+
text: proactivePushEnabled ? "⚫️ Off" : "🟡 Off",
|
|
259
266
|
callback_data: "settings:set:proactive:off",
|
|
260
267
|
},
|
|
261
268
|
],
|
|
@@ -372,7 +379,10 @@ export async function handleTelegramSettingsMenuCallbackAction(
|
|
|
372
379
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
373
380
|
return true;
|
|
374
381
|
}
|
|
375
|
-
if (
|
|
382
|
+
if (
|
|
383
|
+
data === "settings:open:time-injection" ||
|
|
384
|
+
data === "settings:open:time"
|
|
385
|
+
) {
|
|
376
386
|
await updateTimeInjectionModeSettingsMessage(deps);
|
|
377
387
|
await deps.answerCallbackQuery(callbackQueryId);
|
|
378
388
|
return true;
|
|
@@ -506,7 +516,8 @@ export function createTelegramSettingsMenuRuntime<
|
|
|
506
516
|
? query.data.slice("settings:set:time-injection:".length)
|
|
507
517
|
: query.data.slice("settings:set:time:".length);
|
|
508
518
|
if (
|
|
509
|
-
(hasTimeInjectionPrefix ||
|
|
519
|
+
(hasTimeInjectionPrefix ||
|
|
520
|
+
query.data.startsWith("settings:set:time:")) &&
|
|
510
521
|
(timeMode === "off" ||
|
|
511
522
|
timeMode === "hidden" ||
|
|
512
523
|
timeMode === "always" ||
|
package/lib/menu-status.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { formatTelegramCommandEmojiPrefix } from "./commands.ts";
|
|
|
8
8
|
import {
|
|
9
9
|
getTelegramSectionMainMenuRows,
|
|
10
10
|
type TelegramSectionRegistry,
|
|
11
|
-
} from "./
|
|
11
|
+
} from "./sections.ts";
|
|
12
12
|
import {
|
|
13
13
|
formatStatusButtonLabel,
|
|
14
14
|
type TelegramMenuMessageRuntimeDeps,
|
package/lib/menu.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
handleTelegramSectionSettingsOpen,
|
|
11
11
|
parseTelegramSectionCallback,
|
|
12
12
|
type TelegramSectionRegistry,
|
|
13
|
-
} from "./
|
|
13
|
+
} from "./sections.ts";
|
|
14
14
|
import {
|
|
15
15
|
createTelegramModelMenuStateBuilder,
|
|
16
16
|
handleTelegramModelMenuCallbackAction,
|