@llblab/pi-telegram 0.17.4 → 0.18.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 +67 -32
- package/BACKLOG.md +59 -14
- package/CHANGELOG.md +40 -15
- package/README.md +63 -35
- package/docs/README.md +3 -1
- package/docs/architecture.md +55 -23
- package/docs/callback-namespaces.md +1 -1
- package/docs/inbound.md +1 -1
- package/docs/locks.md +0 -2
- package/docs/multi-instance-bus.md +483 -0
- package/docs/outbound.md +4 -3
- package/docs/public-api.md +12 -10
- package/docs/sections.md +2 -2
- package/docs/ui-style.md +76 -0
- package/index.ts +789 -32
- package/lib/bindings.ts +68 -12
- package/lib/bus-api.ts +314 -0
- package/lib/bus-follower.ts +853 -0
- package/lib/bus-leader.ts +915 -0
- package/lib/bus.ts +866 -0
- package/lib/command-templates.ts +9 -11
- package/lib/commands.ts +133 -47
- package/lib/config.ts +53 -5
- package/lib/lifecycle.ts +23 -7
- package/lib/locks.ts +230 -66
- package/lib/media.ts +30 -2
- package/lib/menu-model.ts +48 -17
- package/lib/menu-queue.ts +51 -20
- package/lib/menu-settings.ts +9 -5
- package/lib/menu-status.ts +3 -0
- package/lib/menu-thinking.ts +3 -0
- package/lib/menu.ts +67 -26
- package/lib/outbound-attachments.ts +102 -17
- package/lib/outbound-buttons.ts +6 -2
- package/lib/outbound-voice.ts +31 -11
- package/lib/outbound.ts +6 -4
- package/lib/ownership.ts +119 -0
- package/lib/pi.ts +26 -3
- package/lib/polling.ts +477 -7
- package/lib/preview.ts +141 -88
- package/lib/prompt-templates.ts +3 -3
- package/lib/prompts.ts +80 -30
- package/lib/queue.ts +193 -91
- package/lib/rendering.ts +0 -25
- package/lib/replies.ts +187 -55
- package/lib/routing.ts +1673 -9
- package/lib/runtime-log.ts +123 -0
- package/lib/runtime.ts +84 -12
- package/lib/sections.ts +28 -21
- package/lib/setup.ts +1 -1
- package/lib/status.ts +532 -9
- package/lib/sync.ts +618 -0
- package/lib/target.ts +49 -0
- package/lib/telegram-api.ts +405 -40
- package/lib/text-groups.ts +5 -1
- package/lib/thread-reconciler.ts +915 -0
- package/lib/threads.ts +2205 -0
- package/lib/turns.ts +48 -3
- package/lib/updates.ts +355 -32
- package/package.json +24 -2
- package/screenshot.png +0 -0
- package/docs/telegram-bot-api-rich-messages.md +0 -890
package/lib/bindings.ts
CHANGED
|
@@ -21,6 +21,7 @@ import * as Replies from "./replies.ts";
|
|
|
21
21
|
import * as Runtime from "./runtime.ts";
|
|
22
22
|
import * as Setup from "./setup.ts";
|
|
23
23
|
import * as Status from "./status.ts";
|
|
24
|
+
import * as Target from "./target.ts";
|
|
24
25
|
import * as TelegramApi from "./telegram-api.ts";
|
|
25
26
|
|
|
26
27
|
type ActivePiModel = NonNullable<Pi.ExtensionContext["model"]>;
|
|
@@ -37,10 +38,12 @@ type TelegramBridgeStatusUpdater =
|
|
|
37
38
|
interface TelegramCommandsAndToolsBindingDeps {
|
|
38
39
|
pi: Pi.ExtensionAPI;
|
|
39
40
|
configStore: Config.TelegramConfigStore;
|
|
41
|
+
persistConfig: () => Promise<void>;
|
|
40
42
|
setup: Setup.TelegramSetupGuard;
|
|
41
43
|
activeTurnRuntime: Queue.TelegramActiveTurnStore<Queue.PendingTelegramTurn>;
|
|
42
44
|
lockedPollingRuntime: Locks.TelegramLockedPollingRuntime<Pi.ExtensionContext>;
|
|
43
|
-
|
|
45
|
+
stopPolling?: () => Promise<void | string>;
|
|
46
|
+
getStatusLines: (options?: Status.TelegramBridgeStatusLineOptions) => string[];
|
|
44
47
|
buttonActionStore: OutboundHandlers.TelegramButtonActionStore;
|
|
45
48
|
sendMarkdownReply: (
|
|
46
49
|
chatId: number,
|
|
@@ -50,6 +53,7 @@ interface TelegramCommandsAndToolsBindingDeps {
|
|
|
50
53
|
) => Promise<number | undefined>;
|
|
51
54
|
callMultipart: OutboundHandlers.TelegramVoiceReplySenderDeps["sendMultipart"];
|
|
52
55
|
getDefaultChatId: () => number | undefined;
|
|
56
|
+
getDefaultTarget?: () => OutboundAttachments.TelegramQueuedOutboundAttachmentTurnView["target"];
|
|
53
57
|
canSendDirect: () => boolean;
|
|
54
58
|
updateStatus: TelegramBridgeStatusUpdater;
|
|
55
59
|
recordRuntimeEvent: TelegramRuntimeEventRecorder;
|
|
@@ -58,14 +62,17 @@ interface TelegramCommandsAndToolsBindingDeps {
|
|
|
58
62
|
export function registerTelegramCommandsAndTools({
|
|
59
63
|
pi,
|
|
60
64
|
configStore,
|
|
65
|
+
persistConfig,
|
|
61
66
|
setup,
|
|
62
67
|
activeTurnRuntime,
|
|
63
68
|
lockedPollingRuntime,
|
|
69
|
+
stopPolling,
|
|
64
70
|
getStatusLines,
|
|
65
71
|
buttonActionStore,
|
|
66
72
|
sendMarkdownReply,
|
|
67
73
|
callMultipart,
|
|
68
74
|
getDefaultChatId,
|
|
75
|
+
getDefaultTarget,
|
|
69
76
|
canSendDirect,
|
|
70
77
|
updateStatus,
|
|
71
78
|
recordRuntimeEvent,
|
|
@@ -73,27 +80,29 @@ export function registerTelegramCommandsAndTools({
|
|
|
73
80
|
OutboundAttachments.registerTelegramOutboundAttachmentTool(pi, {
|
|
74
81
|
getActiveTurn: activeTurnRuntime.get,
|
|
75
82
|
getDefaultChatId,
|
|
83
|
+
getDefaultTarget,
|
|
76
84
|
canSendDirect,
|
|
77
85
|
sendMultipart: callMultipart,
|
|
78
86
|
recordRuntimeEvent,
|
|
79
87
|
});
|
|
80
88
|
OutboundAttachments.registerTelegramOutboundMessageTool(pi, {
|
|
81
89
|
getDefaultChatId,
|
|
90
|
+
getDefaultTarget,
|
|
82
91
|
canSendDirect,
|
|
83
|
-
planMessage:
|
|
84
|
-
buttonActionStore,
|
|
85
|
-
),
|
|
92
|
+
planMessage:
|
|
93
|
+
OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore),
|
|
86
94
|
sendMarkdownMessage: (chatId, markdown, options) =>
|
|
87
95
|
sendMarkdownReply(chatId, undefined, markdown, options),
|
|
88
96
|
recordRuntimeEvent,
|
|
89
97
|
});
|
|
98
|
+
Prompts.registerTelegramHelpTool(pi);
|
|
90
99
|
Commands.registerTelegramBridgeCommands(pi, {
|
|
91
100
|
promptForConfig: Setup.createTelegramSetupPromptRuntime({
|
|
92
101
|
getConfig: configStore.get,
|
|
93
102
|
setConfig: configStore.set,
|
|
94
103
|
setupGuard: setup,
|
|
95
104
|
getMe: TelegramApi.fetchTelegramBotIdentity,
|
|
96
|
-
persistConfig
|
|
105
|
+
persistConfig,
|
|
97
106
|
startPolling: lockedPollingRuntime.start,
|
|
98
107
|
updateStatus,
|
|
99
108
|
recordRuntimeEvent,
|
|
@@ -102,7 +111,7 @@ export function registerTelegramCommandsAndTools({
|
|
|
102
111
|
reloadConfig: configStore.load,
|
|
103
112
|
hasBotToken: configStore.hasBotToken,
|
|
104
113
|
startPolling: lockedPollingRuntime.start,
|
|
105
|
-
stopPolling: lockedPollingRuntime.stop,
|
|
114
|
+
stopPolling: stopPolling ?? lockedPollingRuntime.stop,
|
|
106
115
|
updateStatus,
|
|
107
116
|
});
|
|
108
117
|
}
|
|
@@ -115,7 +124,7 @@ interface TelegramLifecycleBindingDeps {
|
|
|
115
124
|
>;
|
|
116
125
|
configStore: Pick<
|
|
117
126
|
Config.TelegramConfigStore,
|
|
118
|
-
"getOutboundHandlers" | "hasBotToken"
|
|
127
|
+
"get" | "getOutboundHandlers" | "hasBotToken" | "load"
|
|
119
128
|
>;
|
|
120
129
|
abort: Runtime.TelegramRuntimeAbortPort;
|
|
121
130
|
typing: Runtime.TelegramRuntimeTypingPort;
|
|
@@ -157,6 +166,14 @@ interface TelegramLifecycleBindingDeps {
|
|
|
157
166
|
Keyboard.TelegramInlineKeyboardMarkup
|
|
158
167
|
>["sendTextReply"] &
|
|
159
168
|
NonNullable<OutboundHandlers.TelegramVoiceReplySenderDeps["sendTextReply"]>;
|
|
169
|
+
editInteractiveMessage: (
|
|
170
|
+
chatId: number,
|
|
171
|
+
messageId: number,
|
|
172
|
+
text: string,
|
|
173
|
+
mode: "html" | "markdown",
|
|
174
|
+
replyMarkup: Keyboard.TelegramInlineKeyboardMarkup,
|
|
175
|
+
) => Promise<void>;
|
|
176
|
+
deleteMessage: (chatId: number, messageId: number) => Promise<void>;
|
|
160
177
|
dispatchNextQueuedTelegramTurn: (ctx: Pi.ExtensionContext) => void;
|
|
161
178
|
answerGuestQuery: NonNullable<
|
|
162
179
|
Queue.TelegramAgentEndHookRuntimeDeps<
|
|
@@ -181,7 +198,9 @@ interface TelegramLifecycleBindingDeps {
|
|
|
181
198
|
Keyboard.TelegramInlineKeyboardMarkup
|
|
182
199
|
>["finalizeMarkdownPreview"];
|
|
183
200
|
proactivePushChatIdGetter: () => number | undefined;
|
|
201
|
+
proactivePushTargetGetter: () => Queue.TelegramQueueTarget | undefined;
|
|
184
202
|
isProactivePushEnabled: () => boolean;
|
|
203
|
+
canSendProactivePush: (ctx: Pi.ExtensionContext) => boolean;
|
|
185
204
|
updateStatus: TelegramBridgeStatusUpdater;
|
|
186
205
|
recordRuntimeEvent: TelegramRuntimeEventRecorder;
|
|
187
206
|
}
|
|
@@ -206,12 +225,16 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
206
225
|
sendRecordVoiceAction,
|
|
207
226
|
sendMarkdownReply,
|
|
208
227
|
sendTextReply,
|
|
228
|
+
editInteractiveMessage,
|
|
229
|
+
deleteMessage,
|
|
209
230
|
dispatchNextQueuedTelegramTurn,
|
|
210
231
|
answerGuestQuery,
|
|
211
232
|
sendGuestReply,
|
|
212
233
|
finalizeMarkdownPreview,
|
|
213
234
|
proactivePushChatIdGetter,
|
|
235
|
+
proactivePushTargetGetter,
|
|
214
236
|
isProactivePushEnabled,
|
|
237
|
+
canSendProactivePush,
|
|
215
238
|
updateStatus,
|
|
216
239
|
recordRuntimeEvent,
|
|
217
240
|
}: TelegramLifecycleBindingDeps): void {
|
|
@@ -258,9 +281,15 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
258
281
|
setFoldQueuedPromptsIntoHistory: lifecycle.setFoldQueuedPromptsIntoHistory,
|
|
259
282
|
setActiveTurn: activeTurnRuntime.set,
|
|
260
283
|
createPreviewState: previewRuntime.resetState,
|
|
261
|
-
startTypingLoop:
|
|
284
|
+
startTypingLoop: (ctx) => {
|
|
285
|
+
const turn = activeTurnRuntime.get();
|
|
286
|
+
promptDispatchRuntime.startTypingLoop(ctx, turn?.chatId, {
|
|
287
|
+
target: turn?.target,
|
|
288
|
+
});
|
|
289
|
+
},
|
|
262
290
|
updateStatus,
|
|
263
291
|
getActiveTurn: activeTurnRuntime.get,
|
|
292
|
+
loadConfig: configStore.load,
|
|
264
293
|
extractAssistant: Replies.extractLatestAssistantMessageText,
|
|
265
294
|
getFoldQueuedPromptsIntoHistory:
|
|
266
295
|
lifecycle.shouldFoldQueuedPromptsIntoHistory,
|
|
@@ -269,6 +298,16 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
269
298
|
dispatchNextQueuedTelegramTurn,
|
|
270
299
|
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
271
300
|
deferredQueueDispatchRuntime.request,
|
|
301
|
+
scheduleActiveTurnDelivery(task) {
|
|
302
|
+
const timer = setTimeout(() => {
|
|
303
|
+
void task().catch((error) => {
|
|
304
|
+
recordRuntimeEvent("delivery", error, {
|
|
305
|
+
phase: "agent-end-background-delivery",
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}, 0);
|
|
309
|
+
timer.unref?.();
|
|
310
|
+
},
|
|
272
311
|
clearPreview: previewRuntime.clear,
|
|
273
312
|
setPreviewPendingText: previewRuntime.setPendingText,
|
|
274
313
|
finalizeMarkdownPreview,
|
|
@@ -280,8 +319,9 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
280
319
|
planOutboundReply: outboundReplyPlanner,
|
|
281
320
|
sendOutboundReplyArtifacts: outboundReplyArtifactSender,
|
|
282
321
|
getDefaultChatId: proactivePushChatIdGetter,
|
|
322
|
+
getDefaultTarget: proactivePushTargetGetter,
|
|
283
323
|
isProactivePushEnabled,
|
|
284
|
-
canSendProactivePush
|
|
324
|
+
canSendProactivePush,
|
|
285
325
|
recordRuntimeEvent,
|
|
286
326
|
getActiveToolExecutions: lifecycle.getActiveToolExecutions,
|
|
287
327
|
setActiveToolExecutions: lifecycle.setActiveToolExecutions,
|
|
@@ -291,11 +331,18 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
291
331
|
const agentStartWithDedupReset = Lifecycle.createAgentStartDedupHook(
|
|
292
332
|
agentLifecycleHooks.onAgentStart,
|
|
293
333
|
);
|
|
334
|
+
const startActiveTurnTypingLoop = (ctx: Pi.ExtensionContext): void => {
|
|
335
|
+
const turn = activeTurnRuntime.get();
|
|
336
|
+
promptDispatchRuntime.startTypingLoop(ctx, turn?.chatId, {
|
|
337
|
+
target: turn?.target,
|
|
338
|
+
});
|
|
339
|
+
};
|
|
294
340
|
const compactionObserver = Lifecycle.createTelegramCompactionObserverRuntime({
|
|
295
341
|
setCompactionInProgress: lifecycle.setCompactionInProgress,
|
|
296
342
|
updateStatus,
|
|
297
|
-
startTypingLoop:
|
|
343
|
+
startTypingLoop: startActiveTurnTypingLoop,
|
|
298
344
|
stopTypingLoop: typing.stop,
|
|
345
|
+
shouldStartTypingLoop: activeTurnRuntime.has,
|
|
299
346
|
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
300
347
|
deferredQueueDispatchRuntime.request,
|
|
301
348
|
dispatchNextQueuedTelegramTurn,
|
|
@@ -304,11 +351,12 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
304
351
|
const messageActivityTypingHooks =
|
|
305
352
|
Lifecycle.createTelegramMessageActivityTypingHooks({
|
|
306
353
|
hasActiveTurn: activeTurnRuntime.has,
|
|
307
|
-
startTypingLoop:
|
|
354
|
+
startTypingLoop: startActiveTurnTypingLoop,
|
|
308
355
|
onMessageStart: previewRuntime.onMessageStart,
|
|
309
356
|
onMessageUpdate: previewRuntime.onMessageUpdate,
|
|
310
357
|
recordRuntimeEvent,
|
|
311
358
|
});
|
|
359
|
+
const messageActivityHooks = messageActivityTypingHooks;
|
|
312
360
|
Lifecycle.registerTelegramLifecycleHooks(pi, {
|
|
313
361
|
...sessionLifecycleRuntime,
|
|
314
362
|
...agentLifecycleHooks,
|
|
@@ -319,11 +367,19 @@ export function registerTelegramLifecycleRuntimeHooks({
|
|
|
319
367
|
onSessionBeforeCompact: compactionObserver.onSessionBeforeCompact,
|
|
320
368
|
onSessionCompact: compactionObserver.onSessionCompact,
|
|
321
369
|
onAgentStart: agentStartWithDedupReset,
|
|
370
|
+
async onToolExecutionStart(event, _ctx) {
|
|
371
|
+
agentLifecycleHooks.onToolExecutionStart();
|
|
372
|
+
},
|
|
373
|
+
onToolExecutionUpdate() {},
|
|
374
|
+
async onToolExecutionEnd(event, ctx) {
|
|
375
|
+
agentLifecycleHooks.onToolExecutionEnd(event, ctx);
|
|
376
|
+
},
|
|
377
|
+
onAgentEnd: agentLifecycleHooks.onAgentEnd,
|
|
322
378
|
onBeforeAgentStart: Prompts.createTelegramProactiveBeforeAgentStartHook({
|
|
323
379
|
isConfigured: configStore.hasBotToken,
|
|
324
380
|
isProactivePushEnabled,
|
|
325
381
|
isCurrentOwner: lockOwnershipGuard.ownsContext,
|
|
326
382
|
}),
|
|
327
|
-
...
|
|
383
|
+
...messageActivityHooks,
|
|
328
384
|
});
|
|
329
385
|
}
|
package/lib/bus-api.ts
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram bus-aware API runtime
|
|
3
|
+
* Zones: multi-instance bus, telegram api transport, live instance routing
|
|
4
|
+
* Wraps the direct Telegram Bot API runtime so follower instances can route outbound calls through the bus leader
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
TelegramApiCallOptions,
|
|
9
|
+
TelegramBridgeApiRuntime,
|
|
10
|
+
TelegramEditMessageTextBody,
|
|
11
|
+
TelegramInputRichMessage,
|
|
12
|
+
TelegramSendMessageBody,
|
|
13
|
+
TelegramSendMessageDraftBody,
|
|
14
|
+
TelegramSendRichMessageBody,
|
|
15
|
+
TelegramSendRichMessageDraftBody,
|
|
16
|
+
TelegramSentMessage,
|
|
17
|
+
TelegramUpdate,
|
|
18
|
+
} from "./telegram-api.ts";
|
|
19
|
+
|
|
20
|
+
export type TelegramBusApiCall = (
|
|
21
|
+
method: string,
|
|
22
|
+
args: unknown[],
|
|
23
|
+
) => Promise<unknown>;
|
|
24
|
+
|
|
25
|
+
export interface TelegramBusAwareApiRuntimeDeps {
|
|
26
|
+
directRuntime: TelegramBridgeApiRuntime;
|
|
27
|
+
ownsDirect: () => boolean;
|
|
28
|
+
callFollowerApi: TelegramBusApiCall;
|
|
29
|
+
getDefaultTarget?: () => { chatId: number; threadId?: number } | undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function asRecord(value: unknown): Record<string, unknown> {
|
|
33
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
34
|
+
? (value as Record<string, unknown>)
|
|
35
|
+
: {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function asBoolean(value: unknown): boolean {
|
|
39
|
+
return Boolean(value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function asSentMessage(value: unknown): TelegramSentMessage {
|
|
43
|
+
return asRecord(value) as unknown as TelegramSentMessage;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function withDefaultThreadTarget(
|
|
47
|
+
body: Record<string, unknown>,
|
|
48
|
+
target: { chatId: number; threadId?: number } | undefined,
|
|
49
|
+
): Record<string, unknown> {
|
|
50
|
+
if (target?.threadId === undefined) return body;
|
|
51
|
+
if (body.message_thread_id !== undefined) return body;
|
|
52
|
+
return body.chat_id === target.chatId
|
|
53
|
+
? { ...body, message_thread_id: target.threadId }
|
|
54
|
+
: body;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function createTelegramAggregateTypingActionSender(
|
|
58
|
+
runtime: Pick<TelegramBridgeApiRuntime, "call">,
|
|
59
|
+
): (chatId: number) => Promise<unknown> {
|
|
60
|
+
return (chatId) =>
|
|
61
|
+
runtime.call("sendChatAction", { chat_id: chatId, action: "typing" });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function createTelegramBusAwareApiRuntime(
|
|
65
|
+
deps: TelegramBusAwareApiRuntimeDeps,
|
|
66
|
+
): TelegramBridgeApiRuntime {
|
|
67
|
+
return {
|
|
68
|
+
call<TResponse>(
|
|
69
|
+
method: string,
|
|
70
|
+
body: Record<string, unknown>,
|
|
71
|
+
options?: TelegramApiCallOptions,
|
|
72
|
+
): Promise<TResponse> {
|
|
73
|
+
return deps.ownsDirect()
|
|
74
|
+
? deps.directRuntime.call<TResponse>(method, body, options)
|
|
75
|
+
: (deps.callFollowerApi("call", [
|
|
76
|
+
method,
|
|
77
|
+
body,
|
|
78
|
+
options,
|
|
79
|
+
]) as Promise<TResponse>);
|
|
80
|
+
},
|
|
81
|
+
callMultipart<TResponse>(
|
|
82
|
+
method: string,
|
|
83
|
+
fields: Record<string, string>,
|
|
84
|
+
fileField: string,
|
|
85
|
+
filePath: string,
|
|
86
|
+
fileName: string,
|
|
87
|
+
options?: TelegramApiCallOptions,
|
|
88
|
+
): Promise<TResponse> {
|
|
89
|
+
return deps.ownsDirect()
|
|
90
|
+
? deps.directRuntime.callMultipart<TResponse>(
|
|
91
|
+
method,
|
|
92
|
+
fields,
|
|
93
|
+
fileField,
|
|
94
|
+
filePath,
|
|
95
|
+
fileName,
|
|
96
|
+
options,
|
|
97
|
+
)
|
|
98
|
+
: (deps.callFollowerApi("callMultipart", [
|
|
99
|
+
method,
|
|
100
|
+
fields,
|
|
101
|
+
fileField,
|
|
102
|
+
filePath,
|
|
103
|
+
fileName,
|
|
104
|
+
options,
|
|
105
|
+
]) as Promise<TResponse>);
|
|
106
|
+
},
|
|
107
|
+
downloadFile(fileId: string, suggestedName: string): Promise<string> {
|
|
108
|
+
return deps.ownsDirect()
|
|
109
|
+
? deps.directRuntime.downloadFile(fileId, suggestedName)
|
|
110
|
+
: (deps.callFollowerApi("downloadFile", [
|
|
111
|
+
fileId,
|
|
112
|
+
suggestedName,
|
|
113
|
+
]) as Promise<string>);
|
|
114
|
+
},
|
|
115
|
+
deleteWebhook(signal?: AbortSignal): Promise<boolean> {
|
|
116
|
+
return deps.directRuntime.deleteWebhook(signal);
|
|
117
|
+
},
|
|
118
|
+
getUpdates(
|
|
119
|
+
body: Record<string, unknown>,
|
|
120
|
+
signal?: AbortSignal,
|
|
121
|
+
): Promise<TelegramUpdate[]> {
|
|
122
|
+
return deps.directRuntime.getUpdates(body, signal);
|
|
123
|
+
},
|
|
124
|
+
setMyCommands(commands): Promise<boolean> {
|
|
125
|
+
return deps.ownsDirect()
|
|
126
|
+
? deps.directRuntime.setMyCommands(commands)
|
|
127
|
+
: deps
|
|
128
|
+
.callFollowerApi("call", ["setMyCommands", { commands }])
|
|
129
|
+
.then(asBoolean);
|
|
130
|
+
},
|
|
131
|
+
sendChatAction(
|
|
132
|
+
chatId: number,
|
|
133
|
+
action: string,
|
|
134
|
+
options?: { message_thread_id?: number },
|
|
135
|
+
): Promise<boolean> {
|
|
136
|
+
const body = withDefaultThreadTarget(
|
|
137
|
+
{
|
|
138
|
+
chat_id: chatId,
|
|
139
|
+
action,
|
|
140
|
+
...(options?.message_thread_id !== undefined
|
|
141
|
+
? { message_thread_id: options.message_thread_id }
|
|
142
|
+
: {}),
|
|
143
|
+
},
|
|
144
|
+
deps.getDefaultTarget?.(),
|
|
145
|
+
);
|
|
146
|
+
return deps.ownsDirect()
|
|
147
|
+
? deps.directRuntime.sendChatAction(chatId, action, options)
|
|
148
|
+
: deps
|
|
149
|
+
.callFollowerApi("call", ["sendChatAction", body])
|
|
150
|
+
.then(asBoolean);
|
|
151
|
+
},
|
|
152
|
+
sendTypingAction(
|
|
153
|
+
chatId: number,
|
|
154
|
+
options?: { message_thread_id?: number },
|
|
155
|
+
): Promise<unknown> {
|
|
156
|
+
const body = withDefaultThreadTarget(
|
|
157
|
+
{
|
|
158
|
+
chat_id: chatId,
|
|
159
|
+
action: "typing",
|
|
160
|
+
...(options?.message_thread_id !== undefined
|
|
161
|
+
? { message_thread_id: options.message_thread_id }
|
|
162
|
+
: {}),
|
|
163
|
+
},
|
|
164
|
+
deps.getDefaultTarget?.(),
|
|
165
|
+
);
|
|
166
|
+
return deps.ownsDirect()
|
|
167
|
+
? deps.directRuntime.sendTypingAction(chatId, options)
|
|
168
|
+
: deps
|
|
169
|
+
.callFollowerApi("call", ["sendChatAction", body])
|
|
170
|
+
.then(asBoolean);
|
|
171
|
+
},
|
|
172
|
+
sendRecordVoiceAction(
|
|
173
|
+
chatId: number,
|
|
174
|
+
options?: { message_thread_id?: number },
|
|
175
|
+
): Promise<unknown> {
|
|
176
|
+
const body = withDefaultThreadTarget(
|
|
177
|
+
{
|
|
178
|
+
chat_id: chatId,
|
|
179
|
+
action: "record_voice",
|
|
180
|
+
...(options?.message_thread_id !== undefined
|
|
181
|
+
? { message_thread_id: options.message_thread_id }
|
|
182
|
+
: {}),
|
|
183
|
+
},
|
|
184
|
+
deps.getDefaultTarget?.(),
|
|
185
|
+
);
|
|
186
|
+
return deps.ownsDirect()
|
|
187
|
+
? deps.directRuntime.sendRecordVoiceAction(chatId, options)
|
|
188
|
+
: deps
|
|
189
|
+
.callFollowerApi("call", ["sendChatAction", body])
|
|
190
|
+
.then(asBoolean);
|
|
191
|
+
},
|
|
192
|
+
sendMessageDraft(
|
|
193
|
+
chatId: number,
|
|
194
|
+
draftId: number,
|
|
195
|
+
text?: string,
|
|
196
|
+
options?: TelegramSendMessageDraftBody extends Record<string, unknown>
|
|
197
|
+
? {
|
|
198
|
+
parse_mode?: string;
|
|
199
|
+
entities?: unknown[];
|
|
200
|
+
message_thread_id?: number;
|
|
201
|
+
}
|
|
202
|
+
: never,
|
|
203
|
+
): Promise<boolean> {
|
|
204
|
+
const body: Record<string, unknown> = {
|
|
205
|
+
chat_id: chatId,
|
|
206
|
+
draft_id: draftId,
|
|
207
|
+
};
|
|
208
|
+
if (text !== undefined) body.text = text;
|
|
209
|
+
if (options?.parse_mode !== undefined)
|
|
210
|
+
body.parse_mode = options.parse_mode;
|
|
211
|
+
if (options?.entities !== undefined) body.entities = options.entities;
|
|
212
|
+
if (options?.message_thread_id !== undefined) {
|
|
213
|
+
body.message_thread_id = options.message_thread_id;
|
|
214
|
+
}
|
|
215
|
+
const scopedBody = withDefaultThreadTarget(
|
|
216
|
+
body,
|
|
217
|
+
deps.getDefaultTarget?.(),
|
|
218
|
+
);
|
|
219
|
+
return deps.ownsDirect()
|
|
220
|
+
? deps.directRuntime.sendMessageDraft(chatId, draftId, text, options)
|
|
221
|
+
: deps
|
|
222
|
+
.callFollowerApi("call", ["sendMessageDraft", scopedBody])
|
|
223
|
+
.then(asBoolean);
|
|
224
|
+
},
|
|
225
|
+
sendMessage(body: TelegramSendMessageBody): Promise<TelegramSentMessage> {
|
|
226
|
+
return deps.ownsDirect()
|
|
227
|
+
? deps.directRuntime.sendMessage(body)
|
|
228
|
+
: deps
|
|
229
|
+
.callFollowerApi("call", ["sendMessage", body])
|
|
230
|
+
.then(asSentMessage);
|
|
231
|
+
},
|
|
232
|
+
sendRichMessage(
|
|
233
|
+
body: TelegramSendRichMessageBody,
|
|
234
|
+
): Promise<TelegramSentMessage> {
|
|
235
|
+
return deps.ownsDirect()
|
|
236
|
+
? deps.directRuntime.sendRichMessage(body)
|
|
237
|
+
: deps
|
|
238
|
+
.callFollowerApi("call", ["sendRichMessage", body])
|
|
239
|
+
.then(asSentMessage);
|
|
240
|
+
},
|
|
241
|
+
sendRichMessageDraft(
|
|
242
|
+
body: TelegramSendRichMessageDraftBody,
|
|
243
|
+
): Promise<boolean> {
|
|
244
|
+
return deps.ownsDirect()
|
|
245
|
+
? deps.directRuntime.sendRichMessageDraft(body)
|
|
246
|
+
: deps
|
|
247
|
+
.callFollowerApi("call", ["sendRichMessageDraft", body])
|
|
248
|
+
.then(asBoolean);
|
|
249
|
+
},
|
|
250
|
+
async editMessageText(
|
|
251
|
+
body: TelegramEditMessageTextBody,
|
|
252
|
+
): Promise<"edited" | "unchanged"> {
|
|
253
|
+
if (deps.ownsDirect()) return deps.directRuntime.editMessageText(body);
|
|
254
|
+
await deps.callFollowerApi("call", ["editMessageText", body]);
|
|
255
|
+
return "edited";
|
|
256
|
+
},
|
|
257
|
+
async answerCallbackQuery(
|
|
258
|
+
callbackQueryId: string,
|
|
259
|
+
text?: string,
|
|
260
|
+
): Promise<void> {
|
|
261
|
+
if (deps.ownsDirect()) {
|
|
262
|
+
await deps.directRuntime.answerCallbackQuery(callbackQueryId, text);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
await deps.callFollowerApi("call", [
|
|
266
|
+
"answerCallbackQuery",
|
|
267
|
+
{
|
|
268
|
+
callback_query_id: callbackQueryId,
|
|
269
|
+
...(text !== undefined ? { text } : {}),
|
|
270
|
+
},
|
|
271
|
+
]);
|
|
272
|
+
},
|
|
273
|
+
async answerGuestQuery(
|
|
274
|
+
guestQueryId: string,
|
|
275
|
+
text?: string,
|
|
276
|
+
options?: { parseMode?: string; richMessage?: TelegramInputRichMessage },
|
|
277
|
+
): Promise<void> {
|
|
278
|
+
if (deps.ownsDirect()) {
|
|
279
|
+
await deps.directRuntime.answerGuestQuery(guestQueryId, text, options);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const body: Record<string, unknown> = { guest_query_id: guestQueryId };
|
|
283
|
+
if (text !== undefined || options?.richMessage) {
|
|
284
|
+
const inputContent: Record<string, unknown> = options?.richMessage
|
|
285
|
+
? { rich_message: options.richMessage }
|
|
286
|
+
: { message_text: text };
|
|
287
|
+
if (!options?.richMessage && options?.parseMode) {
|
|
288
|
+
inputContent.parse_mode = options.parseMode;
|
|
289
|
+
}
|
|
290
|
+
body.result = {
|
|
291
|
+
type: "article",
|
|
292
|
+
id: "1",
|
|
293
|
+
title: "Response",
|
|
294
|
+
input_message_content: inputContent,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
await deps.callFollowerApi("call", ["answerGuestQuery", body]);
|
|
298
|
+
},
|
|
299
|
+
async deleteMessage(chatId: number, messageId: number): Promise<void> {
|
|
300
|
+
if (deps.ownsDirect())
|
|
301
|
+
return deps.directRuntime.deleteMessage(chatId, messageId);
|
|
302
|
+
await deps.callFollowerApi("call", [
|
|
303
|
+
"deleteMessage",
|
|
304
|
+
{
|
|
305
|
+
chat_id: chatId,
|
|
306
|
+
message_id: messageId,
|
|
307
|
+
},
|
|
308
|
+
]);
|
|
309
|
+
},
|
|
310
|
+
prepareTempDir(): Promise<number> {
|
|
311
|
+
return deps.directRuntime.prepareTempDir();
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|