@llblab/pi-telegram 0.49.0 → 0.50.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 +1 -1
- package/CHANGELOG.md +12 -0
- package/README.md +2 -3
- package/dist/lib/activity-verbosity.d.ts +3 -1
- package/dist/lib/activity-verbosity.js +40 -9
- package/dist/lib/bindings.d.ts +1 -3
- package/dist/lib/bindings.js +1 -6
- package/dist/lib/commands.d.ts +3 -5
- package/dist/lib/commands.js +41 -59
- package/dist/lib/extension.js +1 -4
- package/dist/lib/menu-queue.d.ts +1 -0
- package/dist/lib/menu-queue.js +72 -1
- package/dist/lib/queue.d.ts +7 -0
- package/dist/lib/queue.js +27 -0
- package/dist/lib/replies.d.ts +3 -0
- package/dist/lib/replies.js +17 -0
- package/dist/lib/sync.js +8 -1
- package/dist/package.json +1 -1
- package/docs/architecture.md +3 -1
- package/docs/multi-instance-bus.md +1 -1
- package/docs/public-api.md +2 -2
- package/lib/activity-verbosity.ts +46 -9
- package/lib/bindings.ts +0 -10
- package/lib/commands.ts +53 -66
- package/lib/extension.ts +1 -8
- package/lib/menu-queue.ts +126 -1
- package/lib/queue.ts +46 -0
- package/lib/replies.ts +19 -0
- package/lib/sync.ts +8 -1
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -121,7 +121,7 @@ The detailed map is canonical in [`docs/architecture.md`](./docs/architecture.md
|
|
|
121
121
|
- Command templates remain compact and shell-free. Use string leaves or ordered `template` arrays; shell operators are not an execution contract. Examples use portable executable placeholders, never machine-local paths.
|
|
122
122
|
- `telegram_attach` is the canonical file path and `telegram_message` the direct Markdown text/buttons path. Both require current direct or registered-follower authority and must not replace the normal active-turn reply.
|
|
123
123
|
- Inbound handlers transform text/media before queueing; outbound handlers precede programmatic/provider fallbacks. Public contracts and ordering live in `docs/inbound.md`, `docs/outbound.md`, and `docs/public-api.md`.
|
|
124
|
-
- Pi integration uses public hooks and APIs. Telegram `/new` is scheduled against the exact durable update, dispatched only after that update is removed from the journal, and then routed through
|
|
124
|
+
- Pi integration uses public hooks and APIs. Telegram `/new` is scheduled against the exact durable update, dispatched only after that update is removed from the journal, and then routed through the single `/telegram-internal` Pi gateway via `pi.sendUserMessage(..., { expandPromptTemplates: true })`; only a runtime-armed typed action may execute, manual invocation reports that the command cannot be run manually, and the handler receives the real `ExtensionCommandContext` before calling `ctx.newSession()`. Before replacement, CAS-publish one exact expiring handoff in the profile target snapshot. `workspace-thread` successors re-key the matching Workspace binding; `classic-chat` successors preserve Profile/CWD/session/chat continuity without creating a binding or invoking topic APIs. Both atomically claim the intent before one terminal result, and the old `withSession` path never publishes the same success. Never replace the session while inbound authority is unsettled, store stale command contexts, accept an expired or mismatched handoff, inject terminal input, spawn a shadow Pi process, or mutate session files.
|
|
125
125
|
|
|
126
126
|
## 7. Engineering Conventions
|
|
127
127
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.50.1: Queue, lifecycle, and Workspace hotfixes
|
|
8
|
+
|
|
9
|
+
- `Workspace slot recovery`: Uses the canonical binding returned by target-deduplicating Workspace commit for the live leader record and connection result, preventing a provisional recovery slot from consuming a second letter and falsely blocking follower registration at A–Z capacity.
|
|
10
|
+
- `Guest queue Skip`: Lets the leader discard a waiting Guest Mode prompt directly from the queue menu while other work is active. Guest callbacks use stable queue identity, settle durable admission before removal, stop placeholder rotation, and visually clear the inline globe placeholder without starting a model turn.
|
|
11
|
+
- `Internal lifecycle safety`: Binds each armed `/telegram-internal` lifecycle action to an unpredictable one-use token, so manual, mismatched, and replayed calls cannot consume or execute it.
|
|
12
|
+
- `Command surface`: Uses Pi-style argument-first autocomplete hints, keeps descriptions concise and punctuation-consistent, uses `profile` consistently, describes `/telegram-internal` as a manual-use warning, and removes Workspace Thread naming from `/telegram-connect`; Thread names remain configurable from Telegram.
|
|
13
|
+
|
|
14
|
+
## 0.50.0: Internal lifecycle gateway and thinking cadence
|
|
15
|
+
|
|
16
|
+
- `Internal lifecycle gateway`: Replaces the session-specific technical command with one `/telegram-internal` gateway for runtime-armed typed actions. Manual invocation performs no action and explains that the command cannot be run manually; settled `/new` replacement retains the same lifecycle and terminal-result guarantees.
|
|
17
|
+
- `Thinking cadence`: Thinking now follows the same cadence as answer drafts: it accumulates the opening frame for two seconds, then updates at most once every two seconds while still flushing buffered reasoning when the block completes.
|
|
18
|
+
|
|
7
19
|
## 0.49.0: Unified fresh-session continuity
|
|
8
20
|
|
|
9
21
|
- `/new`: Classic and Threaded Mode now share one confirmed fresh-session flow. The settled callback deletes its dialog and publishes an expiring exact-target intent; a same- or cross-process successor preserves the classic chat or re-keys the Thread/slot/name, atomically claims once, then sends one terminal result. Identity mismatches fail closed.
|
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ Paste the bot token. If `~/.pi/agent/telegram.json` already contains a saved tok
|
|
|
55
55
|
/telegram-connect
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
The connected Pi instance owns Telegram polling. Use `/telegram-connect <profile>` to activate a named profile
|
|
58
|
+
The connected Pi instance owns Telegram polling. Use `/telegram-connect <profile>` to activate a named profile. Each profile is a parallel bot runtime with isolated polling, diagnostics, Threaded Mode state, and local bus transport; the `default` profile keeps unsuffixed runtime paths. In classic mode each profile uses a singleton lock. When Telegram private-chat Threaded Mode is available, one live instance becomes the profile's leader and later visible Pi instances register as followers. Reopening or resuming the same Pi session restores its remembered Thread at session startup; a distinct session in the same directory has its own binding and still requires explicit `/telegram-connect` when no remembered binding exists.
|
|
59
59
|
|
|
60
60
|
After an unclean computer shutdown, `/telegram-connect` detects truncated or structurally invalid temporary ownership/routing files, quarantines only the damaged files under `tmp/telegram/recovery/`, and retries once. A journal snapshot removed by older broad temp cleanup is rebuilt when its complete segment history proves an empty result, while a revisionless snapshot is repaired from the first surviving segment's exact predecessor when the reconstructed tail validates. Otherwise the snapshot and segments are quarantined as recovery evidence, a fresh journal is published, and startup continues with an informational diagnostic instead of requiring manual JSON repair. Unsupported journal versions block recovery without rewriting or quarantining the retained files; use a compatible runtime rather than deleting journals. Saved `telegram.json` configuration and runtime diagnostics remain intact. Recovery never replaces a verifiable live owner; if safe automatic recovery cannot complete, the command gives one explicit Pi-restart instruction instead of requiring deletion of the whole `tmp/` directory.
|
|
61
61
|
|
|
@@ -126,7 +126,7 @@ Enable the optional capabilities the bridge needs in the [@BotFather](https://t.
|
|
|
126
126
|
| Model and thinking | Switch model or thinking level from Telegram through safe continuation flows. | Mobile control can adjust execution strategy without tearing down the current session. |
|
|
127
127
|
| Compaction | Confirm `/compact`, show native active status during compaction, and preserve Telegram-owned turn semantics. | Context maintenance is visible and safe from the phone. |
|
|
128
128
|
| Draft previews | Show Telegram's native `…typing` indicator whenever the connected instance is doing agent work, or enable Rich Draft previews for streamed answer text. | Local prompts, Telegram turns, and autonomous continuations remain visibly active while draft visibility stays independent from final rendering. |
|
|
129
|
-
| Activity | Keep the default `verbose` technical surface, show only `thinking`, show only `tools`, or select `quiet` for answer-only delivery. Every instance reloads this shared file-backed choice before a new agent run; thinking
|
|
129
|
+
| Activity | Keep the default `verbose` technical surface, show only `thinking`, show only `tools`, or select `quiet` for answer-only delivery. Every instance reloads this shared file-backed choice before a new agent run; thinking accumulates for two seconds and then updates at most every two seconds in a headerless expandable quote, while each tool uses one iconless closed root row containing nested evidence details. | Persistent collapsed technical activity minimizes chat height and stays bounded, redacted, target-fenced, free of URL previews, and visually separate from semantic assistant answers. |
|
|
130
130
|
| Assistant rendering | Choose Native Rich Markdown or legacy Markdown-to-HTML for final assistant replies. | Renderer compatibility is explicit instead of being conflated with draft previews. |
|
|
131
131
|
| Bridge UI rendering | Render thinking through headerless expandable HTML with inline emphasis/code, render each tool as an iconless native Rich root details tree with immediately visible arguments and collapsed secondary evidence, and keep menus, queue controls, status, settings, diagnostics, and sections on Telegram HTML/plain UI. | Harness-owned surfaces remain operationally predictable and visually distinct from model-authored answers. |
|
|
132
132
|
| Inbound files | Download inbound files to the Pi agent temp directory with size limits. | Screenshots, PDFs, datasets, and artifacts enter Pi as inspectable local files. |
|
|
@@ -183,7 +183,6 @@ Run these inside Pi.
|
|
|
183
183
|
| `/telegram-setup <profile>` | Save or update a named-profile bot token |
|
|
184
184
|
| `/telegram-connect` / `/telegram-connect default` | Activate `profiles.default` and acquire its transport ownership |
|
|
185
185
|
| `/telegram-connect <profile>` | Activate a named profile and acquire its transport ownership |
|
|
186
|
-
| `/telegram-connect [profile] as=Name` | Give a fresh Workspace Thread one unique capitalized Latin-word identity while connecting |
|
|
187
186
|
| `/telegram-disconnect` | Confirm, then stop polling, release ownership, and delete this instance's Threaded Mode tab; graceful Pi quit always preserves restart ownership and independently deletes the tab only when automatic cleanup is enabled |
|
|
188
187
|
| `/telegram-status` | Inspect connection, mode, separate polling/worker progress, journal depth, queue, transport, automatic retry state, and recent diagnostics |
|
|
189
188
|
|
|
@@ -11,7 +11,7 @@ export declare const TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS = 3900;
|
|
|
11
11
|
export declare const TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS = 6;
|
|
12
12
|
export declare const TELEGRAM_REASONING_MESSAGE_MAX_FRAMES = 24;
|
|
13
13
|
export declare const TELEGRAM_REASONING_BUFFER_MAX_CHARS = 1200;
|
|
14
|
-
export declare const TELEGRAM_REASONING_MIN_INTERVAL_MS =
|
|
14
|
+
export declare const TELEGRAM_REASONING_MIN_INTERVAL_MS = 2000;
|
|
15
15
|
export declare const TELEGRAM_TOOL_UPDATE_MAX_ENTRIES = 4;
|
|
16
16
|
interface ToolActivity {
|
|
17
17
|
id: string;
|
|
@@ -41,6 +41,8 @@ export declare function createTelegramActivityVerbosityRuntime<TAuthority>(deps:
|
|
|
41
41
|
getActivityMode: () => "quiet" | "thinking" | "tools" | "verbose";
|
|
42
42
|
refreshActivityMode?: () => Promise<void>;
|
|
43
43
|
getNowMs?: () => number;
|
|
44
|
+
setReasoningTimeout?: (callback: () => void, delayMs: number) => ReturnType<typeof setTimeout>;
|
|
45
|
+
clearReasoningTimeout?: (timer: ReturnType<typeof setTimeout>) => void;
|
|
44
46
|
resolveTarget: (event: TelegramActivityEvent) => TelegramTarget | undefined;
|
|
45
47
|
captureAuthority: () => TAuthority;
|
|
46
48
|
isAuthorityActive: (authority: TAuthority) => boolean;
|
|
@@ -9,7 +9,9 @@ export const TELEGRAM_ACTIVITY_MESSAGE_MAX_CHARS = 3_900;
|
|
|
9
9
|
export const TELEGRAM_ACTIVITY_MESSAGE_MAX_TOOLS = 6;
|
|
10
10
|
export const TELEGRAM_REASONING_MESSAGE_MAX_FRAMES = 24;
|
|
11
11
|
export const TELEGRAM_REASONING_BUFFER_MAX_CHARS = 1_200;
|
|
12
|
-
|
|
12
|
+
// Match native answer drafts: accumulate the opening frame for one full
|
|
13
|
+
// interval, then publish at most one updated frame per interval.
|
|
14
|
+
export const TELEGRAM_REASONING_MIN_INTERVAL_MS = 2_000;
|
|
13
15
|
export const TELEGRAM_TOOL_UPDATE_MAX_ENTRIES = 4;
|
|
14
16
|
function targetEquals(left, right) {
|
|
15
17
|
return left.chatId === right.chatId && left.threadId === right.threadId;
|
|
@@ -234,10 +236,18 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
234
236
|
let reasoningMessage;
|
|
235
237
|
let reasoningBlocked = false;
|
|
236
238
|
let lastReasoningPublishMs = 0;
|
|
239
|
+
let reasoningFlushTimer;
|
|
237
240
|
let toolMessage;
|
|
238
241
|
const tools = new Map();
|
|
239
242
|
const toolOrder = [];
|
|
243
|
+
const clearReasoningFlushTimer = () => {
|
|
244
|
+
if (!reasoningFlushTimer)
|
|
245
|
+
return;
|
|
246
|
+
(deps.clearReasoningTimeout ?? clearTimeout)(reasoningFlushTimer);
|
|
247
|
+
reasoningFlushTimer = undefined;
|
|
248
|
+
};
|
|
240
249
|
const clearActivity = () => {
|
|
250
|
+
clearReasoningFlushTimer();
|
|
241
251
|
activityId = undefined;
|
|
242
252
|
authority = undefined;
|
|
243
253
|
target = undefined;
|
|
@@ -330,6 +340,30 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
330
340
|
deps.recordFailure?.(canEdit ? "reasoning-edit" : "reasoning-send", event, error);
|
|
331
341
|
}
|
|
332
342
|
};
|
|
343
|
+
const scheduleReasoningPublish = (event, acceptedGeneration) => {
|
|
344
|
+
if (reasoningFlushTimer || reasoningBlocked)
|
|
345
|
+
return;
|
|
346
|
+
const elapsed = reasoningMessageFrames === 0
|
|
347
|
+
? 0
|
|
348
|
+
: getNowMs() - lastReasoningPublishMs;
|
|
349
|
+
const delayMs = Math.max(0, TELEGRAM_REASONING_MIN_INTERVAL_MS - elapsed);
|
|
350
|
+
const schedule = deps.setReasoningTimeout ?? setTimeout;
|
|
351
|
+
reasoningFlushTimer = schedule(() => {
|
|
352
|
+
reasoningFlushTimer = undefined;
|
|
353
|
+
const admittedAuthority = authority;
|
|
354
|
+
const task = async () => {
|
|
355
|
+
if (!isCurrent(acceptedGeneration, admittedAuthority) ||
|
|
356
|
+
reasoningChars <= lastReasoningMessageChars)
|
|
357
|
+
return;
|
|
358
|
+
await publishReasoning(event, acceptedGeneration);
|
|
359
|
+
};
|
|
360
|
+
const enqueue = deps.enqueue ?? ((next) => tail.then(next));
|
|
361
|
+
tail = enqueue(task).catch((error) => {
|
|
362
|
+
deps.recordFailure?.("reasoning-send", event, error);
|
|
363
|
+
});
|
|
364
|
+
}, delayMs);
|
|
365
|
+
reasoningFlushTimer?.unref?.();
|
|
366
|
+
};
|
|
333
367
|
const publishTool = async (event, tool, acceptedGeneration) => {
|
|
334
368
|
const admittedAuthority = authority;
|
|
335
369
|
if (!isCurrent(acceptedGeneration, admittedAuthority) || !target) {
|
|
@@ -461,12 +495,8 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
461
495
|
return;
|
|
462
496
|
reasoningChars += event.delta.length;
|
|
463
497
|
reasoningBuffer = `${reasoningBuffer}${event.delta}`.slice(-TELEGRAM_REASONING_BUFFER_MAX_CHARS);
|
|
464
|
-
if (reasoningMessageFrames < TELEGRAM_REASONING_MESSAGE_MAX_FRAMES
|
|
465
|
-
(
|
|
466
|
-
(getNowMs() - lastReasoningPublishMs >=
|
|
467
|
-
TELEGRAM_REASONING_MIN_INTERVAL_MS &&
|
|
468
|
-
reasoningChars - lastReasoningMessageChars >= 160))) {
|
|
469
|
-
await publishReasoning(event, acceptedGeneration);
|
|
498
|
+
if (reasoningMessageFrames < TELEGRAM_REASONING_MESSAGE_MAX_FRAMES) {
|
|
499
|
+
scheduleReasoningPublish(event, acceptedGeneration);
|
|
470
500
|
}
|
|
471
501
|
return;
|
|
472
502
|
}
|
|
@@ -477,6 +507,7 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
477
507
|
reasoningChars = event.text.length;
|
|
478
508
|
reasoningBuffer = event.text.slice(-TELEGRAM_REASONING_BUFFER_MAX_CHARS);
|
|
479
509
|
}
|
|
510
|
+
clearReasoningFlushTimer();
|
|
480
511
|
if (reasoningChars > 0 &&
|
|
481
512
|
reasoningChars > lastReasoningMessageChars &&
|
|
482
513
|
!reasoningBlocked) {
|
|
@@ -550,8 +581,8 @@ export function createTelegramActivityVerbosityRuntime(deps) {
|
|
|
550
581
|
return;
|
|
551
582
|
}
|
|
552
583
|
if (event.type === "agent-end" || event.type === "agent-settled") {
|
|
553
|
-
|
|
554
|
-
|
|
584
|
+
clearReasoningFlushTimer();
|
|
585
|
+
if (reasoningChars > lastReasoningMessageChars &&
|
|
555
586
|
!reasoningBlocked) {
|
|
556
587
|
await publishReasoning(event, acceptedGeneration);
|
|
557
588
|
}
|
package/dist/lib/bindings.d.ts
CHANGED
|
@@ -165,8 +165,6 @@ interface TelegramCommandsAndToolsBindingDeps {
|
|
|
165
165
|
stopPolling?: () => Promise<void | string>;
|
|
166
166
|
recoverPollingStart?: Commands.TelegramBridgeCommandRegistrationDeps["recoverPollingStart"];
|
|
167
167
|
getDisconnectThreadName?: () => string | undefined;
|
|
168
|
-
setRequestedThreadNameForPollingStart?: (threadName: string | undefined) => void;
|
|
169
|
-
validateThreadName?: Commands.TelegramBridgeCommandRegistrationDeps["validateThreadName"];
|
|
170
168
|
onTransportChanged?: () => Promise<void> | void;
|
|
171
169
|
getStatusLines: (options?: Status.TelegramBridgeStatusLineOptions) => string[];
|
|
172
170
|
buttonActionStore: OutboundHandlers.TelegramButtonActionStore;
|
|
@@ -196,7 +194,7 @@ interface TelegramCommandsAndToolsBindingDeps {
|
|
|
196
194
|
updateStatus: TelegramBridgeStatusUpdater;
|
|
197
195
|
recordRuntimeEvent: TelegramRuntimeEventRecorder;
|
|
198
196
|
}
|
|
199
|
-
export declare function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, recoverPollingStart, getDisconnectThreadName,
|
|
197
|
+
export declare function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, recoverPollingStart, getDisconnectThreadName, onTransportChanged, getStatusLines, buttonActionStore, sendMarkdownReply, sendChannelMarkdownMessage, sendChannelMediaMessage, listChannelPosts, mutateChannelPost, callMultipart, getDefaultChatId, getDefaultTarget, resolveAgentTarget, routeAgentMessage, canSendDirect, setGenerativeAppLiveSurfaceRuntime, recordRuntimeEvent, updateStatus, }: TelegramCommandsAndToolsBindingDeps): void;
|
|
200
198
|
interface TelegramLifecycleBindingDeps {
|
|
201
199
|
pi: Pi.ExtensionAPI;
|
|
202
200
|
publicationRuntime: TelegramBridgePublicationRuntime;
|
package/dist/lib/bindings.js
CHANGED
|
@@ -323,7 +323,7 @@ export function createTelegramActivityBindingRuntime(deps) {
|
|
|
323
323
|
},
|
|
324
324
|
};
|
|
325
325
|
}
|
|
326
|
-
export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, recoverPollingStart, getDisconnectThreadName,
|
|
326
|
+
export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, recoverPollingStart, getDisconnectThreadName, onTransportChanged, getStatusLines, buttonActionStore, sendMarkdownReply, sendChannelMarkdownMessage, sendChannelMediaMessage, listChannelPosts, mutateChannelPost, callMultipart, getDefaultChatId, getDefaultTarget, resolveAgentTarget, routeAgentMessage, canSendDirect, setGenerativeAppLiveSurfaceRuntime, recordRuntimeEvent, updateStatus, }) {
|
|
327
327
|
GenerativeApps.registerTelegramBindTool(pi, {
|
|
328
328
|
agentDir,
|
|
329
329
|
getActiveProfileName: configStore.getActiveProfileName,
|
|
@@ -453,7 +453,6 @@ export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, pe
|
|
|
453
453
|
hasBotToken: configStore.hasBotToken,
|
|
454
454
|
getBotTokenDiagnostic: configStore.getBotTokenDiagnostic,
|
|
455
455
|
startPolling: async (ctx, options) => {
|
|
456
|
-
setRequestedThreadNameForPollingStart?.(options?.requestedThreadName);
|
|
457
456
|
try {
|
|
458
457
|
return await lockedPollingRuntime.start(ctx, options);
|
|
459
458
|
}
|
|
@@ -461,14 +460,10 @@ export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, pe
|
|
|
461
460
|
recordRuntimeEvent("recovery", error, { phase: "polling-start" });
|
|
462
461
|
throw error;
|
|
463
462
|
}
|
|
464
|
-
finally {
|
|
465
|
-
setRequestedThreadNameForPollingStart?.(undefined);
|
|
466
|
-
}
|
|
467
463
|
},
|
|
468
464
|
stopPolling: stopPolling ?? lockedPollingRuntime.stop,
|
|
469
465
|
recoverPollingStart,
|
|
470
466
|
getDisconnectThreadName,
|
|
471
|
-
validateThreadName,
|
|
472
467
|
queueAgentConnectionContext,
|
|
473
468
|
updateStatus,
|
|
474
469
|
getProfileNames: () => Config.getTelegramProfileNames(configStore.getStoredConfig()),
|
package/dist/lib/commands.d.ts
CHANGED
|
@@ -87,7 +87,6 @@ export declare function createTelegramBotCommandRegistrar(deps: TelegramBotComma
|
|
|
87
87
|
export interface TelegramBridgeCommandStartPollingOptions {
|
|
88
88
|
force?: boolean;
|
|
89
89
|
forceFreshLeaderThread?: boolean;
|
|
90
|
-
requestedThreadName?: string;
|
|
91
90
|
}
|
|
92
91
|
export interface TelegramBridgeCommandStartPollingResult {
|
|
93
92
|
ok: boolean;
|
|
@@ -119,7 +118,6 @@ export interface TelegramBridgeCommandRegistrationDeps {
|
|
|
119
118
|
getProfileNames?: () => string[];
|
|
120
119
|
activateDefaultProfileConfig?: (ctx: ExtensionCommandContext) => Promise<void>;
|
|
121
120
|
activateProfileConfig?: (ctx: ExtensionCommandContext, profileName: string) => Promise<boolean>;
|
|
122
|
-
validateThreadName?: (threadName: string) => string | undefined;
|
|
123
121
|
}
|
|
124
122
|
export type TelegramThreadDisplayNameRenamePort = (target: {
|
|
125
123
|
chatId: number;
|
|
@@ -145,7 +143,6 @@ export declare function createTelegramThreadDisplayNameRenameBinding(): {
|
|
|
145
143
|
bind: (rename: TelegramThreadDisplayNameRenamePort) => void;
|
|
146
144
|
rename: TelegramThreadDisplayNameRenamePort;
|
|
147
145
|
};
|
|
148
|
-
export declare function parseTelegramRequestedThreadName(args: string): string | undefined;
|
|
149
146
|
export declare function registerTelegramBridgeCommands(pi: ExtensionAPI, deps: TelegramBridgeCommandRegistrationDeps): void;
|
|
150
147
|
export declare const TELEGRAM_RESERVED_COMMAND_NAMES: readonly ["stop", "name", "new", "abort", "next", "continue", "status", "queue", "compact", "model", "thinking", "settings", "help", "start"];
|
|
151
148
|
export type TelegramReservedCommandName = (typeof TELEGRAM_RESERVED_COMMAND_NAMES)[number];
|
|
@@ -574,8 +571,9 @@ export declare function createTelegramCommandHandler<TMessage extends TelegramCo
|
|
|
574
571
|
export declare function createTelegramCommandOrPromptRuntime<TMessage, TContext>(deps: TelegramCommandOrPromptRuntimeDeps<TMessage, TContext>): {
|
|
575
572
|
dispatchMessages: (messages: TMessage[], ctx: TContext) => Promise<void>;
|
|
576
573
|
};
|
|
577
|
-
export declare const
|
|
578
|
-
export declare const
|
|
574
|
+
export declare const TELEGRAM_INTERNAL_COMMAND_NAME = "telegram-internal";
|
|
575
|
+
export declare const TELEGRAM_INTERNAL_COMMAND_DESCRIPTION = "Internal Telegram command cannot be run manually";
|
|
576
|
+
export declare const TELEGRAM_INTERNAL_MANUAL_USE_MESSAGE = "This internal Telegram command cannot be run manually.";
|
|
579
577
|
export declare function delayTelegramSessionAction(delayMs: number): Promise<void>;
|
|
580
578
|
export interface TelegramSessionActionRuntimeDeps {
|
|
581
579
|
registerCommand: Pi.ExtensionAPI["registerCommand"];
|
package/dist/lib/commands.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Zones: telegram controls, pi agent commands, queue controls
|
|
4
4
|
* Owns Telegram slash-command normalization, bot command metadata, pi-side command registration, and command-initiated session replacement orchestration behind runtime ports
|
|
5
5
|
*/
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
6
7
|
import { pairTelegramUserIfNeeded, TELEGRAM_DEFAULT_PROFILE_NAME, } from "./config.js";
|
|
7
8
|
import { escapeHtml } from "./rendering.js";
|
|
8
9
|
import { createTelegramControlItemBuilder, createTelegramControlQueueController, createTelegramQueueAdmissionReceipt, } from "./queue.js";
|
|
@@ -241,14 +242,6 @@ function parseTelegramProfileArg(args) {
|
|
|
241
242
|
return undefined;
|
|
242
243
|
return word === TELEGRAM_DEFAULT_PROFILE_NAME ? undefined : word;
|
|
243
244
|
}
|
|
244
|
-
export function parseTelegramRequestedThreadName(args) {
|
|
245
|
-
const token = args
|
|
246
|
-
.trim()
|
|
247
|
-
.split(/\s+/)
|
|
248
|
-
.find((word) => /^as=/i.test(word));
|
|
249
|
-
const value = token?.slice(3).trim();
|
|
250
|
-
return value || undefined;
|
|
251
|
-
}
|
|
252
245
|
function formatTelegramTakeoverTitle(ctx) {
|
|
253
246
|
return ctx.ui.theme.fg("accent", "pi-telegram");
|
|
254
247
|
}
|
|
@@ -262,7 +255,7 @@ function formatTelegramTakeoverPrompt(ctx, owner) {
|
|
|
262
255
|
}
|
|
263
256
|
export function registerTelegramBridgeCommands(pi, deps) {
|
|
264
257
|
pi.registerCommand("telegram-setup", {
|
|
265
|
-
description: "Configure Telegram bot token
|
|
258
|
+
description: "<profile> — Configure Telegram bot token",
|
|
266
259
|
handler: async (args, ctx) => {
|
|
267
260
|
await deps.promptForConfig(ctx, parseTelegramProfileArg(args));
|
|
268
261
|
},
|
|
@@ -275,26 +268,14 @@ export function registerTelegramBridgeCommands(pi, deps) {
|
|
|
275
268
|
},
|
|
276
269
|
});
|
|
277
270
|
pi.registerCommand("telegram-connect", {
|
|
278
|
-
description: "
|
|
271
|
+
description: "<profile> — Start Telegram bridge",
|
|
279
272
|
handler: async (args, ctx) => {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
.trim()
|
|
283
|
-
.split(/\s+/)
|
|
284
|
-
.filter((word) => /^as=/i.test(word));
|
|
285
|
-
const requestedThreadName = parseTelegramRequestedThreadName(args);
|
|
286
|
-
const requestedNameError = requestedNameTokens.length > 1
|
|
287
|
-
? "Specify at most one as=Name Workspace Thread name."
|
|
288
|
-
: requestedNameTokens.length === 1 && !requestedThreadName
|
|
289
|
-
? "Usage: /telegram-connect [profile] as=Flightprice"
|
|
290
|
-
: requestedThreadName
|
|
291
|
-
? deps.validateThreadName?.(requestedThreadName)
|
|
292
|
-
: undefined;
|
|
293
|
-
if (requestedNameError) {
|
|
294
|
-
ctx.ui.notify(requestedNameError, "warning");
|
|
273
|
+
if (args.trim().split(/\s+/).some((word) => /^as=/i.test(word))) {
|
|
274
|
+
ctx.ui.notify("Thread names are configured from Telegram, not from Pi commands.", "warning");
|
|
295
275
|
deps.updateStatus(ctx);
|
|
296
276
|
return;
|
|
297
277
|
}
|
|
278
|
+
const profileName = parseTelegramProfileArg(args);
|
|
298
279
|
if (profileName && deps.activateProfileConfig) {
|
|
299
280
|
const ok = await deps.activateProfileConfig(ctx, profileName);
|
|
300
281
|
if (!ok) {
|
|
@@ -357,7 +338,6 @@ export function registerTelegramBridgeCommands(pi, deps) {
|
|
|
357
338
|
};
|
|
358
339
|
let result = await startWithRecovery({
|
|
359
340
|
forceFreshLeaderThread: true,
|
|
360
|
-
...(requestedThreadName ? { requestedThreadName } : {}),
|
|
361
341
|
});
|
|
362
342
|
if (result && !result.ok && result.canTakeover) {
|
|
363
343
|
const confirmed = await ctx.ui.confirm(formatTelegramTakeoverTitle(ctx), formatTelegramTakeoverPrompt(ctx, result.owner));
|
|
@@ -369,7 +349,6 @@ export function registerTelegramBridgeCommands(pi, deps) {
|
|
|
369
349
|
result = await startWithRecovery({
|
|
370
350
|
force: true,
|
|
371
351
|
forceFreshLeaderThread: true,
|
|
372
|
-
...(requestedThreadName ? { requestedThreadName } : {}),
|
|
373
352
|
});
|
|
374
353
|
}
|
|
375
354
|
if (result?.message) {
|
|
@@ -382,7 +361,7 @@ export function registerTelegramBridgeCommands(pi, deps) {
|
|
|
382
361
|
},
|
|
383
362
|
});
|
|
384
363
|
pi.registerCommand("telegram-disconnect", {
|
|
385
|
-
description: "Stop Telegram
|
|
364
|
+
description: "Stop Telegram and delete current thread in Threaded Mode",
|
|
386
365
|
handler: async (_args, ctx) => {
|
|
387
366
|
const threadName = deps.getDisconnectThreadName?.();
|
|
388
367
|
if (threadName) {
|
|
@@ -1232,8 +1211,9 @@ async function handleTelegramCommandRuntime(commandName, message, ctx, deps, com
|
|
|
1232
1211
|
},
|
|
1233
1212
|
}, commandArgs);
|
|
1234
1213
|
}
|
|
1235
|
-
export const
|
|
1236
|
-
export const
|
|
1214
|
+
export const TELEGRAM_INTERNAL_COMMAND_NAME = "telegram-internal";
|
|
1215
|
+
export const TELEGRAM_INTERNAL_COMMAND_DESCRIPTION = "Internal Telegram command cannot be run manually";
|
|
1216
|
+
export const TELEGRAM_INTERNAL_MANUAL_USE_MESSAGE = "This internal Telegram command cannot be run manually.";
|
|
1237
1217
|
export function delayTelegramSessionAction(delayMs) {
|
|
1238
1218
|
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1239
1219
|
}
|
|
@@ -1364,8 +1344,7 @@ export function createTelegramSessionActionAssembly(deps) {
|
|
|
1364
1344
|
export function createTelegramSessionActionRuntime(deps) {
|
|
1365
1345
|
let pendingUpdateId;
|
|
1366
1346
|
let pendingTarget;
|
|
1367
|
-
let
|
|
1368
|
-
let commandUpdateId;
|
|
1347
|
+
let pendingAction;
|
|
1369
1348
|
let registered = false;
|
|
1370
1349
|
const reportFailure = (error) => {
|
|
1371
1350
|
try {
|
|
@@ -1380,33 +1359,34 @@ export function createTelegramSessionActionRuntime(deps) {
|
|
|
1380
1359
|
if (registered)
|
|
1381
1360
|
return;
|
|
1382
1361
|
registered = true;
|
|
1383
|
-
deps.registerCommand(
|
|
1384
|
-
description:
|
|
1385
|
-
handler: async (
|
|
1386
|
-
|
|
1362
|
+
deps.registerCommand(TELEGRAM_INTERNAL_COMMAND_NAME, {
|
|
1363
|
+
description: TELEGRAM_INTERNAL_COMMAND_DESCRIPTION,
|
|
1364
|
+
handler: async (args, ctx) => {
|
|
1365
|
+
const action = pendingAction;
|
|
1366
|
+
if (!action || args.trim() !== action.token) {
|
|
1367
|
+
ctx.ui.notify(TELEGRAM_INTERNAL_MANUAL_USE_MESSAGE, "warning");
|
|
1387
1368
|
return;
|
|
1388
|
-
commandPending = false;
|
|
1389
|
-
const target = pendingTarget;
|
|
1390
|
-
const updateId = commandUpdateId;
|
|
1391
|
-
pendingTarget = undefined;
|
|
1392
|
-
commandUpdateId = undefined;
|
|
1393
|
-
if (!target || updateId === undefined)
|
|
1394
|
-
return;
|
|
1395
|
-
try {
|
|
1396
|
-
await deps.prepareReplacement?.(ctx, updateId, target);
|
|
1397
|
-
const result = await ctx.newSession();
|
|
1398
|
-
if (result.cancelled)
|
|
1399
|
-
await deps.notifyResult(target, "cancelled");
|
|
1400
1369
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1370
|
+
pendingAction = undefined;
|
|
1371
|
+
switch (action.kind) {
|
|
1372
|
+
case "replace-session":
|
|
1373
|
+
try {
|
|
1374
|
+
await deps.prepareReplacement?.(ctx, action.updateId, action.target);
|
|
1375
|
+
const result = await ctx.newSession();
|
|
1376
|
+
if (result.cancelled)
|
|
1377
|
+
await deps.notifyResult(action.target, "cancelled");
|
|
1378
|
+
}
|
|
1379
|
+
catch (error) {
|
|
1380
|
+
reportFailure(error);
|
|
1381
|
+
await deps.notifyResult(action.target, "failure");
|
|
1382
|
+
}
|
|
1383
|
+
return;
|
|
1404
1384
|
}
|
|
1405
1385
|
},
|
|
1406
1386
|
});
|
|
1407
1387
|
},
|
|
1408
1388
|
scheduleAfterUpdate(updateId, target) {
|
|
1409
|
-
if (pendingUpdateId !== undefined ||
|
|
1389
|
+
if (pendingUpdateId !== undefined || pendingAction !== undefined)
|
|
1410
1390
|
return false;
|
|
1411
1391
|
pendingUpdateId = updateId;
|
|
1412
1392
|
pendingTarget = { ...target };
|
|
@@ -1416,21 +1396,23 @@ export function createTelegramSessionActionRuntime(deps) {
|
|
|
1416
1396
|
if (pendingUpdateId !== updateId)
|
|
1417
1397
|
return;
|
|
1418
1398
|
pendingUpdateId = undefined;
|
|
1419
|
-
|
|
1420
|
-
|
|
1399
|
+
const target = pendingTarget;
|
|
1400
|
+
pendingTarget = undefined;
|
|
1401
|
+
if (!target)
|
|
1402
|
+
return;
|
|
1403
|
+
const token = randomUUID();
|
|
1404
|
+
pendingAction = { kind: "replace-session", token, updateId, target };
|
|
1421
1405
|
void Promise.resolve()
|
|
1422
|
-
.then(() => deps.sendUserMessage(`/${
|
|
1406
|
+
.then(() => deps.sendUserMessage(`/${TELEGRAM_INTERNAL_COMMAND_NAME} ${token}`, {
|
|
1423
1407
|
expandPromptTemplates: true,
|
|
1424
1408
|
}))
|
|
1425
1409
|
.catch((error) => {
|
|
1426
|
-
|
|
1427
|
-
commandUpdateId = undefined;
|
|
1428
|
-
pendingTarget = undefined;
|
|
1410
|
+
pendingAction = undefined;
|
|
1429
1411
|
reportFailure(error);
|
|
1430
1412
|
});
|
|
1431
1413
|
},
|
|
1432
1414
|
hasPending() {
|
|
1433
|
-
return pendingUpdateId !== undefined ||
|
|
1415
|
+
return pendingUpdateId !== undefined || pendingAction !== undefined;
|
|
1434
1416
|
},
|
|
1435
1417
|
};
|
|
1436
1418
|
}
|
package/dist/lib/extension.js
CHANGED
|
@@ -689,6 +689,7 @@ export default function (pi) {
|
|
|
689
689
|
storeModelMenuState: modelMenuRuntime.storeState,
|
|
690
690
|
updateStatusMessage: menuActions.updateStatusMessage,
|
|
691
691
|
updateStatus,
|
|
692
|
+
dismissGuestPlaceholder: guestPlaceholderRuntime.dismiss,
|
|
692
693
|
});
|
|
693
694
|
const threadDisplaySettingsRuntime = ThreadDisplay.createTelegramThreadDisplaySettingsRuntime({
|
|
694
695
|
getTarget() {
|
|
@@ -1449,10 +1450,6 @@ export default function (pi) {
|
|
|
1449
1450
|
return undefined;
|
|
1450
1451
|
return record.threadName ?? "current Telegram thread";
|
|
1451
1452
|
},
|
|
1452
|
-
setRequestedThreadNameForPollingStart: telegramThreadCapabilityState.setRequestedThreadName,
|
|
1453
|
-
validateThreadName(threadName) {
|
|
1454
|
-
return Threads.getTelegramTopicThreadNameValidationError(threadName, undefined);
|
|
1455
|
-
},
|
|
1456
1453
|
onTransportChanged() {
|
|
1457
1454
|
deliveryLifecycleRuntime.onSessionStart();
|
|
1458
1455
|
activityVerbosityRuntime.reset();
|
package/dist/lib/menu-queue.d.ts
CHANGED
|
@@ -33,5 +33,6 @@ export declare function createTelegramQueueMenuRuntime<Context, TModel extends M
|
|
|
33
33
|
storeModelMenuState: (state: TelegramModelMenuState<TModel>) => void;
|
|
34
34
|
updateStatusMessage: (state: TelegramModelMenuState<TModel>, ctx: Context) => Promise<void>;
|
|
35
35
|
updateStatus: (ctx: Context) => void;
|
|
36
|
+
dismissGuestPlaceholder?: (inlineMessageId: string) => Promise<void>;
|
|
36
37
|
}): TelegramQueueMenuRuntime<Context>;
|
|
37
38
|
export {};
|
package/dist/lib/menu-queue.js
CHANGED
|
@@ -34,6 +34,8 @@ function toTelegramQueueMenuItems(items) {
|
|
|
34
34
|
return {
|
|
35
35
|
chatId: item.chatId,
|
|
36
36
|
replyToMessageId: item.replyToMessageId,
|
|
37
|
+
queueOrder: item.queueOrder,
|
|
38
|
+
isGuest: item.kind === "prompt" && item.guestQueryId !== undefined,
|
|
37
39
|
queuePosition: index + 1,
|
|
38
40
|
isPriority: item.queueLane === "priority",
|
|
39
41
|
priorityEmoji: item.kind === "prompt" ? item.priorityEmoji : undefined,
|
|
@@ -72,7 +74,9 @@ function buildTelegramQueueMenuReplyMarkup(items, emptyRefreshIndex = 0) {
|
|
|
72
74
|
return [
|
|
73
75
|
{
|
|
74
76
|
text: label,
|
|
75
|
-
callback_data:
|
|
77
|
+
callback_data: item.isGuest
|
|
78
|
+
? `queue:guest-pick:${item.queueOrder}`
|
|
79
|
+
: `queue:pick:${item.chatId}:${item.replyToMessageId}`,
|
|
76
80
|
},
|
|
77
81
|
];
|
|
78
82
|
});
|
|
@@ -128,6 +132,19 @@ function getTelegramQueueMenuItemText(item) {
|
|
|
128
132
|
const preview = `<pre>${escapeTelegramQueueMenuHtmlPreview(item.promptText)}</pre>`;
|
|
129
133
|
return `${heading}\n${preview}`;
|
|
130
134
|
}
|
|
135
|
+
function buildTelegramGuestQueueItemSubmenuReplyMarkup(queueOrder) {
|
|
136
|
+
return {
|
|
137
|
+
inline_keyboard: [
|
|
138
|
+
[{ text: "⬆️ Back", callback_data: "queue:list" }],
|
|
139
|
+
[
|
|
140
|
+
{
|
|
141
|
+
text: "🔴 Skip",
|
|
142
|
+
callback_data: `queue:guest-skip:${queueOrder}`,
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
131
148
|
function buildTelegramQueueItemSubmenuReplyMarkup(chatId, replyToMessageId, isPriority, isSkipped) {
|
|
132
149
|
return {
|
|
133
150
|
inline_keyboard: [
|
|
@@ -171,6 +188,21 @@ async function handleTelegramQueueMenuCallback(callbackQueryId, data, replyChatI
|
|
|
171
188
|
await updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMessageId, deps, undefined, refreshMatch[1] === undefined ? 0 : Number(refreshMatch[1]));
|
|
172
189
|
return true;
|
|
173
190
|
}
|
|
191
|
+
const guestPickMatch = data.match(/^queue:guest-pick:(\d+)$/);
|
|
192
|
+
if (guestPickMatch) {
|
|
193
|
+
await handleTelegramGuestQueueMenuPick(callbackQueryId, replyChatId, replyMessageId, Number(guestPickMatch[1]), deps);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
const guestSkipMatch = data.match(/^queue:guest-skip:(\d+)$/);
|
|
197
|
+
if (guestSkipMatch) {
|
|
198
|
+
const skipped = await deps.skipGuest(Number(guestSkipMatch[1]), ctx);
|
|
199
|
+
if (!skipped) {
|
|
200
|
+
await refreshStaleTelegramQueueMenuItem(callbackQueryId, replyChatId, replyMessageId, deps);
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
await updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMessageId, deps, "Guest prompt skipped.");
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
174
206
|
const pickMatch = data.match(/^queue:pick:(\d+):(\d+)$/);
|
|
175
207
|
if (pickMatch) {
|
|
176
208
|
await handleTelegramQueueMenuPick(callbackQueryId, replyChatId, replyMessageId, Number(pickMatch[1]), Number(pickMatch[2]), deps);
|
|
@@ -208,6 +240,14 @@ async function updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMe
|
|
|
208
240
|
async function refreshStaleTelegramQueueMenuItem(callbackQueryId, replyChatId, replyMessageId, deps) {
|
|
209
241
|
await updateTelegramQueueMenuList(callbackQueryId, replyChatId, replyMessageId, deps, "Item no longer in queue.");
|
|
210
242
|
}
|
|
243
|
+
async function handleTelegramGuestQueueMenuPick(callbackQueryId, replyChatId, replyMessageId, queueOrder, deps) {
|
|
244
|
+
const item = deps.findGuestItem(queueOrder);
|
|
245
|
+
if (!item) {
|
|
246
|
+
return refreshStaleTelegramQueueMenuItem(callbackQueryId, replyChatId, replyMessageId, deps);
|
|
247
|
+
}
|
|
248
|
+
await deps.updateQueueMessage(replyChatId, replyMessageId, getTelegramQueueMenuItemText(item), buildTelegramGuestQueueItemSubmenuReplyMarkup(queueOrder));
|
|
249
|
+
await deps.answerCallbackQuery(callbackQueryId);
|
|
250
|
+
}
|
|
211
251
|
async function handleTelegramQueueMenuPick(callbackQueryId, replyChatId, replyMessageId, chatId, msgId, deps) {
|
|
212
252
|
const item = deps.findItem(chatId, msgId);
|
|
213
253
|
if (!item) {
|
|
@@ -272,6 +312,7 @@ export function createTelegramQueueMenuRuntime(deps) {
|
|
|
272
312
|
updateStatusMessage: deps.updateStatusMessage,
|
|
273
313
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
274
314
|
updateStatus: deps.updateStatus,
|
|
315
|
+
dismissGuestPlaceholder: deps.dismissGuestPlaceholder,
|
|
275
316
|
}),
|
|
276
317
|
};
|
|
277
318
|
}
|
|
@@ -318,9 +359,22 @@ function createQueueMenuCallbackHandler(deps) {
|
|
|
318
359
|
const findItem = (cId, rId) => {
|
|
319
360
|
return findTelegramQueueMenuItem(toMenuItems(), cId, rId);
|
|
320
361
|
};
|
|
362
|
+
const findGuestItem = (queueOrder) => {
|
|
363
|
+
return toMenuItems().find((item) => {
|
|
364
|
+
return item.isGuest && item.queueOrder === queueOrder;
|
|
365
|
+
});
|
|
366
|
+
};
|
|
321
367
|
return handleTelegramQueueMenuCallback(query.id, data, chatId, messageId, ctx, {
|
|
322
368
|
getQueuedItems: toMenuItems,
|
|
323
369
|
findItem,
|
|
370
|
+
findGuestItem,
|
|
371
|
+
skipGuest: (queueOrder, callbackContext) => {
|
|
372
|
+
return skipQueuedTelegramGuestPrompt(queueOrder, callbackContext, {
|
|
373
|
+
getQueueSnapshot,
|
|
374
|
+
queueMutationRuntime: deps.queueMutationRuntime,
|
|
375
|
+
dismissGuestPlaceholder: deps.dismissGuestPlaceholder,
|
|
376
|
+
});
|
|
377
|
+
},
|
|
324
378
|
togglePriority: (cId, rId) => {
|
|
325
379
|
return toggleQueuedTelegramPromptPriority(cId, rId, ctx, {
|
|
326
380
|
getQueueSnapshot,
|
|
@@ -345,6 +399,23 @@ function createQueueMenuCallbackHandler(deps) {
|
|
|
345
399
|
});
|
|
346
400
|
};
|
|
347
401
|
}
|
|
402
|
+
async function skipQueuedTelegramGuestPrompt(queueOrder, ctx, deps) {
|
|
403
|
+
const item = deps.getQueueSnapshot().find((candidate) => {
|
|
404
|
+
return candidate.kind === "prompt" &&
|
|
405
|
+
candidate.guestQueryId !== undefined &&
|
|
406
|
+
candidate.queueOrder === queueOrder;
|
|
407
|
+
});
|
|
408
|
+
if (!item || !deps.queueMutationRuntime.removeGuestPromptByQueueOrder) {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
const removed = deps.queueMutationRuntime.removeGuestPromptByQueueOrder(queueOrder, ctx);
|
|
412
|
+
if (!removed)
|
|
413
|
+
return false;
|
|
414
|
+
if (item.guestInlineMessageId && deps.dismissGuestPlaceholder) {
|
|
415
|
+
await deps.dismissGuestPlaceholder(item.guestInlineMessageId);
|
|
416
|
+
}
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
348
419
|
function getQueueMenuReactionDisposition(item, priority, skipped) {
|
|
349
420
|
if (priority && skipped) {
|
|
350
421
|
return {
|