@llblab/pi-telegram 0.43.2 → 0.44.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.
@@ -1783,11 +1783,44 @@ export function createTelegramBridgeApiRuntime(
1783
1783
  */
1784
1784
  export function createTelegramApiClient(
1785
1785
  getBotToken: () => string | undefined,
1786
- options: TelegramAnswerCallbackQueryOptions = {},
1786
+ options: TelegramAnswerCallbackQueryOptions & { now?: () => number } = {},
1787
1787
  ): TelegramApiClient {
1788
+ const now = options.now ?? Date.now;
1789
+ const draftRetryNotBeforeByTarget = new Map<string, number>();
1788
1790
  return {
1789
- call: async (method, body, options) => {
1790
- return callTelegram(getBotToken(), method, body, options);
1791
+ call: async <TResponse>(
1792
+ method: string,
1793
+ body: Record<string, unknown>,
1794
+ options?: TelegramApiCallOptions,
1795
+ ): Promise<TResponse> => {
1796
+ const token = getBotToken();
1797
+ // Cooldown keys retain only the public bot-id prefix, not the credential.
1798
+ const botId = token?.match(/^(\d+):/)?.[1];
1799
+ const isDraft = method === "sendMessageDraft" || method === "sendRichMessageDraft";
1800
+ const draftKey = isDraft && botId
1801
+ ? `${botId}:${String(body.chat_id)}:${String(body.message_thread_id ?? "all")}`
1802
+ : undefined;
1803
+ if (draftKey) {
1804
+ const nowMs = now();
1805
+ for (const [key, deadline] of draftRetryNotBeforeByTarget) {
1806
+ if (nowMs >= deadline) draftRetryNotBeforeByTarget.delete(key);
1807
+ }
1808
+ if (draftRetryNotBeforeByTarget.has(draftKey)) return false as TResponse;
1809
+ }
1810
+ try {
1811
+ // A draft is a replaceable snapshot, not a body to replay after backoff.
1812
+ return await callTelegram<TResponse>(
1813
+ token, method, body, isDraft ? { ...options, maxAttempts: 1 } : options,
1814
+ );
1815
+ } catch (error) {
1816
+ if (draftKey && isRetryableTelegramApiError(error)) {
1817
+ draftRetryNotBeforeByTarget.set(draftKey, Math.max(
1818
+ draftRetryNotBeforeByTarget.get(draftKey) ?? 0,
1819
+ now() + getTelegramRetryDelayMs(error, 0, options?.retryBaseDelayMs ?? 500),
1820
+ ));
1821
+ }
1822
+ throw error;
1823
+ }
1791
1824
  },
1792
1825
  callMultipart: async (
1793
1826
  method,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.43.2",
3
+ "version": "0.44.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"