balebaazoo 1.0.1 → 1.2.1
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.en.md +12 -3
- package/README.md +55 -27
- package/dist/index.cjs +162 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -7
- package/dist/index.d.ts +31 -7
- package/dist/index.js +162 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -41,10 +41,12 @@ Use the ready-made template: [`templates/starter`](templates/starter)
|
|
|
41
41
|
## Quick start
|
|
42
42
|
|
|
43
43
|
```typescript
|
|
44
|
-
import { Bot, InlineKeyboard } from "balebaazoo";
|
|
44
|
+
import { Bot, InlineKeyboard, setupGracefulShutdown } from "balebaazoo";
|
|
45
45
|
|
|
46
46
|
const bot = new Bot(process.env.BOT_TOKEN!);
|
|
47
47
|
|
|
48
|
+
setupGracefulShutdown(bot);
|
|
49
|
+
|
|
48
50
|
bot.command("start", (ctx) =>
|
|
49
51
|
ctx.reply("Hello! Welcome to my bot.", {
|
|
50
52
|
reply_markup: new InlineKeyboard().text("About", "about"),
|
|
@@ -56,14 +58,21 @@ bot.on("callback_query:data", async (ctx) => {
|
|
|
56
58
|
await ctx.reply("More info...");
|
|
57
59
|
});
|
|
58
60
|
|
|
59
|
-
bot.start(
|
|
61
|
+
void bot.start({
|
|
62
|
+
onStart: (me) => console.log(`Running as @${me.username ?? me.id}`),
|
|
63
|
+
onError: (error) => console.error(error),
|
|
64
|
+
});
|
|
60
65
|
```
|
|
61
66
|
|
|
67
|
+
> `bot.start()` blocks until `bot.stop()`. Use `onStart` for post-init logging.
|
|
68
|
+
|
|
62
69
|
## Features
|
|
63
70
|
|
|
64
71
|
- Full Bale Bot API client with TypeScript types
|
|
65
72
|
- Bot framework: middleware, filters, commands, composers
|
|
66
|
-
-
|
|
73
|
+
- Lifecycle: `onStart`, `setupGracefulShutdown`, `dropPendingUpdates`, `launch()`
|
|
74
|
+
- Polling (with AbortSignal) and webhook support
|
|
75
|
+
- `bot.catch()` and `errorHandler` middleware
|
|
67
76
|
- Context shortcuts (`ctx.reply`, `ctx.replyWithPhoto`, ...)
|
|
68
77
|
- Wallet payments, `askReview`, `inquireTransaction`
|
|
69
78
|
- Zero runtime dependencies
|
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vazirmatn@33.0.3/Vazirmatn-font-face.css">
|
|
2
|
+
|
|
3
|
+
<div lang="fa" style="font-family: Vazirmatn, Tahoma, sans-serif;">
|
|
4
|
+
|
|
1
5
|
# balebaazoo
|
|
2
6
|
|
|
3
7
|
SDK مدرن و type-safe برای توسعهٔ بازو (Bot) در پیامرسان بله.
|
|
@@ -19,13 +23,37 @@ SDK مدرن و type-safe برای توسعهٔ بازو (Bot) در پیام
|
|
|
19
23
|
npm install balebaazoo
|
|
20
24
|
```
|
|
21
25
|
|
|
22
|
-
##
|
|
26
|
+
## شروع سریع
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Bot, InlineKeyboard, setupGracefulShutdown } from "balebaazoo";
|
|
30
|
+
|
|
31
|
+
const bot = new Bot(process.env.BOT_TOKEN!);
|
|
32
|
+
|
|
33
|
+
setupGracefulShutdown(bot);
|
|
34
|
+
|
|
35
|
+
bot.command("start", (ctx) =>
|
|
36
|
+
ctx.reply("سلام! به بازو خوش آمدید 👋", {
|
|
37
|
+
reply_markup: new InlineKeyboard().text("درباره ما", "about"),
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
bot.on("callback_query:data", async (ctx) => {
|
|
42
|
+
await ctx.answerCallbackQuery();
|
|
43
|
+
await ctx.reply("اطلاعات بیشتر...");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
void bot.start({
|
|
47
|
+
onStart: (me) => console.log(`Running as @${me.username ?? me.id}`),
|
|
48
|
+
onError: (error) => console.error(error),
|
|
49
|
+
});
|
|
50
|
+
```
|
|
23
51
|
|
|
24
|
-
|
|
52
|
+
> **نکته:** `bot.start()` تا زمان `bot.stop()` ادامه دارد. برای لاگ بعد از راهاندازی از `onStart` استفاده کنید، نه `await` بعد از `start()`.
|
|
25
53
|
|
|
26
|
-
|
|
54
|
+
## TypeScript
|
|
27
55
|
|
|
28
|
-
|
|
56
|
+
پروژهٔ شما باید ESM باشد:
|
|
29
57
|
|
|
30
58
|
```json
|
|
31
59
|
{
|
|
@@ -33,8 +61,6 @@ npm install balebaazoo
|
|
|
33
61
|
}
|
|
34
62
|
```
|
|
35
63
|
|
|
36
|
-
و در `tsconfig.json`:
|
|
37
|
-
|
|
38
64
|
```json
|
|
39
65
|
{
|
|
40
66
|
"compilerOptions": {
|
|
@@ -48,36 +74,36 @@ npm install balebaazoo
|
|
|
48
74
|
|
|
49
75
|
قالب آماده: [`templates/starter`](templates/starter)
|
|
50
76
|
|
|
51
|
-
|
|
77
|
+
### خطای TS1295
|
|
52
78
|
|
|
53
|
-
|
|
54
|
-
import { Bot, InlineKeyboard } from "balebaazoo";
|
|
79
|
+
اگر `ECMAScript imports and exports cannot be written in a CommonJS file` دیدید، `"type": "module"` را به `package.json` اضافه کنید.
|
|
55
80
|
|
|
56
|
-
|
|
81
|
+
### خطای TS2322 با `ctx.reply()`
|
|
57
82
|
|
|
58
|
-
|
|
59
|
-
ctx.reply("سلام! به بازو خوش آمدید 👋", {
|
|
60
|
-
reply_markup: new InlineKeyboard().text("درباره ما", "about"),
|
|
61
|
-
}),
|
|
62
|
-
);
|
|
83
|
+
نسخهٔ `1.0.1+` اجازه میدهد handler مستقیماً `ctx.reply()` را return کند:
|
|
63
84
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
85
|
+
```typescript
|
|
86
|
+
bot.command("start", (ctx) => ctx.reply("سلام!"));
|
|
87
|
+
```
|
|
68
88
|
|
|
69
|
-
|
|
89
|
+
### nodemon + TypeScript
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"watch": ["src"],
|
|
94
|
+
"ext": "ts,json",
|
|
95
|
+
"exec": "tsx src/bot.ts"
|
|
96
|
+
}
|
|
70
97
|
```
|
|
71
98
|
|
|
72
99
|
## ویژگیها
|
|
73
100
|
|
|
74
|
-
- **Type-safe** — تایپهای کامل TypeScript
|
|
75
|
-
- **Middleware** —
|
|
76
|
-
- **
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
79
|
-
- **
|
|
80
|
-
- **بدون dependency** — فقط Node.js 18+ با fetch بومی
|
|
101
|
+
- **Type-safe** — تایپهای کامل TypeScript
|
|
102
|
+
- **Middleware** — `Composer`، `bot.catch()`، `errorHandler`
|
|
103
|
+
- **Lifecycle** — `onStart`، `setupGracefulShutdown`، `dropPendingUpdates`، `launch()`
|
|
104
|
+
- **Polling و Webhook** — long polling با AbortSignal و webhook handler
|
|
105
|
+
- **Bale-native** — `askReview`، `inquireTransaction`، کیفپول
|
|
106
|
+
- **بدون dependency** — Node.js 18+
|
|
81
107
|
|
|
82
108
|
## مثالها
|
|
83
109
|
|
|
@@ -97,3 +123,5 @@ npm test
|
|
|
97
123
|
## لایسنس
|
|
98
124
|
|
|
99
125
|
GPL-3.0-or-later
|
|
126
|
+
|
|
127
|
+
</div>
|
package/dist/index.cjs
CHANGED
|
@@ -94,6 +94,9 @@ async function isFilePath(source) {
|
|
|
94
94
|
if (source.startsWith("http://") || source.startsWith("https://")) {
|
|
95
95
|
return false;
|
|
96
96
|
}
|
|
97
|
+
if (!looksLikePath(source)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
97
100
|
try {
|
|
98
101
|
const info = await promises.stat(source);
|
|
99
102
|
return info.isFile();
|
|
@@ -101,6 +104,9 @@ async function isFilePath(source) {
|
|
|
101
104
|
return false;
|
|
102
105
|
}
|
|
103
106
|
}
|
|
107
|
+
function looksLikePath(source) {
|
|
108
|
+
return source.startsWith("./") || source.startsWith("../") || source.includes("/") || source.includes("\\");
|
|
109
|
+
}
|
|
104
110
|
|
|
105
111
|
// src/api/client.ts
|
|
106
112
|
var DEFAULT_API_BASE = "https://tapi.bale.ai";
|
|
@@ -123,12 +129,15 @@ var Api = class {
|
|
|
123
129
|
methodUrl(method) {
|
|
124
130
|
return `${this.baseUrl}/bot${this.token}/${method}`;
|
|
125
131
|
}
|
|
126
|
-
async call(method, params) {
|
|
132
|
+
async call(method, params, options) {
|
|
127
133
|
let attempt = 0;
|
|
128
134
|
while (true) {
|
|
129
135
|
try {
|
|
130
|
-
return await this.invoke(method, params);
|
|
136
|
+
return await this.invoke(method, params, options);
|
|
131
137
|
} catch (error) {
|
|
138
|
+
if (options?.signal?.aborted || isAbortError(error)) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
132
141
|
if (error instanceof BaleAPIError && error.errorCode === 429 && attempt < this.maxRetries) {
|
|
133
142
|
const retryAfter = (error.parameters?.retry_after ?? 1) * 1e3;
|
|
134
143
|
await sleep(retryAfter);
|
|
@@ -139,7 +148,7 @@ var Api = class {
|
|
|
139
148
|
}
|
|
140
149
|
}
|
|
141
150
|
}
|
|
142
|
-
async invoke(method, params) {
|
|
151
|
+
async invoke(method, params, options) {
|
|
143
152
|
const { body } = await this.prepareBody(params ?? {});
|
|
144
153
|
const headers = {};
|
|
145
154
|
if (!(body instanceof FormData)) {
|
|
@@ -149,7 +158,7 @@ var Api = class {
|
|
|
149
158
|
method: "POST",
|
|
150
159
|
headers,
|
|
151
160
|
body: body instanceof FormData ? body : JSON.stringify(body ?? {})
|
|
152
|
-
});
|
|
161
|
+
}, options);
|
|
153
162
|
}
|
|
154
163
|
async downloadFile(filePath) {
|
|
155
164
|
const url = `${this.fileBaseUrl}/${filePath}`;
|
|
@@ -191,8 +200,8 @@ var Api = class {
|
|
|
191
200
|
}
|
|
192
201
|
return { body: formData, isMultipart: true };
|
|
193
202
|
}
|
|
194
|
-
async request(method, init) {
|
|
195
|
-
const response = await this.fetchWithRetry(this.methodUrl(method), init);
|
|
203
|
+
async request(method, init, options) {
|
|
204
|
+
const response = await this.fetchWithRetry(this.methodUrl(method), init, options);
|
|
196
205
|
const payload = await response.json();
|
|
197
206
|
if (!payload.ok) {
|
|
198
207
|
throw new BaleAPIError(
|
|
@@ -203,12 +212,18 @@ var Api = class {
|
|
|
203
212
|
}
|
|
204
213
|
return payload.result;
|
|
205
214
|
}
|
|
206
|
-
async fetchWithRetry(url, init) {
|
|
215
|
+
async fetchWithRetry(url, init, options) {
|
|
207
216
|
let attempt = 0;
|
|
208
217
|
let lastError;
|
|
209
218
|
while (attempt <= this.maxRetries) {
|
|
219
|
+
if (options?.signal?.aborted) {
|
|
220
|
+
throw new DOMException("The operation was aborted", "AbortError");
|
|
221
|
+
}
|
|
210
222
|
try {
|
|
211
|
-
const response = await this.fetchFn(url,
|
|
223
|
+
const response = await this.fetchFn(url, {
|
|
224
|
+
...init,
|
|
225
|
+
signal: options?.signal
|
|
226
|
+
});
|
|
212
227
|
if (response.status === 429) {
|
|
213
228
|
const retryAfter = Number(response.headers.get("retry-after") ?? "1");
|
|
214
229
|
await sleep(retryAfter * 1e3);
|
|
@@ -223,6 +238,9 @@ var Api = class {
|
|
|
223
238
|
return response;
|
|
224
239
|
} catch (error) {
|
|
225
240
|
lastError = error;
|
|
241
|
+
if (isAbortError(error) || options?.signal?.aborted) {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
226
244
|
if (attempt >= this.maxRetries) {
|
|
227
245
|
break;
|
|
228
246
|
}
|
|
@@ -238,8 +256,8 @@ var Api = class {
|
|
|
238
256
|
getMe() {
|
|
239
257
|
return this.call("getMe");
|
|
240
258
|
}
|
|
241
|
-
getUpdates(params) {
|
|
242
|
-
return this.call("getUpdates", asParams(params));
|
|
259
|
+
getUpdates(params, options) {
|
|
260
|
+
return this.call("getUpdates", asParams(params), options);
|
|
243
261
|
}
|
|
244
262
|
setWebhook(params) {
|
|
245
263
|
return this.call("setWebhook", asParams(params));
|
|
@@ -427,6 +445,9 @@ function appendFormValue(formData, key, value) {
|
|
|
427
445
|
function sleep(ms) {
|
|
428
446
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
429
447
|
}
|
|
448
|
+
function isAbortError(error) {
|
|
449
|
+
return error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted"));
|
|
450
|
+
}
|
|
430
451
|
|
|
431
452
|
// src/middleware/types.ts
|
|
432
453
|
function isMiddlewareObject(value) {
|
|
@@ -497,12 +518,10 @@ function extractCommand(text) {
|
|
|
497
518
|
|
|
498
519
|
// src/filters/query.ts
|
|
499
520
|
function matchesFilter(ctx, filter) {
|
|
500
|
-
|
|
501
|
-
return updateMatches.includes(filter);
|
|
521
|
+
return ctx.updateTypes.includes(filter);
|
|
502
522
|
}
|
|
503
523
|
function matchesAnyFilter(ctx, filters) {
|
|
504
|
-
|
|
505
|
-
return filters.some((filter) => updateMatches.includes(filter));
|
|
524
|
+
return filters.some((filter) => ctx.updateTypes.includes(filter));
|
|
506
525
|
}
|
|
507
526
|
function matchesChatType(ctx, chatType) {
|
|
508
527
|
const chat = ctx.chat;
|
|
@@ -615,6 +634,7 @@ var Context = class {
|
|
|
615
634
|
update;
|
|
616
635
|
botInfo;
|
|
617
636
|
callbackQueryAnswered = false;
|
|
637
|
+
_updateTypes;
|
|
618
638
|
constructor(options) {
|
|
619
639
|
this.api = options.api;
|
|
620
640
|
this.update = options.update;
|
|
@@ -623,6 +643,12 @@ var Context = class {
|
|
|
623
643
|
get updateId() {
|
|
624
644
|
return this.update.update_id;
|
|
625
645
|
}
|
|
646
|
+
get updateTypes() {
|
|
647
|
+
if (!this._updateTypes) {
|
|
648
|
+
this._updateTypes = matchUpdate(this.update);
|
|
649
|
+
}
|
|
650
|
+
return this._updateTypes;
|
|
651
|
+
}
|
|
626
652
|
get message() {
|
|
627
653
|
return this.update.message ?? this.update.edited_message;
|
|
628
654
|
}
|
|
@@ -782,21 +808,14 @@ function autoAnswerCallback() {
|
|
|
782
808
|
}
|
|
783
809
|
};
|
|
784
810
|
}
|
|
785
|
-
function errorHandler(onError) {
|
|
786
|
-
return async (ctx, next) => {
|
|
787
|
-
try {
|
|
788
|
-
await next();
|
|
789
|
-
} catch (error) {
|
|
790
|
-
await onError(error, ctx);
|
|
791
|
-
}
|
|
792
|
-
};
|
|
793
|
-
}
|
|
794
811
|
|
|
795
812
|
// src/runner/polling.ts
|
|
796
813
|
var PollingRunner = class {
|
|
797
814
|
running = false;
|
|
798
815
|
abortController;
|
|
799
816
|
offset = 0;
|
|
817
|
+
loopPromise;
|
|
818
|
+
backoffAttempt = 0;
|
|
800
819
|
async start(bot, options = {}) {
|
|
801
820
|
if (this.running) {
|
|
802
821
|
throw new Error("Polling is already running");
|
|
@@ -807,14 +826,22 @@ var PollingRunner = class {
|
|
|
807
826
|
const onError = options.onError ?? ((error) => {
|
|
808
827
|
console.error("[balebaazoo] polling error:", error);
|
|
809
828
|
});
|
|
829
|
+
this.loopPromise = this.runLoop(bot, options, signal, onError);
|
|
830
|
+
await this.loopPromise;
|
|
831
|
+
}
|
|
832
|
+
async runLoop(bot, options, signal, onError) {
|
|
810
833
|
while (this.running && !signal.aborted) {
|
|
811
834
|
try {
|
|
812
|
-
const updates = await bot.api.getUpdates(
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
835
|
+
const updates = await bot.api.getUpdates(
|
|
836
|
+
{
|
|
837
|
+
offset: this.offset,
|
|
838
|
+
limit: options.limit ?? 100,
|
|
839
|
+
timeout: options.timeout ?? 30,
|
|
840
|
+
allowed_updates: options.allowedUpdates
|
|
841
|
+
},
|
|
842
|
+
{ signal }
|
|
843
|
+
);
|
|
844
|
+
this.backoffAttempt = 0;
|
|
818
845
|
for (const update of updates) {
|
|
819
846
|
this.offset = update.update_id + 1;
|
|
820
847
|
try {
|
|
@@ -827,23 +854,49 @@ var PollingRunner = class {
|
|
|
827
854
|
break;
|
|
828
855
|
}
|
|
829
856
|
} catch (error) {
|
|
830
|
-
if (signal.aborted)
|
|
857
|
+
if (signal.aborted || isAbortError2(error)) {
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
if (error instanceof BaleAPIError && error.errorCode === 401) {
|
|
861
|
+
onError(error);
|
|
862
|
+
this.running = false;
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
831
865
|
onError(error);
|
|
832
|
-
await sleep2(
|
|
866
|
+
await sleep2(backoffDelay(this.backoffAttempt));
|
|
867
|
+
this.backoffAttempt++;
|
|
833
868
|
}
|
|
834
869
|
}
|
|
835
870
|
}
|
|
871
|
+
setOffset(offset) {
|
|
872
|
+
this.offset = offset;
|
|
873
|
+
}
|
|
836
874
|
async stop() {
|
|
837
875
|
this.running = false;
|
|
838
876
|
this.abortController?.abort();
|
|
877
|
+
await this.loopPromise;
|
|
839
878
|
}
|
|
840
879
|
};
|
|
880
|
+
function isAbortError2(error) {
|
|
881
|
+
return error instanceof Error && (error.name === "AbortError" || error.message.includes("aborted"));
|
|
882
|
+
}
|
|
883
|
+
function backoffDelay(attempt) {
|
|
884
|
+
const base = 1e3;
|
|
885
|
+
const max = 3e4;
|
|
886
|
+
const exponential = Math.min(base * 2 ** attempt, max);
|
|
887
|
+
const jitter = Math.random() * 0.3 * exponential;
|
|
888
|
+
return Math.floor(exponential + jitter);
|
|
889
|
+
}
|
|
841
890
|
function sleep2(ms) {
|
|
842
891
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
843
892
|
}
|
|
844
893
|
async function createWebhookHandler(bot, getUpdate, options = {}) {
|
|
845
894
|
await bot.init();
|
|
846
895
|
const maxBodyBytes = options.maxBodyBytes ?? 1024 * 1024;
|
|
896
|
+
const onError = options.onError ?? ((error) => {
|
|
897
|
+
console.error("[balebaazoo] webhook error:", error);
|
|
898
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
899
|
+
});
|
|
847
900
|
return async (request) => {
|
|
848
901
|
if (request.method !== "POST") {
|
|
849
902
|
return new Response("Method Not Allowed", { status: 405 });
|
|
@@ -868,8 +921,8 @@ async function createWebhookHandler(bot, getUpdate, options = {}) {
|
|
|
868
921
|
await bot.handleUpdate(update);
|
|
869
922
|
return new Response("OK", { status: 200 });
|
|
870
923
|
} catch (error) {
|
|
871
|
-
|
|
872
|
-
return new Response("Internal Server Error", { status: 500 });
|
|
924
|
+
const response = onError(error);
|
|
925
|
+
return response ?? new Response("Internal Server Error", { status: 500 });
|
|
873
926
|
}
|
|
874
927
|
};
|
|
875
928
|
}
|
|
@@ -885,6 +938,7 @@ var Bot = class extends Composer {
|
|
|
885
938
|
botInfo;
|
|
886
939
|
polling = new PollingRunner();
|
|
887
940
|
autoAnswer;
|
|
941
|
+
catchHandler;
|
|
888
942
|
constructor(token, options = {}) {
|
|
889
943
|
const api = new Api({ token, ...options });
|
|
890
944
|
super();
|
|
@@ -901,9 +955,22 @@ var Bot = class extends Composer {
|
|
|
901
955
|
}
|
|
902
956
|
return this.botInfo;
|
|
903
957
|
}
|
|
958
|
+
catch(handler) {
|
|
959
|
+
this.catchHandler = handler;
|
|
960
|
+
return this;
|
|
961
|
+
}
|
|
904
962
|
async handleUpdate(update) {
|
|
963
|
+
await this.init();
|
|
905
964
|
const ctx = this.createContext(update);
|
|
906
|
-
|
|
965
|
+
try {
|
|
966
|
+
await this.middleware()(ctx, async () => void 0);
|
|
967
|
+
} catch (error) {
|
|
968
|
+
if (this.catchHandler) {
|
|
969
|
+
await this.catchHandler(error, ctx);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
throw error;
|
|
973
|
+
}
|
|
907
974
|
}
|
|
908
975
|
createContext(update) {
|
|
909
976
|
return new Context({
|
|
@@ -913,9 +980,44 @@ var Bot = class extends Composer {
|
|
|
913
980
|
});
|
|
914
981
|
}
|
|
915
982
|
async start(options = {}) {
|
|
916
|
-
|
|
983
|
+
const onError = options.onError ?? ((error) => {
|
|
984
|
+
console.error("[balebaazoo] polling error:", error);
|
|
985
|
+
});
|
|
986
|
+
try {
|
|
987
|
+
await this.init();
|
|
988
|
+
} catch (error) {
|
|
989
|
+
onError(error);
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
if (options.dropPendingUpdates) {
|
|
993
|
+
try {
|
|
994
|
+
const updates = await this.api.getUpdates({
|
|
995
|
+
offset: -1,
|
|
996
|
+
limit: 1,
|
|
997
|
+
timeout: 0
|
|
998
|
+
});
|
|
999
|
+
if (updates.length > 0) {
|
|
1000
|
+
const last = updates[updates.length - 1];
|
|
1001
|
+
this.polling.setOffset(last.update_id + 1);
|
|
1002
|
+
}
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
onError(error);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (options.onStart) {
|
|
1009
|
+
try {
|
|
1010
|
+
await options.onStart(this.botInfo);
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
onError(error);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
917
1016
|
await this.polling.start(this, options);
|
|
918
1017
|
}
|
|
1018
|
+
launch(options) {
|
|
1019
|
+
return this.start(options);
|
|
1020
|
+
}
|
|
919
1021
|
async stop() {
|
|
920
1022
|
await this.polling.stop();
|
|
921
1023
|
}
|
|
@@ -1012,6 +1114,30 @@ function removeKeyboard() {
|
|
|
1012
1114
|
return { remove_keyboard: true };
|
|
1013
1115
|
}
|
|
1014
1116
|
|
|
1117
|
+
// src/middleware/error-handler.ts
|
|
1118
|
+
function errorHandler(onError) {
|
|
1119
|
+
return async (ctx, next) => {
|
|
1120
|
+
try {
|
|
1121
|
+
await next();
|
|
1122
|
+
} catch (error) {
|
|
1123
|
+
await onError(error, ctx);
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
// src/runner/shutdown.ts
|
|
1129
|
+
function setupGracefulShutdown(bot, options = {}) {
|
|
1130
|
+
const signals = options.signals ?? ["SIGINT", "SIGTERM"];
|
|
1131
|
+
for (const signal of signals) {
|
|
1132
|
+
process.once(signal, () => {
|
|
1133
|
+
void (async () => {
|
|
1134
|
+
await bot.stop();
|
|
1135
|
+
await options.onShutdown?.();
|
|
1136
|
+
})();
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1015
1141
|
exports.Api = Api;
|
|
1016
1142
|
exports.BaleAPIError = BaleAPIError;
|
|
1017
1143
|
exports.BaleError = BaleError;
|
|
@@ -1041,6 +1167,7 @@ exports.md = md;
|
|
|
1041
1167
|
exports.normalizeMiddleware = normalizeMiddleware;
|
|
1042
1168
|
exports.removeKeyboard = removeKeyboard;
|
|
1043
1169
|
exports.runMiddleware = runMiddleware;
|
|
1170
|
+
exports.setupGracefulShutdown = setupGracefulShutdown;
|
|
1044
1171
|
exports.spoiler = spoiler;
|
|
1045
1172
|
exports.webhookFromJson = webhookFromJson;
|
|
1046
1173
|
//# sourceMappingURL=index.cjs.map
|