@llblab/pi-telegram 0.9.6 → 0.9.8
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/BACKLOG.md +2 -6
- package/CHANGELOG.md +19 -0
- package/README.md +3 -16
- package/docs/extension-sections.md +1 -1
- package/index.ts +3 -0
- package/lib/api.ts +59 -17
- package/lib/pi.ts +1 -1
- package/lib/polling.ts +1 -0
- package/lib/preview.ts +12 -2
- package/lib/prompts.ts +2 -2
- package/lib/queue.ts +25 -0
- package/lib/routing.ts +102 -0
- package/lib/runtime.ts +2 -1
- package/lib/updates.ts +98 -3
- package/package.json +10 -7
package/BACKLOG.md
CHANGED
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Open Work
|
|
4
4
|
|
|
5
|
-
- Implement Telegram Extension Sections Platform for the 0.10.0 line.
|
|
5
|
+
- [ ] Implement Telegram Extension Sections Platform for the 0.10.0 line.
|
|
6
6
|
- Exit: Runtime registry, main-menu integration, `section:` callback routing, safe section context ports, diagnostics, docs, and at least one small demo/fixture prove ordinary pi extensions can add Telegram menu sections without owning a second poller.
|
|
7
|
-
- Explore always-available outbound Telegram tools for queued artifacts and controls.
|
|
7
|
+
- [ ] Explore always-available outbound Telegram tools for queued artifacts and controls.
|
|
8
8
|
- Priority: Low.
|
|
9
9
|
- Idea: Provide tools such as `telegram_attach_file` and `telegram_attach_button` that can be called outside an active Telegram turn, using the paired chat/session as the delivery target when safe.
|
|
10
10
|
- Exit: Design note defines active-turn versus ambient delivery semantics, safety constraints, failure modes, and whether the current `telegram_attach` contract should stay turn-scoped or gain an ambient companion.
|
|
11
|
-
- Tighten dependency posture for reproducible extension development.
|
|
12
|
-
- Priority: Medium.
|
|
13
|
-
- Idea: Replace broad peer dependency `*` ranges and dev dependency `latest` ranges with explicit compatible ranges once the supported pi/Node/TypeScript matrix is clear.
|
|
14
|
-
- Exit: `package.json` documents the supported Node expectation and compatible pi package ranges without over-constraining early-stage extension iteration.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.8: Guest Mode Context
|
|
4
|
+
|
|
5
|
+
- `[Guest Mode]` Extended the [telegram] prefix with |from:user (sender) and |guest:GroupName (source chat for group guest messages) so the agent sees who sent the message and where from. Private guest chats omit the guest: suffix.
|
|
6
|
+
- `[Guest Mode]` Added |from:user to the [reply] block so the agent knows the original author of a replied-to message in guest mode.
|
|
7
|
+
- `[Guest Mode]` Formatted guest prompt text identically to regular DMs through buildTelegramTurnPrompt, including [attachments] and [outputs] sections with file downloads and inbound handler processing.
|
|
8
|
+
- `[Prompts]` Added compact agent guidance explaining guest-mode prefix suffixes (|from:, |guest:) and reply-from context.
|
|
9
|
+
|
|
10
|
+
## 0.9.7: Bot API 10.0 Alignment
|
|
11
|
+
|
|
12
|
+
- `[Dependencies]` Migrated peer dependencies and imports from `@mariozechner/*` to `@earendil-works/*` (`pi-agent-core`, `pi-ai`, `pi-coding-agent`). Impact: the extension now tracks the new `@earendil-works` package scope; transitive `@mariozechner` packages remain in the lockfile until their upstreams migrate.
|
|
13
|
+
- `[Package]` Added `engines: { "node": ">=22.0.0" }` to document the supported Node expectation while keeping dev dependencies on `latest` for early-stage iteration. Impact: users know the minimum Node version without constraining the development dependency matrix prematurely.
|
|
14
|
+
- `[Polling]` Added `"guest_message"` to `TELEGRAM_ALLOWED_UPDATES` so the bot receives guest-mode updates. Impact: without this, guest mentions are silently ignored by Telegram.
|
|
15
|
+
- `[Telegram API]` Updated `sendMessageDraft` wrapper for Bot API 10.0 semantics: removed the empty-text guard, made `text` optional, and added optional `parse_mode`, `entities`, and `message_thread_id` parameters. Impact: preview can now show a "Thinking…" placeholder with empty text, and callers can pass rich formatting through `parse_mode` or `entities`.
|
|
16
|
+
- `[Telegram API]` Added `answerGuestQuery` to the API runtime for Bot API 10.0 Guest Mode support. Impact: callers can reply to guest queries in chats where the bot is not a member. Uses `InlineQueryResultArticle` as the result payload per Bot API 10.0 contract.
|
|
17
|
+
- `[Updates]` Extended inbound update routing to recognize `guest_message` updates. Added `getAuthorizedTelegramGuestMessage`, guest flow action, execution plan, runtime handler, and prompt enqueue support. Unauthorized guest queries receive an "Access denied." reply via `answerGuestQuery`. Guest turns customize the agent-end delivery to use `answerGuestQuery` instead of normal reply transport. Impact: the bridge can now receive and route guest-mode mentions in group chats while preserving the existing private-message authorization model.
|
|
18
|
+
- `[Runtime]` Added typing-loop skip for guest turns (`chatId === 0`) to avoid spurious `sendChatAction` errors in the status bar.
|
|
19
|
+
- `[Tests]` Added regression tests for empty-text draft delivery, undefined-text draft delivery, rich preview with `parse_mode` and `entities`, guest query answers, guest extraction, guest flow classification, guest execution plan, guest deny reply, and guest message routing through the runtime.
|
|
20
|
+
- `[Preview]` Updated `sendDraft` interface in `lib/preview.ts` to accept optional text and formatting options, keeping the preview pipeline aligned with the new API wrapper.
|
|
21
|
+
|
|
3
22
|
## 0.9.6: Runtime Adapter Positioning
|
|
4
23
|
|
|
5
24
|
- `[Package]` Bumped package metadata to `0.9.6` and repositioned the package description from "Better Telegram DM bridge extension for π" to "Telegram Runtime Adapter for π". Impact: package metadata now reflects the runtime adapter/operator-console role rather than a narrow pipe metaphor.
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
`pi-telegram` turns a private Telegram DM into a session-local operator console for π. It admits work, preserves context, streams readable replies, keeps busy sessions usable through queues, lets other extensions share one bot, and turns assistant-authored intent into native Telegram artifacts.
|
|
8
8
|
|
|
9
|
-
This repository is an actively maintained fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`
|
|
9
|
+
This repository is an actively maintained fork of [`badlogic/pi-telegram`](https://github.com/badlogic/pi-telegram). It started from upstream commit [`cb34008`](https://github.com/badlogic/pi-telegram/commit/cb34008460b6c1ca036d92322f69d87f626be0fc) and has since diverged substantially.
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
@@ -61,24 +61,11 @@ The first user to message the bot becomes the exclusive owner of the adapter. Me
|
|
|
61
61
|
Most day-to-day controls live in the Telegram menu or π commands. A few important runtime knobs intentionally stay in environment variables because they affect bootstrap, networking, or transport limits before a menu can help:
|
|
62
62
|
|
|
63
63
|
- **Bot token bootstrap**: `/telegram-setup` can prefill from `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN`, or `TELEGRAM_KEY` when no token is already saved.
|
|
64
|
-
- **HTTP/HTTPS proxy**: native `fetch` can use `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` when Node's environment proxy mode is enabled. Use `NODE_USE_ENV_PROXY=1` or start Node with `--use-env-proxy`.
|
|
64
|
+
- **HTTP/HTTPS proxy**: native `fetch` can use `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` when Node's environment proxy mode is enabled. Use `NODE_USE_ENV_PROXY=1` or start Node with `--use-env-proxy`. SOCKS5 is not part of the zero-dependency core. If you need it, run a local HTTP-to-SOCKS bridge or system tunnel and point `HTTP_PROXY` / `HTTPS_PROXY` at the HTTP endpoint.
|
|
65
65
|
- **Agent data root / temp location**: `PI_CODING_AGENT_DIR` changes the base agent directory used for `telegram.json`, locks, generated outbound-handler artifacts, and Telegram temp files. When unset, the adapter uses `~/.pi/agent`, so inbound Telegram files land in `~/.pi/agent/tmp/telegram`.
|
|
66
66
|
- **Inbound file limit**: `PI_TELEGRAM_INBOUND_FILE_MAX_BYTES` or `TELEGRAM_MAX_FILE_SIZE_BYTES` changes the default 50 MiB Telegram download limit.
|
|
67
67
|
- **Outbound attachment limit**: `PI_TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES` or `TELEGRAM_MAX_ATTACHMENT_SIZE_BYTES` changes the default 50 MiB `telegram_attach` delivery limit.
|
|
68
68
|
|
|
69
|
-
Proxy example:
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
export HTTPS_PROXY="http://127.0.0.1:8083"
|
|
73
|
-
export HTTP_PROXY="http://127.0.0.1:8083"
|
|
74
|
-
export NO_PROXY="localhost,127.0.0.1"
|
|
75
|
-
export NODE_USE_ENV_PROXY=1
|
|
76
|
-
|
|
77
|
-
pi
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
SOCKS5 is not part of the zero-dependency core. If you need it, run a local HTTP-to-SOCKS bridge or system tunnel and point `HTTP_PROXY` / `HTTPS_PROXY` at the HTTP endpoint.
|
|
81
|
-
|
|
82
69
|
## Use
|
|
83
70
|
|
|
84
71
|
Once paired, chat with your bot in Telegram. Text, images, files, replies, edits, media groups, and configured handler output are forwarded into π as Telegram-originated turns.
|
|
@@ -134,7 +121,7 @@ The inline application menu is the primary operator surface. It exposes status,
|
|
|
134
121
|
|
|
135
122
|
Messages sent while π is busy enter the prompt queue and are processed in order. Control actions and model-switch continuation turns use higher-priority lanes so operational commands can resume before normal prompts.
|
|
136
123
|
|
|
137
|
-
The menu is the primary way to inspect and mutate the queue. Reactions are an extra shortcut when Telegram delivers `message_reaction` updates for the chat: `👍`, `⚡️`, `❤️`, `🕊`, and `🔥` promote waiting work; `👎`, `👻`, `💔`, `💩`, and `🗑` remove it. The
|
|
124
|
+
The menu is the primary way to inspect and mutate the queue. Reactions are an extra shortcut when Telegram delivers `message_reaction` updates for the chat: `👍`, `⚡️`, `❤️`, `🕊`, and `🔥` promote waiting work; `👎`, `👻`, `💔`, `💩`, and `🗑` remove it. The same rules apply to text, voice, files, images, and media groups.
|
|
138
125
|
|
|
139
126
|
### Streaming and Telegram HTML rendering
|
|
140
127
|
|
|
@@ -114,7 +114,7 @@ unregister();
|
|
|
114
114
|
Sections are registered by normal pi extensions:
|
|
115
115
|
|
|
116
116
|
```ts
|
|
117
|
-
import type { ExtensionAPI } from "@
|
|
117
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
118
118
|
import { registerTelegramSection } from "@llblab/pi-telegram/lib/extension-sections.ts";
|
|
119
119
|
|
|
120
120
|
export default function (pi: ExtensionAPI) {
|
package/index.ts
CHANGED
|
@@ -148,6 +148,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
148
148
|
downloadFile: downloadTelegramBridgeFile,
|
|
149
149
|
editMessageText: editTelegramMessageText,
|
|
150
150
|
answerCallbackQuery,
|
|
151
|
+
answerGuestQuery,
|
|
151
152
|
prepareTempDir,
|
|
152
153
|
} = Api.createDefaultTelegramBridgeApiRuntime({
|
|
153
154
|
getBotToken: configStore.getBotToken,
|
|
@@ -333,6 +334,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
333
334
|
updateStatus,
|
|
334
335
|
dispatchNextQueuedTelegramTurn,
|
|
335
336
|
answerCallbackQuery,
|
|
337
|
+
answerGuestQuery,
|
|
336
338
|
sendTextReply,
|
|
337
339
|
setMyCommands,
|
|
338
340
|
getCommands,
|
|
@@ -494,6 +496,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
494
496
|
sendMarkdownReply,
|
|
495
497
|
sendTextReply,
|
|
496
498
|
sendQueuedAttachments: queuedAttachmentSender,
|
|
499
|
+
answerGuestQuery,
|
|
497
500
|
planOutboundReply: outboundReplyPlanner,
|
|
498
501
|
sendOutboundReplyArtifacts: outboundReplyArtifactSender,
|
|
499
502
|
isCurrentOwner: lockOwnershipGuard.ownsContext,
|
package/lib/api.ts
CHANGED
|
@@ -151,12 +151,26 @@ export interface TelegramMessageReactionUpdated {
|
|
|
151
151
|
date: number;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
export interface TelegramGuestMessage {
|
|
155
|
+
message_id: number;
|
|
156
|
+
from?: TelegramUser;
|
|
157
|
+
chat: TelegramChat;
|
|
158
|
+
date: number;
|
|
159
|
+
text?: string;
|
|
160
|
+
caption?: string;
|
|
161
|
+
guest_query_id: string;
|
|
162
|
+
guest_bot_caller_user?: TelegramUser;
|
|
163
|
+
guest_bot_caller_chat?: TelegramChat;
|
|
164
|
+
reply_to_message?: TelegramMessage;
|
|
165
|
+
}
|
|
166
|
+
|
|
154
167
|
export interface TelegramUpdate {
|
|
155
168
|
update_id: number;
|
|
156
169
|
message?: TelegramMessage;
|
|
157
170
|
edited_message?: TelegramMessage;
|
|
158
171
|
callback_query?: TelegramCallbackQuery;
|
|
159
172
|
message_reaction?: TelegramMessageReactionUpdated;
|
|
173
|
+
guest_message?: TelegramGuestMessage;
|
|
160
174
|
deleted_business_messages?: { message_ids?: unknown };
|
|
161
175
|
}
|
|
162
176
|
|
|
@@ -184,6 +198,15 @@ export type TelegramEditMessageTextBody = Record<string, unknown> & {
|
|
|
184
198
|
parse_mode?: "HTML";
|
|
185
199
|
};
|
|
186
200
|
|
|
201
|
+
export type TelegramSendMessageDraftBody = Record<string, unknown> & {
|
|
202
|
+
chat_id: number;
|
|
203
|
+
draft_id: number;
|
|
204
|
+
text?: string;
|
|
205
|
+
parse_mode?: string;
|
|
206
|
+
entities?: unknown[];
|
|
207
|
+
message_thread_id?: number;
|
|
208
|
+
};
|
|
209
|
+
|
|
187
210
|
interface TelegramApiResponse<T> {
|
|
188
211
|
ok: boolean;
|
|
189
212
|
result?: T;
|
|
@@ -233,6 +256,7 @@ export interface TelegramApiClient {
|
|
|
233
256
|
callbackQueryId: string,
|
|
234
257
|
text?: string,
|
|
235
258
|
) => Promise<void>;
|
|
259
|
+
answerGuestQuery?: (guestQueryId: string, text?: string) => Promise<void>;
|
|
236
260
|
}
|
|
237
261
|
|
|
238
262
|
export interface TelegramBridgeApiRuntimeDeps {
|
|
@@ -275,7 +299,12 @@ export interface TelegramBridgeApiRuntime {
|
|
|
275
299
|
sendMessageDraft: (
|
|
276
300
|
chatId: number,
|
|
277
301
|
draftId: number,
|
|
278
|
-
text
|
|
302
|
+
text?: string,
|
|
303
|
+
options?: {
|
|
304
|
+
parse_mode?: string;
|
|
305
|
+
entities?: unknown[];
|
|
306
|
+
message_thread_id?: number;
|
|
307
|
+
},
|
|
279
308
|
) => Promise<boolean>;
|
|
280
309
|
sendMessage: (body: TelegramSendMessageBody) => Promise<TelegramSentMessage>;
|
|
281
310
|
editMessageText: (
|
|
@@ -285,6 +314,7 @@ export interface TelegramBridgeApiRuntime {
|
|
|
285
314
|
callbackQueryId: string,
|
|
286
315
|
text?: string,
|
|
287
316
|
) => Promise<void>;
|
|
317
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
288
318
|
prepareTempDir: () => Promise<number>;
|
|
289
319
|
}
|
|
290
320
|
|
|
@@ -534,9 +564,7 @@ export async function fetchTelegramBotIdentity(
|
|
|
534
564
|
botToken: string,
|
|
535
565
|
fetchImpl: typeof fetch = fetch,
|
|
536
566
|
): Promise<TelegramBotIdentityResponse> {
|
|
537
|
-
const response = await fetchImpl(
|
|
538
|
-
`${TELEGRAM_API_BASE}/bot${botToken}/getMe`,
|
|
539
|
-
);
|
|
567
|
+
const response = await fetchImpl(`${TELEGRAM_API_BASE}/bot${botToken}/getMe`);
|
|
540
568
|
return response.json() as Promise<TelegramBotIdentityResponse>;
|
|
541
569
|
}
|
|
542
570
|
|
|
@@ -559,14 +587,11 @@ export async function callTelegramMultipart<TResponse>(
|
|
|
559
587
|
form.set(key, value);
|
|
560
588
|
}
|
|
561
589
|
form.set(fileField, fileBlob, fileName);
|
|
562
|
-
return fetch(
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
signal: options?.signal,
|
|
568
|
-
},
|
|
569
|
-
);
|
|
590
|
+
return fetch(`${TELEGRAM_API_BASE}/bot${configuredBotToken}/${method}`, {
|
|
591
|
+
method: "POST",
|
|
592
|
+
body: form,
|
|
593
|
+
signal: options?.signal,
|
|
594
|
+
});
|
|
570
595
|
},
|
|
571
596
|
options,
|
|
572
597
|
);
|
|
@@ -732,13 +757,18 @@ export function createTelegramBridgeApiRuntime(
|
|
|
732
757
|
}),
|
|
733
758
|
"typing",
|
|
734
759
|
),
|
|
735
|
-
sendMessageDraft: (chatId, draftId, text) => {
|
|
736
|
-
|
|
737
|
-
return callRecorded<boolean>("sendMessageDraft", {
|
|
760
|
+
sendMessageDraft: (chatId, draftId, text, options) => {
|
|
761
|
+
const body: Record<string, unknown> = {
|
|
738
762
|
chat_id: chatId,
|
|
739
763
|
draft_id: draftId,
|
|
740
|
-
|
|
741
|
-
|
|
764
|
+
};
|
|
765
|
+
if (text !== undefined) body.text = text;
|
|
766
|
+
if (options?.parse_mode !== undefined)
|
|
767
|
+
body.parse_mode = options.parse_mode;
|
|
768
|
+
if (options?.entities !== undefined) body.entities = options.entities;
|
|
769
|
+
if (options?.message_thread_id !== undefined)
|
|
770
|
+
body.message_thread_id = options.message_thread_id;
|
|
771
|
+
return callRecorded<boolean>("sendMessageDraft", body);
|
|
742
772
|
},
|
|
743
773
|
sendMessage: (body) =>
|
|
744
774
|
callRecorded<TelegramSentMessage>("sendMessage", body),
|
|
@@ -755,6 +785,18 @@ export function createTelegramBridgeApiRuntime(
|
|
|
755
785
|
answerCallbackQuery: (callbackQueryId, text) => {
|
|
756
786
|
return deps.client.answerCallbackQuery(callbackQueryId, text);
|
|
757
787
|
},
|
|
788
|
+
answerGuestQuery: (guestQueryId, text) => {
|
|
789
|
+
const body: Record<string, unknown> = { guest_query_id: guestQueryId };
|
|
790
|
+
if (text !== undefined) {
|
|
791
|
+
body.result = {
|
|
792
|
+
type: "article",
|
|
793
|
+
id: "1",
|
|
794
|
+
title: text.length > 64 ? text.slice(0, 61) + "..." : text,
|
|
795
|
+
input_message_content: { message_text: text },
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
return callRecorded<void>("answerGuestQuery", body);
|
|
799
|
+
},
|
|
758
800
|
prepareTempDir: () =>
|
|
759
801
|
prepareTelegramTempDir(deps.tempDir, deps.tempFileMaxAgeMs),
|
|
760
802
|
};
|
package/lib/pi.ts
CHANGED
package/lib/polling.ts
CHANGED
package/lib/preview.ts
CHANGED
|
@@ -61,7 +61,12 @@ export interface TelegramPreviewRuntimeDeps<
|
|
|
61
61
|
sendDraft: (
|
|
62
62
|
chatId: number,
|
|
63
63
|
draftId: number,
|
|
64
|
-
text
|
|
64
|
+
text?: string,
|
|
65
|
+
options?: {
|
|
66
|
+
parse_mode?: string;
|
|
67
|
+
entities?: unknown[];
|
|
68
|
+
message_thread_id?: number;
|
|
69
|
+
},
|
|
65
70
|
) => Promise<unknown>;
|
|
66
71
|
sendMessage: (
|
|
67
72
|
chatId: number,
|
|
@@ -158,7 +163,12 @@ export interface TelegramPreviewControllerDeps<
|
|
|
158
163
|
sendDraft: (
|
|
159
164
|
chatId: number,
|
|
160
165
|
draftId: number,
|
|
161
|
-
text
|
|
166
|
+
text?: string,
|
|
167
|
+
options?: {
|
|
168
|
+
parse_mode?: string;
|
|
169
|
+
entities?: unknown[];
|
|
170
|
+
message_thread_id?: number;
|
|
171
|
+
},
|
|
162
172
|
) => Promise<unknown>;
|
|
163
173
|
sendMessage: (
|
|
164
174
|
chatId: number,
|
package/lib/prompts.ts
CHANGED
|
@@ -12,8 +12,8 @@ const SYSTEM_PROMPT_SUFFIX = `
|
|
|
12
12
|
Telegram bridge extension is active.
|
|
13
13
|
|
|
14
14
|
Inbound context:
|
|
15
|
-
- \`[telegram]\` marks Telegram-originated messages.
|
|
16
|
-
- \`[reply]\` is quoted context from the replied-to message, not a new instruction by itself. Use it to resolve references like "this", "it", or "that message"; the actual instruction is before [reply] unless it explicitly asks to act on the quote.
|
|
15
|
+
- \`[telegram]\` marks Telegram-originated messages. Suffixes \`|from:user\` (sender) and \`|guest:group\` (guest mode — message from another chat where the bot is not a member) may be present; the bot sees the message as if forwarded from that user/chat.
|
|
16
|
+
- \`[reply]\` is quoted context from the replied-to message, not a new instruction by itself. Suffix \`|from:user\` identifies the original author in guest-mode replies. Use it to resolve references like "this", "it", or "that message"; the actual instruction is before [reply] unless it explicitly asks to act on the quote.
|
|
17
17
|
- \`[attachments]\` gives a base directory plus relative local files; resolve and read them as needed. \`[outputs]\` contains inbound-handler stdout such as transcriptions or extracted text for those attachments.
|
|
18
18
|
- Unknown \`[callback] ...\` messages may be intended for another extension; if you see one, say the callback was not handled and the environment may be misconfigured.
|
|
19
19
|
|
package/lib/queue.ts
CHANGED
|
@@ -66,6 +66,7 @@ export interface TelegramQueueItemBase {
|
|
|
66
66
|
kind: TelegramQueueItemKind;
|
|
67
67
|
chatId: number;
|
|
68
68
|
replyToMessageId: number;
|
|
69
|
+
guestQueryId?: string;
|
|
69
70
|
queueOrder: number;
|
|
70
71
|
queueLane: TelegramQueueLane;
|
|
71
72
|
laneOrder: number;
|
|
@@ -113,6 +114,7 @@ export interface TelegramActiveTurnStore<
|
|
|
113
114
|
clear: () => void;
|
|
114
115
|
getChatId: () => number | undefined;
|
|
115
116
|
getReplyToMessageId: () => number | undefined;
|
|
117
|
+
getGuestQueryId: () => string | undefined;
|
|
116
118
|
getSourceMessageIds: () => number[] | undefined;
|
|
117
119
|
}
|
|
118
120
|
|
|
@@ -203,6 +205,7 @@ export function createTelegramActiveTurnStore<
|
|
|
203
205
|
},
|
|
204
206
|
getChatId: () => activeTurn?.chatId,
|
|
205
207
|
getReplyToMessageId: () => activeTurn?.replyToMessageId,
|
|
208
|
+
getGuestQueryId: () => activeTurn?.guestQueryId,
|
|
206
209
|
getSourceMessageIds: () => activeTurn?.sourceMessageIds,
|
|
207
210
|
};
|
|
208
211
|
}
|
|
@@ -785,6 +788,7 @@ export interface TelegramAgentEndRuntimeDeps<
|
|
|
785
788
|
text: string,
|
|
786
789
|
) => Promise<unknown>;
|
|
787
790
|
sendQueuedAttachments: (turn: TTurn) => Promise<void>;
|
|
791
|
+
answerGuestQuery?: (guestQueryId: string, text?: string) => Promise<void>;
|
|
788
792
|
planOutboundReply?: (
|
|
789
793
|
markdown: string,
|
|
790
794
|
) => TelegramAgentEndOutboundReplyPlan<TReplyMarkup>;
|
|
@@ -832,6 +836,7 @@ export interface TelegramAgentEndHookRuntimeDeps<
|
|
|
832
836
|
>["sendMarkdownReply"];
|
|
833
837
|
sendTextReply: TelegramAgentEndRuntimeDeps<TTurn>["sendTextReply"];
|
|
834
838
|
sendQueuedAttachments: (turn: TTurn) => Promise<void>;
|
|
839
|
+
answerGuestQuery?: TelegramAgentEndRuntimeDeps<TTurn>["answerGuestQuery"];
|
|
835
840
|
planOutboundReply?: TelegramAgentEndRuntimeDeps<
|
|
836
841
|
TTurn,
|
|
837
842
|
TReplyMarkup
|
|
@@ -952,6 +957,7 @@ export function createTelegramAgentEndHook<
|
|
|
952
957
|
sendMarkdownReply: deps.sendMarkdownReply,
|
|
953
958
|
sendTextReply: deps.sendTextReply,
|
|
954
959
|
sendQueuedAttachments: deps.sendQueuedAttachments,
|
|
960
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
955
961
|
planOutboundReply: deps.planOutboundReply,
|
|
956
962
|
sendOutboundReplyArtifacts: deps.sendOutboundReplyArtifacts,
|
|
957
963
|
getDefaultChatId: deps.getDefaultChatId,
|
|
@@ -1007,6 +1013,25 @@ export async function handleTelegramAgentEndRuntime<
|
|
|
1007
1013
|
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1008
1014
|
return;
|
|
1009
1015
|
}
|
|
1016
|
+
if (turn.guestQueryId) {
|
|
1017
|
+
if (deps.isCurrentOwner && !deps.isCurrentOwner()) {
|
|
1018
|
+
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
if (assistant.errorMessage) {
|
|
1022
|
+
await deps.answerGuestQuery?.(
|
|
1023
|
+
turn.guestQueryId,
|
|
1024
|
+
"Telegram bridge: π failed while processing the request.",
|
|
1025
|
+
);
|
|
1026
|
+
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
if (finalText) {
|
|
1030
|
+
await deps.answerGuestQuery?.(turn.guestQueryId, finalText);
|
|
1031
|
+
}
|
|
1032
|
+
if (endPlan.shouldDispatchNext) deps.dispatchNextQueuedTelegramTurn();
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1010
1035
|
if (endPlan.shouldClearPreview) {
|
|
1011
1036
|
await deps.clearPreview(turn.chatId);
|
|
1012
1037
|
}
|
package/lib/routing.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import * as OutboundHandlers from "./outbound-handlers.ts";
|
|
8
8
|
import * as Commands from "./commands.ts";
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
9
10
|
import type { TelegramConfigStore } from "./config.ts";
|
|
10
11
|
import type { TelegramInboundHandlerRuntime } from "./inbound-handlers.ts";
|
|
11
12
|
import * as Media from "./media.ts";
|
|
@@ -17,6 +18,7 @@ import type { TelegramBridgeRuntime } from "./runtime.ts";
|
|
|
17
18
|
import * as TextGroups from "./text-groups.ts";
|
|
18
19
|
import * as Turns from "./turns.ts";
|
|
19
20
|
import * as Updates from "./updates.ts";
|
|
21
|
+
import type { TelegramUser } from "./updates.ts";
|
|
20
22
|
|
|
21
23
|
export type TelegramRoutedMessage = Updates.TelegramUpdateMessage &
|
|
22
24
|
Media.TelegramMediaMessage &
|
|
@@ -80,6 +82,7 @@ export interface TelegramInboundRouteRuntimeDeps<
|
|
|
80
82
|
callbackQueryId: string,
|
|
81
83
|
text?: string,
|
|
82
84
|
) => Promise<void>;
|
|
85
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
83
86
|
sendTextReply: (
|
|
84
87
|
chatId: number,
|
|
85
88
|
replyToMessageId: number,
|
|
@@ -364,6 +367,103 @@ export function createTelegramInboundRouteRuntime<
|
|
|
364
367
|
...deps.telegramQueueStore,
|
|
365
368
|
updateStatus: deps.updateStatus,
|
|
366
369
|
});
|
|
370
|
+
const handleAuthorizedTelegramGuestMessage = async (
|
|
371
|
+
guestMessage: Updates.TelegramGuestMessage & { from: TelegramUser },
|
|
372
|
+
ctx: TContext,
|
|
373
|
+
): Promise<void> => {
|
|
374
|
+
const text = guestMessage.text ?? "";
|
|
375
|
+
const gm = guestMessage as unknown as Record<string, unknown>;
|
|
376
|
+
// Build telegram prefix with guest context
|
|
377
|
+
const fromRaw = gm.from as Record<string, unknown> | undefined;
|
|
378
|
+
const fromName =
|
|
379
|
+
(fromRaw?.username as string) ||
|
|
380
|
+
(fromRaw?.first_name as string) ||
|
|
381
|
+
"";
|
|
382
|
+
const chatRaw = gm.chat as Record<string, unknown>;
|
|
383
|
+
const chatTitle = chatRaw?.title as string | undefined;
|
|
384
|
+
const chatType = chatRaw?.type as string;
|
|
385
|
+
const prefixParts = ["telegram"];
|
|
386
|
+
if (fromName) prefixParts.push(`from:${fromName}`);
|
|
387
|
+
if (chatType !== "private" && chatTitle) {
|
|
388
|
+
prefixParts.push(`guest:${chatTitle}`);
|
|
389
|
+
}
|
|
390
|
+
const telegramPrefix = `[${prefixParts.join("|")}]`;
|
|
391
|
+
// Extract reply context
|
|
392
|
+
const replyMsg = gm.reply_to_message as Record<string, unknown> | undefined;
|
|
393
|
+
const replyText =
|
|
394
|
+
replyMsg
|
|
395
|
+
? ((replyMsg.text as string) || (replyMsg.caption as string) || "").trim()
|
|
396
|
+
: "";
|
|
397
|
+
const replyFrom =
|
|
398
|
+
replyMsg
|
|
399
|
+
? (replyMsg.from as Record<string, unknown> | undefined)?.username as string | undefined
|
|
400
|
+
: undefined;
|
|
401
|
+
// Download files, run inbound handlers
|
|
402
|
+
const guestMsg = guestMessage as unknown as Media.TelegramMediaMessage;
|
|
403
|
+
const files = await Media.downloadTelegramMessageFiles([guestMsg], {
|
|
404
|
+
downloadFile: deps.downloadFile,
|
|
405
|
+
});
|
|
406
|
+
const processed = await deps.inboundHandlerRuntime.process(files, text, ctx);
|
|
407
|
+
let rawText = processed.rawText || text;
|
|
408
|
+
// Append reply context after handler processing
|
|
409
|
+
if (replyText) {
|
|
410
|
+
const replyBlock = replyFrom
|
|
411
|
+
? `[reply|from:${replyFrom}] ${replyText}`
|
|
412
|
+
: `[reply] ${replyText}`;
|
|
413
|
+
rawText = `${rawText}\n\n${replyBlock}`;
|
|
414
|
+
}
|
|
415
|
+
const promptText = Turns.buildTelegramTurnPrompt({
|
|
416
|
+
telegramPrefix,
|
|
417
|
+
rawText,
|
|
418
|
+
files,
|
|
419
|
+
promptFiles: processed.promptFiles,
|
|
420
|
+
handlerOutputs: processed.handlerOutputs,
|
|
421
|
+
});
|
|
422
|
+
const order = deps.bridgeRuntime.queue.allocateItemOrder();
|
|
423
|
+
const content: Queue.TelegramPromptContent[] = [
|
|
424
|
+
{ type: "text", text: promptText },
|
|
425
|
+
];
|
|
426
|
+
for (const file of processed.promptFiles) {
|
|
427
|
+
if (file.isImage && file.mimeType) {
|
|
428
|
+
try {
|
|
429
|
+
const buffer = await readFile(file.path);
|
|
430
|
+
content.push({
|
|
431
|
+
type: "image",
|
|
432
|
+
data: Buffer.from(buffer).toString("base64"),
|
|
433
|
+
mimeType: file.mimeType,
|
|
434
|
+
});
|
|
435
|
+
} catch {
|
|
436
|
+
// skip unreadable files
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
const guestTurn: Queue.PendingTelegramTurn = {
|
|
441
|
+
kind: "prompt",
|
|
442
|
+
chatId: 0,
|
|
443
|
+
replyToMessageId: 0,
|
|
444
|
+
guestQueryId: guestMessage.guest_query_id,
|
|
445
|
+
sourceMessageIds: [],
|
|
446
|
+
queueOrder: order,
|
|
447
|
+
queueLane: "default",
|
|
448
|
+
laneOrder: order,
|
|
449
|
+
queuedAttachments: [],
|
|
450
|
+
content,
|
|
451
|
+
historyText: Turns.formatTelegramTurnStatusSummary(
|
|
452
|
+
processed.rawText || text,
|
|
453
|
+
processed.promptFiles,
|
|
454
|
+
processed.handlerOutputs,
|
|
455
|
+
),
|
|
456
|
+
statusSummary: Turns.truncateTelegramQueueSummary(
|
|
457
|
+
processed.rawText || text,
|
|
458
|
+
),
|
|
459
|
+
};
|
|
460
|
+
const items = deps.telegramQueueStore.getQueuedItems();
|
|
461
|
+
deps.telegramQueueStore.setQueuedItems(
|
|
462
|
+
Queue.appendTelegramQueueItem(items, guestTurn),
|
|
463
|
+
);
|
|
464
|
+
deps.updateStatus(ctx);
|
|
465
|
+
deps.dispatchNextQueuedTelegramTurn(ctx);
|
|
466
|
+
};
|
|
367
467
|
return Updates.createTelegramPairedUpdateRuntime<TContext, TUpdate>({
|
|
368
468
|
getAllowedUserId: deps.configStore.getAllowedUserId,
|
|
369
469
|
setAllowedUserId: deps.configStore.setAllowedUserId,
|
|
@@ -377,9 +477,11 @@ export function createTelegramInboundRouteRuntime<
|
|
|
377
477
|
prioritizeQueuedTelegramTurnByMessageId:
|
|
378
478
|
deps.queueMutationRuntime.prioritizeByMessageId,
|
|
379
479
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
480
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
380
481
|
handleAuthorizedTelegramCallbackQuery: callbackHandler,
|
|
381
482
|
sendTextReply: deps.sendTextReply,
|
|
382
483
|
handleAuthorizedTelegramMessage: textDispatch.handleMessage,
|
|
383
484
|
handleAuthorizedTelegramEditedMessage: editRuntime.updateFromEditedMessage,
|
|
485
|
+
handleAuthorizedTelegramGuestMessage,
|
|
384
486
|
});
|
|
385
487
|
}
|
package/lib/runtime.ts
CHANGED
|
@@ -365,7 +365,8 @@ export function startTelegramTypingLoop(
|
|
|
365
365
|
state: TelegramBridgeRuntimeState,
|
|
366
366
|
deps: TelegramTypingLoopDeps,
|
|
367
367
|
): boolean {
|
|
368
|
-
if (state.typingInterval || deps.chatId === undefined
|
|
368
|
+
if (state.typingInterval || deps.chatId === undefined || deps.chatId === 0)
|
|
369
|
+
return false;
|
|
369
370
|
const sendTyping = (): void => {
|
|
370
371
|
void deps.sendTypingAction(deps.chatId as number);
|
|
371
372
|
};
|
package/lib/updates.ts
CHANGED
|
@@ -130,10 +130,20 @@ export interface TelegramCallbackQuery {
|
|
|
130
130
|
message?: TelegramUpdateMessage;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
export interface TelegramGuestMessage {
|
|
134
|
+
guest_query_id: string;
|
|
135
|
+
chat: TelegramChat;
|
|
136
|
+
from?: TelegramUser;
|
|
137
|
+
message_id?: number;
|
|
138
|
+
text?: string;
|
|
139
|
+
reply_to_message?: TelegramUpdateMessage;
|
|
140
|
+
}
|
|
141
|
+
|
|
133
142
|
export interface TelegramUpdateRouting {
|
|
134
143
|
message?: TelegramUpdateMessage;
|
|
135
144
|
edited_message?: TelegramUpdateMessage;
|
|
136
145
|
callback_query?: TelegramCallbackQuery;
|
|
146
|
+
guest_message?: TelegramGuestMessage;
|
|
137
147
|
}
|
|
138
148
|
|
|
139
149
|
export function getAuthorizedTelegramCallbackQuery(
|
|
@@ -178,6 +188,16 @@ export function getAuthorizedTelegramEditedMessage(
|
|
|
178
188
|
return message;
|
|
179
189
|
}
|
|
180
190
|
|
|
191
|
+
export function getAuthorizedTelegramGuestMessage(
|
|
192
|
+
update: TelegramUpdateRouting,
|
|
193
|
+
): TelegramGuestMessage | undefined {
|
|
194
|
+
const guestMessage = update.guest_message;
|
|
195
|
+
if (!guestMessage || !guestMessage.from || guestMessage.from.is_bot) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
return guestMessage;
|
|
199
|
+
}
|
|
200
|
+
|
|
181
201
|
// --- Flow ---
|
|
182
202
|
|
|
183
203
|
export interface TelegramMessageReactionUpdated {
|
|
@@ -198,6 +218,7 @@ export type TelegramUpdateFlowAction<
|
|
|
198
218
|
TelegramMessageReactionUpdated,
|
|
199
219
|
TCallbackQuery extends TelegramCallbackQuery = TelegramCallbackQuery,
|
|
200
220
|
TMessage extends TelegramUpdateMessage = TelegramUpdateMessage,
|
|
221
|
+
TGuestMessage extends TelegramGuestMessage = TelegramGuestMessage,
|
|
201
222
|
> =
|
|
202
223
|
| { kind: "ignore" }
|
|
203
224
|
| { kind: "deleted"; messageIds: number[] }
|
|
@@ -216,6 +237,11 @@ export type TelegramUpdateFlowAction<
|
|
|
216
237
|
kind: "edited-message";
|
|
217
238
|
message: TMessage & { from: TelegramUser };
|
|
218
239
|
authorization: TelegramAuthorizationState;
|
|
240
|
+
}
|
|
241
|
+
| {
|
|
242
|
+
kind: "guest";
|
|
243
|
+
guestMessage: TGuestMessage & { from: TelegramUser };
|
|
244
|
+
authorization: TelegramAuthorizationState;
|
|
219
245
|
};
|
|
220
246
|
|
|
221
247
|
export function buildTelegramUpdateFlowAction<
|
|
@@ -226,7 +252,8 @@ export function buildTelegramUpdateFlowAction<
|
|
|
226
252
|
): TelegramUpdateFlowAction<
|
|
227
253
|
NonNullable<TUpdate["message_reaction"]>,
|
|
228
254
|
NonNullable<TUpdate["callback_query"]>,
|
|
229
|
-
NonNullable<TUpdate["message"] | TUpdate["edited_message"]
|
|
255
|
+
NonNullable<TUpdate["message"] | TUpdate["edited_message"]>,
|
|
256
|
+
NonNullable<TUpdate["guest_message"]>
|
|
230
257
|
> {
|
|
231
258
|
const deletedMessageIds = extractDeletedTelegramMessageIds(update);
|
|
232
259
|
if (deletedMessageIds.length > 0) {
|
|
@@ -272,6 +299,19 @@ export function buildTelegramUpdateFlowAction<
|
|
|
272
299
|
),
|
|
273
300
|
};
|
|
274
301
|
}
|
|
302
|
+
const guestMessage = getAuthorizedTelegramGuestMessage(update);
|
|
303
|
+
if (guestMessage?.from) {
|
|
304
|
+
return {
|
|
305
|
+
kind: "guest",
|
|
306
|
+
guestMessage: guestMessage as NonNullable<TUpdate["guest_message"]> & {
|
|
307
|
+
from: TelegramUser;
|
|
308
|
+
},
|
|
309
|
+
authorization: getTelegramAuthorizationState(
|
|
310
|
+
guestMessage.from.id,
|
|
311
|
+
allowedUserId,
|
|
312
|
+
),
|
|
313
|
+
};
|
|
314
|
+
}
|
|
275
315
|
return { kind: "ignore" };
|
|
276
316
|
}
|
|
277
317
|
|
|
@@ -282,6 +322,7 @@ export type TelegramUpdateExecutionPlan<
|
|
|
282
322
|
TelegramMessageReactionUpdated,
|
|
283
323
|
TCallbackQuery extends TelegramCallbackQuery = TelegramCallbackQuery,
|
|
284
324
|
TMessage extends TelegramUpdateMessage = TelegramUpdateMessage,
|
|
325
|
+
TGuestMessage extends TelegramGuestMessage = TelegramGuestMessage,
|
|
285
326
|
> =
|
|
286
327
|
| { kind: "ignore" }
|
|
287
328
|
| { kind: "deleted"; messageIds: number[] }
|
|
@@ -307,15 +348,31 @@ export type TelegramUpdateExecutionPlan<
|
|
|
307
348
|
message: TMessage & { from: TelegramUser };
|
|
308
349
|
shouldPair: boolean;
|
|
309
350
|
shouldDeny: boolean;
|
|
351
|
+
}
|
|
352
|
+
| {
|
|
353
|
+
kind: "guest";
|
|
354
|
+
guestMessage: TGuestMessage & { from: TelegramUser };
|
|
355
|
+
shouldDeny: boolean;
|
|
310
356
|
};
|
|
311
357
|
|
|
312
358
|
export function buildTelegramUpdateExecutionPlan<
|
|
313
359
|
TReactionUpdate extends TelegramMessageReactionUpdated,
|
|
314
360
|
TCallbackQuery extends TelegramCallbackQuery,
|
|
315
361
|
TMessage extends TelegramUpdateMessage,
|
|
362
|
+
TGuestMessage extends TelegramGuestMessage,
|
|
316
363
|
>(
|
|
317
|
-
action: TelegramUpdateFlowAction<
|
|
318
|
-
|
|
364
|
+
action: TelegramUpdateFlowAction<
|
|
365
|
+
TReactionUpdate,
|
|
366
|
+
TCallbackQuery,
|
|
367
|
+
TMessage,
|
|
368
|
+
TGuestMessage
|
|
369
|
+
>,
|
|
370
|
+
): TelegramUpdateExecutionPlan<
|
|
371
|
+
TReactionUpdate,
|
|
372
|
+
TCallbackQuery,
|
|
373
|
+
TMessage,
|
|
374
|
+
TGuestMessage
|
|
375
|
+
> {
|
|
319
376
|
switch (action.kind) {
|
|
320
377
|
case "ignore":
|
|
321
378
|
return { kind: "ignore" };
|
|
@@ -345,6 +402,12 @@ export function buildTelegramUpdateExecutionPlan<
|
|
|
345
402
|
shouldPair: action.authorization.kind === "pair",
|
|
346
403
|
shouldDeny: action.authorization.kind === "deny",
|
|
347
404
|
};
|
|
405
|
+
case "guest":
|
|
406
|
+
return {
|
|
407
|
+
kind: "guest",
|
|
408
|
+
guestMessage: action.guestMessage,
|
|
409
|
+
shouldDeny: action.authorization.kind === "deny",
|
|
410
|
+
};
|
|
348
411
|
}
|
|
349
412
|
}
|
|
350
413
|
|
|
@@ -387,6 +450,7 @@ export interface TelegramUpdateRuntimeDeps<
|
|
|
387
450
|
callbackQueryId: string,
|
|
388
451
|
text?: string,
|
|
389
452
|
) => Promise<void>;
|
|
453
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
390
454
|
handleAuthorizedTelegramCallbackQuery: (
|
|
391
455
|
query: TCallbackQuery,
|
|
392
456
|
ctx: TContext,
|
|
@@ -404,6 +468,10 @@ export interface TelegramUpdateRuntimeDeps<
|
|
|
404
468
|
message: TMessage,
|
|
405
469
|
ctx: TContext,
|
|
406
470
|
) => unknown;
|
|
471
|
+
handleAuthorizedTelegramGuestMessage?: (
|
|
472
|
+
guestMessage: TelegramGuestMessage & { from: TelegramUser },
|
|
473
|
+
ctx: TContext,
|
|
474
|
+
) => Promise<void>;
|
|
407
475
|
}
|
|
408
476
|
|
|
409
477
|
export interface TelegramUpdateRuntimeControllerDeps<
|
|
@@ -431,6 +499,7 @@ export interface TelegramUpdateRuntimeControllerDeps<
|
|
|
431
499
|
callbackQueryId: string,
|
|
432
500
|
text?: string,
|
|
433
501
|
) => Promise<void>;
|
|
502
|
+
answerGuestQuery: (guestQueryId: string, text?: string) => Promise<void>;
|
|
434
503
|
handleAuthorizedTelegramCallbackQuery: (
|
|
435
504
|
query: TCallbackQuery,
|
|
436
505
|
ctx: TContext,
|
|
@@ -448,6 +517,10 @@ export interface TelegramUpdateRuntimeControllerDeps<
|
|
|
448
517
|
message: TMessage,
|
|
449
518
|
ctx: TContext,
|
|
450
519
|
) => unknown;
|
|
520
|
+
handleAuthorizedTelegramGuestMessage?: (
|
|
521
|
+
guestMessage: TelegramGuestMessage & { from: TelegramUser },
|
|
522
|
+
ctx: TContext,
|
|
523
|
+
) => Promise<void>;
|
|
451
524
|
}
|
|
452
525
|
|
|
453
526
|
export interface TelegramUpdateRuntimeController<
|
|
@@ -536,12 +609,15 @@ export function createTelegramPairedUpdateRuntime<
|
|
|
536
609
|
updateStatus: deps.updateStatus,
|
|
537
610
|
}).pairIfNeeded,
|
|
538
611
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
612
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
539
613
|
handleAuthorizedTelegramCallbackQuery:
|
|
540
614
|
deps.handleAuthorizedTelegramCallbackQuery,
|
|
541
615
|
sendTextReply: deps.sendTextReply,
|
|
542
616
|
handleAuthorizedTelegramMessage: deps.handleAuthorizedTelegramMessage,
|
|
543
617
|
handleAuthorizedTelegramEditedMessage:
|
|
544
618
|
deps.handleAuthorizedTelegramEditedMessage,
|
|
619
|
+
handleAuthorizedTelegramGuestMessage:
|
|
620
|
+
deps.handleAuthorizedTelegramGuestMessage,
|
|
545
621
|
});
|
|
546
622
|
}
|
|
547
623
|
|
|
@@ -582,12 +658,15 @@ export function createTelegramUpdateRuntime<
|
|
|
582
658
|
handleAuthorizedTelegramReactionUpdate: handleAuthorizedReactionUpdate,
|
|
583
659
|
pairTelegramUserIfNeeded: deps.pairTelegramUserIfNeeded,
|
|
584
660
|
answerCallbackQuery: deps.answerCallbackQuery,
|
|
661
|
+
answerGuestQuery: deps.answerGuestQuery,
|
|
585
662
|
handleAuthorizedTelegramCallbackQuery:
|
|
586
663
|
deps.handleAuthorizedTelegramCallbackQuery,
|
|
587
664
|
sendTextReply: deps.sendTextReply,
|
|
588
665
|
handleAuthorizedTelegramMessage: deps.handleAuthorizedTelegramMessage,
|
|
589
666
|
handleAuthorizedTelegramEditedMessage:
|
|
590
667
|
deps.handleAuthorizedTelegramEditedMessage,
|
|
668
|
+
handleAuthorizedTelegramGuestMessage:
|
|
669
|
+
deps.handleAuthorizedTelegramGuestMessage,
|
|
591
670
|
}),
|
|
592
671
|
};
|
|
593
672
|
}
|
|
@@ -712,6 +791,22 @@ export async function executeTelegramUpdatePlan<
|
|
|
712
791
|
await deps.handleAuthorizedTelegramCallbackQuery(plan.query, deps.ctx);
|
|
713
792
|
return;
|
|
714
793
|
}
|
|
794
|
+
if (plan.kind === "guest") {
|
|
795
|
+
if (plan.shouldDeny) {
|
|
796
|
+
await deps.answerGuestQuery(
|
|
797
|
+
plan.guestMessage.guest_query_id,
|
|
798
|
+
"Access denied.",
|
|
799
|
+
);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
if (deps.handleAuthorizedTelegramGuestMessage) {
|
|
803
|
+
await deps.handleAuthorizedTelegramGuestMessage(
|
|
804
|
+
plan.guestMessage,
|
|
805
|
+
deps.ctx,
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
715
810
|
const pairedNow = plan.shouldPair
|
|
716
811
|
? await deps.pairTelegramUserIfNeeded(plan.message.from.id, deps.ctx)
|
|
717
812
|
: false;
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-telegram",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.8",
|
|
4
4
|
"private": false,
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
5
8
|
"description": "Telegram Runtime Adapter for π",
|
|
6
9
|
"type": "module",
|
|
7
10
|
"keywords": [
|
|
@@ -20,6 +23,9 @@
|
|
|
20
23
|
"bugs": {
|
|
21
24
|
"url": "https://github.com/llblab/pi-telegram/issues"
|
|
22
25
|
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22.0.0"
|
|
28
|
+
},
|
|
23
29
|
"scripts": {
|
|
24
30
|
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
|
25
31
|
"typecheck": "tsc --noEmit",
|
|
@@ -37,9 +43,6 @@
|
|
|
37
43
|
"docs/",
|
|
38
44
|
"screenshot.png"
|
|
39
45
|
],
|
|
40
|
-
"publishConfig": {
|
|
41
|
-
"access": "public"
|
|
42
|
-
},
|
|
43
46
|
"pi": {
|
|
44
47
|
"extensions": [
|
|
45
48
|
"./index.ts"
|
|
@@ -47,9 +50,9 @@
|
|
|
47
50
|
"image": "https://github.com/llblab/pi-telegram/raw/main/screenshot.png"
|
|
48
51
|
},
|
|
49
52
|
"peerDependencies": {
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
+
"@earendil-works/pi-agent-core": "*",
|
|
54
|
+
"@earendil-works/pi-ai": "*",
|
|
55
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
53
56
|
"@sinclair/typebox": "*"
|
|
54
57
|
},
|
|
55
58
|
"devDependencies": {
|