@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/lib/menu-queue.ts
CHANGED
|
@@ -31,6 +31,8 @@ type TelegramQueueMenuReplyMarkup = TelegramInlineKeyboardMarkup;
|
|
|
31
31
|
interface TelegramQueueMenuItem {
|
|
32
32
|
chatId: number;
|
|
33
33
|
replyToMessageId: number;
|
|
34
|
+
queueOrder: number;
|
|
35
|
+
isGuest: boolean;
|
|
34
36
|
queuePosition: number;
|
|
35
37
|
isPriority: boolean;
|
|
36
38
|
priorityEmoji?: string;
|
|
@@ -60,6 +62,8 @@ function toTelegramQueueMenuItems<Context>(
|
|
|
60
62
|
return {
|
|
61
63
|
chatId: item.chatId,
|
|
62
64
|
replyToMessageId: item.replyToMessageId,
|
|
65
|
+
queueOrder: item.queueOrder,
|
|
66
|
+
isGuest: item.kind === "prompt" && item.guestQueryId !== undefined,
|
|
63
67
|
queuePosition: index + 1,
|
|
64
68
|
isPriority: item.queueLane === "priority",
|
|
65
69
|
priorityEmoji: item.kind === "prompt" ? item.priorityEmoji : undefined,
|
|
@@ -106,7 +110,9 @@ function buildTelegramQueueMenuReplyMarkup(
|
|
|
106
110
|
return [
|
|
107
111
|
{
|
|
108
112
|
text: label,
|
|
109
|
-
callback_data:
|
|
113
|
+
callback_data: item.isGuest
|
|
114
|
+
? `queue:guest-pick:${item.queueOrder}`
|
|
115
|
+
: `queue:pick:${item.chatId}:${item.replyToMessageId}`,
|
|
110
116
|
},
|
|
111
117
|
];
|
|
112
118
|
});
|
|
@@ -178,6 +184,22 @@ function getTelegramQueueMenuItemText(item: TelegramQueueMenuItem): string {
|
|
|
178
184
|
return `${heading}\n${preview}`;
|
|
179
185
|
}
|
|
180
186
|
|
|
187
|
+
function buildTelegramGuestQueueItemSubmenuReplyMarkup(
|
|
188
|
+
queueOrder: number,
|
|
189
|
+
): TelegramQueueMenuReplyMarkup {
|
|
190
|
+
return {
|
|
191
|
+
inline_keyboard: [
|
|
192
|
+
[{ text: "⬆️ Back", callback_data: "queue:list" }],
|
|
193
|
+
[
|
|
194
|
+
{
|
|
195
|
+
text: "🔴 Skip",
|
|
196
|
+
callback_data: `queue:guest-skip:${queueOrder}`,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
181
203
|
function buildTelegramQueueItemSubmenuReplyMarkup(
|
|
182
204
|
chatId: number,
|
|
183
205
|
replyToMessageId: number,
|
|
@@ -217,6 +239,8 @@ interface TelegramQueueMenuCallbackDeps<Context = unknown> {
|
|
|
217
239
|
chatId: number,
|
|
218
240
|
replyToMessageId: number,
|
|
219
241
|
) => TelegramQueueMenuItem | undefined;
|
|
242
|
+
findGuestItem: (queueOrder: number) => TelegramQueueMenuItem | undefined;
|
|
243
|
+
skipGuest: (queueOrder: number, ctx: Context) => Promise<boolean>;
|
|
220
244
|
togglePriority: (chatId: number, replyToMessageId: number) => boolean;
|
|
221
245
|
setPriority: (
|
|
222
246
|
chatId: number,
|
|
@@ -275,6 +299,38 @@ async function handleTelegramQueueMenuCallback<Context>(
|
|
|
275
299
|
);
|
|
276
300
|
return true;
|
|
277
301
|
}
|
|
302
|
+
const guestPickMatch = data.match(/^queue:guest-pick:(\d+)$/);
|
|
303
|
+
if (guestPickMatch) {
|
|
304
|
+
await handleTelegramGuestQueueMenuPick(
|
|
305
|
+
callbackQueryId,
|
|
306
|
+
replyChatId,
|
|
307
|
+
replyMessageId,
|
|
308
|
+
Number(guestPickMatch[1]),
|
|
309
|
+
deps,
|
|
310
|
+
);
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
const guestSkipMatch = data.match(/^queue:guest-skip:(\d+)$/);
|
|
314
|
+
if (guestSkipMatch) {
|
|
315
|
+
const skipped = await deps.skipGuest(Number(guestSkipMatch[1]), ctx);
|
|
316
|
+
if (!skipped) {
|
|
317
|
+
await refreshStaleTelegramQueueMenuItem(
|
|
318
|
+
callbackQueryId,
|
|
319
|
+
replyChatId,
|
|
320
|
+
replyMessageId,
|
|
321
|
+
deps,
|
|
322
|
+
);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
await updateTelegramQueueMenuList(
|
|
326
|
+
callbackQueryId,
|
|
327
|
+
replyChatId,
|
|
328
|
+
replyMessageId,
|
|
329
|
+
deps,
|
|
330
|
+
"Guest prompt skipped.",
|
|
331
|
+
);
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
278
334
|
const pickMatch = data.match(/^queue:pick:(\d+):(\d+)$/);
|
|
279
335
|
if (pickMatch) {
|
|
280
336
|
await handleTelegramQueueMenuPick(
|
|
@@ -379,6 +435,31 @@ async function refreshStaleTelegramQueueMenuItem<Context>(
|
|
|
379
435
|
);
|
|
380
436
|
}
|
|
381
437
|
|
|
438
|
+
async function handleTelegramGuestQueueMenuPick<Context>(
|
|
439
|
+
callbackQueryId: string,
|
|
440
|
+
replyChatId: number,
|
|
441
|
+
replyMessageId: number,
|
|
442
|
+
queueOrder: number,
|
|
443
|
+
deps: TelegramQueueMenuCallbackDeps<Context>,
|
|
444
|
+
): Promise<void> {
|
|
445
|
+
const item = deps.findGuestItem(queueOrder);
|
|
446
|
+
if (!item) {
|
|
447
|
+
return refreshStaleTelegramQueueMenuItem(
|
|
448
|
+
callbackQueryId,
|
|
449
|
+
replyChatId,
|
|
450
|
+
replyMessageId,
|
|
451
|
+
deps,
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
await deps.updateQueueMessage(
|
|
455
|
+
replyChatId,
|
|
456
|
+
replyMessageId,
|
|
457
|
+
getTelegramQueueMenuItemText(item),
|
|
458
|
+
buildTelegramGuestQueueItemSubmenuReplyMarkup(queueOrder),
|
|
459
|
+
);
|
|
460
|
+
await deps.answerCallbackQuery(callbackQueryId);
|
|
461
|
+
}
|
|
462
|
+
|
|
382
463
|
async function handleTelegramQueueMenuPick<Context>(
|
|
383
464
|
callbackQueryId: string,
|
|
384
465
|
replyChatId: number,
|
|
@@ -606,6 +687,7 @@ export function createTelegramQueueMenuRuntime<
|
|
|
606
687
|
ctx: Context,
|
|
607
688
|
) => Promise<void>;
|
|
608
689
|
updateStatus: (ctx: Context) => void;
|
|
690
|
+
dismissGuestPlaceholder?: (inlineMessageId: string) => Promise<void>;
|
|
609
691
|
}): TelegramQueueMenuRuntime<Context> {
|
|
610
692
|
const sendQueueMenuMessage = createQueueMenuSendMessageAdapter(
|
|
611
693
|
deps.sendInteractiveMessage,
|
|
@@ -628,6 +710,7 @@ export function createTelegramQueueMenuRuntime<
|
|
|
628
710
|
updateStatusMessage: deps.updateStatusMessage,
|
|
629
711
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
630
712
|
updateStatus: deps.updateStatus,
|
|
713
|
+
dismissGuestPlaceholder: deps.dismissGuestPlaceholder,
|
|
631
714
|
}),
|
|
632
715
|
};
|
|
633
716
|
}
|
|
@@ -695,6 +778,7 @@ function createQueueMenuCallbackHandler<
|
|
|
695
778
|
text?: string,
|
|
696
779
|
) => Promise<void>;
|
|
697
780
|
updateStatus: (ctx: Context) => void;
|
|
781
|
+
dismissGuestPlaceholder?: (inlineMessageId: string) => Promise<void>;
|
|
698
782
|
}) {
|
|
699
783
|
return async (
|
|
700
784
|
query: TelegramQueueMenuCallbackQuery,
|
|
@@ -737,6 +821,11 @@ function createQueueMenuCallbackHandler<
|
|
|
737
821
|
const findItem = (cId: number, rId: number) => {
|
|
738
822
|
return findTelegramQueueMenuItem(toMenuItems(), cId, rId);
|
|
739
823
|
};
|
|
824
|
+
const findGuestItem = (queueOrder: number) => {
|
|
825
|
+
return toMenuItems().find((item) => {
|
|
826
|
+
return item.isGuest && item.queueOrder === queueOrder;
|
|
827
|
+
});
|
|
828
|
+
};
|
|
740
829
|
return handleTelegramQueueMenuCallback(
|
|
741
830
|
query.id,
|
|
742
831
|
data,
|
|
@@ -746,6 +835,14 @@ function createQueueMenuCallbackHandler<
|
|
|
746
835
|
{
|
|
747
836
|
getQueuedItems: toMenuItems,
|
|
748
837
|
findItem,
|
|
838
|
+
findGuestItem,
|
|
839
|
+
skipGuest: (queueOrder, callbackContext) => {
|
|
840
|
+
return skipQueuedTelegramGuestPrompt(queueOrder, callbackContext, {
|
|
841
|
+
getQueueSnapshot,
|
|
842
|
+
queueMutationRuntime: deps.queueMutationRuntime,
|
|
843
|
+
dismissGuestPlaceholder: deps.dismissGuestPlaceholder,
|
|
844
|
+
});
|
|
845
|
+
},
|
|
749
846
|
togglePriority: (cId, rId) => {
|
|
750
847
|
return toggleQueuedTelegramPromptPriority(cId, rId, ctx, {
|
|
751
848
|
getQueueSnapshot,
|
|
@@ -772,6 +869,34 @@ function createQueueMenuCallbackHandler<
|
|
|
772
869
|
};
|
|
773
870
|
}
|
|
774
871
|
|
|
872
|
+
async function skipQueuedTelegramGuestPrompt<Context>(
|
|
873
|
+
queueOrder: number,
|
|
874
|
+
ctx: Context,
|
|
875
|
+
deps: {
|
|
876
|
+
getQueueSnapshot: () => Queue.TelegramQueueItem<Context>[];
|
|
877
|
+
queueMutationRuntime: Queue.TelegramQueueMutationController<Context>;
|
|
878
|
+
dismissGuestPlaceholder?: (inlineMessageId: string) => Promise<void>;
|
|
879
|
+
},
|
|
880
|
+
): Promise<boolean> {
|
|
881
|
+
const item = deps.getQueueSnapshot().find((candidate) => {
|
|
882
|
+
return candidate.kind === "prompt" &&
|
|
883
|
+
candidate.guestQueryId !== undefined &&
|
|
884
|
+
candidate.queueOrder === queueOrder;
|
|
885
|
+
});
|
|
886
|
+
if (!item || !deps.queueMutationRuntime.removeGuestPromptByQueueOrder) {
|
|
887
|
+
return false;
|
|
888
|
+
}
|
|
889
|
+
const removed = deps.queueMutationRuntime.removeGuestPromptByQueueOrder(
|
|
890
|
+
queueOrder,
|
|
891
|
+
ctx,
|
|
892
|
+
);
|
|
893
|
+
if (!removed) return false;
|
|
894
|
+
if (item.guestInlineMessageId && deps.dismissGuestPlaceholder) {
|
|
895
|
+
await deps.dismissGuestPlaceholder(item.guestInlineMessageId);
|
|
896
|
+
}
|
|
897
|
+
return true;
|
|
898
|
+
}
|
|
899
|
+
|
|
775
900
|
function getQueueMenuReactionDisposition<Context>(
|
|
776
901
|
item: Queue.TelegramQueueItem<Context>,
|
|
777
902
|
priority: boolean,
|
package/lib/queue.ts
CHANGED
|
@@ -922,6 +922,28 @@ export function removeTelegramQueueItemsByMessageIds<TContext = unknown>(
|
|
|
922
922
|
};
|
|
923
923
|
}
|
|
924
924
|
|
|
925
|
+
export function removeTelegramQueuedGuestPromptByOrder<TContext = unknown>(
|
|
926
|
+
items: TelegramQueueItem<TContext>[],
|
|
927
|
+
queueOrder: number,
|
|
928
|
+
): {
|
|
929
|
+
items: TelegramQueueItem<TContext>[];
|
|
930
|
+
removedItems: PendingTelegramTurn[];
|
|
931
|
+
removedCount: number;
|
|
932
|
+
} {
|
|
933
|
+
const index = items.findIndex((item) => {
|
|
934
|
+
return isPendingTelegramTurn(item) &&
|
|
935
|
+
item.guestQueryId !== undefined &&
|
|
936
|
+
item.queueOrder === queueOrder;
|
|
937
|
+
});
|
|
938
|
+
if (index < 0) return { items, removedItems: [], removedCount: 0 };
|
|
939
|
+
const removedItem = items[index] as PendingTelegramTurn;
|
|
940
|
+
return {
|
|
941
|
+
items: [...items.slice(0, index), ...items.slice(index + 1)],
|
|
942
|
+
removedItems: [removedItem],
|
|
943
|
+
removedCount: 1,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
|
|
925
947
|
export function applyTelegramQueuePromptReactionDisposition<
|
|
926
948
|
TContext = unknown,
|
|
927
949
|
>(
|
|
@@ -2331,6 +2353,10 @@ export interface TelegramQueueMutationController<TContext> {
|
|
|
2331
2353
|
ctx: TContext,
|
|
2332
2354
|
scope?: TelegramQueueMessageScope,
|
|
2333
2355
|
) => number;
|
|
2356
|
+
removeGuestPromptByQueueOrder?: (
|
|
2357
|
+
queueOrder: number,
|
|
2358
|
+
ctx: TContext,
|
|
2359
|
+
) => boolean;
|
|
2334
2360
|
applyReactionByMessageId: (
|
|
2335
2361
|
messageId: number,
|
|
2336
2362
|
disposition: TelegramQueueReactionDisposition,
|
|
@@ -2604,6 +2630,11 @@ export function createTelegramQueueMutationController<TContext>(
|
|
|
2604
2630
|
buildRuntimeDeps(ctx),
|
|
2605
2631
|
scope,
|
|
2606
2632
|
),
|
|
2633
|
+
removeGuestPromptByQueueOrder: (queueOrder, ctx) =>
|
|
2634
|
+
removeTelegramQueuedGuestPromptByOrderRuntime(
|
|
2635
|
+
queueOrder,
|
|
2636
|
+
buildRuntimeDeps(ctx),
|
|
2637
|
+
),
|
|
2607
2638
|
applyReactionByMessageId: (messageId, disposition, ctx, scope) =>
|
|
2608
2639
|
applyTelegramQueuePromptReactionDispositionRuntime(
|
|
2609
2640
|
messageId,
|
|
@@ -2684,6 +2715,21 @@ export function removeTelegramQueueItemsByMessageIdsRuntime<TContext>(
|
|
|
2684
2715
|
return removedCount;
|
|
2685
2716
|
}
|
|
2686
2717
|
|
|
2718
|
+
export function removeTelegramQueuedGuestPromptByOrderRuntime<TContext>(
|
|
2719
|
+
queueOrder: number,
|
|
2720
|
+
deps: TelegramQueueMutationRuntimeDeps<TContext>,
|
|
2721
|
+
): boolean {
|
|
2722
|
+
const { items, removedItems, removedCount } =
|
|
2723
|
+
removeTelegramQueuedGuestPromptByOrder(deps.getQueuedItems(), queueOrder);
|
|
2724
|
+
if (removedCount === 0) return false;
|
|
2725
|
+
// A menu Skip means permanent discard, so settle durable admission before
|
|
2726
|
+
// removing the live item. A failed settlement keeps the prompt retryable.
|
|
2727
|
+
deps.onItemsDiscarded?.(removedItems, deps.ctx);
|
|
2728
|
+
deps.setQueuedItems(items);
|
|
2729
|
+
updateTelegramQueueStatusRuntime(deps);
|
|
2730
|
+
return true;
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2687
2733
|
export function applyTelegramQueuePromptReactionDispositionRuntime<TContext>(
|
|
2688
2734
|
messageId: number,
|
|
2689
2735
|
disposition: TelegramQueueReactionDisposition,
|
package/lib/replies.ts
CHANGED
|
@@ -1018,6 +1018,10 @@ export const TELEGRAM_GUEST_PLACEHOLDER_FRAMES = [
|
|
|
1018
1018
|
"<b>🌏 Working on it...</b>",
|
|
1019
1019
|
] as const;
|
|
1020
1020
|
|
|
1021
|
+
// Telegram does not expose deletion for inline messages. This non-printing,
|
|
1022
|
+
// non-empty edit visually clears a guest placeholder after its query is skipped.
|
|
1023
|
+
export const TELEGRAM_DISMISSED_GUEST_PLACEHOLDER_TEXT = "\u2063";
|
|
1024
|
+
|
|
1021
1025
|
export function buildTelegramGuestPlaceholderFrame(step: number): string {
|
|
1022
1026
|
const frames: readonly string[] = TELEGRAM_GUEST_PLACEHOLDER_FRAMES;
|
|
1023
1027
|
const index = ((step % frames.length) + frames.length) % frames.length;
|
|
@@ -1052,6 +1056,8 @@ export interface TelegramGuestPlaceholderRuntime {
|
|
|
1052
1056
|
stop: (inlineMessageId: string) => Promise<void>;
|
|
1053
1057
|
/** Cancels every loop without waiting for in-flight edits (session shutdown). */
|
|
1054
1058
|
stopAll: () => void;
|
|
1059
|
+
/** Stops rotation and visually clears the inline placeholder. */
|
|
1060
|
+
dismiss: (inlineMessageId: string) => Promise<void>;
|
|
1055
1061
|
}
|
|
1056
1062
|
|
|
1057
1063
|
interface TelegramGuestPlaceholderSession {
|
|
@@ -1202,5 +1208,18 @@ export function createTelegramGuestPlaceholderRuntime(
|
|
|
1202
1208
|
}
|
|
1203
1209
|
sessions.clear();
|
|
1204
1210
|
},
|
|
1211
|
+
async dismiss(inlineMessageId) {
|
|
1212
|
+
await stop(inlineMessageId);
|
|
1213
|
+
try {
|
|
1214
|
+
await deps.editGuestInlineMessage(inlineMessageId, {
|
|
1215
|
+
text: TELEGRAM_DISMISSED_GUEST_PLACEHOLDER_TEXT,
|
|
1216
|
+
parseMode: "HTML",
|
|
1217
|
+
});
|
|
1218
|
+
} catch (error) {
|
|
1219
|
+
deps.recordRuntimeEvent?.("guest", error, {
|
|
1220
|
+
phase: "guest-placeholder-dismiss",
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
},
|
|
1205
1224
|
};
|
|
1206
1225
|
}
|
package/lib/sync.ts
CHANGED
|
@@ -684,7 +684,14 @@ export async function ensureTelegramLeaderThreadBinding(
|
|
|
684
684
|
assertLeaderEpoch("before-workspace-persist");
|
|
685
685
|
await deps.topicTargetStore.persist();
|
|
686
686
|
assertLeaderEpoch("after-workspace-persist");
|
|
687
|
-
|
|
687
|
+
const { target: _target, slot: _slot, threadName: _threadName, displayTitle: _displayTitle, ...rest } = result;
|
|
688
|
+
return {
|
|
689
|
+
...rest,
|
|
690
|
+
target: { ...committed.target },
|
|
691
|
+
slot: committed.slot ?? result.slot,
|
|
692
|
+
...(committed.threadName ? { threadName: committed.threadName } : {}),
|
|
693
|
+
...(committed.displayTitle ? { displayTitle: committed.displayTitle } : {}),
|
|
694
|
+
};
|
|
688
695
|
};
|
|
689
696
|
try {
|
|
690
697
|
const unavailableTargetKeys = new Set([
|