balebaazoo 1.0.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
@@ -593,6 +593,9 @@ interface ApiClientOptions {
593
593
  maxRetries?: number;
594
594
  retryDelayMs?: number;
595
595
  }
596
+ interface CallOptions {
597
+ signal?: AbortSignal;
598
+ }
596
599
  declare class Api {
597
600
  readonly token: string;
598
601
  private readonly baseUrl;
@@ -602,14 +605,14 @@ declare class Api {
602
605
  constructor(options: ApiClientOptions);
603
606
  get fileBaseUrl(): string;
604
607
  private methodUrl;
605
- call<M extends ApiMethod>(method: M, params?: Record<string, unknown>): Promise<ApiMethodResultMap[M]>;
608
+ call<M extends ApiMethod>(method: M, params?: Record<string, unknown>, options?: CallOptions): Promise<ApiMethodResultMap[M]>;
606
609
  private invoke;
607
610
  downloadFile(filePath: string): Promise<ArrayBuffer>;
608
611
  private prepareBody;
609
612
  private request;
610
613
  private fetchWithRetry;
611
614
  getMe(): Promise<User>;
612
- getUpdates(params?: GetUpdatesParams): Promise<Update[]>;
615
+ getUpdates(params?: GetUpdatesParams, options?: CallOptions): Promise<Update[]>;
613
616
  setWebhook(params: SetWebhookParams): Promise<boolean>;
614
617
  deleteWebhook(): Promise<boolean>;
615
618
  getWebhookInfo(): Promise<WebhookInfo>;
@@ -674,8 +677,10 @@ declare class Context {
674
677
  readonly update: Update;
675
678
  readonly botInfo?: User;
676
679
  callbackQueryAnswered: boolean;
680
+ private _updateTypes?;
677
681
  constructor(options: ContextOptions);
678
682
  get updateId(): number;
683
+ get updateTypes(): string[];
679
684
  get message(): Message | undefined;
680
685
  get editedMessage(): Message | undefined;
681
686
  get callbackQuery(): CallbackQuery | undefined;
@@ -768,36 +773,48 @@ interface PollingOptions {
768
773
  limit?: number;
769
774
  timeout?: number;
770
775
  allowedUpdates?: string[];
776
+ dropPendingUpdates?: boolean;
777
+ onStart?: (botInfo: User) => void | Promise<void>;
771
778
  onError?: (error: unknown) => void;
772
779
  signal?: AbortSignal;
773
780
  }
781
+ interface WebhookHandlerOptions {
782
+ secretToken?: string;
783
+ maxBodyBytes?: number;
784
+ onError?: (error: unknown) => Response | void;
785
+ }
774
786
  declare class PollingRunner {
775
787
  private running;
776
788
  private abortController?;
777
789
  private offset;
790
+ private loopPromise?;
791
+ private backoffAttempt;
778
792
  start(bot: PollingBot, options?: PollingOptions): Promise<void>;
793
+ private runLoop;
794
+ setOffset(offset: number): void;
779
795
  stop(): Promise<void>;
780
796
  }
781
- declare function createWebhookHandler(bot: PollingBot, getUpdate: (request: Request) => Promise<Update>, options?: {
782
- secretToken?: string;
783
- maxBodyBytes?: number;
784
- }): Promise<(request: Request) => Promise<Response>>;
797
+ declare function createWebhookHandler(bot: PollingBot, getUpdate: (request: Request) => Promise<Update>, options?: WebhookHandlerOptions): Promise<(request: Request) => Promise<Response>>;
785
798
  declare function webhookFromJson(bot: PollingBot): Promise<(request: Request) => Promise<Response>>;
786
799
 
787
800
  interface BotOptions extends ApiClientOptions {
788
801
  botInfo?: User;
789
802
  autoAnswerCallback?: boolean;
790
803
  }
804
+
791
805
  declare class Bot<C extends Context = Context> extends Composer<C> {
792
806
  readonly api: Api;
793
807
  private botInfo?;
794
808
  private readonly polling;
795
809
  private readonly autoAnswer;
810
+ private catchHandler?;
796
811
  constructor(token: string, options?: Omit<BotOptions, "token">);
797
812
  init(): Promise<User>;
813
+ catch(handler: (error: unknown, ctx: C) => void | Promise<void>): this;
798
814
  handleUpdate(update: Update): Promise<void>;
799
815
  createContext(update: Update): Context;
800
816
  start(options?: PollingOptions): Promise<void>;
817
+ launch(options?: PollingOptions): Promise<void>;
801
818
  stop(): Promise<void>;
802
819
  webhookCallback(): (update: Update) => Promise<void>;
803
820
  use(...middleware: MiddlewareLike<C>[]): this;
@@ -855,6 +872,13 @@ declare class ReplyKeyboard implements ReplyKeyboardMarkup {
855
872
  declare function removeKeyboard(): ReplyKeyboardRemove;
856
873
 
857
874
  declare function autoAnswerCallback(): Middleware<Context>;
875
+
858
876
  declare function errorHandler(onError: (error: unknown, ctx: Context) => Promise<void> | void): Middleware<Context>;
859
877
 
860
- export { type AddStickerToSetParams, type Animation, type AnswerCallbackQueryParams, type AnswerPreCheckoutQueryParams, Api, type ApiClientOptions, type ApiMethod, type ApiMethodResultMap, type AskReviewParams, type Audio, BaleAPIError, type BaleApiResponse, BaleError, BaleNetworkError, type BanChatMemberParams, Bot, type BotOptions, type CallbackQuery, type Chat, type ChatAction, type ChatFullInfo, type ChatId, type ChatMember, type ChatMemberAdministrator, type ChatMemberMember, type ChatMemberOwner, type ChatMemberRestricted, type ChatPhoto, Composer, type Contact, Context, type ContextOptions, type CopyMessageParams, type CopyTextButton, type CreateChatInviteLinkParams, type CreateInvoiceLinkParams, type CreateNewStickerSetParams, DEFAULT_API_BASE, type DeleteMessageParams, type Document, type EditMessageCaptionParams, type EditMessageReplyMarkupParams, type EditMessageTextParams, type File, type FilterContext, type FilterQuery, type ForwardMessageParams, type GetChatMemberParams, type GetChatParams, type GetFileParams, type GetUpdatesParams, InlineKeyboard, type InlineKeyboardButton, type InlineKeyboardMarkup, InputFile, type InputMedia, type InputMediaAnimation, type InputMediaAudio, type InputMediaDocument, type InputMediaPhoto, type InputMediaVideo, type InputSticker, type InquireTransactionParams, type Invoice, type KeyboardButton, type LabeledPrice, type LeaveChatParams, type Location, type Message, type MessageEntity, type MessageId, type Middleware, type MiddlewareLike, type MiddlewareObject, type NextFunction, type PhotoSize, type PinChatMessageParams, type PollingBot, type PollingOptions, PollingRunner, type PreCheckoutQuery, type PromoteChatMemberParams, ReplyKeyboard, type ReplyKeyboardMarkup, type ReplyKeyboardRemove, type ReplyMarkup, type ReplyOptions, type ResponseParameters, type RevokeChatInviteLinkParams, type SendAnimationParams, type SendAudioParams, type SendChatActionParams, type SendContactParams, type SendDocumentParams, type SendInvoiceParams, type SendLocationParams, type SendMediaGroupParams, type SendMediaParams, type SendMessageParams, type SendPhotoParams, type SendVideoParams, type SendVoiceParams, type SetChatDescriptionParams, type SetChatPhotoParams, type SetChatTitleParams, type SetWebhookParams, type Sticker, type StickerSet, type SuccessfulPayment, type Transaction, type UnbanChatMemberParams, type Update, type UploadStickerFileParams, type User, type UserId, type Video, type Voice, type WebAppData, type WebAppInfo, type WebhookInfo, autoAnswerCallback, bold, createWebhookHandler, errorHandler, extractCommand, isFilePath, isMiddlewareObject, italic, link, matchUpdate, matchesAnyFilter, matchesChatType, matchesFilter, md, normalizeMiddleware, removeKeyboard, runMiddleware, spoiler, webhookFromJson };
878
+ interface GracefulShutdownOptions {
879
+ signals?: NodeJS.Signals[];
880
+ onShutdown?: () => void | Promise<void>;
881
+ }
882
+ declare function setupGracefulShutdown(bot: Bot, options?: GracefulShutdownOptions): void;
883
+
884
+ export { type AddStickerToSetParams, type Animation, type AnswerCallbackQueryParams, type AnswerPreCheckoutQueryParams, Api, type ApiClientOptions, type ApiMethod, type ApiMethodResultMap, type AskReviewParams, type Audio, BaleAPIError, type BaleApiResponse, BaleError, BaleNetworkError, type BanChatMemberParams, Bot, type BotOptions, type CallOptions, type CallbackQuery, type Chat, type ChatAction, type ChatFullInfo, type ChatId, type ChatMember, type ChatMemberAdministrator, type ChatMemberMember, type ChatMemberOwner, type ChatMemberRestricted, type ChatPhoto, Composer, type Contact, Context, type ContextOptions, type CopyMessageParams, type CopyTextButton, type CreateChatInviteLinkParams, type CreateInvoiceLinkParams, type CreateNewStickerSetParams, DEFAULT_API_BASE, type DeleteMessageParams, type Document, type EditMessageCaptionParams, type EditMessageReplyMarkupParams, type EditMessageTextParams, type File, type FilterContext, type FilterQuery, type ForwardMessageParams, type GetChatMemberParams, type GetChatParams, type GetFileParams, type GetUpdatesParams, type GracefulShutdownOptions, InlineKeyboard, type InlineKeyboardButton, type InlineKeyboardMarkup, InputFile, type InputMedia, type InputMediaAnimation, type InputMediaAudio, type InputMediaDocument, type InputMediaPhoto, type InputMediaVideo, type InputSticker, type InquireTransactionParams, type Invoice, type KeyboardButton, type LabeledPrice, type LeaveChatParams, type Location, type Message, type MessageEntity, type MessageId, type Middleware, type MiddlewareLike, type MiddlewareObject, type NextFunction, type PhotoSize, type PinChatMessageParams, type PollingBot, type PollingOptions, PollingRunner, type PreCheckoutQuery, type PromoteChatMemberParams, ReplyKeyboard, type ReplyKeyboardMarkup, type ReplyKeyboardRemove, type ReplyMarkup, type ReplyOptions, type ResponseParameters, type RevokeChatInviteLinkParams, type SendAnimationParams, type SendAudioParams, type SendChatActionParams, type SendContactParams, type SendDocumentParams, type SendInvoiceParams, type SendLocationParams, type SendMediaGroupParams, type SendMediaParams, type SendMessageParams, type SendPhotoParams, type SendVideoParams, type SendVoiceParams, type SetChatDescriptionParams, type SetChatPhotoParams, type SetChatTitleParams, type SetWebhookParams, type Sticker, type StickerSet, type SuccessfulPayment, type Transaction, type UnbanChatMemberParams, type Update, type UploadStickerFileParams, type User, type UserId, type Video, type Voice, type WebAppData, type WebAppInfo, type WebhookHandlerOptions, type WebhookInfo, autoAnswerCallback, bold, createWebhookHandler, errorHandler, extractCommand, isFilePath, isMiddlewareObject, italic, link, matchUpdate, matchesAnyFilter, matchesChatType, matchesFilter, md, normalizeMiddleware, removeKeyboard, runMiddleware, setupGracefulShutdown, spoiler, webhookFromJson };
package/dist/index.js CHANGED
@@ -92,6 +92,9 @@ async function isFilePath(source) {
92
92
  if (source.startsWith("http://") || source.startsWith("https://")) {
93
93
  return false;
94
94
  }
95
+ if (!looksLikePath(source)) {
96
+ return false;
97
+ }
95
98
  try {
96
99
  const info = await stat(source);
97
100
  return info.isFile();
@@ -99,6 +102,9 @@ async function isFilePath(source) {
99
102
  return false;
100
103
  }
101
104
  }
105
+ function looksLikePath(source) {
106
+ return source.startsWith("./") || source.startsWith("../") || source.includes("/") || source.includes("\\");
107
+ }
102
108
 
103
109
  // src/api/client.ts
104
110
  var DEFAULT_API_BASE = "https://tapi.bale.ai";
@@ -121,12 +127,15 @@ var Api = class {
121
127
  methodUrl(method) {
122
128
  return `${this.baseUrl}/bot${this.token}/${method}`;
123
129
  }
124
- async call(method, params) {
130
+ async call(method, params, options) {
125
131
  let attempt = 0;
126
132
  while (true) {
127
133
  try {
128
- return await this.invoke(method, params);
134
+ return await this.invoke(method, params, options);
129
135
  } catch (error) {
136
+ if (options?.signal?.aborted || isAbortError(error)) {
137
+ throw error;
138
+ }
130
139
  if (error instanceof BaleAPIError && error.errorCode === 429 && attempt < this.maxRetries) {
131
140
  const retryAfter = (error.parameters?.retry_after ?? 1) * 1e3;
132
141
  await sleep(retryAfter);
@@ -137,7 +146,7 @@ var Api = class {
137
146
  }
138
147
  }
139
148
  }
140
- async invoke(method, params) {
149
+ async invoke(method, params, options) {
141
150
  const { body } = await this.prepareBody(params ?? {});
142
151
  const headers = {};
143
152
  if (!(body instanceof FormData)) {
@@ -147,7 +156,7 @@ var Api = class {
147
156
  method: "POST",
148
157
  headers,
149
158
  body: body instanceof FormData ? body : JSON.stringify(body ?? {})
150
- });
159
+ }, options);
151
160
  }
152
161
  async downloadFile(filePath) {
153
162
  const url = `${this.fileBaseUrl}/${filePath}`;
@@ -189,8 +198,8 @@ var Api = class {
189
198
  }
190
199
  return { body: formData, isMultipart: true };
191
200
  }
192
- async request(method, init) {
193
- const response = await this.fetchWithRetry(this.methodUrl(method), init);
201
+ async request(method, init, options) {
202
+ const response = await this.fetchWithRetry(this.methodUrl(method), init, options);
194
203
  const payload = await response.json();
195
204
  if (!payload.ok) {
196
205
  throw new BaleAPIError(
@@ -201,12 +210,18 @@ var Api = class {
201
210
  }
202
211
  return payload.result;
203
212
  }
204
- async fetchWithRetry(url, init) {
213
+ async fetchWithRetry(url, init, options) {
205
214
  let attempt = 0;
206
215
  let lastError;
207
216
  while (attempt <= this.maxRetries) {
217
+ if (options?.signal?.aborted) {
218
+ throw new DOMException("The operation was aborted", "AbortError");
219
+ }
208
220
  try {
209
- const response = await this.fetchFn(url, init);
221
+ const response = await this.fetchFn(url, {
222
+ ...init,
223
+ signal: options?.signal
224
+ });
210
225
  if (response.status === 429) {
211
226
  const retryAfter = Number(response.headers.get("retry-after") ?? "1");
212
227
  await sleep(retryAfter * 1e3);
@@ -221,6 +236,9 @@ var Api = class {
221
236
  return response;
222
237
  } catch (error) {
223
238
  lastError = error;
239
+ if (isAbortError(error) || options?.signal?.aborted) {
240
+ throw error;
241
+ }
224
242
  if (attempt >= this.maxRetries) {
225
243
  break;
226
244
  }
@@ -236,8 +254,8 @@ var Api = class {
236
254
  getMe() {
237
255
  return this.call("getMe");
238
256
  }
239
- getUpdates(params) {
240
- return this.call("getUpdates", asParams(params));
257
+ getUpdates(params, options) {
258
+ return this.call("getUpdates", asParams(params), options);
241
259
  }
242
260
  setWebhook(params) {
243
261
  return this.call("setWebhook", asParams(params));
@@ -425,6 +443,9 @@ function appendFormValue(formData, key, value) {
425
443
  function sleep(ms) {
426
444
  return new Promise((resolve) => setTimeout(resolve, ms));
427
445
  }
446
+ function isAbortError(error) {
447
+ return error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted"));
448
+ }
428
449
 
429
450
  // src/middleware/types.ts
430
451
  function isMiddlewareObject(value) {
@@ -495,12 +516,10 @@ function extractCommand(text) {
495
516
 
496
517
  // src/filters/query.ts
497
518
  function matchesFilter(ctx, filter) {
498
- const updateMatches = matchUpdate(ctx.update);
499
- return updateMatches.includes(filter);
519
+ return ctx.updateTypes.includes(filter);
500
520
  }
501
521
  function matchesAnyFilter(ctx, filters) {
502
- const updateMatches = matchUpdate(ctx.update);
503
- return filters.some((filter) => updateMatches.includes(filter));
522
+ return filters.some((filter) => ctx.updateTypes.includes(filter));
504
523
  }
505
524
  function matchesChatType(ctx, chatType) {
506
525
  const chat = ctx.chat;
@@ -613,6 +632,7 @@ var Context = class {
613
632
  update;
614
633
  botInfo;
615
634
  callbackQueryAnswered = false;
635
+ _updateTypes;
616
636
  constructor(options) {
617
637
  this.api = options.api;
618
638
  this.update = options.update;
@@ -621,6 +641,12 @@ var Context = class {
621
641
  get updateId() {
622
642
  return this.update.update_id;
623
643
  }
644
+ get updateTypes() {
645
+ if (!this._updateTypes) {
646
+ this._updateTypes = matchUpdate(this.update);
647
+ }
648
+ return this._updateTypes;
649
+ }
624
650
  get message() {
625
651
  return this.update.message ?? this.update.edited_message;
626
652
  }
@@ -780,21 +806,14 @@ function autoAnswerCallback() {
780
806
  }
781
807
  };
782
808
  }
783
- function errorHandler(onError) {
784
- return async (ctx, next) => {
785
- try {
786
- await next();
787
- } catch (error) {
788
- await onError(error, ctx);
789
- }
790
- };
791
- }
792
809
 
793
810
  // src/runner/polling.ts
794
811
  var PollingRunner = class {
795
812
  running = false;
796
813
  abortController;
797
814
  offset = 0;
815
+ loopPromise;
816
+ backoffAttempt = 0;
798
817
  async start(bot, options = {}) {
799
818
  if (this.running) {
800
819
  throw new Error("Polling is already running");
@@ -805,14 +824,22 @@ var PollingRunner = class {
805
824
  const onError = options.onError ?? ((error) => {
806
825
  console.error("[balebaazoo] polling error:", error);
807
826
  });
827
+ this.loopPromise = this.runLoop(bot, options, signal, onError);
828
+ await this.loopPromise;
829
+ }
830
+ async runLoop(bot, options, signal, onError) {
808
831
  while (this.running && !signal.aborted) {
809
832
  try {
810
- const updates = await bot.api.getUpdates({
811
- offset: this.offset,
812
- limit: options.limit ?? 100,
813
- timeout: options.timeout ?? 30,
814
- allowed_updates: options.allowedUpdates
815
- });
833
+ const updates = await bot.api.getUpdates(
834
+ {
835
+ offset: this.offset,
836
+ limit: options.limit ?? 100,
837
+ timeout: options.timeout ?? 30,
838
+ allowed_updates: options.allowedUpdates
839
+ },
840
+ { signal }
841
+ );
842
+ this.backoffAttempt = 0;
816
843
  for (const update of updates) {
817
844
  this.offset = update.update_id + 1;
818
845
  try {
@@ -825,23 +852,49 @@ var PollingRunner = class {
825
852
  break;
826
853
  }
827
854
  } catch (error) {
828
- if (signal.aborted) break;
855
+ if (signal.aborted || isAbortError2(error)) {
856
+ break;
857
+ }
858
+ if (error instanceof BaleAPIError && error.errorCode === 401) {
859
+ onError(error);
860
+ this.running = false;
861
+ break;
862
+ }
829
863
  onError(error);
830
- await sleep2(1e3);
864
+ await sleep2(backoffDelay(this.backoffAttempt));
865
+ this.backoffAttempt++;
831
866
  }
832
867
  }
833
868
  }
869
+ setOffset(offset) {
870
+ this.offset = offset;
871
+ }
834
872
  async stop() {
835
873
  this.running = false;
836
874
  this.abortController?.abort();
875
+ await this.loopPromise;
837
876
  }
838
877
  };
878
+ function isAbortError2(error) {
879
+ return error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted"));
880
+ }
881
+ function backoffDelay(attempt) {
882
+ const base = 1e3;
883
+ const max = 3e4;
884
+ const exponential = Math.min(base * 2 ** attempt, max);
885
+ const jitter = Math.random() * 0.3 * exponential;
886
+ return Math.floor(exponential + jitter);
887
+ }
839
888
  function sleep2(ms) {
840
889
  return new Promise((resolve) => setTimeout(resolve, ms));
841
890
  }
842
891
  async function createWebhookHandler(bot, getUpdate, options = {}) {
843
892
  await bot.init();
844
893
  const maxBodyBytes = options.maxBodyBytes ?? 1024 * 1024;
894
+ const onError = options.onError ?? ((error) => {
895
+ console.error("[balebaazoo] webhook error:", error);
896
+ return new Response("Internal Server Error", { status: 500 });
897
+ });
845
898
  return async (request) => {
846
899
  if (request.method !== "POST") {
847
900
  return new Response("Method Not Allowed", { status: 405 });
@@ -866,8 +919,8 @@ async function createWebhookHandler(bot, getUpdate, options = {}) {
866
919
  await bot.handleUpdate(update);
867
920
  return new Response("OK", { status: 200 });
868
921
  } catch (error) {
869
- console.error("[balebaazoo] webhook error:", error);
870
- return new Response("Internal Server Error", { status: 500 });
922
+ const response = onError(error);
923
+ return response ?? new Response("Internal Server Error", { status: 500 });
871
924
  }
872
925
  };
873
926
  }
@@ -883,6 +936,7 @@ var Bot = class extends Composer {
883
936
  botInfo;
884
937
  polling = new PollingRunner();
885
938
  autoAnswer;
939
+ catchHandler;
886
940
  constructor(token, options = {}) {
887
941
  const api = new Api({ token, ...options });
888
942
  super();
@@ -899,9 +953,22 @@ var Bot = class extends Composer {
899
953
  }
900
954
  return this.botInfo;
901
955
  }
956
+ catch(handler) {
957
+ this.catchHandler = handler;
958
+ return this;
959
+ }
902
960
  async handleUpdate(update) {
961
+ await this.init();
903
962
  const ctx = this.createContext(update);
904
- await this.middleware()(ctx, async () => void 0);
963
+ try {
964
+ await this.middleware()(ctx, async () => void 0);
965
+ } catch (error) {
966
+ if (this.catchHandler) {
967
+ await this.catchHandler(error, ctx);
968
+ return;
969
+ }
970
+ throw error;
971
+ }
905
972
  }
906
973
  createContext(update) {
907
974
  return new Context({
@@ -911,9 +978,44 @@ var Bot = class extends Composer {
911
978
  });
912
979
  }
913
980
  async start(options = {}) {
914
- await this.init();
981
+ const onError = options.onError ?? ((error) => {
982
+ console.error("[balebaazoo] polling error:", error);
983
+ });
984
+ try {
985
+ await this.init();
986
+ } catch (error) {
987
+ onError(error);
988
+ return;
989
+ }
990
+ if (options.dropPendingUpdates) {
991
+ try {
992
+ const updates = await this.api.getUpdates({
993
+ offset: -1,
994
+ limit: 1,
995
+ timeout: 0
996
+ });
997
+ if (updates.length > 0) {
998
+ const last = updates[updates.length - 1];
999
+ this.polling.setOffset(last.update_id + 1);
1000
+ }
1001
+ } catch (error) {
1002
+ onError(error);
1003
+ return;
1004
+ }
1005
+ }
1006
+ if (options.onStart) {
1007
+ try {
1008
+ await options.onStart(this.botInfo);
1009
+ } catch (error) {
1010
+ onError(error);
1011
+ return;
1012
+ }
1013
+ }
915
1014
  await this.polling.start(this, options);
916
1015
  }
1016
+ launch(options) {
1017
+ return this.start(options);
1018
+ }
917
1019
  async stop() {
918
1020
  await this.polling.stop();
919
1021
  }
@@ -1010,6 +1112,30 @@ function removeKeyboard() {
1010
1112
  return { remove_keyboard: true };
1011
1113
  }
1012
1114
 
1013
- export { Api, BaleAPIError, BaleError, BaleNetworkError, Bot, Composer, Context, DEFAULT_API_BASE, InlineKeyboard, InputFile, PollingRunner, ReplyKeyboard, autoAnswerCallback, bold, createWebhookHandler, errorHandler, extractCommand, isFilePath, isMiddlewareObject, italic, link, matchUpdate, matchesAnyFilter, matchesChatType, matchesFilter, md, normalizeMiddleware, removeKeyboard, runMiddleware, spoiler, webhookFromJson };
1115
+ // src/middleware/error-handler.ts
1116
+ function errorHandler(onError) {
1117
+ return async (ctx, next) => {
1118
+ try {
1119
+ await next();
1120
+ } catch (error) {
1121
+ await onError(error, ctx);
1122
+ }
1123
+ };
1124
+ }
1125
+
1126
+ // src/runner/shutdown.ts
1127
+ function setupGracefulShutdown(bot, options = {}) {
1128
+ const signals = options.signals ?? ["SIGINT", "SIGTERM"];
1129
+ for (const signal of signals) {
1130
+ process.once(signal, () => {
1131
+ void (async () => {
1132
+ await bot.stop();
1133
+ await options.onShutdown?.();
1134
+ })();
1135
+ });
1136
+ }
1137
+ }
1138
+
1139
+ export { Api, BaleAPIError, BaleError, BaleNetworkError, Bot, Composer, Context, DEFAULT_API_BASE, InlineKeyboard, InputFile, PollingRunner, ReplyKeyboard, autoAnswerCallback, bold, createWebhookHandler, errorHandler, extractCommand, isFilePath, isMiddlewareObject, italic, link, matchUpdate, matchesAnyFilter, matchesChatType, matchesFilter, md, normalizeMiddleware, removeKeyboard, runMiddleware, setupGracefulShutdown, spoiler, webhookFromJson };
1014
1140
  //# sourceMappingURL=index.js.map
1015
1141
  //# sourceMappingURL=index.js.map