@sympoies/dsh-telegram 0.0.0-bootstrap.0 → 0.6.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/LICENSE +1 -2
- package/NOTICE +19 -0
- package/README.md +589 -2
- package/cordis.patch.yml +14 -0
- package/lib/access.d.ts +76 -0
- package/lib/access.d.ts.map +1 -0
- package/lib/access.js +137 -0
- package/lib/access.js.map +1 -0
- package/lib/client.js +835 -0
- package/lib/commands.d.ts +51 -0
- package/lib/commands.d.ts.map +1 -0
- package/lib/commands.js +83 -0
- package/lib/commands.js.map +1 -0
- package/lib/config.d.ts +125 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +97 -0
- package/lib/config.js.map +1 -0
- package/lib/diagnostics.d.ts +58 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +59 -0
- package/lib/diagnostics.js.map +1 -0
- package/lib/failures.d.ts +71 -0
- package/lib/failures.d.ts.map +1 -0
- package/lib/failures.js +139 -0
- package/lib/failures.js.map +1 -0
- package/lib/harness/host.d.ts +106 -0
- package/lib/harness/host.d.ts.map +1 -0
- package/lib/harness/host.js +142 -0
- package/lib/harness/host.js.map +1 -0
- package/lib/harness/message.d.ts +45 -0
- package/lib/harness/message.d.ts.map +1 -0
- package/lib/harness/message.js +28 -0
- package/lib/harness/message.js.map +1 -0
- package/lib/harness/model-selection.d.ts +82 -0
- package/lib/harness/model-selection.d.ts.map +1 -0
- package/lib/harness/model-selection.js +103 -0
- package/lib/harness/model-selection.js.map +1 -0
- package/lib/harness/questions-seam.d.ts +34 -0
- package/lib/harness/questions-seam.d.ts.map +1 -0
- package/lib/harness/questions-seam.js +45 -0
- package/lib/harness/questions-seam.js.map +1 -0
- package/lib/harness/types.d.ts +108 -0
- package/lib/harness/types.d.ts.map +1 -0
- package/lib/harness/types.js +23 -0
- package/lib/harness/types.js.map +1 -0
- package/lib/index.d.ts +65 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +977 -0
- package/lib/index.js.map +1 -0
- package/lib/interact/approvals.d.ts +59 -0
- package/lib/interact/approvals.d.ts.map +1 -0
- package/lib/interact/approvals.js +132 -0
- package/lib/interact/approvals.js.map +1 -0
- package/lib/interact/pending.d.ts +56 -0
- package/lib/interact/pending.d.ts.map +1 -0
- package/lib/interact/pending.js +89 -0
- package/lib/interact/pending.js.map +1 -0
- package/lib/interact/questions.d.ts +100 -0
- package/lib/interact/questions.d.ts.map +1 -0
- package/lib/interact/questions.js +255 -0
- package/lib/interact/questions.js.map +1 -0
- package/lib/interact/surface.d.ts +33 -0
- package/lib/interact/surface.d.ts.map +1 -0
- package/lib/interact/surface.js +39 -0
- package/lib/interact/surface.js.map +1 -0
- package/lib/interact/text-capture.d.ts +38 -0
- package/lib/interact/text-capture.d.ts.map +1 -0
- package/lib/interact/text-capture.js +75 -0
- package/lib/interact/text-capture.js.map +1 -0
- package/lib/media/collect.d.ts +157 -0
- package/lib/media/collect.d.ts.map +1 -0
- package/lib/media/collect.js +235 -0
- package/lib/media/collect.js.map +1 -0
- package/lib/media/extractor.d.ts +117 -0
- package/lib/media/extractor.d.ts.map +1 -0
- package/lib/media/extractor.js +207 -0
- package/lib/media/extractor.js.map +1 -0
- package/lib/media/intake.d.ts +57 -0
- package/lib/media/intake.d.ts.map +1 -0
- package/lib/media/intake.js +115 -0
- package/lib/media/intake.js.map +1 -0
- package/lib/media/limits.d.ts +69 -0
- package/lib/media/limits.d.ts.map +1 -0
- package/lib/media/limits.js +97 -0
- package/lib/media/limits.js.map +1 -0
- package/lib/media/ocr.d.ts +66 -0
- package/lib/media/ocr.d.ts.map +1 -0
- package/lib/media/ocr.js +131 -0
- package/lib/media/ocr.js.map +1 -0
- package/lib/media/screenshot.d.ts +57 -0
- package/lib/media/screenshot.d.ts.map +1 -0
- package/lib/media/screenshot.js +101 -0
- package/lib/media/screenshot.js.map +1 -0
- package/lib/media/vision.d.ts +60 -0
- package/lib/media/vision.d.ts.map +1 -0
- package/lib/media/vision.js +74 -0
- package/lib/media/vision.js.map +1 -0
- package/lib/render/clamp.d.ts +34 -0
- package/lib/render/clamp.d.ts.map +1 -0
- package/lib/render/clamp.js +65 -0
- package/lib/render/clamp.js.map +1 -0
- package/lib/render/escape.d.ts +15 -0
- package/lib/render/escape.d.ts.map +1 -0
- package/lib/render/escape.js +17 -0
- package/lib/render/escape.js.map +1 -0
- package/lib/reply/activity.d.ts +31 -0
- package/lib/reply/activity.d.ts.map +1 -0
- package/lib/reply/activity.js +88 -0
- package/lib/reply/activity.js.map +1 -0
- package/lib/reply/rich-stream.d.ts +173 -0
- package/lib/reply/rich-stream.d.ts.map +1 -0
- package/lib/reply/rich-stream.js +286 -0
- package/lib/reply/rich-stream.js.map +1 -0
- package/lib/reply/split-markdown.d.ts +21 -0
- package/lib/reply/split-markdown.d.ts.map +1 -0
- package/lib/reply/split-markdown.js +87 -0
- package/lib/reply/split-markdown.js.map +1 -0
- package/lib/reply/turn-bridge.d.ts +143 -0
- package/lib/reply/turn-bridge.d.ts.map +1 -0
- package/lib/reply/turn-bridge.js +182 -0
- package/lib/reply/turn-bridge.js.map +1 -0
- package/lib/router.d.ts +415 -0
- package/lib/router.d.ts.map +1 -0
- package/lib/router.js +663 -0
- package/lib/router.js.map +1 -0
- package/lib/secrets.d.ts +30 -0
- package/lib/secrets.d.ts.map +1 -0
- package/lib/secrets.js +51 -0
- package/lib/secrets.js.map +1 -0
- package/lib/session/bindings.d.ts +81 -0
- package/lib/session/bindings.d.ts.map +1 -0
- package/lib/session/bindings.js +170 -0
- package/lib/session/bindings.js.map +1 -0
- package/lib/session/history.d.ts +50 -0
- package/lib/session/history.d.ts.map +1 -0
- package/lib/session/history.js +127 -0
- package/lib/session/history.js.map +1 -0
- package/lib/session/models.d.ts +99 -0
- package/lib/session/models.d.ts.map +1 -0
- package/lib/session/models.js +123 -0
- package/lib/session/models.js.map +1 -0
- package/lib/session/permission.d.ts +83 -0
- package/lib/session/permission.d.ts.map +1 -0
- package/lib/session/permission.js +107 -0
- package/lib/session/permission.js.map +1 -0
- package/lib/session/picker.d.ts +49 -0
- package/lib/session/picker.d.ts.map +1 -0
- package/lib/session/picker.js +107 -0
- package/lib/session/picker.js.map +1 -0
- package/lib/session/preferences.d.ts +55 -0
- package/lib/session/preferences.d.ts.map +1 -0
- package/lib/session/preferences.js +102 -0
- package/lib/session/preferences.js.map +1 -0
- package/lib/session/recovery.d.ts +66 -0
- package/lib/session/recovery.d.ts.map +1 -0
- package/lib/session/recovery.js +112 -0
- package/lib/session/recovery.js.map +1 -0
- package/lib/session/runner.d.ts +223 -0
- package/lib/session/runner.d.ts.map +1 -0
- package/lib/session/runner.js +260 -0
- package/lib/session/runner.js.map +1 -0
- package/lib/session/workspaces.d.ts +28 -0
- package/lib/session/workspaces.d.ts.map +1 -0
- package/lib/session/workspaces.js +53 -0
- package/lib/session/workspaces.js.map +1 -0
- package/lib/telegram/addressing.d.ts +39 -0
- package/lib/telegram/addressing.d.ts.map +1 -0
- package/lib/telegram/addressing.js +81 -0
- package/lib/telegram/addressing.js.map +1 -0
- package/lib/telegram/albums.d.ts +46 -0
- package/lib/telegram/albums.d.ts.map +1 -0
- package/lib/telegram/albums.js +104 -0
- package/lib/telegram/albums.js.map +1 -0
- package/lib/telegram/api.d.ts +238 -0
- package/lib/telegram/api.d.ts.map +1 -0
- package/lib/telegram/api.js +518 -0
- package/lib/telegram/api.js.map +1 -0
- package/lib/telegram/poller.d.ts +59 -0
- package/lib/telegram/poller.d.ts.map +1 -0
- package/lib/telegram/poller.js +94 -0
- package/lib/telegram/poller.js.map +1 -0
- package/lib/telegram/types.d.ts +130 -0
- package/lib/telegram/types.d.ts.map +1 -0
- package/lib/telegram/types.js +9 -0
- package/lib/telegram/types.js.map +1 -0
- package/lib/telegram/typing.d.ts +47 -0
- package/lib/telegram/typing.d.ts.map +1 -0
- package/lib/telegram/typing.js +91 -0
- package/lib/telegram/typing.js.map +1 -0
- package/lib/versions.d.ts +63 -0
- package/lib/versions.d.ts.map +1 -0
- package/lib/versions.js +135 -0
- package/lib/versions.js.map +1 -0
- package/package.json +94 -10
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Bot API client.
|
|
3
|
+
*
|
|
4
|
+
* Speaks the HTTP protocol directly — no SDK — because the surface this plugin
|
|
5
|
+
* needs is a dozen methods wide and an SDK would only add a dependency and a
|
|
6
|
+
* translation layer between us and the error messages that matter.
|
|
7
|
+
*
|
|
8
|
+
* Three behaviours are deliberate and are the reason this client exists rather
|
|
9
|
+
* than a thin fetch wrapper:
|
|
10
|
+
*
|
|
11
|
+
* - **Replies are sent as rich markdown.** Since Bot API 10.1 Telegram parses
|
|
12
|
+
* markdown itself, so the agent's tables, headings and lists arrive as real
|
|
13
|
+
* elements. The plugin's own prompts stay on plain HTML: they are short,
|
|
14
|
+
* built from escaped text, and carry the inline keyboards.
|
|
15
|
+
* - **Rate limits are waited out, not thrown.** A 429 carries `retry_after`;
|
|
16
|
+
* the client sleeps that long and retries, so a burst of streaming edits
|
|
17
|
+
* degrades into slower edits rather than a failed turn.
|
|
18
|
+
* - **The token never reaches a log.** It lives in the request path, so every
|
|
19
|
+
* error message is redacted before it escapes.
|
|
20
|
+
*/
|
|
21
|
+
/** Attempts for a request that fails with a retryable status. */
|
|
22
|
+
const MAX_RETRIES = 3;
|
|
23
|
+
/** Backoff for retryable failures that carry no `retry_after`. */
|
|
24
|
+
const BACKOFF_MS = [500, 1500, 4000];
|
|
25
|
+
/** Statuses worth retrying: rate limit, and Telegram's own transient faults. */
|
|
26
|
+
const RETRYABLE = new Set([429, 500, 502, 503, 504]);
|
|
27
|
+
/**
|
|
28
|
+
* Methods safe to repeat after a transport failure.
|
|
29
|
+
*
|
|
30
|
+
* A dropped connection leaves no way to know whether the request arrived, so
|
|
31
|
+
* repeating a send could post the same message twice. These are all reads or
|
|
32
|
+
* idempotent, so repeating one costs nothing and rescues the call.
|
|
33
|
+
*
|
|
34
|
+
* This matters most for `getFile`: it runs while the long poll holds its own
|
|
35
|
+
* connection open, so it is the call most likely to need a fresh one, and a
|
|
36
|
+
* single flaky connect was turning a sent screenshot into an error.
|
|
37
|
+
*/
|
|
38
|
+
const IDEMPOTENT = new Set(['getMe', 'getFile', 'getUpdates', 'deleteWebhook', 'downloadFile']);
|
|
39
|
+
/** Deadline for one file download; larger than an API call's, for large files. */
|
|
40
|
+
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 60_000;
|
|
41
|
+
/** A Bot API call that failed, with the token stripped from every field. */
|
|
42
|
+
export class TelegramApiError extends Error {
|
|
43
|
+
code;
|
|
44
|
+
description;
|
|
45
|
+
retryAfterSeconds;
|
|
46
|
+
constructor(message, code, description,
|
|
47
|
+
/** Seconds Telegram asked us to wait, from a 429's `parameters.retry_after`. */
|
|
48
|
+
retryAfterSeconds) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.code = code;
|
|
51
|
+
this.description = description;
|
|
52
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
53
|
+
this.name = 'TelegramApiError';
|
|
54
|
+
}
|
|
55
|
+
/** Whether the failure means the bot token itself is not valid. */
|
|
56
|
+
get isAuthFailure() {
|
|
57
|
+
return this.code === 401;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export class TelegramApi {
|
|
61
|
+
options;
|
|
62
|
+
fetchImpl;
|
|
63
|
+
sleep;
|
|
64
|
+
downloadTimeoutMs;
|
|
65
|
+
constructor(options) {
|
|
66
|
+
this.options = options;
|
|
67
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
68
|
+
this.sleep = options.sleep ?? defaultSleep;
|
|
69
|
+
this.downloadTimeoutMs = options.downloadTimeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS;
|
|
70
|
+
}
|
|
71
|
+
/** Verify the token and read the bot's own identity. */
|
|
72
|
+
getMe() {
|
|
73
|
+
return this.call('getMe', {});
|
|
74
|
+
}
|
|
75
|
+
/** Drop any webhook so long polling is the only delivery path. */
|
|
76
|
+
async deleteWebhook() {
|
|
77
|
+
await this.call('deleteWebhook', { drop_pending_updates: false });
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Long-poll for updates.
|
|
81
|
+
*
|
|
82
|
+
* `allowed_updates` includes `callback_query`, which is what lets a button
|
|
83
|
+
* press reach the agent at all.
|
|
84
|
+
*/
|
|
85
|
+
getUpdates(offset, timeoutSeconds, signal) {
|
|
86
|
+
return this.call('getUpdates', {
|
|
87
|
+
offset,
|
|
88
|
+
timeout: timeoutSeconds,
|
|
89
|
+
allowed_updates: ['message', 'callback_query'],
|
|
90
|
+
}, { ...(signal ? { signal } : {}), timeoutMs: timeoutSeconds * 1000 + 5000 });
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Send one HTML message, falling back to plain text if Telegram rejects the
|
|
94
|
+
* markup rather than letting the content vanish.
|
|
95
|
+
*/
|
|
96
|
+
async sendMessage(options) {
|
|
97
|
+
const body = {
|
|
98
|
+
chat_id: options.chatId,
|
|
99
|
+
text: options.html,
|
|
100
|
+
parse_mode: 'HTML',
|
|
101
|
+
link_preview_options: { is_disabled: true },
|
|
102
|
+
...(options.keyboard ? { reply_markup: toReplyMarkup(options.keyboard) } : {}),
|
|
103
|
+
...(options.replyToMessageId
|
|
104
|
+
? { reply_parameters: { message_id: options.replyToMessageId } }
|
|
105
|
+
: {}),
|
|
106
|
+
...(options.threadId !== undefined ? { message_thread_id: options.threadId } : {}),
|
|
107
|
+
};
|
|
108
|
+
const sent = await this.call('sendMessage', body, {
|
|
109
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
110
|
+
});
|
|
111
|
+
return { messageId: sent.message_id };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Edit one message in place — the mechanism behind streamed replies.
|
|
115
|
+
*
|
|
116
|
+
* Telegram answers 400 when the new text equals the old, which during
|
|
117
|
+
* streaming is an ordinary and frequent event, so it is reported rather than
|
|
118
|
+
* thrown.
|
|
119
|
+
*/
|
|
120
|
+
async editMessageText(options) {
|
|
121
|
+
const body = {
|
|
122
|
+
chat_id: options.chatId,
|
|
123
|
+
message_id: options.messageId,
|
|
124
|
+
text: options.html,
|
|
125
|
+
parse_mode: 'HTML',
|
|
126
|
+
link_preview_options: { is_disabled: true },
|
|
127
|
+
...(options.keyboard ? { reply_markup: toReplyMarkup(options.keyboard) } : {}),
|
|
128
|
+
};
|
|
129
|
+
try {
|
|
130
|
+
await this.call('editMessageText', body, {
|
|
131
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
132
|
+
});
|
|
133
|
+
return 'edited';
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (isNotModified(error))
|
|
137
|
+
return 'unchanged';
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Send one rich message.
|
|
143
|
+
*
|
|
144
|
+
* Telegram parses the Rich Markdown itself, so tables, headings, lists and
|
|
145
|
+
* task lists arrive as real elements rather than as an approximation of
|
|
146
|
+
* them — and the cap is 32768 characters rather than 4096.
|
|
147
|
+
*/
|
|
148
|
+
async sendRichMessage(options) {
|
|
149
|
+
const sent = await this.call('sendRichMessage', {
|
|
150
|
+
chat_id: options.chatId,
|
|
151
|
+
rich_message: { markdown: options.markdown },
|
|
152
|
+
...(options.keyboard ? { reply_markup: toReplyMarkup(options.keyboard) } : {}),
|
|
153
|
+
...(options.replyToMessageId
|
|
154
|
+
? { reply_parameters: { message_id: options.replyToMessageId } }
|
|
155
|
+
: {}),
|
|
156
|
+
...(options.threadId !== undefined ? { message_thread_id: options.threadId } : {}),
|
|
157
|
+
}, { ...(options.signal ? { signal: options.signal } : {}) });
|
|
158
|
+
return { messageId: sent.message_id };
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Stream one frame of a partial reply.
|
|
162
|
+
*
|
|
163
|
+
* The draft is a 30-second ephemeral preview, and Telegram animates changes
|
|
164
|
+
* carrying the same `draftId` — so a turn keeps one id throughout and the
|
|
165
|
+
* text grows in place. It must still be persisted with a real send when the
|
|
166
|
+
* turn ends, and it works in private chats only.
|
|
167
|
+
*/
|
|
168
|
+
async sendRichMessageDraft(options) {
|
|
169
|
+
await this.call('sendRichMessageDraft', {
|
|
170
|
+
chat_id: options.chatId,
|
|
171
|
+
draft_id: options.draftId,
|
|
172
|
+
rich_message: { markdown: options.markdown },
|
|
173
|
+
...(options.threadId !== undefined ? { message_thread_id: options.threadId } : {}),
|
|
174
|
+
}, { ...(options.signal ? { signal: options.signal } : {}) });
|
|
175
|
+
}
|
|
176
|
+
/** Replace a message's content with rich markdown. */
|
|
177
|
+
async editRichMessage(options) {
|
|
178
|
+
try {
|
|
179
|
+
await this.call('editMessageText', {
|
|
180
|
+
chat_id: options.chatId,
|
|
181
|
+
message_id: options.messageId,
|
|
182
|
+
rich_message: { markdown: options.markdown },
|
|
183
|
+
...(options.keyboard ? { reply_markup: toReplyMarkup(options.keyboard) } : {}),
|
|
184
|
+
});
|
|
185
|
+
return 'edited';
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (isNotModified(error))
|
|
189
|
+
return 'unchanged';
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/** Replace or clear a message's inline keyboard, leaving its text alone. */
|
|
194
|
+
async editMessageReplyMarkup(options) {
|
|
195
|
+
try {
|
|
196
|
+
await this.call('editMessageReplyMarkup', {
|
|
197
|
+
chat_id: options.chatId,
|
|
198
|
+
message_id: options.messageId,
|
|
199
|
+
reply_markup: toReplyMarkup(options.keyboard),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
if (!isNotModified(error))
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Acknowledge a button press. Telegram shows a spinner on the button until
|
|
209
|
+
* this lands, so it is sent before any slow work the press triggers.
|
|
210
|
+
*/
|
|
211
|
+
async answerCallbackQuery(id, text) {
|
|
212
|
+
try {
|
|
213
|
+
await this.call('answerCallbackQuery', {
|
|
214
|
+
callback_query_id: id,
|
|
215
|
+
...(text ? { text } : {}),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// An expired callback id is normal (the user pressed twice, or waited);
|
|
220
|
+
// failing to acknowledge must never abort the work the press started.
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Upload one file to a chat.
|
|
225
|
+
*
|
|
226
|
+
* Multipart rather than the JSON path every other call uses, because
|
|
227
|
+
* Telegram takes file bytes no other way. Node builds the body: `FormData`
|
|
228
|
+
* and `Blob` are standard here, so there is no boundary to get wrong.
|
|
229
|
+
*
|
|
230
|
+
* Not retried. A retry would re-upload the whole file, and the failures that
|
|
231
|
+
* matter for one — too large, wrong type — do not pass on a second attempt.
|
|
232
|
+
*
|
|
233
|
+
* @param method - `sendPhoto` or `sendDocument`.
|
|
234
|
+
* @param field - the form field Telegram expects the bytes under.
|
|
235
|
+
* @param options - the chat, the bytes, and what to call them.
|
|
236
|
+
*/
|
|
237
|
+
async uploadFile(method, field, options) {
|
|
238
|
+
const form = new FormData();
|
|
239
|
+
form.set('chat_id', options.chatId);
|
|
240
|
+
if (options.caption !== undefined)
|
|
241
|
+
form.set('caption', options.caption);
|
|
242
|
+
if (options.caption !== undefined)
|
|
243
|
+
form.set('parse_mode', 'HTML');
|
|
244
|
+
if (options.threadId !== undefined)
|
|
245
|
+
form.set('message_thread_id', String(options.threadId));
|
|
246
|
+
form.set(field,
|
|
247
|
+
// Copied into a plain ArrayBuffer: a Uint8Array view may sit on a larger
|
|
248
|
+
// pooled buffer, and Blob would take the whole thing.
|
|
249
|
+
new Blob([options.data.slice().buffer], { type: options.contentType }), options.filename);
|
|
250
|
+
const controller = new AbortController();
|
|
251
|
+
const timer = setTimeout(() => controller.abort(), this.downloadTimeoutMs);
|
|
252
|
+
try {
|
|
253
|
+
const response = await this.fetchImpl(`${this.base()}/bot${this.options.token}/${method}`, {
|
|
254
|
+
method: 'POST',
|
|
255
|
+
body: form,
|
|
256
|
+
signal: controller.signal,
|
|
257
|
+
});
|
|
258
|
+
const envelope = parseEnvelope(await response.text(), method);
|
|
259
|
+
if (!envelope.ok) {
|
|
260
|
+
throw new TelegramApiError(this.redact(`telegram ${method} failed: ${envelope.description ?? `http ${response.status}`}`), envelope.error_code ?? response.status, envelope.description === undefined ? undefined : this.redact(envelope.description));
|
|
261
|
+
}
|
|
262
|
+
return { messageId: envelope.result.message_id };
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
throw this.normalize(error, method);
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Publish the command menu Telegram offers when someone types `/`.
|
|
273
|
+
*
|
|
274
|
+
* Without this the bot answers every command correctly and advertises none
|
|
275
|
+
* of them, so they are discoverable only by reading the README. Best-effort:
|
|
276
|
+
* a bot that cannot publish its menu still works, it is only harder to find
|
|
277
|
+
* your way around.
|
|
278
|
+
*
|
|
279
|
+
* @param commands - name and one-line description, in menu order.
|
|
280
|
+
*/
|
|
281
|
+
async setMyCommands(commands) {
|
|
282
|
+
try {
|
|
283
|
+
await this.call('setMyCommands', { commands });
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
// Cosmetic only.
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** Show a progress indicator in the chat. Best-effort by design. */
|
|
290
|
+
async sendChatAction(chatId, action, threadId) {
|
|
291
|
+
try {
|
|
292
|
+
await this.call('sendChatAction', {
|
|
293
|
+
chat_id: chatId,
|
|
294
|
+
action,
|
|
295
|
+
...(threadId === undefined ? {} : { message_thread_id: threadId }),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// Cosmetic only.
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/** Resolve a file id to downloadable metadata. */
|
|
303
|
+
getFile(fileId) {
|
|
304
|
+
return this.call('getFile', { file_id: fileId });
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Download a resolved file's bytes.
|
|
308
|
+
*
|
|
309
|
+
* The download URL carries the token in its path, and a transport failure
|
|
310
|
+
* quotes the URL it was given — so this fetch is wrapped like every other,
|
|
311
|
+
* or a dropped connection would hand the token to the caller's log.
|
|
312
|
+
*/
|
|
313
|
+
async downloadFile(filePath, signal) {
|
|
314
|
+
const url = `${this.base()}/file/bot${this.options.token}/${filePath.replace(/^\/+/, '')}`;
|
|
315
|
+
return this.withRetries('downloadFile', signal, async () => {
|
|
316
|
+
// Its own deadline, and a generous one: without a timeout this waited on
|
|
317
|
+
// the operating system's TCP timeout, which is minutes, and a file may
|
|
318
|
+
// legitimately be 20 MB over a slow link.
|
|
319
|
+
const controller = new AbortController();
|
|
320
|
+
const timer = setTimeout(() => controller.abort(), this.downloadTimeoutMs);
|
|
321
|
+
const onAbort = () => controller.abort();
|
|
322
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
323
|
+
try {
|
|
324
|
+
const response = await this.fetchImpl(url, { signal: controller.signal });
|
|
325
|
+
if (!response.ok) {
|
|
326
|
+
throw new TelegramApiError(`telegram file download failed with http ${response.status}`, response.status, undefined);
|
|
327
|
+
}
|
|
328
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
throw this.normalize(error, 'downloadFile');
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
clearTimeout(timer);
|
|
335
|
+
signal?.removeEventListener('abort', onAbort);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
/** Base url without a trailing slash. */
|
|
340
|
+
base() {
|
|
341
|
+
return this.options.baseUrl.replace(/\/+$/, '');
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* One Bot API call, with retries for transient failures.
|
|
345
|
+
*
|
|
346
|
+
* @throws {TelegramApiError} with the token redacted from every field.
|
|
347
|
+
*/
|
|
348
|
+
call(method, body, options = {}) {
|
|
349
|
+
return this.withRetries(method, options.signal, () => this.request(method, body, options));
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Run one operation, backing off and repeating what is safe to repeat.
|
|
353
|
+
*
|
|
354
|
+
* Shared with the download path rather than living inside `call`: a download
|
|
355
|
+
* is the most retry-worthy request this client makes — a pure read of bytes
|
|
356
|
+
* — and leaving it outside the retry loop is what turned an intermittent
|
|
357
|
+
* connection into a screenshot the agent never saw.
|
|
358
|
+
*/
|
|
359
|
+
async withRetries(method, signal, run) {
|
|
360
|
+
let lastError;
|
|
361
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt += 1) {
|
|
362
|
+
try {
|
|
363
|
+
return await run();
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
lastError = error;
|
|
367
|
+
const delay = retryDelay(error, attempt, IDEMPOTENT.has(method));
|
|
368
|
+
if (delay === undefined)
|
|
369
|
+
throw error;
|
|
370
|
+
await this.sleep(delay, signal);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
throw lastError;
|
|
374
|
+
}
|
|
375
|
+
/** One attempt: build, send, validate, unwrap. */
|
|
376
|
+
async request(method, body, options) {
|
|
377
|
+
const controller = new AbortController();
|
|
378
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs ?? this.options.timeoutMs);
|
|
379
|
+
const onAbort = () => controller.abort();
|
|
380
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
381
|
+
try {
|
|
382
|
+
const response = await this.fetchImpl(`${this.base()}/bot${this.options.token}/${method}`, {
|
|
383
|
+
method: 'POST',
|
|
384
|
+
headers: { 'content-type': 'application/json' },
|
|
385
|
+
body: JSON.stringify(prune(body)),
|
|
386
|
+
signal: controller.signal,
|
|
387
|
+
});
|
|
388
|
+
const envelope = parseEnvelope(await response.text(), method);
|
|
389
|
+
if (!envelope.ok) {
|
|
390
|
+
throw new TelegramApiError(this.redact(`telegram ${method} failed: ${envelope.description ?? `http ${response.status}`}`), envelope.error_code ?? response.status,
|
|
391
|
+
// Redacted too, though Telegram's own descriptions do not quote the
|
|
392
|
+
// request. `description` is read straight into a log line on an auth
|
|
393
|
+
// failure, and a field that skips this is a field that only has to be
|
|
394
|
+
// wrong once.
|
|
395
|
+
envelope.description === undefined ? undefined : this.redact(envelope.description), envelope.parameters?.retry_after);
|
|
396
|
+
}
|
|
397
|
+
return envelope.result;
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
throw this.normalize(error, method);
|
|
401
|
+
}
|
|
402
|
+
finally {
|
|
403
|
+
clearTimeout(timer);
|
|
404
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/** Wrap any thrown value as a redacted TelegramApiError. */
|
|
408
|
+
normalize(error, method) {
|
|
409
|
+
if (error instanceof TelegramApiError)
|
|
410
|
+
return error;
|
|
411
|
+
return new TelegramApiError(`telegram ${method} failed: ${this.redact(describeCause(error))}`, undefined, undefined);
|
|
412
|
+
}
|
|
413
|
+
/** Remove the bot token from any string bound for a log or an error. */
|
|
414
|
+
redact(text) {
|
|
415
|
+
return text.split(this.options.token).join('<redacted>');
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/** Parse a response body, treating unparseable output as a failed call. */
|
|
419
|
+
function parseEnvelope(text, method) {
|
|
420
|
+
if (text === '')
|
|
421
|
+
return { ok: true, result: undefined };
|
|
422
|
+
try {
|
|
423
|
+
return JSON.parse(text);
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
throw new TelegramApiError(`telegram ${method} returned a non-JSON response`, undefined, undefined);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* How long to wait before retrying, or undefined when the failure is final.
|
|
431
|
+
* A 429 names its own delay; everything else uses fixed backoff.
|
|
432
|
+
*
|
|
433
|
+
* @param repeatable - whether repeating this method is safe when the transport
|
|
434
|
+
* failed and the request's fate is unknown.
|
|
435
|
+
*/
|
|
436
|
+
function retryDelay(error, attempt, repeatable) {
|
|
437
|
+
if (!(error instanceof TelegramApiError))
|
|
438
|
+
return undefined;
|
|
439
|
+
if (attempt >= MAX_RETRIES - 1)
|
|
440
|
+
return undefined;
|
|
441
|
+
// No status means the request never got an answer: a refused connection, a
|
|
442
|
+
// dropped socket, a name that would not resolve. The poll loop already
|
|
443
|
+
// survives those; an individual call did not, so one flaky connect surfaced
|
|
444
|
+
// to the user as a failure.
|
|
445
|
+
if (error.code === undefined) {
|
|
446
|
+
return repeatable ? (BACKOFF_MS[attempt] ?? BACKOFF_MS[BACKOFF_MS.length - 1]) : undefined;
|
|
447
|
+
}
|
|
448
|
+
if (!RETRYABLE.has(error.code))
|
|
449
|
+
return undefined;
|
|
450
|
+
if (error.retryAfterSeconds !== undefined)
|
|
451
|
+
return error.retryAfterSeconds * 1000;
|
|
452
|
+
const stated = /retry after (\d+)/i.exec(error.description ?? '')?.[1];
|
|
453
|
+
if (stated)
|
|
454
|
+
return Number(stated) * 1000;
|
|
455
|
+
if (error.code === 429)
|
|
456
|
+
return 1000;
|
|
457
|
+
return BACKOFF_MS[attempt] ?? BACKOFF_MS[BACKOFF_MS.length - 1];
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* One readable line from an error and everything underneath it.
|
|
461
|
+
*
|
|
462
|
+
* Node's fetch reports every transport failure as the same three words —
|
|
463
|
+
* `fetch failed` — and puts what actually happened in `cause`: a refused
|
|
464
|
+
* connection, an unresolved name, an expired certificate, a timeout. Reporting
|
|
465
|
+
* only the top layer turns four distinct problems into one unactionable
|
|
466
|
+
* sentence, which is exactly how far a diagnosis gets on it.
|
|
467
|
+
*
|
|
468
|
+
* @param error - any thrown value.
|
|
469
|
+
* @returns the message chain, outermost first.
|
|
470
|
+
*/
|
|
471
|
+
export function describeCause(error) {
|
|
472
|
+
const chain = [];
|
|
473
|
+
let current = error;
|
|
474
|
+
// Bounded: a cause chain is normally two or three deep, and a cyclic one
|
|
475
|
+
// must not become an infinite loop inside an error handler.
|
|
476
|
+
for (let depth = 0; depth < 5 && current !== undefined && current !== null; depth += 1) {
|
|
477
|
+
const message = current instanceof Error ? current.message : String(current);
|
|
478
|
+
const code = current.code;
|
|
479
|
+
const labelled = typeof code === 'string' && !message.includes(code) ? `${message} (${code})` : message;
|
|
480
|
+
if (labelled !== '' && !chain.includes(labelled))
|
|
481
|
+
chain.push(labelled);
|
|
482
|
+
current = current instanceof Error ? current.cause : undefined;
|
|
483
|
+
}
|
|
484
|
+
return chain.length > 0 ? chain.join(': ') : String(error);
|
|
485
|
+
}
|
|
486
|
+
/** Whether a failure means "the edit changed nothing", which is not a failure. */
|
|
487
|
+
function isNotModified(error) {
|
|
488
|
+
if (!(error instanceof TelegramApiError))
|
|
489
|
+
return false;
|
|
490
|
+
return /message is not modified/i.test(error.description ?? '');
|
|
491
|
+
}
|
|
492
|
+
/** Convert our keyboard shape into the Bot API's snake-cased markup. */
|
|
493
|
+
function toReplyMarkup(keyboard) {
|
|
494
|
+
return {
|
|
495
|
+
inline_keyboard: keyboard.map((row) => row.map((button) => ({ text: button.text, callback_data: button.callbackData }))),
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/** Drop undefined values so `parse_mode: undefined` really omits the field. */
|
|
499
|
+
function prune(body) {
|
|
500
|
+
return Object.fromEntries(Object.entries(body).filter(([, value]) => value !== undefined));
|
|
501
|
+
}
|
|
502
|
+
/** Cancellable sleep used by the retry path. */
|
|
503
|
+
function defaultSleep(ms, signal) {
|
|
504
|
+
return new Promise((resolve) => {
|
|
505
|
+
if (signal?.aborted)
|
|
506
|
+
return resolve();
|
|
507
|
+
const timer = setTimeout(() => {
|
|
508
|
+
signal?.removeEventListener('abort', onAbort);
|
|
509
|
+
resolve();
|
|
510
|
+
}, ms);
|
|
511
|
+
const onAbort = () => {
|
|
512
|
+
clearTimeout(timer);
|
|
513
|
+
resolve();
|
|
514
|
+
};
|
|
515
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/telegram/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The long-poll receive loop.
|
|
3
|
+
*
|
|
4
|
+
* Telegram's `getUpdates` offset is the delivery guarantee: an update is
|
|
5
|
+
* redelivered until an offset past it is acknowledged. So the offset advances
|
|
6
|
+
* only after the handler for that update returns. A handler that throws leaves
|
|
7
|
+
* the offset where it is and the update comes back on the next poll — which is
|
|
8
|
+
* why the router above it never throws for ordinary failures, and only a real
|
|
9
|
+
* fault gets a retry.
|
|
10
|
+
*
|
|
11
|
+
* Reconnection is the loop's other job. A laptop that sleeps, a network that
|
|
12
|
+
* drops, a Telegram hiccup — each surfaces as a rejected poll, and the bot has
|
|
13
|
+
* to come back on its own rather than going quietly silent.
|
|
14
|
+
*/
|
|
15
|
+
import type { Logger } from '../harness/types.js';
|
|
16
|
+
import type { TelegramUpdate } from './types.js';
|
|
17
|
+
/** Where updates come from — narrowed so tests need no Bot API client. */
|
|
18
|
+
export interface UpdateSource {
|
|
19
|
+
getUpdates(offset: number, timeoutSeconds: number, signal?: AbortSignal): Promise<TelegramUpdate[]>;
|
|
20
|
+
}
|
|
21
|
+
/** Construction options. */
|
|
22
|
+
export interface PollerOptions {
|
|
23
|
+
readonly source: UpdateSource;
|
|
24
|
+
/** Handles one update; may throw, which leaves the update unacknowledged. */
|
|
25
|
+
readonly onUpdate: (update: TelegramUpdate) => Promise<void>;
|
|
26
|
+
/** Seconds Telegram holds an empty poll open. */
|
|
27
|
+
readonly longPollSeconds?: number;
|
|
28
|
+
/** First reconnect delay; doubles up to `maxDelayMs`. */
|
|
29
|
+
readonly baseDelayMs?: number;
|
|
30
|
+
readonly maxDelayMs?: number;
|
|
31
|
+
/** Called the first time a poll succeeds after a failure. */
|
|
32
|
+
readonly onConnected?: () => void;
|
|
33
|
+
readonly sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
34
|
+
readonly logger?: Logger;
|
|
35
|
+
}
|
|
36
|
+
export declare class UpdatePoller {
|
|
37
|
+
private readonly options;
|
|
38
|
+
private readonly logger;
|
|
39
|
+
private readonly sleep;
|
|
40
|
+
private offset;
|
|
41
|
+
private connected;
|
|
42
|
+
constructor(options: PollerOptions);
|
|
43
|
+
/** The next offset that will be acknowledged; exposed for tests and status. */
|
|
44
|
+
get acknowledged(): number;
|
|
45
|
+
/**
|
|
46
|
+
* Poll until the signal aborts.
|
|
47
|
+
*
|
|
48
|
+
* @param signal - stops the loop; an in-flight poll is aborted with it.
|
|
49
|
+
*/
|
|
50
|
+
run(signal: AbortSignal): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Hand each update to the handler, acknowledging only after it returns.
|
|
53
|
+
* A throw stops the batch so the failed update is redelivered.
|
|
54
|
+
*/
|
|
55
|
+
private dispatch;
|
|
56
|
+
/** Exponential backoff, capped so a long outage still retries regularly. */
|
|
57
|
+
private backoff;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=poller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../../src/telegram/poller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAGjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;CACpG;AAED,4BAA4B;AAC5B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,iDAAiD;IACjD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,yDAAyD;IACzD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,qBAAa,YAAY;IAMX,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqD;IAC3E,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,SAAS,CAAQ;gBAEI,OAAO,EAAE,aAAa;IAKnD,+EAA+E;IAC/E,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;;OAIG;IACG,GAAG,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B7C;;;OAGG;YACW,QAAQ;IAQtB,4EAA4E;IAC5E,OAAO,CAAC,OAAO;CAKhB"}
|