@adminforth/chat-surface-adapter-telegram 1.1.1 → 1.2.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/dist/index.d.ts CHANGED
@@ -5,6 +5,16 @@ export { getFinalMessageStreamPreview, renderFinalMessageImages, renderHtmlBlock
5
5
  type TelegramUpdate = {
6
6
  message?: {
7
7
  text?: string;
8
+ caption?: string;
9
+ voice?: {
10
+ file_id?: string;
11
+ mime_type?: string;
12
+ };
13
+ audio?: {
14
+ file_id?: string;
15
+ file_name?: string;
16
+ mime_type?: string;
17
+ };
8
18
  chat?: {
9
19
  id?: number | string;
10
20
  };
@@ -37,6 +47,24 @@ export declare class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
37
47
  externalUserId: string;
38
48
  userTimeZone: string;
39
49
  metadata: {
50
+ isStartCommand: boolean;
51
+ startPayload: string | null;
52
+ telegramUpdate: TelegramUpdate;
53
+ };
54
+ audio?: undefined;
55
+ } | {
56
+ surface: string;
57
+ prompt: string;
58
+ audio: {
59
+ buffer: Buffer<ArrayBuffer>;
60
+ filename: string;
61
+ mimeType: string;
62
+ };
63
+ externalConversationId: string;
64
+ externalUserId: string;
65
+ userTimeZone: string;
66
+ metadata: {
67
+ isStartCommand: boolean;
40
68
  startPayload: string | null;
41
69
  telegramUpdate: TelegramUpdate;
42
70
  };
@@ -46,6 +74,10 @@ export declare class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
46
74
  private sendFinalMessage;
47
75
  private sendPhoto;
48
76
  private sendChatAction;
77
+ private downloadTelegramFile;
78
+ private sendAudioFile;
79
+ private telegramMultipart;
80
+ private telegramJson;
49
81
  private sendMessageDraft;
50
82
  }
51
83
  export default TelegramChatSurfaceAdapter;
package/dist/index.js CHANGED
@@ -45,12 +45,14 @@ function splitTelegramMessage(text) {
45
45
  }
46
46
  return chunks;
47
47
  }
48
- function parseTelegramStartPayload(text) {
48
+ function parseTelegramStartCommand(text) {
49
49
  const [command, ...payloadParts] = text.trim().split(TELEGRAM_COMMAND_PARTS_RE);
50
- if (command !== TELEGRAM_START_COMMAND_PREFIX && !command.startsWith(`${TELEGRAM_START_COMMAND_PREFIX}@`)) {
51
- return null;
52
- }
53
- return payloadParts.join(" ") || null;
50
+ const isStartCommand = command === TELEGRAM_START_COMMAND_PREFIX ||
51
+ command.startsWith(`${TELEGRAM_START_COMMAND_PREFIX}@`);
52
+ return {
53
+ isStartCommand,
54
+ payload: isStartCommand ? payloadParts.join(" ") || null : null,
55
+ };
54
56
  }
55
57
  export class TelegramChatSurfaceAdapter {
56
58
  constructor(options) {
@@ -71,27 +73,58 @@ export class TelegramChatSurfaceAdapter {
71
73
  }
72
74
  parseIncomingMessage(ctx) {
73
75
  return __awaiter(this, void 0, void 0, function* () {
74
- var _a, _b, _c, _d, _e;
76
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
75
77
  if (this.options.webhookSecret
76
78
  && getHeaderValue(ctx.headers, TELEGRAM_SECRET_HEADER) !== this.options.webhookSecret) {
77
79
  return null;
78
80
  }
79
81
  const update = ctx.body;
80
- const text = (_a = update.message) === null || _a === void 0 ? void 0 : _a.text;
81
- const chatId = (_c = (_b = update.message) === null || _b === void 0 ? void 0 : _b.chat) === null || _c === void 0 ? void 0 : _c.id;
82
- const userId = (_e = (_d = update.message) === null || _d === void 0 ? void 0 : _d.from) === null || _e === void 0 ? void 0 : _e.id;
83
- if (!text || chatId === undefined || userId === undefined) {
82
+ const message = update.message;
83
+ const text = message === null || message === void 0 ? void 0 : message.text;
84
+ const chatId = (_a = message === null || message === void 0 ? void 0 : message.chat) === null || _a === void 0 ? void 0 : _a.id;
85
+ const userId = (_b = message === null || message === void 0 ? void 0 : message.from) === null || _b === void 0 ? void 0 : _b.id;
86
+ if (chatId === undefined || userId === undefined) {
87
+ return null;
88
+ }
89
+ const startCommand = text
90
+ ? parseTelegramStartCommand(text)
91
+ : { isStartCommand: false, payload: null };
92
+ if (text) {
93
+ return {
94
+ surface: this.name,
95
+ prompt: text,
96
+ externalConversationId: String(chatId),
97
+ externalUserId: String(userId),
98
+ userTimeZone: "UTC",
99
+ metadata: {
100
+ isStartCommand: startCommand.isStartCommand,
101
+ startPayload: startCommand.payload,
102
+ telegramUpdate: update,
103
+ },
104
+ };
105
+ }
106
+ const voiceFileId = (_c = message === null || message === void 0 ? void 0 : message.voice) === null || _c === void 0 ? void 0 : _c.file_id;
107
+ const audioFileId = (_d = message === null || message === void 0 ? void 0 : message.audio) === null || _d === void 0 ? void 0 : _d.file_id;
108
+ const fileId = voiceFileId !== null && voiceFileId !== void 0 ? voiceFileId : audioFileId;
109
+ if (!fileId) {
84
110
  return null;
85
111
  }
86
- const startPayload = parseTelegramStartPayload(text);
112
+ const audio = yield this.downloadTelegramFile({
113
+ fileId,
114
+ filename: (_f = (_e = message === null || message === void 0 ? void 0 : message.audio) === null || _e === void 0 ? void 0 : _e.file_name) !== null && _f !== void 0 ? _f : (voiceFileId ? "telegram-voice.ogg" : "telegram-audio"),
115
+ mimeType: (_k = (_h = (_g = message === null || message === void 0 ? void 0 : message.voice) === null || _g === void 0 ? void 0 : _g.mime_type) !== null && _h !== void 0 ? _h : (_j = message === null || message === void 0 ? void 0 : message.audio) === null || _j === void 0 ? void 0 : _j.mime_type) !== null && _k !== void 0 ? _k : "application/octet-stream",
116
+ abortSignal: ctx.abortSignal,
117
+ });
87
118
  return {
88
119
  surface: this.name,
89
- prompt: text,
120
+ prompt: (_l = message === null || message === void 0 ? void 0 : message.caption) !== null && _l !== void 0 ? _l : "",
121
+ audio,
90
122
  externalConversationId: String(chatId),
91
123
  externalUserId: String(userId),
92
124
  userTimeZone: "UTC",
93
125
  metadata: {
94
- startPayload,
126
+ isStartCommand: startCommand.isStartCommand,
127
+ startPayload: startCommand.payload,
95
128
  telegramUpdate: update,
96
129
  },
97
130
  };
@@ -182,6 +215,10 @@ export class TelegramChatSurfaceAdapter {
182
215
  yield this.sendFinalMessage(chatId, text || event.text);
183
216
  return;
184
217
  }
218
+ if (event.type === "audio") {
219
+ yield this.sendAudioFile(chatId, event.audio, event.filename, event.mimeType);
220
+ return;
221
+ }
185
222
  if (event.type === "error") {
186
223
  done = true;
187
224
  stopTyping();
@@ -202,20 +239,11 @@ export class TelegramChatSurfaceAdapter {
202
239
  return;
203
240
  }
204
241
  for (const chunk of splitTelegramMessage(text)) {
205
- const response = yield fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendMessage`, {
206
- method: "POST",
207
- headers: {
208
- "Content-Type": "application/json",
209
- },
210
- body: JSON.stringify({
211
- chat_id: chatId,
212
- text: chunk,
213
- parse_mode: "Markdown",
214
- }),
242
+ yield this.telegramJson("sendMessage", {
243
+ chat_id: chatId,
244
+ text: chunk,
245
+ parse_mode: "Markdown",
215
246
  });
216
- if (!response.ok) {
217
- throw new Error(`Telegram sendMessage failed: ${response.status} ${yield response.text()}`);
218
- }
219
247
  }
220
248
  });
221
249
  }
@@ -236,49 +264,87 @@ export class TelegramChatSurfaceAdapter {
236
264
  formData.append("photo", new Blob([photoBytes], {
237
265
  type: "image/png",
238
266
  }), filename);
239
- const response = yield fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendPhoto`, {
267
+ yield this.telegramMultipart("sendPhoto", formData);
268
+ });
269
+ }
270
+ sendChatAction(chatId, action) {
271
+ return __awaiter(this, void 0, void 0, function* () {
272
+ yield this.telegramJson("sendChatAction", {
273
+ chat_id: chatId,
274
+ action,
275
+ });
276
+ });
277
+ }
278
+ downloadTelegramFile(input) {
279
+ return __awaiter(this, void 0, void 0, function* () {
280
+ var _a, _b;
281
+ const fileResponse = yield fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/getFile?file_id=${encodeURIComponent(input.fileId)}`, { signal: input.abortSignal });
282
+ if (!fileResponse.ok) {
283
+ throw new Error(`Telegram getFile failed: ${fileResponse.status} ${yield fileResponse.text()}`);
284
+ }
285
+ const fileData = yield fileResponse.json();
286
+ const filePath = (_a = fileData.result) === null || _a === void 0 ? void 0 : _a.file_path;
287
+ if (!fileData.ok || !filePath) {
288
+ throw new Error(`Telegram getFile failed: ${(_b = fileData.description) !== null && _b !== void 0 ? _b : "file_path is missing"}`);
289
+ }
290
+ const downloadResponse = yield fetch(`${TELEGRAM_API_BASE_URL}/file/bot${this.options.botToken}/${filePath}`, { signal: input.abortSignal });
291
+ if (!downloadResponse.ok) {
292
+ throw new Error(`Telegram file download failed: ${downloadResponse.status} ${yield downloadResponse.text()}`);
293
+ }
294
+ return {
295
+ buffer: Buffer.from(yield downloadResponse.arrayBuffer()),
296
+ filename: input.filename,
297
+ mimeType: input.mimeType,
298
+ };
299
+ });
300
+ }
301
+ sendAudioFile(chatId, audio, filename, mimeType) {
302
+ return __awaiter(this, void 0, void 0, function* () {
303
+ const sendAsVoice = ["audio/ogg", "audio/opus"].includes(mimeType) || filename.endsWith(".ogg") || filename.endsWith(".opus");
304
+ const method = sendAsVoice ? "sendVoice" : "sendAudio";
305
+ const fieldName = sendAsVoice ? "voice" : "audio";
306
+ const audioBytes = new Uint8Array(audio);
307
+ const formData = new FormData();
308
+ formData.append("chat_id", chatId);
309
+ formData.append(fieldName, new Blob([audioBytes], {
310
+ type: mimeType,
311
+ }), filename);
312
+ yield this.telegramMultipart(method, formData);
313
+ });
314
+ }
315
+ telegramMultipart(method, formData) {
316
+ return __awaiter(this, void 0, void 0, function* () {
317
+ const response = yield fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/${method}`, {
240
318
  method: "POST",
241
319
  body: formData,
242
320
  });
243
321
  if (!response.ok) {
244
- throw new Error(`Telegram sendPhoto failed: ${response.status} ${yield response.text()}`);
322
+ throw new Error(`Telegram ${method} failed: ${response.status} ${yield response.text()}`);
245
323
  }
246
324
  });
247
325
  }
248
- sendChatAction(chatId, action) {
326
+ telegramJson(method, body) {
249
327
  return __awaiter(this, void 0, void 0, function* () {
250
- const response = yield fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendChatAction`, {
328
+ const response = yield fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/${method}`, {
251
329
  method: "POST",
252
330
  headers: {
253
331
  "Content-Type": "application/json",
254
332
  },
255
- body: JSON.stringify({
256
- chat_id: chatId,
257
- action,
258
- }),
333
+ body: JSON.stringify(body),
259
334
  });
260
335
  if (!response.ok) {
261
- throw new Error(`Telegram sendChatAction failed: ${response.status} ${yield response.text()}`);
336
+ throw new Error(`Telegram ${method} failed: ${response.status} ${yield response.text()}`);
262
337
  }
263
338
  });
264
339
  }
265
340
  sendMessageDraft(input) {
266
341
  return __awaiter(this, void 0, void 0, function* () {
267
- const response = yield fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendMessageDraft`, {
268
- method: "POST",
269
- headers: {
270
- "Content-Type": "application/json",
271
- },
272
- body: JSON.stringify({
273
- chat_id: Number(input.chatId),
274
- draft_id: input.draftId,
275
- text: input.text,
276
- parse_mode: input.parseMode,
277
- }),
342
+ yield this.telegramJson("sendMessageDraft", {
343
+ chat_id: Number(input.chatId),
344
+ draft_id: input.draftId,
345
+ text: input.text,
346
+ parse_mode: input.parseMode,
278
347
  });
279
- if (!response.ok) {
280
- throw new Error(`Telegram sendMessageDraft failed: ${response.status} ${yield response.text()}`);
281
- }
282
348
  });
283
349
  }
284
350
  }
package/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  type ChatSurfaceAdapter,
3
+ type ChatSurfaceEvent,
3
4
  type ChatSurfaceEventSink,
4
5
  type ChatSurfaceIncomingMessage,
5
6
  type ChatSurfaceRequestContext,
@@ -24,6 +25,16 @@ export {
24
25
  type TelegramUpdate = {
25
26
  message?: {
26
27
  text?: string;
28
+ caption?: string;
29
+ voice?: {
30
+ file_id?: string;
31
+ mime_type?: string;
32
+ };
33
+ audio?: {
34
+ file_id?: string;
35
+ file_name?: string;
36
+ mime_type?: string;
37
+ };
27
38
  chat?: {
28
39
  id?: number | string;
29
40
  };
@@ -37,12 +48,29 @@ type TelegramUpdate = {
37
48
  };
38
49
  };
39
50
 
51
+ type TelegramGetFileResponse = {
52
+ ok: boolean;
53
+ result?: {
54
+ file_path?: string;
55
+ };
56
+ description?: string;
57
+ };
58
+
40
59
  type ChatSurfaceConnectAction = {
41
60
  type: "url";
42
61
  label: string;
43
62
  url: string;
44
63
  };
45
64
 
65
+ type TelegramChatSurfaceEvent =
66
+ | ChatSurfaceEvent
67
+ | {
68
+ type: "audio";
69
+ audio: Buffer;
70
+ filename: string;
71
+ mimeType: string;
72
+ };
73
+
46
74
  const TELEGRAM_API_BASE_URL = "https://api.telegram.org";
47
75
  const TELEGRAM_SECRET_HEADER = "x-telegram-bot-api-secret-token";
48
76
  const TELEGRAM_MESSAGE_MAX_LENGTH = 4096;
@@ -93,14 +121,16 @@ function splitTelegramMessage(text: string) {
93
121
  return chunks;
94
122
  }
95
123
 
96
- function parseTelegramStartPayload(text: string) {
124
+ function parseTelegramStartCommand(text: string) {
97
125
  const [command, ...payloadParts] = text.trim().split(TELEGRAM_COMMAND_PARTS_RE);
126
+ const isStartCommand =
127
+ command === TELEGRAM_START_COMMAND_PREFIX ||
128
+ command.startsWith(`${TELEGRAM_START_COMMAND_PREFIX}@`);
98
129
 
99
- if (command !== TELEGRAM_START_COMMAND_PREFIX && !command.startsWith(`${TELEGRAM_START_COMMAND_PREFIX}@`)) {
100
- return null;
101
- }
102
-
103
- return payloadParts.join(" ") || null;
130
+ return {
131
+ isStartCommand,
132
+ payload: isStartCommand ? payloadParts.join(" ") || null : null,
133
+ };
104
134
  }
105
135
 
106
136
  export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
@@ -132,24 +162,59 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
132
162
  }
133
163
 
134
164
  const update = ctx.body as TelegramUpdate;
135
- const text = update.message?.text;
136
- const chatId = update.message?.chat?.id;
137
- const userId = update.message?.from?.id;
165
+ const message = update.message;
166
+ const text = message?.text;
167
+ const chatId = message?.chat?.id;
168
+ const userId = message?.from?.id;
138
169
 
139
- if (!text || chatId === undefined || userId === undefined) {
170
+ if (chatId === undefined || userId === undefined) {
140
171
  return null;
141
172
  }
142
173
 
143
- const startPayload = parseTelegramStartPayload(text);
174
+ const startCommand = text
175
+ ? parseTelegramStartCommand(text)
176
+ : { isStartCommand: false, payload: null };
177
+
178
+ if (text) {
179
+ return {
180
+ surface: this.name,
181
+ prompt: text,
182
+ externalConversationId: String(chatId),
183
+ externalUserId: String(userId),
184
+ userTimeZone: "UTC",
185
+ metadata: {
186
+ isStartCommand: startCommand.isStartCommand,
187
+ startPayload: startCommand.payload,
188
+ telegramUpdate: update,
189
+ },
190
+ };
191
+ }
192
+
193
+ const voiceFileId = message?.voice?.file_id;
194
+ const audioFileId = message?.audio?.file_id;
195
+ const fileId = voiceFileId ?? audioFileId;
196
+
197
+ if (!fileId) {
198
+ return null;
199
+ }
200
+
201
+ const audio = await this.downloadTelegramFile({
202
+ fileId,
203
+ filename: message?.audio?.file_name ?? (voiceFileId ? "telegram-voice.ogg" : "telegram-audio"),
204
+ mimeType: message?.voice?.mime_type ?? message?.audio?.mime_type ?? "application/octet-stream",
205
+ abortSignal: ctx.abortSignal,
206
+ });
144
207
 
145
208
  return {
146
209
  surface: this.name,
147
- prompt: text,
210
+ prompt: message?.caption ?? "",
211
+ audio,
148
212
  externalConversationId: String(chatId),
149
213
  externalUserId: String(userId),
150
214
  userTimeZone: "UTC",
151
215
  metadata: {
152
- startPayload,
216
+ isStartCommand: startCommand.isStartCommand,
217
+ startPayload: startCommand.payload,
153
218
  telegramUpdate: update,
154
219
  },
155
220
  };
@@ -245,7 +310,7 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
245
310
  startTyping();
246
311
 
247
312
  return {
248
- emit: async (event) => {
313
+ emit: async (event: TelegramChatSurfaceEvent) => {
249
314
  if (closed) {
250
315
  return;
251
316
  }
@@ -265,9 +330,16 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
265
330
  stopTyping();
266
331
  clearDraftTimer();
267
332
 
268
- await this.sendFinalMessage(
333
+ await this.sendFinalMessage(chatId, text || event.text);
334
+ return;
335
+ }
336
+
337
+ if (event.type === "audio") {
338
+ await this.sendAudioFile(
269
339
  chatId,
270
- text || event.text,
340
+ event.audio,
341
+ event.filename,
342
+ event.mimeType,
271
343
  );
272
344
  return;
273
345
  }
@@ -298,21 +370,11 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
298
370
  }
299
371
 
300
372
  for (const chunk of splitTelegramMessage(text)) {
301
- const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendMessage`, {
302
- method: "POST",
303
- headers: {
304
- "Content-Type": "application/json",
305
- },
306
- body: JSON.stringify({
307
- chat_id: chatId,
308
- text: chunk,
309
- parse_mode: "Markdown",
310
- }),
373
+ await this.telegramJson("sendMessage", {
374
+ chat_id: chatId,
375
+ text: chunk,
376
+ parse_mode: "Markdown",
311
377
  });
312
-
313
- if (!response.ok) {
314
- throw new Error(`Telegram sendMessage failed: ${response.status} ${await response.text()}`);
315
- }
316
378
  }
317
379
  }
318
380
 
@@ -334,30 +396,95 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
334
396
  type: "image/png",
335
397
  }), filename);
336
398
 
337
- const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendPhoto`, {
399
+ await this.telegramMultipart("sendPhoto", formData);
400
+ }
401
+
402
+ private async sendChatAction(chatId: string, action: "typing" | "upload_voice" | "upload_audio") {
403
+ await this.telegramJson("sendChatAction", {
404
+ chat_id: chatId,
405
+ action,
406
+ });
407
+ }
408
+
409
+ private async downloadTelegramFile(input: {
410
+ fileId: string;
411
+ filename: string;
412
+ mimeType: string;
413
+ abortSignal: AbortSignal;
414
+ }) {
415
+ const fileResponse = await fetch(
416
+ `${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/getFile?file_id=${encodeURIComponent(input.fileId)}`,
417
+ { signal: input.abortSignal },
418
+ );
419
+
420
+ if (!fileResponse.ok) {
421
+ throw new Error(`Telegram getFile failed: ${fileResponse.status} ${await fileResponse.text()}`);
422
+ }
423
+
424
+ const fileData = await fileResponse.json() as TelegramGetFileResponse;
425
+ const filePath = fileData.result?.file_path;
426
+
427
+ if (!fileData.ok || !filePath) {
428
+ throw new Error(`Telegram getFile failed: ${fileData.description ?? "file_path is missing"}`);
429
+ }
430
+
431
+ const downloadResponse = await fetch(
432
+ `${TELEGRAM_API_BASE_URL}/file/bot${this.options.botToken}/${filePath}`,
433
+ { signal: input.abortSignal },
434
+ );
435
+
436
+ if (!downloadResponse.ok) {
437
+ throw new Error(`Telegram file download failed: ${downloadResponse.status} ${await downloadResponse.text()}`);
438
+ }
439
+
440
+ return {
441
+ buffer: Buffer.from(await downloadResponse.arrayBuffer()),
442
+ filename: input.filename,
443
+ mimeType: input.mimeType,
444
+ };
445
+ }
446
+
447
+ private async sendAudioFile(
448
+ chatId: string,
449
+ audio: Buffer,
450
+ filename: string,
451
+ mimeType: string,
452
+ ) {
453
+ const sendAsVoice = ["audio/ogg", "audio/opus"].includes(mimeType) || filename.endsWith(".ogg") || filename.endsWith(".opus");
454
+ const method = sendAsVoice ? "sendVoice" : "sendAudio";
455
+ const fieldName = sendAsVoice ? "voice" : "audio";
456
+ const audioBytes = new Uint8Array(audio);
457
+ const formData = new FormData();
458
+ formData.append("chat_id", chatId);
459
+ formData.append(fieldName, new Blob([audioBytes], {
460
+ type: mimeType,
461
+ }), filename);
462
+
463
+ await this.telegramMultipart(method, formData);
464
+ }
465
+
466
+ private async telegramMultipart(method: string, formData: FormData) {
467
+ const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/${method}`, {
338
468
  method: "POST",
339
469
  body: formData,
340
470
  });
341
471
 
342
472
  if (!response.ok) {
343
- throw new Error(`Telegram sendPhoto failed: ${response.status} ${await response.text()}`);
473
+ throw new Error(`Telegram ${method} failed: ${response.status} ${await response.text()}`);
344
474
  }
345
475
  }
346
476
 
347
- private async sendChatAction(chatId: string, action: "typing") {
348
- const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendChatAction`, {
477
+ private async telegramJson(method: string, body: unknown) {
478
+ const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/${method}`, {
349
479
  method: "POST",
350
480
  headers: {
351
481
  "Content-Type": "application/json",
352
482
  },
353
- body: JSON.stringify({
354
- chat_id: chatId,
355
- action,
356
- }),
483
+ body: JSON.stringify(body),
357
484
  });
358
485
 
359
486
  if (!response.ok) {
360
- throw new Error(`Telegram sendChatAction failed: ${response.status} ${await response.text()}`);
487
+ throw new Error(`Telegram ${method} failed: ${response.status} ${await response.text()}`);
361
488
  }
362
489
  }
363
490
 
@@ -367,22 +494,12 @@ export class TelegramChatSurfaceAdapter implements ChatSurfaceAdapter {
367
494
  text: string;
368
495
  parseMode?: "Markdown" | "MarkdownV2" | "HTML";
369
496
  }) {
370
- const response = await fetch(`${TELEGRAM_API_BASE_URL}/bot${this.options.botToken}/sendMessageDraft`, {
371
- method: "POST",
372
- headers: {
373
- "Content-Type": "application/json",
374
- },
375
- body: JSON.stringify({
376
- chat_id: Number(input.chatId),
377
- draft_id: input.draftId,
378
- text: input.text,
379
- parse_mode: input.parseMode,
380
- }),
497
+ await this.telegramJson("sendMessageDraft", {
498
+ chat_id: Number(input.chatId),
499
+ draft_id: input.draftId,
500
+ text: input.text,
501
+ parse_mode: input.parseMode,
381
502
  });
382
-
383
- if (!response.ok) {
384
- throw new Error(`Telegram sendMessageDraft failed: ${response.status} ${await response.text()}`);
385
- }
386
503
  }
387
504
  }
388
505
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/chat-surface-adapter-telegram",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -33,7 +33,6 @@
33
33
  "playwright": "^1.57.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "@adminforth/agent": ">=1.0.2",
37
36
  "adminforth": ">=2.60.0"
38
37
  },
39
38
  "release": {