balebaazoo 1.2.2 → 1.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/README.md CHANGED
@@ -6,7 +6,7 @@ SDK مدرن و type-safe برای توسعهٔ بازو (Bot) در پیام‌
6
6
 
7
7
  بر پایهٔ [مستندات رسمی API بازوی بله](https://docs.bale.ai/) ساخته شده است.
8
8
 
9
- **English:** [README.en.md](README.en.md) · **مستندات فارسی:** [docs/fa/usage.md](docs/fa/usage.md)
9
+ **English:** [README.en.md](README.en.md) · **مستندات فارسی:** [docs/fa/usage.md](docs/fa/usage.md) · **سایت مستندات:** https://hadimardanian.github.io/baleBaazooSDK/fa/usage.html
10
10
 
11
11
  ## نصب
12
12
 
@@ -48,11 +48,13 @@ void bot.start({
48
48
 
49
49
  </div>
50
50
 
51
- > **نکته:** `bot.start()` تا زمان `bot.stop()` ادامه دارد. برای لاگ بعد از راه‌اندازی از `onStart` استفاده کنید، نه `await` بعد از `start()`.
51
+ <blockquote dir="rtl">
52
+ <p><strong>نکته:</strong> تابع <code dir="ltr">bot.start()</code> تا زمان <code dir="ltr">bot.stop()</code> ادامه دارد. برای لاگ بعد از راه‌اندازی از <code dir="ltr">onStart</code> استفاده کنید، نه <code dir="ltr">await</code> بعد از <code dir="ltr">start()</code>.</p>
53
+ </blockquote>
52
54
 
53
55
  ## TypeScript
54
56
 
55
- پروژهٔ شما باید ESM باشد:
57
+ <p dir="rtl">پروژهٔ شما باید <code dir="ltr">ESM</code> باشد:</p>
56
58
 
57
59
  <div dir="ltr">
58
60
 
@@ -79,15 +81,15 @@ void bot.start({
79
81
 
80
82
  </div>
81
83
 
82
- قالب آماده: [`templates/starter`](templates/starter)
84
+ <p dir="rtl">قالب آماده: <a href="templates/starter"><code dir="ltr">templates/starter</code></a></p>
83
85
 
84
86
  ### خطای TS1295
85
87
 
86
- اگر `ECMAScript imports and exports cannot be written in a CommonJS file` دیدید، `"type": "module"` را به `package.json` اضافه کنید.
88
+ <p dir="rtl">اگر خطای <code dir="ltr">ECMAScript imports and exports cannot be written in a CommonJS file</code> دیدید، <code dir="ltr">"type": "module"</code> را به <code dir="ltr">package.json</code> اضافه کنید.</p>
87
89
 
88
- ### خطای TS2322 با `ctx.reply()`
90
+ ### خطای TS2322 با ctx.reply
89
91
 
90
- نسخهٔ `1.0.1+` اجازه می‌دهد handler مستقیماً `ctx.reply()` را return کند:
92
+ <p dir="rtl">نسخهٔ <code dir="ltr">1.0.1+</code> اجازه می‌دهد handler مستقیماً <code dir="ltr">ctx.reply()</code> را return کند:</p>
91
93
 
92
94
  <div dir="ltr">
93
95
 
@@ -113,19 +115,23 @@ bot.command("start", (ctx) => ctx.reply("سلام!"));
113
115
 
114
116
  ## ویژگی‌ها
115
117
 
116
- - **Type-safe** — تایپ‌های کامل TypeScript
117
- - **Middleware** `Composer`، `bot.catch()`، `errorHandler`
118
- - **Lifecycle** `onStart`، `setupGracefulShutdown`، `dropPendingUpdates`، `launch()`
119
- - **Polling و Webhook** long polling با AbortSignal و webhook handler
120
- - **Bale-native**`askReview`، `inquireTransaction`، کیف‌پول
121
- - **بدون dependency** Node.js 18+
118
+ <ul dir="rtl">
119
+ <li><strong>Type-safe</strong>تایپ‌های کامل TypeScript</li>
120
+ <li><strong>Middleware</strong><code dir="ltr">Composer</code>، <code dir="ltr">bot.catch()</code>، <code dir="ltr">errorHandler</code></li>
121
+ <li><strong>Lifecycle</strong> <code dir="ltr">onStart</code>، <code dir="ltr">setupGracefulShutdown</code>، <code dir="ltr">dropPendingUpdates</code>، <code dir="ltr">launch()</code></li>
122
+ <li><strong>Polling و Webhook</strong> long polling با AbortSignal و webhook handler</li>
123
+ <li><strong>Bale-native</strong> <code dir="ltr">askReview</code>، <code dir="ltr">inquireTransaction</code>، کیف‌پول</li>
124
+ <li><strong>بدون dependency</strong> — Node.js 18+</li>
125
+ </ul>
122
126
 
123
127
  ## مثال‌ها
124
128
 
125
- - [`examples/echo-bot.ts`](examples/echo-bot.ts)
126
- - [`examples/inline-keyboard.ts`](examples/inline-keyboard.ts)
127
- - [`examples/payment-bot.ts`](examples/payment-bot.ts)
128
- - [`examples/webhook-server.ts`](examples/webhook-server.ts)
129
+ <ul dir="rtl">
130
+ <li><a href="examples/echo-bot.ts"><code dir="ltr">examples/echo-bot.ts</code></a></li>
131
+ <li><a href="examples/inline-keyboard.ts"><code dir="ltr">examples/inline-keyboard.ts</code></a></li>
132
+ <li><a href="examples/payment-bot.ts"><code dir="ltr">examples/payment-bot.ts</code></a></li>
133
+ <li><a href="examples/webhook-server.ts"><code dir="ltr">examples/webhook-server.ts</code></a></li>
134
+ </ul>
129
135
 
130
136
  ## توسعه
131
137
 
package/dist/index.cjs CHANGED
@@ -416,7 +416,13 @@ var Api = class {
416
416
  return this.call("answerPreCheckoutQuery", asParams(params));
417
417
  }
418
418
  inquireTransaction(params) {
419
- return this.call("inquireTransaction", asParams(params));
419
+ const transactionId = params.transaction_id || params.provider_payment_charge_id;
420
+ if (!transactionId) {
421
+ throw new Error("transaction_id is required for inquireTransaction");
422
+ }
423
+ return this.call("inquireTransaction", {
424
+ transaction_id: transactionId
425
+ });
420
426
  }
421
427
  };
422
428
  function asParams(params) {
@@ -473,6 +479,13 @@ function matchUpdate(update) {
473
479
  const matches = [];
474
480
  if (update.message) {
475
481
  matches.push("message");
482
+ if (update.message.animation) matches.push("message:animation");
483
+ if (update.message.new_chat_members?.length) {
484
+ matches.push("message:new_chat_members");
485
+ }
486
+ if (update.message.left_chat_member) {
487
+ matches.push("message:left_chat_member");
488
+ }
476
489
  if (update.message.text !== void 0) matches.push("message:text");
477
490
  if (update.message.photo) matches.push("message:photo");
478
491
  if (update.message.document) matches.push("message:document");
@@ -532,20 +545,32 @@ function matchesChatType(ctx, chatType) {
532
545
 
533
546
  // src/composer.ts
534
547
  var Composer = class _Composer {
535
- handler;
536
548
  registered = [];
549
+ dirty = true;
550
+ snapshot = [];
551
+ cachedHandler;
537
552
  constructor(...middleware) {
538
553
  this.registered.push(...middleware.map(normalizeMiddleware));
539
- this.handler = async (ctx, next) => {
540
- await runMiddleware(this.registered, ctx);
541
- await next();
542
- };
554
+ this.markDirty();
555
+ }
556
+ markDirty() {
557
+ this.dirty = true;
558
+ this.cachedHandler = void 0;
543
559
  }
544
560
  middleware() {
545
- return this.handler;
561
+ if (!this.cachedHandler || this.dirty) {
562
+ this.snapshot = [...this.registered];
563
+ this.cachedHandler = async (ctx, next) => {
564
+ await runMiddleware(this.snapshot, ctx);
565
+ await next();
566
+ };
567
+ this.dirty = false;
568
+ }
569
+ return this.cachedHandler;
546
570
  }
547
571
  use(...middleware) {
548
572
  this.registered.push(...middleware.map(normalizeMiddleware));
573
+ this.markDirty();
549
574
  return this;
550
575
  }
551
576
  on(filter, ...middleware) {
@@ -569,6 +594,7 @@ var Composer = class _Composer {
569
594
  );
570
595
  });
571
596
  composer.registered.push(...middleware);
597
+ this.markDirty();
572
598
  return this;
573
599
  }
574
600
  command(command, ...middleware) {
@@ -623,6 +649,17 @@ var Composer = class _Composer {
623
649
  ...middleware
624
650
  );
625
651
  }
652
+ static compose(...composers) {
653
+ const result = new _Composer();
654
+ for (const item of composers) {
655
+ if (item instanceof _Composer) {
656
+ result.use(item);
657
+ } else {
658
+ result.use(item);
659
+ }
660
+ }
661
+ return result;
662
+ }
626
663
  };
627
664
  function escapeRegex(value) {
628
665
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -635,6 +672,8 @@ var Context = class {
635
672
  botInfo;
636
673
  callbackQueryAnswered = false;
637
674
  _updateTypes;
675
+ session = {};
676
+ state = {};
638
677
  constructor(options) {
639
678
  this.api = options.api;
640
679
  this.update = options.update;
@@ -733,6 +772,54 @@ var Context = class {
733
772
  const chatId = this.requireChatId();
734
773
  return this.api.sendInvoice({ chat_id: chatId, ...params });
735
774
  }
775
+ targetMessage() {
776
+ return this.callbackQuery?.message ?? this.message;
777
+ }
778
+ async editMessageText(text, extra) {
779
+ const message = this.targetMessage();
780
+ if (!message) {
781
+ throw new Error("No message to edit in context");
782
+ }
783
+ return this.api.editMessageText({
784
+ chat_id: message.chat.id,
785
+ message_id: message.message_id,
786
+ text,
787
+ ...extra
788
+ });
789
+ }
790
+ async deleteMessage(params) {
791
+ const message = this.targetMessage();
792
+ const chatId = params?.chat_id ?? message?.chat.id ?? this.chatId;
793
+ const messageId = params?.message_id ?? message?.message_id;
794
+ if (chatId === void 0 || messageId === void 0) {
795
+ throw new Error("No message to delete in context");
796
+ }
797
+ return this.api.deleteMessage({ chat_id: chatId, message_id: messageId });
798
+ }
799
+ async forwardMessage(toChatId, params) {
800
+ const message = this.targetMessage();
801
+ if (!message && !params?.from_chat_id) {
802
+ throw new Error("No source message in context");
803
+ }
804
+ return this.api.forwardMessage({
805
+ chat_id: toChatId,
806
+ from_chat_id: params?.from_chat_id ?? message.chat.id,
807
+ message_id: params?.message_id ?? message.message_id,
808
+ ...params
809
+ });
810
+ }
811
+ async copyMessage(toChatId, params) {
812
+ const message = this.targetMessage();
813
+ if (!message && !params?.from_chat_id) {
814
+ throw new Error("No source message in context");
815
+ }
816
+ return this.api.copyMessage({
817
+ chat_id: toChatId,
818
+ from_chat_id: params?.from_chat_id ?? message.chat.id,
819
+ message_id: params?.message_id ?? message.message_id,
820
+ ...params
821
+ });
822
+ }
736
823
  async sendChatAction(action) {
737
824
  const chatId = this.requireChatId();
738
825
  return this.api.sendChatAction({ chat_id: chatId, action });
@@ -842,14 +929,7 @@ var PollingRunner = class {
842
929
  { signal }
843
930
  );
844
931
  this.backoffAttempt = 0;
845
- for (const update of updates) {
846
- this.offset = update.update_id + 1;
847
- try {
848
- await bot.handleUpdate(update);
849
- } catch (error) {
850
- onError(error);
851
- }
852
- }
932
+ await this.processUpdates(bot, updates, options, onError);
853
933
  if (updates.length === 0 && signal.aborted) {
854
934
  break;
855
935
  }
@@ -868,6 +948,34 @@ var PollingRunner = class {
868
948
  }
869
949
  }
870
950
  }
951
+ async processUpdates(bot, updates, options, onError) {
952
+ const concurrency = Math.max(1, options.concurrency ?? 1);
953
+ if (concurrency === 1) {
954
+ for (const update of updates) {
955
+ this.offset = update.update_id + 1;
956
+ try {
957
+ await bot.handleUpdate(update);
958
+ } catch (error) {
959
+ onError(error);
960
+ }
961
+ }
962
+ return;
963
+ }
964
+ let index = 0;
965
+ const workers = Array.from({ length: Math.min(concurrency, updates.length) }, async () => {
966
+ while (index < updates.length) {
967
+ const current = index++;
968
+ const update = updates[current];
969
+ this.offset = update.update_id + 1;
970
+ try {
971
+ await bot.handleUpdate(update);
972
+ } catch (error) {
973
+ onError(error);
974
+ }
975
+ }
976
+ });
977
+ await Promise.all(workers);
978
+ }
871
979
  setOffset(offset) {
872
980
  this.offset = offset;
873
981
  }
@@ -935,7 +1043,7 @@ async function webhookFromJson(bot) {
935
1043
  // src/bot.ts
936
1044
  var Bot = class extends Composer {
937
1045
  api;
938
- botInfo;
1046
+ _botInfo;
939
1047
  polling = new PollingRunner();
940
1048
  autoAnswer;
941
1049
  catchHandler;
@@ -943,17 +1051,23 @@ var Bot = class extends Composer {
943
1051
  const api = new Api({ token, ...options });
944
1052
  super();
945
1053
  this.api = api;
946
- this.botInfo = options.botInfo;
1054
+ this._botInfo = options.botInfo;
947
1055
  this.autoAnswer = options.autoAnswerCallback ?? true;
948
1056
  if (this.autoAnswer) {
949
1057
  this.use(autoAnswerCallback());
950
1058
  }
951
1059
  }
1060
+ get telegram() {
1061
+ return this.api;
1062
+ }
1063
+ get botInfo() {
1064
+ return this._botInfo;
1065
+ }
952
1066
  async init() {
953
- if (!this.botInfo) {
954
- this.botInfo = await this.api.getMe();
1067
+ if (!this._botInfo) {
1068
+ this._botInfo = await this.api.getMe();
955
1069
  }
956
- return this.botInfo;
1070
+ return this._botInfo;
957
1071
  }
958
1072
  catch(handler) {
959
1073
  this.catchHandler = handler;
@@ -976,7 +1090,7 @@ var Bot = class extends Composer {
976
1090
  return new Context({
977
1091
  api: this.api,
978
1092
  update,
979
- botInfo: this.botInfo
1093
+ botInfo: this._botInfo
980
1094
  });
981
1095
  }
982
1096
  async start(options = {}) {
@@ -1007,7 +1121,7 @@ var Bot = class extends Composer {
1007
1121
  }
1008
1122
  if (options.onStart) {
1009
1123
  try {
1010
- await options.onStart(this.botInfo);
1124
+ await options.onStart(this._botInfo);
1011
1125
  } catch (error) {
1012
1126
  onError(error);
1013
1127
  return;
@@ -1018,6 +1132,13 @@ var Bot = class extends Composer {
1018
1132
  launch(options) {
1019
1133
  return this.start(options);
1020
1134
  }
1135
+ async switchToPolling(options = {}) {
1136
+ await this.api.deleteWebhook();
1137
+ await this.start({
1138
+ ...options,
1139
+ dropPendingUpdates: options.dropPendingUpdates ?? true
1140
+ });
1141
+ }
1021
1142
  async stop() {
1022
1143
  await this.polling.stop();
1023
1144
  }
@@ -1138,6 +1259,132 @@ function setupGracefulShutdown(bot, options = {}) {
1138
1259
  }
1139
1260
  }
1140
1261
 
1262
+ // src/scenes/index.ts
1263
+ var Scene = class extends Composer {
1264
+ id;
1265
+ enterMiddleware = [];
1266
+ leaveMiddleware = [];
1267
+ constructor(id, ...middleware) {
1268
+ super(...middleware);
1269
+ this.id = id;
1270
+ }
1271
+ enter(...middleware) {
1272
+ this.enterMiddleware.push(...middleware);
1273
+ return this;
1274
+ }
1275
+ leave(...middleware) {
1276
+ this.leaveMiddleware.push(...middleware);
1277
+ return this;
1278
+ }
1279
+ async runEnter(ctx) {
1280
+ await runMiddleware(this.enterMiddleware, ctx);
1281
+ }
1282
+ async runLeave(ctx) {
1283
+ await runMiddleware(this.leaveMiddleware, ctx);
1284
+ }
1285
+ };
1286
+ var Stage = class extends Composer {
1287
+ scenes = /* @__PURE__ */ new Map();
1288
+ register(...scenes) {
1289
+ for (const scene of scenes) {
1290
+ this.scenes.set(scene.id, scene);
1291
+ }
1292
+ return this;
1293
+ }
1294
+ getScene(id) {
1295
+ return this.scenes.get(id);
1296
+ }
1297
+ async enter(ctx, sceneId, state = {}) {
1298
+ const scene = this.scenes.get(sceneId);
1299
+ if (!scene) {
1300
+ throw new Error(`Scene "${sceneId}" is not registered`);
1301
+ }
1302
+ const session2 = ctx.session;
1303
+ const currentId = session2.__scene?.id;
1304
+ if (currentId && currentId !== sceneId) {
1305
+ const current = this.scenes.get(currentId);
1306
+ if (current) {
1307
+ await leaveScene(ctx, current);
1308
+ }
1309
+ }
1310
+ await enterScene(ctx, sceneId, scene, state);
1311
+ }
1312
+ async leave(ctx) {
1313
+ const session2 = ctx.session;
1314
+ const sceneId = session2.__scene?.id;
1315
+ if (!sceneId) return;
1316
+ const scene = this.scenes.get(sceneId);
1317
+ if (scene) {
1318
+ await leaveScene(ctx, scene);
1319
+ }
1320
+ }
1321
+ middleware() {
1322
+ const sceneMiddleware = super.middleware();
1323
+ return async (ctx, next) => {
1324
+ const session2 = ctx.session;
1325
+ const sceneId = session2.__scene?.id;
1326
+ if (!sceneId) {
1327
+ await sceneMiddleware(ctx, next);
1328
+ return;
1329
+ }
1330
+ const scene = this.scenes.get(sceneId);
1331
+ if (!scene) {
1332
+ delete session2.__scene;
1333
+ await sceneMiddleware(ctx, next);
1334
+ return;
1335
+ }
1336
+ await scene.middleware()(ctx, next);
1337
+ };
1338
+ }
1339
+ };
1340
+ async function enterScene(ctx, sceneId, scene, initialState = {}) {
1341
+ const session2 = ctx.session;
1342
+ session2.__scene = { ...initialState, id: sceneId };
1343
+ await scene.runEnter(ctx);
1344
+ }
1345
+ async function leaveScene(ctx, scene) {
1346
+ const session2 = ctx.session;
1347
+ await scene.runLeave(ctx);
1348
+ delete session2.__scene;
1349
+ }
1350
+
1351
+ // src/session/memory.ts
1352
+ function memorySessionStore() {
1353
+ const data = /* @__PURE__ */ new Map();
1354
+ return {
1355
+ get: (key) => data.get(key),
1356
+ set: (key, value) => {
1357
+ data.set(key, value);
1358
+ },
1359
+ delete: (key) => {
1360
+ data.delete(key);
1361
+ }
1362
+ };
1363
+ }
1364
+
1365
+ // src/session/index.ts
1366
+ function session(options) {
1367
+ const store = options.store ?? memorySessionStore();
1368
+ return async (ctx, next) => {
1369
+ const key = options.getSessionKey(ctx);
1370
+ if (!key) {
1371
+ await next();
1372
+ return;
1373
+ }
1374
+ let current = store.get(key) ?? options.defaultSession(ctx);
1375
+ Object.defineProperty(ctx, "session", {
1376
+ configurable: true,
1377
+ enumerable: true,
1378
+ get: () => current,
1379
+ set: (value) => {
1380
+ current = value;
1381
+ }
1382
+ });
1383
+ await next();
1384
+ store.set(key, current);
1385
+ };
1386
+ }
1387
+
1141
1388
  exports.Api = Api;
1142
1389
  exports.BaleAPIError = BaleAPIError;
1143
1390
  exports.BaleError = BaleError;
@@ -1150,23 +1397,29 @@ exports.InlineKeyboard = InlineKeyboard;
1150
1397
  exports.InputFile = InputFile;
1151
1398
  exports.PollingRunner = PollingRunner;
1152
1399
  exports.ReplyKeyboard = ReplyKeyboard;
1400
+ exports.Scene = Scene;
1401
+ exports.Stage = Stage;
1153
1402
  exports.autoAnswerCallback = autoAnswerCallback;
1154
1403
  exports.bold = bold;
1155
1404
  exports.createWebhookHandler = createWebhookHandler;
1405
+ exports.enterScene = enterScene;
1156
1406
  exports.errorHandler = errorHandler;
1157
1407
  exports.extractCommand = extractCommand;
1158
1408
  exports.isFilePath = isFilePath;
1159
1409
  exports.isMiddlewareObject = isMiddlewareObject;
1160
1410
  exports.italic = italic;
1411
+ exports.leaveScene = leaveScene;
1161
1412
  exports.link = link;
1162
1413
  exports.matchUpdate = matchUpdate;
1163
1414
  exports.matchesAnyFilter = matchesAnyFilter;
1164
1415
  exports.matchesChatType = matchesChatType;
1165
1416
  exports.matchesFilter = matchesFilter;
1166
1417
  exports.md = md;
1418
+ exports.memorySessionStore = memorySessionStore;
1167
1419
  exports.normalizeMiddleware = normalizeMiddleware;
1168
1420
  exports.removeKeyboard = removeKeyboard;
1169
1421
  exports.runMiddleware = runMiddleware;
1422
+ exports.session = session;
1170
1423
  exports.setupGracefulShutdown = setupGracefulShutdown;
1171
1424
  exports.spoiler = spoiler;
1172
1425
  exports.webhookFromJson = webhookFromJson;