gramio 0.3.1 → 0.4.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.cjs CHANGED
@@ -6,10 +6,10 @@ var callbackData = require('@gramio/callback-data');
6
6
  var contexts = require('@gramio/contexts');
7
7
  var files = require('@gramio/files');
8
8
  var format = require('@gramio/format');
9
- var debug = require('debug');
10
- var utils = require('./utils-BbVj3DtT.cjs');
9
+ var utils = require('./utils-BIdmQNPz.cjs');
11
10
  var middlewareIo = require('middleware-io');
12
11
  var keyboards = require('@gramio/keyboards');
12
+ require('debug');
13
13
 
14
14
  class Composer {
15
15
  composer = middlewareIo.Composer.builder();
@@ -298,9 +298,11 @@ class UpdateQueue {
298
298
  class Updates {
299
299
  bot;
300
300
  isStarted = false;
301
+ isRequestActive = false;
301
302
  offset = 0;
302
303
  composer;
303
304
  queue;
305
+ stopPollingPromiseResolve;
304
306
  constructor(bot, onError) {
305
307
  this.bot = bot;
306
308
  this.composer = new Composer(onError);
@@ -339,40 +341,83 @@ class Updates {
339
341
  }
340
342
  }
341
343
  /** @deprecated use bot.start instead @internal */
342
- startPolling(params = {}) {
344
+ startPolling(params = {}, options = {}) {
343
345
  if (this.isStarted) throw new Error("[UPDATES] Polling already started!");
344
346
  this.isStarted = true;
345
- this.startFetchLoop(params);
347
+ this.startFetchLoop(params, options);
346
348
  return;
347
349
  }
348
- async startFetchLoop(params = {}) {
350
+ async startFetchLoop(params = {}, options = {}) {
351
+ if (options.dropPendingUpdates)
352
+ await this.dropPendingUpdates(options.deleteWebhookOnConflict);
349
353
  while (this.isStarted) {
350
354
  try {
351
- const updates = await this.bot.api.getUpdates({
352
- ...params,
353
- offset: this.offset,
354
- timeout: 30
355
- });
355
+ this.isRequestActive = true;
356
+ const updates = await utils.withRetries(
357
+ () => this.bot.api.getUpdates({
358
+ timeout: 30,
359
+ ...params,
360
+ offset: this.offset
361
+ })
362
+ );
363
+ this.isRequestActive = false;
356
364
  const updateId = updates.at(-1)?.update_id;
357
365
  this.offset = updateId ? updateId + 1 : this.offset;
358
366
  for await (const update of updates) {
359
367
  this.queue.add(update);
360
368
  }
361
369
  } catch (error) {
370
+ if (error instanceof utils.TelegramError) {
371
+ if (error.code === 409 && error.message.includes("deleteWebhook")) {
372
+ if (options.deleteWebhookOnConflict)
373
+ await this.bot.api.deleteWebhook();
374
+ continue;
375
+ }
376
+ }
362
377
  console.error("Error received when fetching updates", error);
363
- if (error instanceof utils.TelegramError && error.payload?.retry_after) {
364
- await utils.sleep(error.payload.retry_after * 1e3);
365
- } else await utils.sleep(this.bot.options.api.retryGetUpdatesWait ?? 1e3);
378
+ await utils.sleep(this.bot.options.api.retryGetUpdatesWait ?? 1e3);
366
379
  }
367
380
  }
381
+ this.stopPollingPromiseResolve?.();
368
382
  }
369
- stopPolling() {
383
+ async dropPendingUpdates(deleteWebhookOnConflict = false) {
384
+ const result = await this.bot.api.getUpdates({
385
+ // The negative offset can be specified to retrieve updates starting from *-offset* update from the end of the updates queue.
386
+ // All previous updates will be forgotten.
387
+ offset: -1,
388
+ timeout: 0,
389
+ suppress: true
390
+ });
391
+ if (result instanceof utils.TelegramError) {
392
+ if (result.code === 409 && result.message.includes("deleteWebhook")) {
393
+ if (deleteWebhookOnConflict) {
394
+ await this.bot.api.deleteWebhook({
395
+ drop_pending_updates: true
396
+ });
397
+ return;
398
+ }
399
+ }
400
+ throw result;
401
+ }
402
+ const lastUpdateId = result.at(-1)?.update_id;
403
+ utils.debug$updates(
404
+ "Dropping pending updates... Set offset to last update id %s + 1",
405
+ lastUpdateId
406
+ );
407
+ this.offset = lastUpdateId ? lastUpdateId + 1 : this.offset;
408
+ }
409
+ /**
410
+ * ! Soon waitPendingRequests param default will changed to true
411
+ */
412
+ stopPolling(waitPendingRequests = false) {
370
413
  this.isStarted = false;
414
+ if (!this.isRequestActive || !waitPendingRequests) return Promise.resolve();
415
+ return new Promise((resolve) => {
416
+ this.stopPollingPromiseResolve = resolve;
417
+ });
371
418
  }
372
419
  }
373
420
 
374
- const $debugger = debug("gramio");
375
- const debug$api = $debugger.extend("api");
376
421
  class Bot {
377
422
  /** @deprecated use `~` instead*/
378
423
  _ = {
@@ -473,7 +518,7 @@ class Bot {
473
518
  }
474
519
  }
475
520
  async _callApi(method, params = {}) {
476
- const debug$api$method = debug$api.extend(method);
521
+ const debug$api$method = utils.debug$api.extend(method);
477
522
  let url = `${this.options.api.baseURL}${this.options.token}/${this.options.api.useTest ? "test/" : ""}${method}`;
478
523
  const reqOptions = {
479
524
  method: "POST",
@@ -1010,20 +1055,30 @@ class Bot {
1010
1055
  */
1011
1056
  async start({
1012
1057
  webhook,
1058
+ longPolling,
1013
1059
  dropPendingUpdates,
1014
- allowedUpdates
1060
+ allowedUpdates,
1061
+ deleteWebhook: deleteWebhookRaw
1015
1062
  } = {}) {
1016
1063
  await this.init();
1064
+ const deleteWebhook = deleteWebhookRaw ?? "on-conflict-with-polling";
1017
1065
  if (!webhook) {
1018
- await utils.withRetries(
1019
- () => this.api.deleteWebhook({
1020
- drop_pending_updates: dropPendingUpdates
1021
- // suppress: true,
1022
- })
1066
+ if (deleteWebhook === true)
1067
+ await utils.withRetries(
1068
+ () => this.api.deleteWebhook({
1069
+ drop_pending_updates: dropPendingUpdates
1070
+ })
1071
+ );
1072
+ this.updates.startPolling(
1073
+ {
1074
+ ...longPolling,
1075
+ allowed_updates: allowedUpdates
1076
+ },
1077
+ {
1078
+ dropPendingUpdates,
1079
+ deleteWebhookOnConflict: deleteWebhook === "on-conflict-with-polling"
1080
+ }
1023
1081
  );
1024
- this.updates.startPolling({
1025
- allowed_updates: allowedUpdates
1026
- });
1027
1082
  this.runImmutableHooks("onStart", {
1028
1083
  plugins: this.dependencies,
1029
1084
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
@@ -1033,14 +1088,15 @@ class Bot {
1033
1088
  return this.info;
1034
1089
  }
1035
1090
  if (this.updates.isStarted) this.updates.stopPolling();
1036
- await utils.withRetries(
1037
- async () => this.api.setWebhook({
1038
- ...webhook,
1039
- drop_pending_updates: dropPendingUpdates,
1040
- allowed_updates: allowedUpdates
1041
- // suppress: true,
1042
- })
1043
- );
1091
+ if (webhook !== true)
1092
+ await utils.withRetries(
1093
+ async () => this.api.setWebhook({
1094
+ ...typeof webhook === "string" ? { url: webhook } : webhook,
1095
+ drop_pending_updates: dropPendingUpdates,
1096
+ allowed_updates: allowedUpdates
1097
+ // suppress: true,
1098
+ })
1099
+ );
1044
1100
  this.runImmutableHooks("onStart", {
1045
1101
  plugins: this.dependencies,
1046
1102
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
@@ -1051,13 +1107,14 @@ class Bot {
1051
1107
  }
1052
1108
  /**
1053
1109
  * Stops receiving events via long-polling or webhook
1054
- * Currently does not implement graceful shutdown
1055
1110
  * */
1056
1111
  async stop(timeout = 3e3) {
1057
- if (this.updates.isStarted) {
1058
- this.updates.stopPolling();
1059
- }
1060
- await this.updates.queue.stop(timeout);
1112
+ await Promise.all(
1113
+ [
1114
+ this.updates.queue.stop(timeout),
1115
+ this.updates.isStarted ? this.updates.stopPolling() : void 0
1116
+ ].filter(Boolean)
1117
+ );
1061
1118
  await this.runImmutableHooks("onStop", {
1062
1119
  plugins: this.dependencies,
1063
1120
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
@@ -1134,7 +1191,7 @@ function webhookHandler(bot, framework, secretToken) {
1134
1191
  ...args
1135
1192
  );
1136
1193
  if (secretToken && header !== secretToken) return unauthorized();
1137
- await bot.updates.handleUpdate(await update);
1194
+ bot.updates.queue.add(await update);
1138
1195
  if (response) return response();
1139
1196
  };
1140
1197
  }
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@ import { CallbackData } from '@gramio/callback-data';
2
2
  export * from '@gramio/callback-data';
3
3
  import { Context, UpdateName, MaybeArray, ContextType, BotLike, Attachment } from '@gramio/contexts';
4
4
  export * from '@gramio/contexts';
5
- import { APIMethods, TelegramResponseParameters, TelegramAPIResponseError, TelegramUser, APIMethodParams, APIMethodReturn, TelegramUpdate, TelegramReactionTypeEmojiEmoji, SetMyCommandsParams, TelegramBotCommand } from '@gramio/types';
5
+ import { APIMethods, TelegramResponseParameters, TelegramAPIResponseError, TelegramUser, APIMethodParams, APIMethodReturn, SetWebhookParams, TelegramUpdate, TelegramReactionTypeEmojiEmoji, SetMyCommandsParams, TelegramBotCommand } from '@gramio/types';
6
6
  export * from '@gramio/types';
7
7
  import * as middleware_io from 'middleware-io';
8
8
  import { Composer as Composer$1, Middleware, CaughtMiddlewareHandler, NextMiddleware } from 'middleware-io';
@@ -559,6 +559,20 @@ type AnyPlugin = Plugin<any, any>;
559
559
  type CallbackQueryShorthandContext<BotType extends BotLike, Trigger extends CallbackData | string | RegExp> = Omit<ContextType<BotType, "callback_query">, "data"> & BotType["__Derives"]["global"] & BotType["__Derives"]["callback_query"] & {
560
560
  queryData: Trigger extends CallbackData ? ReturnType<Trigger["unpack"]> : Trigger extends RegExp ? RegExpMatchArray : never;
561
561
  };
562
+ type BotStartOptionsLongPolling = Omit<NonNullable<APIMethodParams<"getUpdates">>, "allowed_updates" | "offset">;
563
+ type BotStartOptionsWebhook = true | string | Omit<SetWebhookParams, "drop_pending_updates" | "allowed_updates">;
564
+ type AllowedUpdates = Exclude<NonNullable<APIMethodParams<"getUpdates">>["allowed_updates"], "update_id">;
565
+ interface BotStartOptions {
566
+ webhook?: BotStartOptionsWebhook;
567
+ longPolling?: BotStartOptionsLongPolling;
568
+ dropPendingUpdates?: boolean;
569
+ allowedUpdates?: AllowedUpdates;
570
+ deleteWebhook?: boolean | "on-conflict-with-polling";
571
+ }
572
+ interface PollingStartOptions {
573
+ dropPendingUpdates?: boolean;
574
+ deleteWebhookOnConflict?: boolean;
575
+ }
562
576
 
563
577
  declare class UpdateQueue<Data = TelegramUpdate> {
564
578
  private updateQueue;
@@ -576,15 +590,21 @@ declare class UpdateQueue<Data = TelegramUpdate> {
576
590
  declare class Updates {
577
591
  private readonly bot;
578
592
  isStarted: boolean;
593
+ isRequestActive: boolean;
579
594
  private offset;
580
595
  composer: Composer;
581
596
  queue: UpdateQueue<TelegramUpdate>;
597
+ stopPollingPromiseResolve: ((value?: undefined) => void) | undefined;
582
598
  constructor(bot: AnyBot, onError: CaughtMiddlewareHandler<Context<any>>);
583
599
  handleUpdate(data: TelegramUpdate, mode?: "wait" | "lazy"): Promise<unknown>;
584
600
  /** @deprecated use bot.start instead @internal */
585
- startPolling(params?: APIMethodParams<"getUpdates">): void;
586
- startFetchLoop(params?: APIMethodParams<"getUpdates">): Promise<void>;
587
- stopPolling(): void;
601
+ startPolling(params?: APIMethodParams<"getUpdates">, options?: PollingStartOptions): void;
602
+ startFetchLoop(params?: APIMethodParams<"getUpdates">, options?: PollingStartOptions): Promise<void>;
603
+ dropPendingUpdates(deleteWebhookOnConflict?: boolean): Promise<void>;
604
+ /**
605
+ * ! Soon waitPendingRequests param default will changed to true
606
+ */
607
+ stopPolling(waitPendingRequests?: boolean): Promise<void>;
588
608
  }
589
609
 
590
610
  /** Bot instance
@@ -978,14 +998,9 @@ declare class Bot<Errors extends ErrorDefinitions = {}, Derives extends DeriveDe
978
998
  * bot.start();
979
999
  * ```
980
1000
  */
981
- start({ webhook, dropPendingUpdates, allowedUpdates, }?: {
982
- webhook?: Omit<APIMethodParams<"setWebhook">, "drop_pending_updates" | "allowed_updates">;
983
- dropPendingUpdates?: boolean;
984
- allowedUpdates?: NonNullable<APIMethodParams<"getUpdates">>["allowed_updates"];
985
- }): Promise<TelegramUser | undefined>;
1001
+ start({ webhook, longPolling, dropPendingUpdates, allowedUpdates, deleteWebhook: deleteWebhookRaw, }?: BotStartOptions): Promise<TelegramUser>;
986
1002
  /**
987
1003
  * Stops receiving events via long-polling or webhook
988
- * Currently does not implement graceful shutdown
989
1004
  * */
990
1005
  stop(timeout?: number): Promise<void>;
991
1006
  }
@@ -1069,4 +1084,4 @@ declare function webhookHandler<Framework extends keyof typeof frameworks>(bot:
1069
1084
  response: () => any;
1070
1085
  } ? (...args: Parameters<(typeof frameworks)[Framework]>) => ReturnType<ReturnType<(typeof frameworks)[Framework]>["response"]> : (...args: Parameters<(typeof frameworks)[Framework]>) => void;
1071
1086
 
1072
- export { type AnyBot, type AnyPlugin, Bot, type BotOptions, type CallbackQueryShorthandContext, Composer, type DeriveDefinitions, type ErrorDefinitions, ErrorKind, type FilterDefinitions, type Handler, Hooks, type MaybePromise, type MaybeSuppressedParams, type MaybeSuppressedReturn, Plugin, type Suppress, type SuppressedAPIMethodParams, type SuppressedAPIMethodReturn, type SuppressedAPIMethods, TelegramError, Updates, type WebhookHandlers, webhookHandler };
1087
+ export { type AllowedUpdates, type AnyBot, type AnyPlugin, Bot, type BotOptions, type BotStartOptions, type BotStartOptionsLongPolling, type BotStartOptionsWebhook, type CallbackQueryShorthandContext, Composer, type DeriveDefinitions, type ErrorDefinitions, ErrorKind, type FilterDefinitions, type Handler, Hooks, type MaybePromise, type MaybeSuppressedParams, type MaybeSuppressedReturn, Plugin, type PollingStartOptions, type Suppress, type SuppressedAPIMethodParams, type SuppressedAPIMethodReturn, type SuppressedAPIMethods, TelegramError, Updates, type WebhookHandlers, webhookHandler };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { CallbackData } from '@gramio/callback-data';
2
2
  export * from '@gramio/callback-data';
3
3
  import { Context, UpdateName, MaybeArray, ContextType, BotLike, Attachment } from '@gramio/contexts';
4
4
  export * from '@gramio/contexts';
5
- import { APIMethods, TelegramResponseParameters, TelegramAPIResponseError, TelegramUser, APIMethodParams, APIMethodReturn, TelegramUpdate, TelegramReactionTypeEmojiEmoji, SetMyCommandsParams, TelegramBotCommand } from '@gramio/types';
5
+ import { APIMethods, TelegramResponseParameters, TelegramAPIResponseError, TelegramUser, APIMethodParams, APIMethodReturn, SetWebhookParams, TelegramUpdate, TelegramReactionTypeEmojiEmoji, SetMyCommandsParams, TelegramBotCommand } from '@gramio/types';
6
6
  export * from '@gramio/types';
7
7
  import * as middleware_io from 'middleware-io';
8
8
  import { Composer as Composer$1, Middleware, CaughtMiddlewareHandler, NextMiddleware } from 'middleware-io';
@@ -559,6 +559,20 @@ type AnyPlugin = Plugin<any, any>;
559
559
  type CallbackQueryShorthandContext<BotType extends BotLike, Trigger extends CallbackData | string | RegExp> = Omit<ContextType<BotType, "callback_query">, "data"> & BotType["__Derives"]["global"] & BotType["__Derives"]["callback_query"] & {
560
560
  queryData: Trigger extends CallbackData ? ReturnType<Trigger["unpack"]> : Trigger extends RegExp ? RegExpMatchArray : never;
561
561
  };
562
+ type BotStartOptionsLongPolling = Omit<NonNullable<APIMethodParams<"getUpdates">>, "allowed_updates" | "offset">;
563
+ type BotStartOptionsWebhook = true | string | Omit<SetWebhookParams, "drop_pending_updates" | "allowed_updates">;
564
+ type AllowedUpdates = Exclude<NonNullable<APIMethodParams<"getUpdates">>["allowed_updates"], "update_id">;
565
+ interface BotStartOptions {
566
+ webhook?: BotStartOptionsWebhook;
567
+ longPolling?: BotStartOptionsLongPolling;
568
+ dropPendingUpdates?: boolean;
569
+ allowedUpdates?: AllowedUpdates;
570
+ deleteWebhook?: boolean | "on-conflict-with-polling";
571
+ }
572
+ interface PollingStartOptions {
573
+ dropPendingUpdates?: boolean;
574
+ deleteWebhookOnConflict?: boolean;
575
+ }
562
576
 
563
577
  declare class UpdateQueue<Data = TelegramUpdate> {
564
578
  private updateQueue;
@@ -576,15 +590,21 @@ declare class UpdateQueue<Data = TelegramUpdate> {
576
590
  declare class Updates {
577
591
  private readonly bot;
578
592
  isStarted: boolean;
593
+ isRequestActive: boolean;
579
594
  private offset;
580
595
  composer: Composer;
581
596
  queue: UpdateQueue<TelegramUpdate>;
597
+ stopPollingPromiseResolve: ((value?: undefined) => void) | undefined;
582
598
  constructor(bot: AnyBot, onError: CaughtMiddlewareHandler<Context<any>>);
583
599
  handleUpdate(data: TelegramUpdate, mode?: "wait" | "lazy"): Promise<unknown>;
584
600
  /** @deprecated use bot.start instead @internal */
585
- startPolling(params?: APIMethodParams<"getUpdates">): void;
586
- startFetchLoop(params?: APIMethodParams<"getUpdates">): Promise<void>;
587
- stopPolling(): void;
601
+ startPolling(params?: APIMethodParams<"getUpdates">, options?: PollingStartOptions): void;
602
+ startFetchLoop(params?: APIMethodParams<"getUpdates">, options?: PollingStartOptions): Promise<void>;
603
+ dropPendingUpdates(deleteWebhookOnConflict?: boolean): Promise<void>;
604
+ /**
605
+ * ! Soon waitPendingRequests param default will changed to true
606
+ */
607
+ stopPolling(waitPendingRequests?: boolean): Promise<void>;
588
608
  }
589
609
 
590
610
  /** Bot instance
@@ -978,14 +998,9 @@ declare class Bot<Errors extends ErrorDefinitions = {}, Derives extends DeriveDe
978
998
  * bot.start();
979
999
  * ```
980
1000
  */
981
- start({ webhook, dropPendingUpdates, allowedUpdates, }?: {
982
- webhook?: Omit<APIMethodParams<"setWebhook">, "drop_pending_updates" | "allowed_updates">;
983
- dropPendingUpdates?: boolean;
984
- allowedUpdates?: NonNullable<APIMethodParams<"getUpdates">>["allowed_updates"];
985
- }): Promise<TelegramUser | undefined>;
1001
+ start({ webhook, longPolling, dropPendingUpdates, allowedUpdates, deleteWebhook: deleteWebhookRaw, }?: BotStartOptions): Promise<TelegramUser>;
986
1002
  /**
987
1003
  * Stops receiving events via long-polling or webhook
988
- * Currently does not implement graceful shutdown
989
1004
  * */
990
1005
  stop(timeout?: number): Promise<void>;
991
1006
  }
@@ -1069,4 +1084,4 @@ declare function webhookHandler<Framework extends keyof typeof frameworks>(bot:
1069
1084
  response: () => any;
1070
1085
  } ? (...args: Parameters<(typeof frameworks)[Framework]>) => ReturnType<ReturnType<(typeof frameworks)[Framework]>["response"]> : (...args: Parameters<(typeof frameworks)[Framework]>) => void;
1071
1086
 
1072
- export { type AnyBot, type AnyPlugin, Bot, type BotOptions, type CallbackQueryShorthandContext, Composer, type DeriveDefinitions, type ErrorDefinitions, ErrorKind, type FilterDefinitions, type Handler, Hooks, type MaybePromise, type MaybeSuppressedParams, type MaybeSuppressedReturn, Plugin, type Suppress, type SuppressedAPIMethodParams, type SuppressedAPIMethodReturn, type SuppressedAPIMethods, TelegramError, Updates, type WebhookHandlers, webhookHandler };
1087
+ export { type AllowedUpdates, type AnyBot, type AnyPlugin, Bot, type BotOptions, type BotStartOptions, type BotStartOptionsLongPolling, type BotStartOptionsWebhook, type CallbackQueryShorthandContext, Composer, type DeriveDefinitions, type ErrorDefinitions, ErrorKind, type FilterDefinitions, type Handler, Hooks, type MaybePromise, type MaybeSuppressedParams, type MaybeSuppressedReturn, Plugin, type PollingStartOptions, type Suppress, type SuppressedAPIMethodParams, type SuppressedAPIMethodReturn, type SuppressedAPIMethods, TelegramError, Updates, type WebhookHandlers, webhookHandler };
package/dist/index.js CHANGED
@@ -8,10 +8,10 @@ import { isMediaUpload, convertJsonToFormData, extractFilesToFormData } from '@g
8
8
  export * from '@gramio/files';
9
9
  import { FormattableMap } from '@gramio/format';
10
10
  export * from '@gramio/format';
11
- import debug from 'debug';
12
- import { E as ErrorKind, s as sleep, T as TelegramError, I as IS_BUN, a as simplifyObject, w as withRetries } from './utils-DTFsIb2X.js';
11
+ import { E as ErrorKind, s as sleep, w as withRetries, T as TelegramError, d as debug$updates, a as debug$api, I as IS_BUN, b as simplifyObject } from './utils-CV4fnsQV.js';
13
12
  import { Composer as Composer$1, noopNext } from 'middleware-io';
14
13
  export * from '@gramio/keyboards';
14
+ import 'debug';
15
15
 
16
16
  class Composer {
17
17
  composer = Composer$1.builder();
@@ -300,9 +300,11 @@ class UpdateQueue {
300
300
  class Updates {
301
301
  bot;
302
302
  isStarted = false;
303
+ isRequestActive = false;
303
304
  offset = 0;
304
305
  composer;
305
306
  queue;
307
+ stopPollingPromiseResolve;
306
308
  constructor(bot, onError) {
307
309
  this.bot = bot;
308
310
  this.composer = new Composer(onError);
@@ -341,40 +343,83 @@ class Updates {
341
343
  }
342
344
  }
343
345
  /** @deprecated use bot.start instead @internal */
344
- startPolling(params = {}) {
346
+ startPolling(params = {}, options = {}) {
345
347
  if (this.isStarted) throw new Error("[UPDATES] Polling already started!");
346
348
  this.isStarted = true;
347
- this.startFetchLoop(params);
349
+ this.startFetchLoop(params, options);
348
350
  return;
349
351
  }
350
- async startFetchLoop(params = {}) {
352
+ async startFetchLoop(params = {}, options = {}) {
353
+ if (options.dropPendingUpdates)
354
+ await this.dropPendingUpdates(options.deleteWebhookOnConflict);
351
355
  while (this.isStarted) {
352
356
  try {
353
- const updates = await this.bot.api.getUpdates({
354
- ...params,
355
- offset: this.offset,
356
- timeout: 30
357
- });
357
+ this.isRequestActive = true;
358
+ const updates = await withRetries(
359
+ () => this.bot.api.getUpdates({
360
+ timeout: 30,
361
+ ...params,
362
+ offset: this.offset
363
+ })
364
+ );
365
+ this.isRequestActive = false;
358
366
  const updateId = updates.at(-1)?.update_id;
359
367
  this.offset = updateId ? updateId + 1 : this.offset;
360
368
  for await (const update of updates) {
361
369
  this.queue.add(update);
362
370
  }
363
371
  } catch (error) {
372
+ if (error instanceof TelegramError) {
373
+ if (error.code === 409 && error.message.includes("deleteWebhook")) {
374
+ if (options.deleteWebhookOnConflict)
375
+ await this.bot.api.deleteWebhook();
376
+ continue;
377
+ }
378
+ }
364
379
  console.error("Error received when fetching updates", error);
365
- if (error instanceof TelegramError && error.payload?.retry_after) {
366
- await sleep(error.payload.retry_after * 1e3);
367
- } else await sleep(this.bot.options.api.retryGetUpdatesWait ?? 1e3);
380
+ await sleep(this.bot.options.api.retryGetUpdatesWait ?? 1e3);
368
381
  }
369
382
  }
383
+ this.stopPollingPromiseResolve?.();
370
384
  }
371
- stopPolling() {
385
+ async dropPendingUpdates(deleteWebhookOnConflict = false) {
386
+ const result = await this.bot.api.getUpdates({
387
+ // The negative offset can be specified to retrieve updates starting from *-offset* update from the end of the updates queue.
388
+ // All previous updates will be forgotten.
389
+ offset: -1,
390
+ timeout: 0,
391
+ suppress: true
392
+ });
393
+ if (result instanceof TelegramError) {
394
+ if (result.code === 409 && result.message.includes("deleteWebhook")) {
395
+ if (deleteWebhookOnConflict) {
396
+ await this.bot.api.deleteWebhook({
397
+ drop_pending_updates: true
398
+ });
399
+ return;
400
+ }
401
+ }
402
+ throw result;
403
+ }
404
+ const lastUpdateId = result.at(-1)?.update_id;
405
+ debug$updates(
406
+ "Dropping pending updates... Set offset to last update id %s + 1",
407
+ lastUpdateId
408
+ );
409
+ this.offset = lastUpdateId ? lastUpdateId + 1 : this.offset;
410
+ }
411
+ /**
412
+ * ! Soon waitPendingRequests param default will changed to true
413
+ */
414
+ stopPolling(waitPendingRequests = false) {
372
415
  this.isStarted = false;
416
+ if (!this.isRequestActive || !waitPendingRequests) return Promise.resolve();
417
+ return new Promise((resolve) => {
418
+ this.stopPollingPromiseResolve = resolve;
419
+ });
373
420
  }
374
421
  }
375
422
 
376
- const $debugger = debug("gramio");
377
- const debug$api = $debugger.extend("api");
378
423
  class Bot {
379
424
  /** @deprecated use `~` instead*/
380
425
  _ = {
@@ -1012,20 +1057,30 @@ class Bot {
1012
1057
  */
1013
1058
  async start({
1014
1059
  webhook,
1060
+ longPolling,
1015
1061
  dropPendingUpdates,
1016
- allowedUpdates
1062
+ allowedUpdates,
1063
+ deleteWebhook: deleteWebhookRaw
1017
1064
  } = {}) {
1018
1065
  await this.init();
1066
+ const deleteWebhook = deleteWebhookRaw ?? "on-conflict-with-polling";
1019
1067
  if (!webhook) {
1020
- await withRetries(
1021
- () => this.api.deleteWebhook({
1022
- drop_pending_updates: dropPendingUpdates
1023
- // suppress: true,
1024
- })
1068
+ if (deleteWebhook === true)
1069
+ await withRetries(
1070
+ () => this.api.deleteWebhook({
1071
+ drop_pending_updates: dropPendingUpdates
1072
+ })
1073
+ );
1074
+ this.updates.startPolling(
1075
+ {
1076
+ ...longPolling,
1077
+ allowed_updates: allowedUpdates
1078
+ },
1079
+ {
1080
+ dropPendingUpdates,
1081
+ deleteWebhookOnConflict: deleteWebhook === "on-conflict-with-polling"
1082
+ }
1025
1083
  );
1026
- this.updates.startPolling({
1027
- allowed_updates: allowedUpdates
1028
- });
1029
1084
  this.runImmutableHooks("onStart", {
1030
1085
  plugins: this.dependencies,
1031
1086
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
@@ -1035,14 +1090,15 @@ class Bot {
1035
1090
  return this.info;
1036
1091
  }
1037
1092
  if (this.updates.isStarted) this.updates.stopPolling();
1038
- await withRetries(
1039
- async () => this.api.setWebhook({
1040
- ...webhook,
1041
- drop_pending_updates: dropPendingUpdates,
1042
- allowed_updates: allowedUpdates
1043
- // suppress: true,
1044
- })
1045
- );
1093
+ if (webhook !== true)
1094
+ await withRetries(
1095
+ async () => this.api.setWebhook({
1096
+ ...typeof webhook === "string" ? { url: webhook } : webhook,
1097
+ drop_pending_updates: dropPendingUpdates,
1098
+ allowed_updates: allowedUpdates
1099
+ // suppress: true,
1100
+ })
1101
+ );
1046
1102
  this.runImmutableHooks("onStart", {
1047
1103
  plugins: this.dependencies,
1048
1104
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
@@ -1053,13 +1109,14 @@ class Bot {
1053
1109
  }
1054
1110
  /**
1055
1111
  * Stops receiving events via long-polling or webhook
1056
- * Currently does not implement graceful shutdown
1057
1112
  * */
1058
1113
  async stop(timeout = 3e3) {
1059
- if (this.updates.isStarted) {
1060
- this.updates.stopPolling();
1061
- }
1062
- await this.updates.queue.stop(timeout);
1114
+ await Promise.all(
1115
+ [
1116
+ this.updates.queue.stop(timeout),
1117
+ this.updates.isStarted ? this.updates.stopPolling() : void 0
1118
+ ].filter(Boolean)
1119
+ );
1063
1120
  await this.runImmutableHooks("onStop", {
1064
1121
  plugins: this.dependencies,
1065
1122
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
@@ -1136,7 +1193,7 @@ function webhookHandler(bot, framework, secretToken) {
1136
1193
  ...args
1137
1194
  );
1138
1195
  if (secretToken && header !== secretToken) return unauthorized();
1139
- await bot.updates.handleUpdate(await update);
1196
+ bot.updates.queue.add(await update);
1140
1197
  if (response) return response();
1141
1198
  };
1142
1199
  }
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var debug = require('debug');
4
+
3
5
  const ErrorKind = Symbol("ErrorKind");
4
6
  class TelegramError extends Error {
5
7
  /** Name of the API Method */
@@ -40,6 +42,9 @@ function simplifyObject(obj) {
40
42
  return result;
41
43
  }
42
44
  const IS_BUN = typeof Bun !== "undefined";
45
+ const $debugger = debug("gramio");
46
+ const debug$api = $debugger.extend("api");
47
+ const debug$updates = $debugger.extend("updates");
43
48
 
44
49
  async function suppressError(fn) {
45
50
  try {
@@ -67,6 +72,8 @@ async function withRetries(resultPromise) {
67
72
  exports.ErrorKind = ErrorKind;
68
73
  exports.IS_BUN = IS_BUN;
69
74
  exports.TelegramError = TelegramError;
75
+ exports.debug$api = debug$api;
76
+ exports.debug$updates = debug$updates;
70
77
  exports.simplifyObject = simplifyObject;
71
78
  exports.sleep = sleep;
72
79
  exports.withRetries = withRetries;
@@ -1,3 +1,5 @@
1
+ import debug from 'debug';
2
+
1
3
  const ErrorKind = Symbol("ErrorKind");
2
4
  class TelegramError extends Error {
3
5
  /** Name of the API Method */
@@ -38,6 +40,9 @@ function simplifyObject(obj) {
38
40
  return result;
39
41
  }
40
42
  const IS_BUN = typeof Bun !== "undefined";
43
+ const $debugger = debug("gramio");
44
+ const debug$api = $debugger.extend("api");
45
+ const debug$updates = $debugger.extend("updates");
41
46
 
42
47
  async function suppressError(fn) {
43
48
  try {
@@ -62,4 +67,4 @@ async function withRetries(resultPromise) {
62
67
  return result;
63
68
  }
64
69
 
65
- export { ErrorKind as E, IS_BUN as I, TelegramError as T, simplifyObject as a, sleep as s, withRetries as w };
70
+ export { ErrorKind as E, IS_BUN as I, TelegramError as T, debug$api as a, simplifyObject as b, debug$updates as d, sleep as s, withRetries as w };
package/dist/utils.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var utils = require('./utils-BbVj3DtT.cjs');
3
+ var utils = require('./utils-BIdmQNPz.cjs');
4
+ require('debug');
4
5
 
5
6
 
6
7
 
package/dist/utils.js CHANGED
@@ -1 +1,2 @@
1
- export { w as withRetries } from './utils-DTFsIb2X.js';
1
+ export { w as withRetries } from './utils-CV4fnsQV.js';
2
+ import 'debug';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gramio",
3
3
  "type": "module",
4
- "version": "0.3.1",
4
+ "version": "0.4.0",
5
5
  "description": "Powerful, extensible and really type-safe Telegram Bot API framework",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.js",
@@ -68,7 +68,7 @@
68
68
  "@gramio/files": "^0.3.0",
69
69
  "@gramio/format": "^0.2.0",
70
70
  "@gramio/keyboards": "^1.2.1",
71
- "@gramio/types": "^9.0.1",
71
+ "@gramio/types": "^9.0.2",
72
72
  "debug": "^4.4.0",
73
73
  "middleware-io": "^2.8.1"
74
74
  },