gramio 0.1.5 → 0.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/README.md CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- [![Bot API](https://img.shields.io/badge/Bot%20API-8.0-blue?logo=telegram&style=flat&labelColor=000&color=3b82f6)](https://core.telegram.org/bots/api)
5
+ [![Bot API](https://img.shields.io/badge/Bot%20API-8.1-blue?logo=telegram&style=flat&labelColor=000&color=3b82f6)](https://core.telegram.org/bots/api)
6
6
  [![npm](https://img.shields.io/npm/v/gramio?logo=npm&style=flat&labelColor=000&color=3b82f6)](https://www.npmjs.org/package/gramio)
7
+ [![npm downloads](https://img.shields.io/npm/dw/gramio?logo=npm&style=flat&labelColor=000&color=3b82f6)](https://www.npmjs.org/package/gramio)
7
8
  [![JSR](https://jsr.io/badges/@gramio/core)](https://jsr.io/@gramio/core)
8
9
  [![JSR Score](https://jsr.io/badges/@gramio/core/score)](https://jsr.io/@gramio/core)
9
10
 
package/dist/index.cjs CHANGED
@@ -95,6 +95,9 @@ class Composer {
95
95
  compose(context, next = middlewareIo.noopNext) {
96
96
  this.composed(context, next);
97
97
  }
98
+ composeWait(context, next = middlewareIo.noopNext) {
99
+ return this.composed(context, next);
100
+ }
98
101
  }
99
102
 
100
103
  var __create$1 = Object.create;
@@ -175,6 +178,7 @@ class Plugin {
175
178
  errorsDefinitions: {},
176
179
  decorators: {}
177
180
  };
181
+ "~" = this._;
178
182
  /** Create new Plugin. Please provide `name` */
179
183
  constructor(name, { dependencies } = {}) {
180
184
  this._.name = name;
@@ -308,18 +312,61 @@ Plugin = __decorateElement$1(_init$1, 0, "Plugin", _Plugin_decorators, Plugin);
308
312
  __runInitializers$1(_init$1, 1, Plugin);
309
313
 
310
314
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
315
+ class UpdateQueue {
316
+ updateQueue = [];
317
+ pendingUpdates = /* @__PURE__ */ new Set();
318
+ handler;
319
+ onIdleResolver;
320
+ onIdlePromise;
321
+ isActive = false;
322
+ constructor(handler) {
323
+ this.handler = handler;
324
+ }
325
+ add(update) {
326
+ this.updateQueue.push(update);
327
+ this.start();
328
+ }
329
+ start() {
330
+ this.isActive = true;
331
+ while (this.updateQueue.length && this.isActive) {
332
+ const update = this.updateQueue.shift();
333
+ if (!update) continue;
334
+ const promise = this.handler(update);
335
+ this.pendingUpdates.add(promise);
336
+ promise.finally(async () => {
337
+ this.pendingUpdates.delete(promise);
338
+ if (this.pendingUpdates.size === 0 && this.updateQueue.length === 0 && this.onIdleResolver) {
339
+ this.onIdleResolver();
340
+ this.onIdleResolver = void 0;
341
+ }
342
+ });
343
+ }
344
+ }
345
+ async stop(timeout = 3e3) {
346
+ if (this.updateQueue.length === 0 && this.pendingUpdates.size === 0) {
347
+ return;
348
+ }
349
+ this.onIdlePromise = new Promise((resolve) => {
350
+ this.onIdleResolver = resolve;
351
+ });
352
+ await Promise.race([this.onIdlePromise, sleep(timeout)]);
353
+ this.isActive = false;
354
+ }
355
+ }
356
+
311
357
  class Updates {
312
358
  bot;
313
359
  isStarted = false;
314
360
  offset = 0;
315
361
  composer;
362
+ queue;
316
363
  constructor(bot, onError) {
317
364
  this.bot = bot;
318
365
  this.composer = new Composer(onError);
366
+ this.queue = new UpdateQueue(this.handleUpdate.bind(this));
319
367
  }
320
368
  async handleUpdate(data) {
321
369
  const updateType = Object.keys(data).at(1);
322
- this.offset = data.update_id + 1;
323
370
  const UpdateContext = contexts.contextsMappings[updateType];
324
371
  if (!UpdateContext) throw new Error(updateType);
325
372
  const updatePayload = data[updateType];
@@ -345,7 +392,7 @@ class Updates {
345
392
  updateId: data.update_id
346
393
  });
347
394
  }
348
- this.composer.compose(context);
395
+ return this.composer.composeWait(context);
349
396
  } catch (error) {
350
397
  throw new Error(`[UPDATES] Update type ${updateType} not supported.`);
351
398
  }
@@ -362,10 +409,13 @@ class Updates {
362
409
  try {
363
410
  const updates = await this.bot.api.getUpdates({
364
411
  ...params,
365
- offset: this.offset
412
+ offset: this.offset,
413
+ timeout: 30
366
414
  });
415
+ const updateId = updates.at(-1)?.update_id;
416
+ this.offset = updateId ? updateId + 1 : this.offset;
367
417
  for await (const update of updates) {
368
- await this.handleUpdate(update).catch(console.error);
418
+ this.queue.add(update);
369
419
  }
370
420
  } catch (error) {
371
421
  console.error("Error received when fetching updates", error);
@@ -424,6 +474,7 @@ class Bot {
424
474
  };
425
475
  /** @internal. Remap generic */
426
476
  __Derives;
477
+ "~" = this._;
427
478
  filters = {
428
479
  context: (name2) => (context) => context.is(name2)
429
480
  };
@@ -1076,9 +1127,11 @@ class Bot {
1076
1127
  * Stops receiving events via long-polling or webhook
1077
1128
  * Currently does not implement graceful shutdown
1078
1129
  * */
1079
- async stop() {
1080
- if (this.updates.isStarted) this.updates.stopPolling();
1081
- else await this.api.deleteWebhook();
1130
+ async stop(timeout = 3e3) {
1131
+ if (this.updates.isStarted) {
1132
+ this.updates.stopPolling();
1133
+ }
1134
+ await this.updates.queue.stop(timeout);
1082
1135
  await this.runImmutableHooks("onStop", {
1083
1136
  plugins: this.dependencies,
1084
1137
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
package/dist/index.d.cts CHANGED
@@ -56,6 +56,7 @@ declare class Composer {
56
56
  derive<Update extends UpdateName, Handler extends Hooks.Derive<ContextType<AnyBot, Update>>>(updateNameOrHandler: MaybeArray<Update> | Handler, handler?: Handler): this;
57
57
  protected recompose(): this;
58
58
  compose(context: Context<AnyBot>, next?: middleware_io.NextMiddleware): void;
59
+ composeWait(context: Context<AnyBot>, next?: middleware_io.NextMiddleware): unknown;
59
60
  }
60
61
 
61
62
  /**
@@ -132,6 +133,42 @@ declare class Plugin<Errors extends ErrorDefinitions = {}, Derives extends Deriv
132
133
  }>;
133
134
  decorators: Record<string, unknown>;
134
135
  };
136
+ "~": {
137
+ /** Name of plugin */
138
+ name: string;
139
+ /** List of plugin dependencies. If user does't extend from listed there dependencies it throw a error */
140
+ dependencies: string[];
141
+ /** remap generic type. {} in runtime */
142
+ Errors: Errors;
143
+ /** remap generic type. {} in runtime */
144
+ Derives: Derives;
145
+ /** Composer */
146
+ composer: Composer;
147
+ /** Store plugin preRequests hooks */
148
+ preRequests: [Hooks.PreRequest<any>, MaybeArray<keyof APIMethods> | undefined][];
149
+ /** Store plugin onResponses hooks */
150
+ onResponses: [Hooks.OnResponse<any>, MaybeArray<keyof APIMethods> | undefined][];
151
+ /** Store plugin onResponseErrors hooks */
152
+ onResponseErrors: [Hooks.OnResponseError<any>, MaybeArray<keyof APIMethods> | undefined][];
153
+ /**
154
+ * Store plugin groups
155
+ *
156
+ * If you use `on` or `use` in group and on plugin-level groups handlers are registered after plugin-level handlers
157
+ * */
158
+ groups: ((bot: AnyBot) => AnyBot)[];
159
+ /** Store plugin onStarts hooks */
160
+ onStarts: Hooks.OnStart[];
161
+ /** Store plugin onStops hooks */
162
+ onStops: Hooks.OnStop[];
163
+ /** Store plugin onErrors hooks */
164
+ onErrors: Hooks.OnError<any, any>[];
165
+ /** Map of plugin errors */
166
+ errorsDefinitions: Record<string, {
167
+ new (...args: any): any;
168
+ prototype: Error;
169
+ }>;
170
+ decorators: Record<string, unknown>;
171
+ };
135
172
  /** Create new Plugin. Please provide `name` */
136
173
  constructor(name: string, { dependencies }?: {
137
174
  dependencies?: string[];
@@ -517,13 +554,27 @@ type AnyBot = Bot<any, any>;
517
554
  /** Type of Bot that accepts any generics */
518
555
  type AnyPlugin = Plugin<any, any>;
519
556
 
557
+ declare class UpdateQueue<Data = TelegramUpdate> {
558
+ private updateQueue;
559
+ private pendingUpdates;
560
+ private handler;
561
+ private onIdleResolver;
562
+ private onIdlePromise;
563
+ private isActive;
564
+ constructor(handler: (update: Data) => Promise<unknown>);
565
+ add(update: Data): void;
566
+ private start;
567
+ stop(timeout?: number): Promise<void>;
568
+ }
569
+
520
570
  declare class Updates {
521
571
  private readonly bot;
522
572
  isStarted: boolean;
523
573
  private offset;
524
574
  composer: Composer;
575
+ queue: UpdateQueue<TelegramUpdate>;
525
576
  constructor(bot: AnyBot, onError: CaughtMiddlewareHandler<Context<any>>);
526
- handleUpdate(data: TelegramUpdate): Promise<void>;
577
+ handleUpdate(data: TelegramUpdate): Promise<unknown>;
527
578
  /** @deprecated use bot.start instead @internal */
528
579
  startPolling(params?: APIMethodParams<"getUpdates">): Promise<void>;
529
580
  startFetchLoop(params?: APIMethodParams<"getUpdates">): Promise<void>;
@@ -550,6 +601,10 @@ declare class Bot<Errors extends ErrorDefinitions = {}, Derives extends DeriveDe
550
601
  };
551
602
  /** @internal. Remap generic */
552
603
  __Derives: Derives;
604
+ "~": {
605
+ /** @internal. Remap generic */
606
+ derives: Derives;
607
+ };
553
608
  private filters;
554
609
  /** Options provided to instance */
555
610
  readonly options: BotOptions;
@@ -927,7 +982,7 @@ declare class Bot<Errors extends ErrorDefinitions = {}, Derives extends DeriveDe
927
982
  * Stops receiving events via long-polling or webhook
928
983
  * Currently does not implement graceful shutdown
929
984
  * */
930
- stop(): Promise<void>;
985
+ stop(timeout?: number): Promise<void>;
931
986
  }
932
987
 
933
988
  declare const frameworks: {
package/dist/index.d.ts CHANGED
@@ -56,6 +56,7 @@ declare class Composer {
56
56
  derive<Update extends UpdateName, Handler extends Hooks.Derive<ContextType<AnyBot, Update>>>(updateNameOrHandler: MaybeArray<Update> | Handler, handler?: Handler): this;
57
57
  protected recompose(): this;
58
58
  compose(context: Context<AnyBot>, next?: middleware_io.NextMiddleware): void;
59
+ composeWait(context: Context<AnyBot>, next?: middleware_io.NextMiddleware): unknown;
59
60
  }
60
61
 
61
62
  /**
@@ -132,6 +133,42 @@ declare class Plugin<Errors extends ErrorDefinitions = {}, Derives extends Deriv
132
133
  }>;
133
134
  decorators: Record<string, unknown>;
134
135
  };
136
+ "~": {
137
+ /** Name of plugin */
138
+ name: string;
139
+ /** List of plugin dependencies. If user does't extend from listed there dependencies it throw a error */
140
+ dependencies: string[];
141
+ /** remap generic type. {} in runtime */
142
+ Errors: Errors;
143
+ /** remap generic type. {} in runtime */
144
+ Derives: Derives;
145
+ /** Composer */
146
+ composer: Composer;
147
+ /** Store plugin preRequests hooks */
148
+ preRequests: [Hooks.PreRequest<any>, MaybeArray<keyof APIMethods> | undefined][];
149
+ /** Store plugin onResponses hooks */
150
+ onResponses: [Hooks.OnResponse<any>, MaybeArray<keyof APIMethods> | undefined][];
151
+ /** Store plugin onResponseErrors hooks */
152
+ onResponseErrors: [Hooks.OnResponseError<any>, MaybeArray<keyof APIMethods> | undefined][];
153
+ /**
154
+ * Store plugin groups
155
+ *
156
+ * If you use `on` or `use` in group and on plugin-level groups handlers are registered after plugin-level handlers
157
+ * */
158
+ groups: ((bot: AnyBot) => AnyBot)[];
159
+ /** Store plugin onStarts hooks */
160
+ onStarts: Hooks.OnStart[];
161
+ /** Store plugin onStops hooks */
162
+ onStops: Hooks.OnStop[];
163
+ /** Store plugin onErrors hooks */
164
+ onErrors: Hooks.OnError<any, any>[];
165
+ /** Map of plugin errors */
166
+ errorsDefinitions: Record<string, {
167
+ new (...args: any): any;
168
+ prototype: Error;
169
+ }>;
170
+ decorators: Record<string, unknown>;
171
+ };
135
172
  /** Create new Plugin. Please provide `name` */
136
173
  constructor(name: string, { dependencies }?: {
137
174
  dependencies?: string[];
@@ -517,13 +554,27 @@ type AnyBot = Bot<any, any>;
517
554
  /** Type of Bot that accepts any generics */
518
555
  type AnyPlugin = Plugin<any, any>;
519
556
 
557
+ declare class UpdateQueue<Data = TelegramUpdate> {
558
+ private updateQueue;
559
+ private pendingUpdates;
560
+ private handler;
561
+ private onIdleResolver;
562
+ private onIdlePromise;
563
+ private isActive;
564
+ constructor(handler: (update: Data) => Promise<unknown>);
565
+ add(update: Data): void;
566
+ private start;
567
+ stop(timeout?: number): Promise<void>;
568
+ }
569
+
520
570
  declare class Updates {
521
571
  private readonly bot;
522
572
  isStarted: boolean;
523
573
  private offset;
524
574
  composer: Composer;
575
+ queue: UpdateQueue<TelegramUpdate>;
525
576
  constructor(bot: AnyBot, onError: CaughtMiddlewareHandler<Context<any>>);
526
- handleUpdate(data: TelegramUpdate): Promise<void>;
577
+ handleUpdate(data: TelegramUpdate): Promise<unknown>;
527
578
  /** @deprecated use bot.start instead @internal */
528
579
  startPolling(params?: APIMethodParams<"getUpdates">): Promise<void>;
529
580
  startFetchLoop(params?: APIMethodParams<"getUpdates">): Promise<void>;
@@ -550,6 +601,10 @@ declare class Bot<Errors extends ErrorDefinitions = {}, Derives extends DeriveDe
550
601
  };
551
602
  /** @internal. Remap generic */
552
603
  __Derives: Derives;
604
+ "~": {
605
+ /** @internal. Remap generic */
606
+ derives: Derives;
607
+ };
553
608
  private filters;
554
609
  /** Options provided to instance */
555
610
  readonly options: BotOptions;
@@ -927,7 +982,7 @@ declare class Bot<Errors extends ErrorDefinitions = {}, Derives extends DeriveDe
927
982
  * Stops receiving events via long-polling or webhook
928
983
  * Currently does not implement graceful shutdown
929
984
  * */
930
- stop(): Promise<void>;
985
+ stop(timeout?: number): Promise<void>;
931
986
  }
932
987
 
933
988
  declare const frameworks: {
package/dist/index.js CHANGED
@@ -97,6 +97,9 @@ class Composer {
97
97
  compose(context, next = noopNext) {
98
98
  this.composed(context, next);
99
99
  }
100
+ composeWait(context, next = noopNext) {
101
+ return this.composed(context, next);
102
+ }
100
103
  }
101
104
 
102
105
  var __create$1 = Object.create;
@@ -177,6 +180,7 @@ class Plugin {
177
180
  errorsDefinitions: {},
178
181
  decorators: {}
179
182
  };
183
+ "~" = this._;
180
184
  /** Create new Plugin. Please provide `name` */
181
185
  constructor(name, { dependencies } = {}) {
182
186
  this._.name = name;
@@ -310,18 +314,61 @@ Plugin = __decorateElement$1(_init$1, 0, "Plugin", _Plugin_decorators, Plugin);
310
314
  __runInitializers$1(_init$1, 1, Plugin);
311
315
 
312
316
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
317
+ class UpdateQueue {
318
+ updateQueue = [];
319
+ pendingUpdates = /* @__PURE__ */ new Set();
320
+ handler;
321
+ onIdleResolver;
322
+ onIdlePromise;
323
+ isActive = false;
324
+ constructor(handler) {
325
+ this.handler = handler;
326
+ }
327
+ add(update) {
328
+ this.updateQueue.push(update);
329
+ this.start();
330
+ }
331
+ start() {
332
+ this.isActive = true;
333
+ while (this.updateQueue.length && this.isActive) {
334
+ const update = this.updateQueue.shift();
335
+ if (!update) continue;
336
+ const promise = this.handler(update);
337
+ this.pendingUpdates.add(promise);
338
+ promise.finally(async () => {
339
+ this.pendingUpdates.delete(promise);
340
+ if (this.pendingUpdates.size === 0 && this.updateQueue.length === 0 && this.onIdleResolver) {
341
+ this.onIdleResolver();
342
+ this.onIdleResolver = void 0;
343
+ }
344
+ });
345
+ }
346
+ }
347
+ async stop(timeout = 3e3) {
348
+ if (this.updateQueue.length === 0 && this.pendingUpdates.size === 0) {
349
+ return;
350
+ }
351
+ this.onIdlePromise = new Promise((resolve) => {
352
+ this.onIdleResolver = resolve;
353
+ });
354
+ await Promise.race([this.onIdlePromise, sleep(timeout)]);
355
+ this.isActive = false;
356
+ }
357
+ }
358
+
313
359
  class Updates {
314
360
  bot;
315
361
  isStarted = false;
316
362
  offset = 0;
317
363
  composer;
364
+ queue;
318
365
  constructor(bot, onError) {
319
366
  this.bot = bot;
320
367
  this.composer = new Composer(onError);
368
+ this.queue = new UpdateQueue(this.handleUpdate.bind(this));
321
369
  }
322
370
  async handleUpdate(data) {
323
371
  const updateType = Object.keys(data).at(1);
324
- this.offset = data.update_id + 1;
325
372
  const UpdateContext = contextsMappings[updateType];
326
373
  if (!UpdateContext) throw new Error(updateType);
327
374
  const updatePayload = data[updateType];
@@ -347,7 +394,7 @@ class Updates {
347
394
  updateId: data.update_id
348
395
  });
349
396
  }
350
- this.composer.compose(context);
397
+ return this.composer.composeWait(context);
351
398
  } catch (error) {
352
399
  throw new Error(`[UPDATES] Update type ${updateType} not supported.`);
353
400
  }
@@ -364,10 +411,13 @@ class Updates {
364
411
  try {
365
412
  const updates = await this.bot.api.getUpdates({
366
413
  ...params,
367
- offset: this.offset
414
+ offset: this.offset,
415
+ timeout: 30
368
416
  });
417
+ const updateId = updates.at(-1)?.update_id;
418
+ this.offset = updateId ? updateId + 1 : this.offset;
369
419
  for await (const update of updates) {
370
- await this.handleUpdate(update).catch(console.error);
420
+ this.queue.add(update);
371
421
  }
372
422
  } catch (error) {
373
423
  console.error("Error received when fetching updates", error);
@@ -426,6 +476,7 @@ class Bot {
426
476
  };
427
477
  /** @internal. Remap generic */
428
478
  __Derives;
479
+ "~" = this._;
429
480
  filters = {
430
481
  context: (name2) => (context) => context.is(name2)
431
482
  };
@@ -1078,9 +1129,11 @@ class Bot {
1078
1129
  * Stops receiving events via long-polling or webhook
1079
1130
  * Currently does not implement graceful shutdown
1080
1131
  * */
1081
- async stop() {
1082
- if (this.updates.isStarted) this.updates.stopPolling();
1083
- else await this.api.deleteWebhook();
1132
+ async stop(timeout = 3e3) {
1133
+ if (this.updates.isStarted) {
1134
+ this.updates.stopPolling();
1135
+ }
1136
+ await this.updates.queue.stop(timeout);
1084
1137
  await this.runImmutableHooks("onStop", {
1085
1138
  plugins: this.dependencies,
1086
1139
  // biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gramio",
3
3
  "type": "module",
4
- "version": "0.1.5",
4
+ "version": "0.2.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",
@@ -48,17 +48,14 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@gramio/callback-data": "^0.0.3",
51
- "@gramio/contexts": "^0.0.23",
52
- "@gramio/files": "^0.1.0",
51
+ "@gramio/contexts": "^0.1.2",
52
+ "@gramio/files": "^0.1.2",
53
53
  "@gramio/format": "^0.1.5",
54
- "@gramio/keyboards": "^1.0.0",
54
+ "@gramio/keyboards": "^1.0.2",
55
55
  "@gramio/types": "^8.1.0",
56
- "debug": "^4.3.7",
56
+ "debug": "^4.4.0",
57
57
  "inspectable": "^3.0.2",
58
58
  "middleware-io": "^2.8.1"
59
59
  },
60
- "overrides": {
61
- "@gramio/types": "^8.1.0"
62
- },
63
60
  "files": ["dist"]
64
61
  }