grammy 1.14.0 → 1.15.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 +10 -3
- package/out/bot.d.ts +4 -1
- package/out/bot.js +69 -15
- package/out/context.d.ts +11 -5
- package/out/context.js +1 -1
- package/out/convenience/frameworks.d.ts +65 -0
- package/out/convenience/frameworks.js +264 -0
- package/out/convenience/session.d.ts +21 -2
- package/out/convenience/session.js +15 -2
- package/out/convenience/webhook.d.ts +1 -147
- package/out/convenience/webhook.js +3 -3
- package/out/core/api.d.ts +144 -30
- package/out/core/api.js +144 -23
- package/out/platform.node.d.ts +1 -0
- package/out/platform.node.js +3 -1
- package/out/types.node.d.ts +11 -10
- package/out/web.mjs +317 -168
- package/package.json +4 -4
- package/out/convenience/frameworks.node.d.ts +0 -97
- package/out/convenience/frameworks.node.js +0 -104
- package/out/convenience/frameworks.shared.d.ts +0 -47
- package/out/convenience/frameworks.shared.js +0 -97
- package/out/convenience/frameworks.web.d.ts +0 -35
- package/out/convenience/frameworks.web.js +0 -98
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
<!-- deno-fmt-ignore-start -->
|
|
12
12
|
|
|
13
|
-
[](https://core.telegram.org/bots/api)
|
|
14
14
|
[](https://deno.land/x/grammy)
|
|
15
15
|
[](https://www.npmjs.org/package/grammy)
|
|
16
16
|
[](#contributors-)
|
|
@@ -245,7 +245,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
245
245
|
<tr>
|
|
246
246
|
<td align="center" valign="top" width="14.28%"><a href="http://ak4zh.com"><img src="https://avatars.githubusercontent.com/u/26350053?v=4?s=100" width="100px;" alt="ak4zh"/><br /><sub><b>ak4zh</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/pulls?q=is%3Apr+reviewed-by%3Aak4zh" title="Reviewed Pull Requests">👀</a> <a href="#ideas-ak4zh" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/grammyjs/grammY/commits?author=ak4zh" title="Code">💻</a></td>
|
|
247
247
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nlapshin"><img src="https://avatars.githubusercontent.com/u/39495311?v=4?s=100" width="100px;" alt="Nikolay Lapshin"/><br /><sub><b>Nikolay Lapshin</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=nlapshin" title="Code">💻</a></td>
|
|
248
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Aquathing"><img src="https://avatars.githubusercontent.com/u/81624781?v=4?s=100" width="100px;" alt="Aquatica"/><br /><sub><b>Aquatica</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=Aquathing" title="Documentation">📖</a></td>
|
|
248
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Aquathing"><img src="https://avatars.githubusercontent.com/u/81624781?v=4?s=100" width="100px;" alt="Aquatica"/><br /><sub><b>Aquatica</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=Aquathing" title="Documentation">📖</a> <a href="#question-Aquathing" title="Answering Questions">💬</a></td>
|
|
249
249
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fadzikri"><img src="https://avatars.githubusercontent.com/u/109416385?v=4?s=100" width="100px;" alt="Fa Dzikri"/><br /><sub><b>Fa Dzikri</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/pulls?q=is%3Apr+reviewed-by%3Afadzikri" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/grammyjs/grammY/commits?author=fadzikri" title="Documentation">📖</a> <a href="#translation-fadzikri" title="Translation">🌍</a></td>
|
|
250
250
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/solidprinciples"><img src="https://avatars.githubusercontent.com/u/7939765?v=4?s=100" width="100px;" alt="Chandler Lattin"/><br /><sub><b>Chandler Lattin</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=solidprinciples" title="Code">💻</a> <a href="https://github.com/grammyjs/grammY/pulls?q=is%3Apr+reviewed-by%3Asolidprinciples" title="Reviewed Pull Requests">👀</a> <a href="#plugin-solidprinciples" title="Plugin/utility libraries">🔌</a></td>
|
|
251
251
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sparfenyuk"><img src="https://avatars.githubusercontent.com/u/134065?v=4?s=100" width="100px;" alt="Sergey Parfenyuk"/><br /><sub><b>Sergey Parfenyuk</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/issues?q=author%3Asparfenyuk" title="Bug reports">🐛</a></td>
|
|
@@ -256,7 +256,14 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
256
256
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ByMsx"><img src="https://avatars.githubusercontent.com/u/5565836?v=4?s=100" width="100px;" alt="Maxim Lebedev"/><br /><sub><b>Maxim Lebedev</b></sub></a><br /><a href="#ideas-ByMsx" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/grammyjs/grammY/commits?author=ByMsx" title="Code">💻</a></td>
|
|
257
257
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Madnex"><img src="https://avatars.githubusercontent.com/u/14137610?v=4?s=100" width="100px;" alt="Madnex"/><br /><sub><b>Madnex</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=Madnex" title="Documentation">📖</a></td>
|
|
258
258
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tup4ienko"><img src="https://avatars.githubusercontent.com/u/89318613?v=4?s=100" width="100px;" alt="Svyatoslav Tupchienko"/><br /><sub><b>Svyatoslav Tupchienko</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=tup4ienko" title="Code">💻</a></td>
|
|
259
|
-
<td align="center" valign="top" width="14.28%"><a href="https://github.com/evermake"><img src="https://avatars.githubusercontent.com/u/53311479?v=4?s=100" width="100px;" alt="Vladislav Deryabkin"/><br /><sub><b>Vladislav Deryabkin</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/issues?q=author%3Aevermake" title="Bug reports">🐛</a> <a href="https://github.com/grammyjs/grammY/commits?author=evermake" title="Code">💻</a></td>
|
|
259
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/evermake"><img src="https://avatars.githubusercontent.com/u/53311479?v=4?s=100" width="100px;" alt="Vladislav Deryabkin"/><br /><sub><b>Vladislav Deryabkin</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/issues?q=author%3Aevermake" title="Bug reports">🐛</a> <a href="https://github.com/grammyjs/grammY/commits?author=evermake" title="Code">💻</a> <a href="https://github.com/grammyjs/grammY/pulls?q=is%3Apr+reviewed-by%3Aevermake" title="Reviewed Pull Requests">👀</a></td>
|
|
260
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kashyapndps00"><img src="https://avatars.githubusercontent.com/u/98746601?v=4?s=100" width="100px;" alt="Kashyap Sharma"/><br /><sub><b>Kashyap Sharma</b></sub></a><br /><a href="#example-kashyapndps00" title="Examples">💡</a></td>
|
|
261
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AlexOwl"><img src="https://avatars.githubusercontent.com/u/47189254?v=4?s=100" width="100px;" alt="AlexOwl"/><br /><sub><b>AlexOwl</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/issues?q=author%3AAlexOwl" title="Bug reports">🐛</a> <a href="https://github.com/grammyjs/grammY/commits?author=AlexOwl" title="Code">💻</a></td>
|
|
262
|
+
</tr>
|
|
263
|
+
<tr>
|
|
264
|
+
<td align="center" valign="top" width="14.28%"><a href="https://www.shrimadhavuk.me/"><img src="https://avatars.githubusercontent.com/u/6317196?v=4?s=100" width="100px;" alt="Shrimadhav U K"/><br /><sub><b>Shrimadhav U K</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=SpEcHiDe" title="Code">💻</a></td>
|
|
265
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/binamralamsal"><img src="https://avatars.githubusercontent.com/u/61900781?v=4?s=100" width="100px;" alt="Binamra Lamsal"/><br /><sub><b>Binamra Lamsal</b></sub></a><br /><a href="#ideas-binamralamsal" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
266
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gertminov"><img src="https://avatars.githubusercontent.com/u/78727928?v=4?s=100" width="100px;" alt="gertminov"/><br /><sub><b>gertminov</b></sub></a><br /><a href="https://github.com/grammyjs/grammY/commits?author=gertminov" title="Documentation">📖</a> <a href="#tutorial-gertminov" title="Tutorials">✅</a></td>
|
|
260
267
|
</tr>
|
|
261
268
|
</tbody>
|
|
262
269
|
</table>
|
package/out/bot.d.ts
CHANGED
|
@@ -169,8 +169,10 @@ export declare class Bot<C extends Context = Context, A extends Api = Api> exten
|
|
|
169
169
|
* Initializes the bot, i.e. fetches information about the bot itself. This
|
|
170
170
|
* method is called automatically, you usually don't have to call it
|
|
171
171
|
* manually.
|
|
172
|
+
*
|
|
173
|
+
* @param signal Optional `AbortSignal` to cancel the initialization
|
|
172
174
|
*/
|
|
173
|
-
init(): Promise<void>;
|
|
175
|
+
init(signal?: AbortSignal): Promise<void>;
|
|
174
176
|
/**
|
|
175
177
|
* Internal. Do not call. Handles an update batch sequentially by supplying
|
|
176
178
|
* it one-by-one to the middleware. Handles middleware errors and stores the
|
|
@@ -279,3 +281,4 @@ export declare class Bot<C extends Context = Context, A extends Api = Api> exten
|
|
|
279
281
|
*/
|
|
280
282
|
private handlePollingError;
|
|
281
283
|
}
|
|
284
|
+
import { AbortSignal } from "./shim.node.js";
|
package/out/bot.js
CHANGED
|
@@ -113,12 +113,14 @@ class Bot extends composer_js_1.Composer {
|
|
|
113
113
|
* Initializes the bot, i.e. fetches information about the bot itself. This
|
|
114
114
|
* method is called automatically, you usually don't have to call it
|
|
115
115
|
* manually.
|
|
116
|
+
*
|
|
117
|
+
* @param signal Optional `AbortSignal` to cancel the initialization
|
|
116
118
|
*/
|
|
117
|
-
async init() {
|
|
119
|
+
async init(signal) {
|
|
118
120
|
var _a;
|
|
119
121
|
if (!this.isInited()) {
|
|
120
122
|
debug("Initializing bot");
|
|
121
|
-
(_a = this.mePromise) !== null && _a !== void 0 ? _a : (this.mePromise = withRetries(() => this.api.getMe()));
|
|
123
|
+
(_a = this.mePromise) !== null && _a !== void 0 ? _a : (this.mePromise = withRetries(() => this.api.getMe(signal), signal));
|
|
122
124
|
let me;
|
|
123
125
|
try {
|
|
124
126
|
me = await this.mePromise;
|
|
@@ -230,19 +232,26 @@ a known bot info object.");
|
|
|
230
232
|
* @param options Options to use for simple long polling
|
|
231
233
|
*/
|
|
232
234
|
async start(options) {
|
|
233
|
-
var _a;
|
|
235
|
+
var _a, _b, _c;
|
|
234
236
|
// Perform setup
|
|
235
|
-
if (!this.isInited())
|
|
236
|
-
await this.init();
|
|
237
|
+
if (!this.isInited()) {
|
|
238
|
+
await this.init((_a = this.pollingAbortController) === null || _a === void 0 ? void 0 : _a.signal);
|
|
239
|
+
}
|
|
237
240
|
if (this.pollingRunning) {
|
|
238
241
|
debug("Simple long polling already running!");
|
|
239
242
|
return;
|
|
240
243
|
}
|
|
241
|
-
await withRetries(() =>
|
|
242
|
-
|
|
243
|
-
|
|
244
|
+
await withRetries(() => {
|
|
245
|
+
var _a;
|
|
246
|
+
return this.api.deleteWebhook({
|
|
247
|
+
drop_pending_updates: options === null || options === void 0 ? void 0 : options.drop_pending_updates,
|
|
248
|
+
}, (_a = this.pollingAbortController) === null || _a === void 0 ? void 0 : _a.signal);
|
|
249
|
+
}, (_b = this.pollingAbortController) === null || _b === void 0 ? void 0 : _b.signal);
|
|
244
250
|
// All async ops of setup complete, run callback
|
|
245
|
-
await ((
|
|
251
|
+
await ((_c = options === null || options === void 0 ? void 0 : options.onStart) === null || _c === void 0 ? void 0 : _c.call(options, this.botInfo));
|
|
252
|
+
// Bot was stopped during `onStart`
|
|
253
|
+
if (!this.pollingRunning)
|
|
254
|
+
return;
|
|
246
255
|
// Prevent common misuse that causes memory leak
|
|
247
256
|
this.use = () => {
|
|
248
257
|
throw new Error(`It looks like you are registering more listeners \
|
|
@@ -391,13 +400,26 @@ exports.Bot = Bot;
|
|
|
391
400
|
/**
|
|
392
401
|
* Performs a network call task, retrying upon known errors until success.
|
|
393
402
|
*
|
|
403
|
+
* If the task errors and a retry_after value can be used, a subsequent retry
|
|
404
|
+
* will be delayed by the specified period of time.
|
|
405
|
+
*
|
|
406
|
+
* Otherwise, if the first attempt at running the task fails, the task is
|
|
407
|
+
* retried immediately. If second attempt fails, too, waits for 100 ms, and then
|
|
408
|
+
* doubles this delay for every subsequent attemt. Never waits longer than 1
|
|
409
|
+
* hour before retrying.
|
|
410
|
+
*
|
|
394
411
|
* @param task Async task to perform
|
|
412
|
+
* @param signal Optional `AbortSignal` to prevent further retries
|
|
395
413
|
*/
|
|
396
|
-
async function withRetries(task) {
|
|
414
|
+
async function withRetries(task, signal) {
|
|
397
415
|
let result = { ok: false };
|
|
416
|
+
const INITIAL_DELAY = 100; // ms
|
|
417
|
+
let delay = INITIAL_DELAY;
|
|
398
418
|
while (!result.ok) {
|
|
419
|
+
let mustDelay = true;
|
|
399
420
|
try {
|
|
400
421
|
result = { ok: true, value: await task() };
|
|
422
|
+
mustDelay = false;
|
|
401
423
|
}
|
|
402
424
|
catch (error) {
|
|
403
425
|
debugErr(error);
|
|
@@ -408,20 +430,52 @@ async function withRetries(task) {
|
|
|
408
430
|
continue;
|
|
409
431
|
if (error.error_code === 429) {
|
|
410
432
|
const retryAfter = error.parameters.retry_after;
|
|
411
|
-
if (retryAfter !== undefined)
|
|
412
|
-
await sleep(retryAfter);
|
|
433
|
+
if (retryAfter !== undefined) {
|
|
434
|
+
await sleep(retryAfter, signal);
|
|
435
|
+
mustDelay = false;
|
|
436
|
+
}
|
|
413
437
|
continue;
|
|
414
438
|
}
|
|
415
439
|
}
|
|
416
440
|
throw error;
|
|
417
441
|
}
|
|
442
|
+
finally {
|
|
443
|
+
if (mustDelay) {
|
|
444
|
+
if (delay !== INITIAL_DELAY) {
|
|
445
|
+
await sleep(delay, signal);
|
|
446
|
+
}
|
|
447
|
+
// double the next delay but cap it at 1 hour
|
|
448
|
+
delay = Math.min(1 * 60 * 60 * 1000, delay + delay);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
418
451
|
}
|
|
419
452
|
return result.value;
|
|
420
453
|
}
|
|
421
454
|
/**
|
|
422
|
-
* Returns a new promise that resolves after the specified number of seconds
|
|
455
|
+
* Returns a new promise that resolves after the specified number of seconds, or
|
|
456
|
+
* rejects as soon as the given signal is aborted.
|
|
423
457
|
*/
|
|
424
|
-
function sleep(seconds) {
|
|
425
|
-
|
|
458
|
+
async function sleep(seconds, signal) {
|
|
459
|
+
let handle;
|
|
460
|
+
let reject;
|
|
461
|
+
function abort() {
|
|
462
|
+
reject === null || reject === void 0 ? void 0 : reject(new Error("Aborted delay"));
|
|
463
|
+
if (handle !== undefined)
|
|
464
|
+
clearTimeout(handle);
|
|
465
|
+
}
|
|
466
|
+
try {
|
|
467
|
+
await new Promise((res, rej) => {
|
|
468
|
+
reject = rej;
|
|
469
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
470
|
+
abort();
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener("abort", abort);
|
|
474
|
+
handle = setTimeout(res, 1000 * seconds);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
finally {
|
|
478
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener("abort", abort);
|
|
479
|
+
}
|
|
426
480
|
}
|
|
427
481
|
const shim_node_js_1 = require("./shim.node.js");
|
package/out/context.d.ts
CHANGED
|
@@ -426,7 +426,9 @@ export declare class Context implements RenamedUpdate {
|
|
|
426
426
|
*
|
|
427
427
|
* **Official reference:** https://core.telegram.org/bots/api#editmessagelivelocation
|
|
428
428
|
*/
|
|
429
|
-
editMessageLiveLocation(latitude: number, longitude: number, other?: Other<"editMessageLiveLocation", "chat_id" | "message_id" | "inline_message_id" | "latitude" | "longitude">, signal?: AbortSignal): Promise<true | (Update.Edited & Message.CommonMessage &
|
|
429
|
+
editMessageLiveLocation(latitude: number, longitude: number, other?: Other<"editMessageLiveLocation", "chat_id" | "message_id" | "inline_message_id" | "latitude" | "longitude">, signal?: AbortSignal): Promise<true | (Update.Edited & Message.CommonMessage & {
|
|
430
|
+
location: import("@grammyjs/types/message.js").Location;
|
|
431
|
+
})>;
|
|
430
432
|
/**
|
|
431
433
|
* Context-aware alias for `api.stopMessageLiveLocation`. Use this method to stop updating a live location message before live_period expires. On success, if the message is not an inline message, the edited Message is returned, otherwise True is returned.
|
|
432
434
|
*
|
|
@@ -435,7 +437,9 @@ export declare class Context implements RenamedUpdate {
|
|
|
435
437
|
*
|
|
436
438
|
* **Official reference:** https://core.telegram.org/bots/api#stopmessagelivelocation
|
|
437
439
|
*/
|
|
438
|
-
stopMessageLiveLocation(other?: Other<"stopMessageLiveLocation", "chat_id" | "message_id" | "inline_message_id">, signal?: AbortSignal): Promise<true | (Update.Edited & Message.CommonMessage &
|
|
440
|
+
stopMessageLiveLocation(other?: Other<"stopMessageLiveLocation", "chat_id" | "message_id" | "inline_message_id">, signal?: AbortSignal): Promise<true | (Update.Edited & Message.CommonMessage & {
|
|
441
|
+
location: import("@grammyjs/types/message.js").Location;
|
|
442
|
+
})>;
|
|
439
443
|
/**
|
|
440
444
|
* Context-aware alias for `api.sendVenue`. Use this method to send information about a venue. On success, the sent Message is returned.
|
|
441
445
|
*
|
|
@@ -939,7 +943,7 @@ export declare class Context implements RenamedUpdate {
|
|
|
939
943
|
*
|
|
940
944
|
* **Official reference:** https://core.telegram.org/bots/api#setchatmenubutton
|
|
941
945
|
*/
|
|
942
|
-
getChatMenuButton(other?: Other<"getChatMenuButton">, signal?: AbortSignal): Promise<import("@grammyjs/types/
|
|
946
|
+
getChatMenuButton(other?: Other<"getChatMenuButton">, signal?: AbortSignal): Promise<import("@grammyjs/types/settings.js").MenuButton>;
|
|
943
947
|
/**
|
|
944
948
|
* Context-aware alias for `api.setMyDefaultAdministratorRights`. Use this method to the change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. These rights will be suggested to users, but they are are free to modify the list before adding the bot. Returns True on success.
|
|
945
949
|
*
|
|
@@ -965,7 +969,9 @@ export declare class Context implements RenamedUpdate {
|
|
|
965
969
|
*
|
|
966
970
|
* **Official reference:** https://core.telegram.org/bots/api#editmessagetext
|
|
967
971
|
*/
|
|
968
|
-
editMessageText(text: string, other?: Other<"editMessageText", "chat_id" | "message_id" | "inline_message_id" | "text">, signal?: AbortSignal): Promise<true | (Update.Edited & Message.CommonMessage &
|
|
972
|
+
editMessageText(text: string, other?: Other<"editMessageText", "chat_id" | "message_id" | "inline_message_id" | "text">, signal?: AbortSignal): Promise<true | (Update.Edited & Message.CommonMessage & {
|
|
973
|
+
text: string;
|
|
974
|
+
})>;
|
|
969
975
|
/**
|
|
970
976
|
* Context-aware alias for `api.editMessageCaption`. Use this method to edit captions of messages. On success, if the edited message is not an inline message, the edited Message is returned, otherwise True is returned.
|
|
971
977
|
*
|
|
@@ -1057,7 +1063,7 @@ export declare class Context implements RenamedUpdate {
|
|
|
1057
1063
|
* @param title Product name, 1-32 characters
|
|
1058
1064
|
* @param description Product description, 1-255 characters
|
|
1059
1065
|
* @param payload Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
|
|
1060
|
-
* @param provider_token
|
|
1066
|
+
* @param provider_token Payment provider token, obtained via @BotFather
|
|
1061
1067
|
* @param currency Three-letter ISO 4217 currency code, see more on currencies
|
|
1062
1068
|
* @param prices Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
|
|
1063
1069
|
* @param other Optional remaining parameters, confer the official reference below
|
package/out/context.js
CHANGED
|
@@ -1298,7 +1298,7 @@ class Context {
|
|
|
1298
1298
|
* @param title Product name, 1-32 characters
|
|
1299
1299
|
* @param description Product description, 1-255 characters
|
|
1300
1300
|
* @param payload Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
|
|
1301
|
-
* @param provider_token
|
|
1301
|
+
* @param provider_token Payment provider token, obtained via @BotFather
|
|
1302
1302
|
* @param currency Three-letter ISO 4217 currency code, see more on currencies
|
|
1303
1303
|
* @param prices Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
|
|
1304
1304
|
* @param other Optional remaining parameters, confer the official reference below
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type Update } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* HTTP Web frameworks for which grammY provides compatible callback out of the
|
|
4
|
+
* box.
|
|
5
|
+
*/
|
|
6
|
+
export type SupportedFrameworks = keyof typeof adapters;
|
|
7
|
+
/**
|
|
8
|
+
* Abstraction over a request-response cycle, providing access to the update, as
|
|
9
|
+
* well as a mechanism for responding to the request and to end it.
|
|
10
|
+
*/
|
|
11
|
+
export interface ReqResHandler {
|
|
12
|
+
/**
|
|
13
|
+
* The update object sent from Telegram, usually resolves the request's JSON
|
|
14
|
+
* body
|
|
15
|
+
*/
|
|
16
|
+
update: Promise<Update>;
|
|
17
|
+
/**
|
|
18
|
+
* X-Telegram-Bot-Api-Secret-Token header of the request, or undefined if
|
|
19
|
+
* not present
|
|
20
|
+
*/
|
|
21
|
+
header?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Ends the request immediately without body, called after every request
|
|
24
|
+
* unless a webhook reply was performed
|
|
25
|
+
*/
|
|
26
|
+
end?: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Sends the specified JSON as a payload in the body, used for webhook
|
|
29
|
+
* replies
|
|
30
|
+
*/
|
|
31
|
+
respond: (json: string) => unknown | Promise<unknown>;
|
|
32
|
+
/**
|
|
33
|
+
* Responds that the request is unauthorized due to mismatching
|
|
34
|
+
* X-Telegram-Bot-Api-Secret-Token headers
|
|
35
|
+
*/
|
|
36
|
+
unauthorized: () => unknown | Promise<unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* Some frameworks (e.g. Deno's std/http `listenAndServe`) assume that
|
|
39
|
+
* handler returns something
|
|
40
|
+
*/
|
|
41
|
+
handlerReturn?: any;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Middleware for a web framework. Creates a request-response handler for a
|
|
45
|
+
* request. The handler will be used to integrate with the compatible framework.
|
|
46
|
+
*/
|
|
47
|
+
export type FrameworkAdapter = (...args: any[]) => ReqResHandler;
|
|
48
|
+
export declare const adapters: {
|
|
49
|
+
express: FrameworkAdapter;
|
|
50
|
+
koa: FrameworkAdapter;
|
|
51
|
+
fastify: FrameworkAdapter;
|
|
52
|
+
serveHttp: FrameworkAdapter;
|
|
53
|
+
"std/http": FrameworkAdapter;
|
|
54
|
+
oak: FrameworkAdapter;
|
|
55
|
+
http: FrameworkAdapter;
|
|
56
|
+
https: FrameworkAdapter;
|
|
57
|
+
"aws-lambda": FrameworkAdapter;
|
|
58
|
+
azure: FrameworkAdapter;
|
|
59
|
+
"next-js": FrameworkAdapter;
|
|
60
|
+
sveltekit: FrameworkAdapter;
|
|
61
|
+
cloudflare: FrameworkAdapter;
|
|
62
|
+
"cloudflare-mod": FrameworkAdapter;
|
|
63
|
+
hono: FrameworkAdapter;
|
|
64
|
+
worktop: FrameworkAdapter;
|
|
65
|
+
};
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.adapters = void 0;
|
|
4
|
+
const SECRET_HEADER = "X-Telegram-Bot-Api-Secret-Token";
|
|
5
|
+
const WRONG_TOKEN_ERROR = "secret token is wrong";
|
|
6
|
+
const ok = () => new Response(null, { status: 200 });
|
|
7
|
+
const okJson = (json) => new Response(json, {
|
|
8
|
+
status: 200,
|
|
9
|
+
headers: { "Content-Type": "application/json" },
|
|
10
|
+
});
|
|
11
|
+
const unauthorized = () => new Response('"unauthorized"', {
|
|
12
|
+
status: 401,
|
|
13
|
+
statusText: WRONG_TOKEN_ERROR,
|
|
14
|
+
});
|
|
15
|
+
/** express web framework */
|
|
16
|
+
const express = (req, res) => ({
|
|
17
|
+
update: Promise.resolve(req.body),
|
|
18
|
+
header: req.header(SECRET_HEADER),
|
|
19
|
+
end: () => res.end(),
|
|
20
|
+
respond: (json) => {
|
|
21
|
+
res.set("Content-Type", "application/json");
|
|
22
|
+
res.send(json);
|
|
23
|
+
},
|
|
24
|
+
unauthorized: () => {
|
|
25
|
+
res.send(401, WRONG_TOKEN_ERROR);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
/** koa web framework */
|
|
29
|
+
const koa = (ctx) => ({
|
|
30
|
+
update: Promise.resolve(ctx.request.body),
|
|
31
|
+
header: ctx.get(SECRET_HEADER),
|
|
32
|
+
end: () => {
|
|
33
|
+
ctx.body = "";
|
|
34
|
+
},
|
|
35
|
+
respond: (json) => {
|
|
36
|
+
ctx.set("Content-Type", "application/json");
|
|
37
|
+
ctx.response.body = json;
|
|
38
|
+
},
|
|
39
|
+
unauthorized: () => {
|
|
40
|
+
ctx.status = 401;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
/** fastify web framework */
|
|
44
|
+
const fastify = (req, reply) => ({
|
|
45
|
+
update: Promise.resolve(req.body),
|
|
46
|
+
header: req.headers[SECRET_HEADER.toLowerCase()],
|
|
47
|
+
end: () => reply.status(200).send(),
|
|
48
|
+
respond: (json) => reply.send(json),
|
|
49
|
+
unauthorized: () => reply.code(401).send(WRONG_TOKEN_ERROR),
|
|
50
|
+
});
|
|
51
|
+
const serveHttp = (requestEvent) => ({
|
|
52
|
+
update: requestEvent.request.json(),
|
|
53
|
+
header: requestEvent.request.headers.get(SECRET_HEADER) || undefined,
|
|
54
|
+
end: () => requestEvent.respondWith(ok()),
|
|
55
|
+
respond: (json) => requestEvent.respondWith(okJson(json)),
|
|
56
|
+
unauthorized: () => requestEvent.respondWith(unauthorized()),
|
|
57
|
+
});
|
|
58
|
+
/** std/http web server */
|
|
59
|
+
const stdHttp = (req) => {
|
|
60
|
+
let resolveResponse;
|
|
61
|
+
return {
|
|
62
|
+
update: req.json(),
|
|
63
|
+
header: req.headers.get(SECRET_HEADER) || undefined,
|
|
64
|
+
end: () => {
|
|
65
|
+
if (resolveResponse)
|
|
66
|
+
resolveResponse(ok());
|
|
67
|
+
},
|
|
68
|
+
respond: (json) => {
|
|
69
|
+
if (resolveResponse)
|
|
70
|
+
resolveResponse(okJson(json));
|
|
71
|
+
},
|
|
72
|
+
unauthorized: () => {
|
|
73
|
+
if (resolveResponse)
|
|
74
|
+
resolveResponse(unauthorized());
|
|
75
|
+
},
|
|
76
|
+
handlerReturn: new Promise((resolve) => {
|
|
77
|
+
resolveResponse = resolve;
|
|
78
|
+
}),
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
/** oak web framework */
|
|
82
|
+
const oak = (ctx) => ({
|
|
83
|
+
update: ctx.request.body({ type: "json" }).value,
|
|
84
|
+
header: ctx.request.headers.get(SECRET_HEADER) || undefined,
|
|
85
|
+
end: () => {
|
|
86
|
+
ctx.response.status = 200;
|
|
87
|
+
},
|
|
88
|
+
respond: (json) => {
|
|
89
|
+
ctx.response.type = "json";
|
|
90
|
+
ctx.response.body = json;
|
|
91
|
+
},
|
|
92
|
+
unauthorized: () => {
|
|
93
|
+
ctx.response.status = 401;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
/** Node.js native 'http' and 'https' modules */
|
|
97
|
+
const http = (req, res) => {
|
|
98
|
+
const secretHeaderFromRequest = req.headers[SECRET_HEADER.toLowerCase()];
|
|
99
|
+
return {
|
|
100
|
+
update: new Promise((resolve, reject) => {
|
|
101
|
+
const chunks = [];
|
|
102
|
+
req.on("data", (chunk) => chunks.push(chunk))
|
|
103
|
+
.once("end", () => {
|
|
104
|
+
// @ts-ignore `Buffer` is Node-only
|
|
105
|
+
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
106
|
+
resolve(JSON.parse(raw));
|
|
107
|
+
})
|
|
108
|
+
.once("error", reject);
|
|
109
|
+
}),
|
|
110
|
+
header: Array.isArray(secretHeaderFromRequest)
|
|
111
|
+
? secretHeaderFromRequest[0]
|
|
112
|
+
: secretHeaderFromRequest,
|
|
113
|
+
end: () => res.end(),
|
|
114
|
+
respond: (json) => res
|
|
115
|
+
.writeHead(200, { "Content-Type": "application/json" })
|
|
116
|
+
.end(json),
|
|
117
|
+
unauthorized: () => res.writeHead(401).end(WRONG_TOKEN_ERROR),
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
/** AWS lambda serverless functions */
|
|
121
|
+
const awsLambda = (event, _context, callback) => ({
|
|
122
|
+
update: JSON.parse(event.body),
|
|
123
|
+
header: event.headers[SECRET_HEADER],
|
|
124
|
+
end: () => callback(null, { statusCode: 200 }),
|
|
125
|
+
respond: (json) => callback(null, { statusCode: 200, body: json }),
|
|
126
|
+
unauthorized: () => callback(null, { statusCode: 401 }),
|
|
127
|
+
});
|
|
128
|
+
/** Azure Functions */
|
|
129
|
+
const azure = (context, req) => ({
|
|
130
|
+
update: Promise.resolve(req.body),
|
|
131
|
+
header: context.res.headers[SECRET_HEADER],
|
|
132
|
+
end: () => (context.res = {
|
|
133
|
+
status: 200,
|
|
134
|
+
body: "",
|
|
135
|
+
}),
|
|
136
|
+
respond: (json) => {
|
|
137
|
+
context.res.set("Content-Type", "application/json");
|
|
138
|
+
context.res.send(json);
|
|
139
|
+
},
|
|
140
|
+
unauthorized: () => {
|
|
141
|
+
context.res.send(401, WRONG_TOKEN_ERROR);
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
/** Next.js Serverless Functions */
|
|
145
|
+
const nextJs = (req, res) => ({
|
|
146
|
+
update: Promise.resolve(req.body),
|
|
147
|
+
header: req.headers[SECRET_HEADER],
|
|
148
|
+
end: () => res.end(),
|
|
149
|
+
respond: (json) => res.status(200).json(json),
|
|
150
|
+
unauthorized: () => res.status(401).send(WRONG_TOKEN_ERROR),
|
|
151
|
+
});
|
|
152
|
+
/** Sveltekit Serverless Functions */
|
|
153
|
+
const sveltekit = ({ request }) => {
|
|
154
|
+
let resolveResponse;
|
|
155
|
+
return {
|
|
156
|
+
update: Promise.resolve(request.json()),
|
|
157
|
+
header: request.headers.get(SECRET_HEADER) || undefined,
|
|
158
|
+
end: () => {
|
|
159
|
+
if (resolveResponse)
|
|
160
|
+
resolveResponse(ok());
|
|
161
|
+
},
|
|
162
|
+
respond: (json) => {
|
|
163
|
+
if (resolveResponse)
|
|
164
|
+
resolveResponse(okJson(json));
|
|
165
|
+
},
|
|
166
|
+
unauthorized: () => {
|
|
167
|
+
if (resolveResponse)
|
|
168
|
+
resolveResponse(unauthorized());
|
|
169
|
+
},
|
|
170
|
+
handlerReturn: new Promise((resolve) => {
|
|
171
|
+
resolveResponse = resolve;
|
|
172
|
+
}),
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
/** Native CloudFlare workers (service worker) */
|
|
176
|
+
const cloudflare = (event) => {
|
|
177
|
+
let resolveResponse;
|
|
178
|
+
event.respondWith(new Promise((resolve) => {
|
|
179
|
+
resolveResponse = resolve;
|
|
180
|
+
}));
|
|
181
|
+
return {
|
|
182
|
+
update: event.request.json(),
|
|
183
|
+
header: event.request.headers.get(SECRET_HEADER) || undefined,
|
|
184
|
+
end: () => {
|
|
185
|
+
resolveResponse(ok());
|
|
186
|
+
},
|
|
187
|
+
respond: (json) => {
|
|
188
|
+
resolveResponse(okJson(json));
|
|
189
|
+
},
|
|
190
|
+
unauthorized: () => {
|
|
191
|
+
resolveResponse(unauthorized());
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
/** Native CloudFlare workers (module worker) */
|
|
196
|
+
const cloudflareModule = (request) => {
|
|
197
|
+
let resolveResponse;
|
|
198
|
+
return {
|
|
199
|
+
update: request.json(),
|
|
200
|
+
header: request.headers.get(SECRET_HEADER) || undefined,
|
|
201
|
+
end: () => {
|
|
202
|
+
resolveResponse(ok());
|
|
203
|
+
},
|
|
204
|
+
respond: (json) => {
|
|
205
|
+
resolveResponse(okJson(json));
|
|
206
|
+
},
|
|
207
|
+
unauthorized: () => {
|
|
208
|
+
resolveResponse(unauthorized());
|
|
209
|
+
},
|
|
210
|
+
handlerReturn: new Promise((resolve) => {
|
|
211
|
+
resolveResponse = resolve;
|
|
212
|
+
}),
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
/** hono web framework */
|
|
216
|
+
const hono = (ctx) => {
|
|
217
|
+
let resolveResponse;
|
|
218
|
+
return {
|
|
219
|
+
update: ctx.req.json(),
|
|
220
|
+
header: ctx.req.headers.get(SECRET_HEADER) || undefined,
|
|
221
|
+
end: () => {
|
|
222
|
+
resolveResponse(ctx.body());
|
|
223
|
+
},
|
|
224
|
+
respond: (json) => {
|
|
225
|
+
ctx.header('Content-Type", "application/json');
|
|
226
|
+
resolveResponse(ctx.body(json));
|
|
227
|
+
},
|
|
228
|
+
unauthorized: () => {
|
|
229
|
+
ctx.status(401);
|
|
230
|
+
ctx.statusText(WRONG_TOKEN_ERROR);
|
|
231
|
+
resolveResponse(ctx.body());
|
|
232
|
+
},
|
|
233
|
+
handlerReturn: new Promise((resolve) => {
|
|
234
|
+
resolveResponse = resolve;
|
|
235
|
+
}),
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
/** worktop CloudFlare workers framework */
|
|
239
|
+
const worktop = (req, res) => ({
|
|
240
|
+
update: Promise.resolve(req.body.json()),
|
|
241
|
+
header: req.headers.get(SECRET_HEADER),
|
|
242
|
+
end: () => res.end(),
|
|
243
|
+
respond: (json) => res.send(200, json),
|
|
244
|
+
unauthorized: () => res.send(401, WRONG_TOKEN_ERROR),
|
|
245
|
+
});
|
|
246
|
+
// Please open a PR if you want to add another adapter
|
|
247
|
+
exports.adapters = {
|
|
248
|
+
express,
|
|
249
|
+
koa,
|
|
250
|
+
fastify,
|
|
251
|
+
serveHttp,
|
|
252
|
+
"std/http": stdHttp,
|
|
253
|
+
oak,
|
|
254
|
+
http,
|
|
255
|
+
https: http,
|
|
256
|
+
"aws-lambda": awsLambda,
|
|
257
|
+
azure,
|
|
258
|
+
"next-js": nextJs,
|
|
259
|
+
sveltekit,
|
|
260
|
+
cloudflare,
|
|
261
|
+
"cloudflare-mod": cloudflareModule,
|
|
262
|
+
hono,
|
|
263
|
+
worktop,
|
|
264
|
+
};
|
|
@@ -79,6 +79,22 @@ export interface StorageAdapter<T> {
|
|
|
79
79
|
* Deletes a value for the given key from the storage.
|
|
80
80
|
*/
|
|
81
81
|
delete: (key: string) => MaybePromise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Checks whether a key exists in the storage.
|
|
84
|
+
*/
|
|
85
|
+
has?: (key: string) => MaybePromise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Lists all keys.
|
|
88
|
+
*/
|
|
89
|
+
readAllKeys?: () => Iterable<string> | AsyncIterable<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Lists all values.
|
|
92
|
+
*/
|
|
93
|
+
readAllValues?: () => Iterable<T> | AsyncIterable<T>;
|
|
94
|
+
/**
|
|
95
|
+
* Lists all keys with their values.
|
|
96
|
+
*/
|
|
97
|
+
readAllEntries?: () => Iterable<[key: string, value: T]> | AsyncIterable<[key: string, value: T]>;
|
|
82
98
|
}
|
|
83
99
|
/**
|
|
84
100
|
* Options for session middleware.
|
|
@@ -309,10 +325,13 @@ export declare class MemorySessionStorage<S> implements StorageAdapter<S> {
|
|
|
309
325
|
constructor(timeToLive?: number | undefined);
|
|
310
326
|
read(key: string): S | undefined;
|
|
311
327
|
/**
|
|
312
|
-
*
|
|
313
|
-
* an array.
|
|
328
|
+
* @deprecated Use {@link readAllValues} instead
|
|
314
329
|
*/
|
|
315
330
|
readAll(): S[];
|
|
331
|
+
readAllKeys(): string[];
|
|
332
|
+
readAllValues(): S[];
|
|
333
|
+
readAllEntries(): [string, S][];
|
|
334
|
+
has(key: string): boolean;
|
|
316
335
|
write(key: string, value: S): void;
|
|
317
336
|
delete(key: string): void;
|
|
318
337
|
}
|
|
@@ -406,15 +406,28 @@ class MemorySessionStorage {
|
|
|
406
406
|
return value.session;
|
|
407
407
|
}
|
|
408
408
|
/**
|
|
409
|
-
*
|
|
410
|
-
* an array.
|
|
409
|
+
* @deprecated Use {@link readAllValues} instead
|
|
411
410
|
*/
|
|
412
411
|
readAll() {
|
|
412
|
+
return this.readAllValues();
|
|
413
|
+
}
|
|
414
|
+
readAllKeys() {
|
|
415
|
+
return Array.from(this.storage.keys());
|
|
416
|
+
}
|
|
417
|
+
readAllValues() {
|
|
413
418
|
return Array
|
|
414
419
|
.from(this.storage.keys())
|
|
415
420
|
.map((key) => this.read(key))
|
|
416
421
|
.filter((value) => value !== undefined);
|
|
417
422
|
}
|
|
423
|
+
readAllEntries() {
|
|
424
|
+
return Array.from(this.storage.keys())
|
|
425
|
+
.map((key) => [key, this.read(key)])
|
|
426
|
+
.filter((pair) => pair[1] !== undefined);
|
|
427
|
+
}
|
|
428
|
+
has(key) {
|
|
429
|
+
return this.storage.has(key);
|
|
430
|
+
}
|
|
418
431
|
write(key, value) {
|
|
419
432
|
this.storage.set(key, addExpiryDate(value, this.timeToLive));
|
|
420
433
|
}
|