@llblab/pi-telegram 0.24.10 → 0.25.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.
@@ -16,16 +16,6 @@ interface TelegramTopLevelFenceState {
16
16
  length: number;
17
17
  }
18
18
 
19
- function isTelegramActionCommentContent(content: string): boolean {
20
- const normalizedContent = content.replace(/^\s+/, "");
21
- const [head = ""] = normalizedContent.split(/\r?\n/, 1);
22
- return ["telegram_voice", "telegram_button"].some((command) => {
23
- if (!head.startsWith(command)) return false;
24
- const nextChar = head[command.length];
25
- return nextChar === undefined || /\s|:/.test(nextChar);
26
- });
27
- }
28
-
29
19
  function getMarkdownLineEnd(markdown: string, offset: number): number {
30
20
  const newlineIndex = markdown.indexOf("\n", offset);
31
21
  return newlineIndex === -1 ? markdown.length : newlineIndex + 1;
@@ -64,58 +54,6 @@ function isTopLevelClosingFence(
64
54
  );
65
55
  }
66
56
 
67
- function collectPairedTelegramVoiceActionBody(
68
- markdown: string,
69
- bodyStart: number,
70
- commentContent: string,
71
- ): { content: string; end: number } | undefined {
72
- const normalizedContent = commentContent.trim();
73
- if (
74
- !normalizedContent.startsWith("telegram_voice") ||
75
- !isTelegramActionCommentContent(commentContent)
76
- ) {
77
- return undefined;
78
- }
79
- let offset = bodyStart;
80
- while (offset < markdown.length) {
81
- const lineEnd = getMarkdownLineEnd(markdown, offset);
82
- const line = getMarkdownLineText(markdown, offset, lineEnd);
83
- if (line === "<!-- /telegram_voice -->") {
84
- const body = markdown.slice(bodyStart, offset).trim();
85
- if (!body) return undefined;
86
- return {
87
- content: `${commentContent.trimEnd()}\n${body}`,
88
- end: lineEnd,
89
- };
90
- }
91
- if (line.startsWith("<!--")) return undefined;
92
- offset = lineEnd;
93
- }
94
- return undefined;
95
- }
96
-
97
- function collectInlineClosedTelegramActionBody(
98
- markdown: string,
99
- bodyStart: number,
100
- commentContent: string,
101
- ): { content: string; end: number } | undefined {
102
- const bodyLineEnd = getMarkdownLineEnd(markdown, bodyStart);
103
- const bodyLine = getMarkdownLineText(markdown, bodyStart, bodyLineEnd);
104
- const closeLineEnd = getMarkdownLineEnd(markdown, bodyLineEnd);
105
- const closeLine = getMarkdownLineText(markdown, bodyLineEnd, closeLineEnd);
106
- const hasRecoverableBody =
107
- isTelegramActionCommentContent(commentContent) &&
108
- bodyLine.trim() !== "" &&
109
- !bodyLine.startsWith("<!--") &&
110
- !bodyLine.startsWith("-->") &&
111
- closeLine === "-->";
112
- if (!hasRecoverableBody) return undefined;
113
- return {
114
- content: `${commentContent.trimEnd()}\n${bodyLine}`,
115
- end: bodyLineEnd + 3,
116
- };
117
- }
118
-
119
57
  export function collectTopLevelHtmlComments(markdown: string): {
120
58
  comments: TelegramTopLevelHtmlComment[];
121
59
  openCommentStart?: number;
@@ -140,27 +78,9 @@ export function collectTopLevelHtmlComments(markdown: string): {
140
78
  if (line.startsWith("<!--")) {
141
79
  const closeIndex = markdown.indexOf("-->", offset + 4);
142
80
  if (closeIndex === -1) return { comments, openCommentStart: offset };
143
- let end = closeIndex + 3;
144
- let raw = markdown.slice(offset, end);
145
- let content = raw.slice(4, -3);
146
- const closeColumn = closeIndex - offset;
147
- const closesOnOpeningLine = closeIndex < lineEnd;
148
- const hasOnlyWhitespaceAfterClose =
149
- line.slice(closeColumn + 3).trim() === "";
150
- const pairedVoiceBody =
151
- closesOnOpeningLine && hasOnlyWhitespaceAfterClose
152
- ? collectPairedTelegramVoiceActionBody(markdown, lineEnd, content)
153
- : undefined;
154
- const inlineBody =
155
- !pairedVoiceBody && closesOnOpeningLine && hasOnlyWhitespaceAfterClose
156
- ? collectInlineClosedTelegramActionBody(markdown, lineEnd, content)
157
- : undefined;
158
- const recoveredBody = pairedVoiceBody ?? inlineBody;
159
- if (recoveredBody) {
160
- end = recoveredBody.end;
161
- raw = markdown.slice(offset, end);
162
- content = recoveredBody.content;
163
- }
81
+ const end = closeIndex + 3;
82
+ const raw = markdown.slice(offset, end);
83
+ const content = raw.slice(4, -3);
164
84
  comments.push({ raw, content, start: offset, end });
165
85
  offset = getMarkdownLineEnd(markdown, end);
166
86
  continue;
@@ -233,18 +153,47 @@ export function parseTopLevelTelegramComment(
233
153
  };
234
154
  }
235
155
 
236
- export function parseTelegramCommentAttributes(
237
- input: string,
238
- ): Record<string, string> {
156
+ function parseCanonicalTelegramActionAttributes(
157
+ source: string,
158
+ ): Record<string, string> | undefined {
239
159
  const attributes: Record<string, string> = {};
240
- for (const match of input.matchAll(
241
- /([A-Za-z_][A-Za-z0-9_-]*)=(?:"([^"]*)"|'([^']*)'|(\S+))/g,
242
- )) {
243
- const key = match[1];
244
- const value = (match[2] ?? match[3] ?? match[4] ?? "").trim();
245
- if (value) attributes[key] = value;
160
+ const pattern = /\s*([A-Za-z_][A-Za-z0-9_-]*)="([^"]*)"/y;
161
+ let offset = 0;
162
+ while (offset < source.length) {
163
+ pattern.lastIndex = offset;
164
+ const match = pattern.exec(source);
165
+ if (!match) return undefined;
166
+ const value = match[2].trim();
167
+ if (value) attributes[match[1]] = value;
168
+ offset = pattern.lastIndex;
169
+ }
170
+ return Object.keys(attributes).length > 0 ? attributes : undefined;
171
+ }
172
+
173
+ export function parseTelegramActionPayload(
174
+ comment: TelegramTopLevelHtmlComment,
175
+ command: string,
176
+ ): Record<string, unknown> | undefined {
177
+ const parsed = parseTopLevelTelegramComment(comment, command);
178
+ if (!parsed) return undefined;
179
+ const source = [parsed.head, parsed.body]
180
+ .filter((part): part is string => part !== undefined)
181
+ .join("\n")
182
+ .trim()
183
+ .replace(/^:\s*/, "");
184
+ if (!source) return undefined;
185
+ if (source.startsWith("{")) {
186
+ try {
187
+ const value: unknown = JSON.parse(source);
188
+ return value !== null && typeof value === "object" && !Array.isArray(value)
189
+ ? (value as Record<string, unknown>)
190
+ : undefined;
191
+ } catch {
192
+ return undefined;
193
+ }
246
194
  }
247
- return attributes;
195
+ if (parsed.body !== undefined) return undefined;
196
+ return parseCanonicalTelegramActionAttributes(source);
248
197
  }
249
198
 
250
199
  export function normalizeMarkdownAfterVoiceExtraction(
@@ -295,58 +244,14 @@ export interface TelegramVoiceReplyPlan {
295
244
  rate?: string;
296
245
  }
297
246
 
298
- function parseVoiceReplyAttributes(input: string): {
299
- lang?: string;
300
- rate?: string;
301
- text?: string;
302
- } {
303
- const attributes = parseTelegramCommentAttributes(input);
304
- return {
305
- ...(attributes.lang ? { lang: attributes.lang } : {}),
306
- ...(attributes.rate ? { rate: attributes.rate } : {}),
307
- ...(attributes.text ? { text: attributes.text } : {}),
308
- };
309
- }
310
-
311
- function parseVoiceCommentBody(
312
- head: string,
313
- body: string | undefined,
314
- ): {
315
- attrs: string;
316
- text: string;
317
- } {
318
- const trimmedHead = head.trim();
319
- if (body !== undefined) {
320
- return { attrs: trimmedHead.replace(/^:/, "").trim(), text: body.trim() };
321
- }
322
- let colonIndex = -1;
323
- let inQuote = false;
324
- let quoteChar = "";
325
- for (let i = 0; i < trimmedHead.length; i++) {
326
- const char = trimmedHead[i];
327
- if (inQuote) {
328
- if (char === quoteChar) inQuote = false;
329
- } else {
330
- if (char === '"' || char === "'") {
331
- inQuote = true;
332
- quoteChar = char;
333
- } else if (char === ":") {
334
- colonIndex = i;
335
- break;
336
- }
337
- }
338
- }
339
- if (colonIndex > 0) {
340
- const attrsPart = trimmedHead.slice(0, colonIndex).trim();
341
- const textPart = trimmedHead.slice(colonIndex + 1).trim();
342
- const attrs = parseVoiceReplyAttributes(attrsPart);
343
- return { attrs: attrsPart, text: textPart || attrs.text || "", ...attrs };
344
- }
345
- if (trimmedHead.startsWith(":")) {
346
- return { attrs: "", text: trimmedHead.slice(1).trim() };
347
- }
348
- const attrs = parseVoiceReplyAttributes(trimmedHead);
349
- return { attrs: trimmedHead, text: attrs.text ?? "" };
247
+ function getTelegramActionString(
248
+ payload: Record<string, unknown>,
249
+ key: string,
250
+ ): string | undefined {
251
+ const value = payload[key];
252
+ if (typeof value !== "string") return undefined;
253
+ const trimmed = value.trim();
254
+ return trimmed || undefined;
350
255
  }
351
256
 
352
257
  export function planTelegramVoiceReply(
@@ -356,26 +261,24 @@ export function planTelegramVoiceReply(
356
261
  let lang: string | undefined;
357
262
  let rate: string | undefined;
358
263
  const stripped = replaceTopLevelHtmlComments(markdown, (comment) => {
359
- let command = parseTopLevelTelegramComment(comment, "telegram_voice");
360
- if (!command) {
361
- let content = comment.content.replace(/^\s+/, "").replace(/^!/, "");
362
- if (content.startsWith("telegram_voice")) {
363
- const headPart = content.slice("telegram_voice".length).trim();
364
- command = { head: headPart, body: undefined };
365
- }
366
- }
367
- if (!command) return "";
368
- const parsed = parseVoiceCommentBody(command.head, command.body);
369
- const attrs = parseVoiceReplyAttributes(parsed.attrs);
370
- if (parsed.text) {
264
+ const command = parseTopLevelTelegramComment(comment, "telegram_voice");
265
+ if (!command) return comment.raw;
266
+ const payload = parseTelegramActionPayload(comment, "telegram_voice");
267
+ if (!payload) return "";
268
+ const text =
269
+ getTelegramActionString(payload, "text") ??
270
+ getTelegramActionString(payload, "value");
271
+ const itemLang = getTelegramActionString(payload, "lang");
272
+ const itemRate = getTelegramActionString(payload, "rate");
273
+ if (text) {
371
274
  voiceReplies.push({
372
- text: parsed.text,
373
- ...(attrs.lang ? { lang: attrs.lang } : {}),
374
- ...(attrs.rate ? { rate: attrs.rate } : {}),
275
+ text,
276
+ ...(itemLang ? { lang: itemLang } : {}),
277
+ ...(itemRate ? { rate: itemRate } : {}),
375
278
  });
376
279
  }
377
- if (attrs.lang) lang = attrs.lang;
378
- if (attrs.rate) rate = attrs.rate;
280
+ if (itemLang) lang = itemLang;
281
+ if (itemRate) rate = itemRate;
379
282
  return "";
380
283
  });
381
284
  const voiceText = voiceReplies
package/lib/polling.ts CHANGED
@@ -319,7 +319,6 @@ export interface TelegramThreadCapabilityRuntimeDeps<
319
319
  TContext,
320
320
  > extends TelegramThreadCapabilityReaderDeps {
321
321
  topicTargetStore: TelegramThreadCapabilityStore;
322
- isBusConfigured: () => boolean;
323
322
  ownsLock: (ctx: TContext) => boolean;
324
323
  getPollingStartedWithTelegramBus: () => boolean;
325
324
  setPollingStartedWithTelegramBus: (started: boolean) => void;
@@ -394,7 +393,6 @@ export interface TelegramThreadAwarePollingDeps<
394
393
  TContext,
395
394
  TOwner,
396
395
  > extends TelegramStartupThreadCapabilityProbeDeps {
397
- isBusConfigured: () => boolean;
398
396
  isBusRuntimeEnabled: () => boolean;
399
397
  isTopicModeUnavailableError: (error: unknown) => boolean;
400
398
  getPollingStartedWithTelegramBus: () => boolean;
@@ -419,7 +417,6 @@ export interface TelegramThreadCapabilityOrchestrationDeps<
419
417
  > extends TelegramThreadCapabilityReaderDeps {
420
418
  state: TelegramThreadCapabilityStateRuntime;
421
419
  topicTargetStore: TelegramThreadCapabilityStore;
422
- isBusConfigured: () => boolean;
423
420
  isBusRuntimeEnabled: () => boolean;
424
421
  ownsLock: (ctx: TContext) => boolean;
425
422
  startClassicPolling: (ctx: TContext) => MaybePromise<void>;
@@ -475,7 +472,6 @@ export function createTelegramThreadCapabilityOrchestration<TContext, TOwner>(
475
472
  getAllowedUserId: deps.getAllowedUserId,
476
473
  callApi: deps.callApi,
477
474
  topicTargetStore: deps.topicTargetStore,
478
- isBusConfigured: deps.isBusConfigured,
479
475
  ownsLock: deps.ownsLock,
480
476
  getPollingStartedWithTelegramBus: deps.state.isBusPollingStarted,
481
477
  setPollingStartedWithTelegramBus: deps.state.setBusPollingStarted,
@@ -498,7 +494,6 @@ export function createTelegramThreadCapabilityOrchestration<TContext, TOwner>(
498
494
  getAllowedUserId: deps.getAllowedUserId,
499
495
  callApi: deps.callApi,
500
496
  topicTargetStore: deps.topicTargetStore,
501
- isBusConfigured: deps.isBusConfigured,
502
497
  isBusRuntimeEnabled: deps.isBusRuntimeEnabled,
503
498
  isTopicModeUnavailableError: deps.isTopicModeUnavailableError,
504
499
  getPollingStartedWithTelegramBus: deps.state.isBusPollingStarted,
@@ -590,7 +585,6 @@ export async function applyTelegramThreadCapability<TContext>(
590
585
  deps: TelegramThreadCapabilityRuntimeDeps<TContext>,
591
586
  ): Promise<void> {
592
587
  await deps.topicTargetStore.load();
593
- if (!deps.isBusConfigured()) return;
594
588
  const nowMs = (deps.getNowMs ?? Date.now)();
595
589
  const previousBotState = deps.topicTargetStore.getBotState();
596
590
  if (!threadModeEnabled) {
@@ -691,17 +685,14 @@ export function createTelegramThreadAwarePollingPorts<TContext, TOwner>(
691
685
  ctx: TContext,
692
686
  options?: { forceFreshLeaderThread?: boolean },
693
687
  ): Promise<void> => {
694
- if (deps.isBusConfigured()) {
695
- await deps.topicTargetStore.load();
696
- let startupThreadCapability: boolean | undefined;
697
- try {
698
- startupThreadCapability =
699
- await probeTelegramStartupThreadCapability(deps);
700
- } catch (error) {
701
- deps.recordEvent("bus", error, { phase: "startup-thread-mode-probe" });
702
- }
703
- deps.setTopicModeUnavailable(startupThreadCapability !== true);
688
+ await deps.topicTargetStore.load();
689
+ let startupThreadCapability: boolean | undefined;
690
+ try {
691
+ startupThreadCapability = await probeTelegramStartupThreadCapability(deps);
692
+ } catch (error) {
693
+ deps.recordEvent("bus", error, { phase: "startup-thread-mode-probe" });
704
694
  }
695
+ deps.setTopicModeUnavailable(startupThreadCapability !== true);
705
696
  if (deps.isBusRuntimeEnabled()) {
706
697
  deps.setTopicModeUnavailable(false);
707
698
  try {
@@ -886,7 +877,6 @@ export function createTelegramThreadCapabilityMonitor<TContext>(
886
877
  return {
887
878
  start(ctx) {
888
879
  stop();
889
- if (!deps.isBusConfigured()) return;
890
880
  interval = setInterval(() => {
891
881
  check(ctx);
892
882
  }, intervalMs);
package/lib/prompts.ts CHANGED
@@ -138,13 +138,13 @@ How to answer Telegram turns:
138
138
  Assistant-authored Telegram actions:
139
139
  - \`telegram_voice\` and \`telegram_button\` are hidden top-level HTML comments, not Pi tools.
140
140
  - Put action comments at column zero, outside code, quotes, lists, and indented examples.
141
- - Voice forms: \`<!-- telegram_voice text="Short summary" -->\`, \`<!-- telegram_voice: Short summary -->\`, multiline \`<!-- telegram_voice lang=ru\nShort summary.\n-->\`, or paired \`<!-- telegram_voice lang=ru -->\nShort summary.\n<!-- /telegram_voice -->\`.
142
- - Keep the complete action at top level and include a non-empty voice payload.
141
+ - Action payloads use either JSON or double-quoted attributes; the colon after the action name is optional and does not affect parsing. Voice accepts equivalent \`text\` or \`value\`: \`<!-- telegram_voice: {"value":"Short summary","lang":"en"} -->\` or \`<!-- telegram_voice text="Short summary" lang="en" -->\`.
142
+ - Keep the complete action in one top-level comment and include non-empty \`text\` or \`value\`; encode line breaks inside JSON strings as \`\\n\`.
143
143
  - Keep voice text TTS-friendly; avoid raw Markdown, code, and tables in voice text.
144
144
  - Voice delivery generates and attaches OGG automatically; do not also call \`telegram_attach\` for the same audio.
145
145
  - Voice reply modes are compact: \`hidden\` emits no automatic context, \`mirror\` emits it for voice/audio input, and \`always\` emits it for every Telegram turn. Explicit \`telegram_voice\` remains available for an intentionally distinct spoken payload.
146
- - Button forms: \`<!-- telegram_button: OK -->\`, \`<!-- telegram_button label=Continue prompt="Continue with the current plan." -->\`, or multiline \`<!-- telegram_button label="Show risks" selected_style="danger"\nList the main risks first.\n-->\`.
147
- - Optional \`selected_style\` controls the button after queue admission: \`primary\` (default, blue), \`success\` (green), or \`danger\` (red). It never suppresses the prompt.
146
+ - Button payloads use \`label\` plus \`prompt\`, or compact \`value\` when both are identical: \`<!-- telegram_button: {"label":"Continue","prompt":"Continue with the current plan."} -->\` or \`<!-- telegram_button value="Continue" -->\`.
147
+ - Optional \`selected_style\` in either payload form controls the button after queue admission: \`primary\` (default, blue), \`success\` (green), or \`danger\` (red). It never suppresses the prompt.
148
148
  - If hidden button comments form the whole reply, the bridge supplies the standard \`☑️ **Choose an option:**\` heading automatically.
149
149
 
150
150
  Local/TUI direct delivery:
package/lib/queue.ts CHANGED
@@ -968,6 +968,7 @@ export interface TelegramAgentEndRuntimeDeps<
968
968
  isSessionActive?: () => boolean;
969
969
  isTurnTransportActive?: (turn: TTurn) => boolean;
970
970
  waitForTypingIdle?: () => Promise<void>;
971
+ waitForActivityIdle?: () => Promise<void>;
971
972
  updateStatus: () => void;
972
973
  dispatchNextQueuedTelegramTurn: () => void;
973
974
  scheduleActiveTurnDelivery?: (task: () => Promise<void>) => void;
@@ -1047,6 +1048,7 @@ export interface TelegramAgentEndHookRuntimeDeps<
1047
1048
  isSessionActive?: (ctx: TContext) => boolean;
1048
1049
  isTurnTransportActive?: (turn: TTurn) => boolean;
1049
1050
  waitForTypingIdle?: () => Promise<void>;
1051
+ waitForActivityIdle?: () => Promise<void>;
1050
1052
  updateStatus: (ctx: TContext) => void;
1051
1053
  dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
1052
1054
  requestDeferredDispatchNextQueuedTelegramTurn: (
@@ -1188,6 +1190,7 @@ export function createTelegramAgentEndHook<
1188
1190
  isSessionActive: () => deps.isSessionActive?.(ctx) ?? true,
1189
1191
  isTurnTransportActive: deps.isTurnTransportActive,
1190
1192
  waitForTypingIdle: deps.waitForTypingIdle,
1193
+ waitForActivityIdle: deps.waitForActivityIdle,
1191
1194
  updateStatus: () => deps.updateStatus(ctx),
1192
1195
  dispatchNextQueuedTelegramTurn: () => {
1193
1196
  deps.requestDeferredDispatchNextQueuedTelegramTurn(
@@ -1358,6 +1361,7 @@ export async function handleTelegramAgentEndRuntime<
1358
1361
  return;
1359
1362
  }
1360
1363
  const deliverActiveTurn = async () => {
1364
+ await deps.waitForActivityIdle?.();
1361
1365
  if (!isDeliveryActive()) return;
1362
1366
  if (finalText) deps.setPreviewPendingText(finalText);
1363
1367
  if (!finalText && hasOutboundArtifacts)
@@ -274,6 +274,24 @@ type TelegramInputRichMessageCommon = {
274
274
  skip_entity_detection?: boolean;
275
275
  };
276
276
 
277
+ export type TelegramRichText =
278
+ | string
279
+ | TelegramRichText[]
280
+ | { type: "bold" | "code"; text: TelegramRichText };
281
+
282
+ export type TelegramInputRichBlock =
283
+ | { type: "pre"; text: TelegramRichText; language?: string }
284
+ | {
285
+ type: "details";
286
+ summary: TelegramRichText;
287
+ blocks: TelegramInputRichBlock[];
288
+ is_open?: true;
289
+ };
290
+
291
+ export type TelegramInputRichDraftBlock =
292
+ | TelegramInputRichBlock
293
+ | { type: "thinking"; text: TelegramRichText };
294
+
277
295
  export type TelegramInputRichMessage = TelegramInputRichMessageCommon &
278
296
  (
279
297
  | {
@@ -288,6 +306,12 @@ export type TelegramInputRichMessage = TelegramInputRichMessageCommon &
288
306
  blocks?: never;
289
307
  media?: TelegramInputRichMessageMedia[];
290
308
  }
309
+ | {
310
+ blocks: TelegramInputRichBlock[];
311
+ markdown?: never;
312
+ html?: never;
313
+ media?: never;
314
+ }
291
315
  );
292
316
 
293
317
  export type TelegramSendRichMessageBody = Record<string, unknown> & {
@@ -318,7 +342,14 @@ export type TelegramSendMessageDraftBody = Record<string, unknown> & {
318
342
  export type TelegramSendRichMessageDraftBody = Record<string, unknown> & {
319
343
  chat_id: number;
320
344
  draft_id: number;
321
- rich_message: TelegramInputRichMessage;
345
+ rich_message:
346
+ | TelegramInputRichMessage
347
+ | (TelegramInputRichMessageCommon & {
348
+ blocks: TelegramInputRichDraftBlock[];
349
+ markdown?: never;
350
+ html?: never;
351
+ media?: never;
352
+ });
322
353
  message_thread_id?: number;
323
354
  };
324
355
 
@@ -497,6 +528,11 @@ export interface TelegramBridgeApiRuntime {
497
528
  editMessageText: (
498
529
  body: TelegramEditMessageTextBody,
499
530
  ) => Promise<"edited" | "unchanged">;
531
+ editMessageReplyMarkup: (
532
+ chatId: number,
533
+ messageId: number,
534
+ replyMarkup: unknown,
535
+ ) => Promise<void>;
500
536
  answerCallbackQuery: (
501
537
  callbackQueryId: string,
502
538
  text?: string,
@@ -1569,6 +1605,13 @@ export function createTelegramBridgeApiRuntime(
1569
1605
  throw error;
1570
1606
  }
1571
1607
  },
1608
+ editMessageReplyMarkup: async (chatId, messageId, replyMarkup) => {
1609
+ await callRecorded("editMessageReplyMarkup", {
1610
+ chat_id: chatId,
1611
+ message_id: messageId,
1612
+ reply_markup: replyMarkup,
1613
+ });
1614
+ },
1572
1615
  answerCallbackQuery: async (callbackQueryId, text) => {
1573
1616
  try {
1574
1617
  await deps.client.answerCallbackQuery(callbackQueryId, text);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.24.10",
3
+ "version": "0.25.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"