chz-telegram-bot 0.0.25 → 0.0.27

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.
Files changed (65) hide show
  1. package/dist/entities/actions/commandAction.d.ts +3 -3
  2. package/dist/entities/actions/commandAction.d.ts.map +1 -1
  3. package/dist/entities/actions/commandAction.js +12 -7
  4. package/dist/entities/actions/scheduledAction.d.ts +2 -2
  5. package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
  6. package/dist/entities/botInstance.d.ts.map +1 -1
  7. package/dist/entities/botInstance.js +3 -2
  8. package/dist/entities/context/chatContext.d.ts +3 -2
  9. package/dist/entities/context/chatContext.d.ts.map +1 -1
  10. package/dist/entities/context/chatContext.js +6 -6
  11. package/dist/entities/context/messageContext.d.ts +3 -1
  12. package/dist/entities/context/messageContext.d.ts.map +1 -1
  13. package/dist/entities/context/messageContext.js +19 -11
  14. package/dist/entities/responses/imageMessage.d.ts +3 -2
  15. package/dist/entities/responses/imageMessage.d.ts.map +1 -1
  16. package/dist/entities/responses/imageMessage.js +2 -2
  17. package/dist/entities/responses/reaction.d.ts +3 -2
  18. package/dist/entities/responses/reaction.d.ts.map +1 -1
  19. package/dist/entities/responses/reaction.js +2 -2
  20. package/dist/entities/responses/textMessage.d.ts +3 -2
  21. package/dist/entities/responses/textMessage.d.ts.map +1 -1
  22. package/dist/entities/responses/textMessage.js +2 -2
  23. package/dist/entities/responses/unpin.d.ts +3 -2
  24. package/dist/entities/responses/unpin.d.ts.map +1 -1
  25. package/dist/entities/responses/unpin.js +2 -2
  26. package/dist/entities/responses/videoMessage.d.ts +3 -2
  27. package/dist/entities/responses/videoMessage.d.ts.map +1 -1
  28. package/dist/entities/responses/videoMessage.js +2 -2
  29. package/dist/helpers/builders/commandActionBuilder.d.ts +3 -3
  30. package/dist/helpers/builders/commandActionBuilder.d.ts.map +1 -1
  31. package/dist/services/jsonFileStorage.d.ts +6 -7
  32. package/dist/services/jsonFileStorage.d.ts.map +1 -1
  33. package/dist/services/jsonFileStorage.js +29 -23
  34. package/dist/services/logger.d.ts.map +1 -1
  35. package/dist/services/logger.js +2 -8
  36. package/dist/services/taskScheduler.d.ts.map +1 -1
  37. package/dist/services/taskScheduler.js +0 -1
  38. package/dist/services/telegramApi.d.ts +1 -1
  39. package/dist/services/telegramApi.d.ts.map +1 -1
  40. package/dist/services/telegramApi.js +6 -9
  41. package/dist/types/actionWithState.d.ts +4 -1
  42. package/dist/types/actionWithState.d.ts.map +1 -1
  43. package/dist/types/response.d.ts +2 -1
  44. package/dist/types/response.d.ts.map +1 -1
  45. package/dist/types/storage.d.ts +4 -4
  46. package/dist/types/storage.d.ts.map +1 -1
  47. package/entities/actions/commandAction.ts +20 -12
  48. package/entities/actions/scheduledAction.ts +3 -3
  49. package/entities/botInstance.ts +4 -5
  50. package/entities/context/chatContext.ts +9 -15
  51. package/entities/context/messageContext.ts +30 -27
  52. package/entities/responses/imageMessage.ts +4 -3
  53. package/entities/responses/reaction.ts +4 -3
  54. package/entities/responses/textMessage.ts +4 -3
  55. package/entities/responses/unpin.ts +4 -3
  56. package/entities/responses/videoMessage.ts +4 -3
  57. package/helpers/builders/commandActionBuilder.ts +3 -3
  58. package/package.json +1 -1
  59. package/services/jsonFileStorage.ts +41 -27
  60. package/services/logger.ts +6 -8
  61. package/services/taskScheduler.ts +0 -1
  62. package/services/telegramApi.ts +9 -18
  63. package/types/actionWithState.ts +3 -1
  64. package/types/response.ts +3 -1
  65. package/types/storage.ts +4 -4
@@ -5,10 +5,9 @@ const fs_1 = require("fs");
5
5
  const path_1 = require("path");
6
6
  const promises_1 = require("fs/promises");
7
7
  const async_sema_1 = require("async-sema");
8
- const actionStateBase_1 = require("../entities/states/actionStateBase");
9
8
  class JsonFileStorage {
10
- constructor(botName, path) {
11
- this.semaphore = new async_sema_1.Sema(1);
9
+ constructor(botName, actions, path) {
10
+ this.locks = new Map();
12
11
  this.cache = new Map();
13
12
  this.botName = botName;
14
13
  this.storagePath = path ?? 'storage';
@@ -17,14 +16,21 @@ class JsonFileStorage {
17
16
  recursive: true
18
17
  });
19
18
  }
19
+ for (const action of actions) {
20
+ this.locks.set(action.key, new async_sema_1.Sema(1));
21
+ }
20
22
  }
21
- async lock(action) {
22
- await this.semaphore.acquire();
23
+ async lock(key, action) {
24
+ const lock = this.locks.get(key);
25
+ if (!lock) {
26
+ throw new Error(`Lock for action ${key} not found`);
27
+ }
28
+ await lock.acquire();
23
29
  try {
24
30
  return await action();
25
31
  }
26
32
  finally {
27
- this.semaphore.release();
33
+ lock.release();
28
34
  }
29
35
  }
30
36
  async loadInternal(key) {
@@ -54,26 +60,24 @@ class JsonFileStorage {
54
60
  return `${this.storagePath}/${this.botName}/${key.replaceAll(':', '/')}.json`;
55
61
  }
56
62
  async load(key) {
57
- return await this.lock(async () => {
63
+ return await this.lock(key, async () => {
58
64
  return this.loadInternal(key);
59
65
  });
60
66
  }
61
67
  async saveMetadata(actions, botName) {
62
- return await this.lock(async () => {
63
- const targetPath = this.buidPathFromKey(`Metadata-${botName}`);
64
- await (0, promises_1.writeFile)(targetPath, JSON.stringify(actions), {
65
- flag: 'w+'
66
- });
68
+ const targetPath = this.buidPathFromKey(`Metadata-${botName}`);
69
+ await (0, promises_1.writeFile)(targetPath, JSON.stringify(actions), {
70
+ flag: 'w+'
67
71
  });
68
72
  }
69
- async getActionState(entity, chatId) {
70
- return await this.lock(async () => {
71
- const data = await this.loadInternal(entity.key);
72
- return Object.assign(entity.stateConstructor(), data[chatId]);
73
+ async getActionState(action, chatId) {
74
+ return await this.lock(action.key, async () => {
75
+ const data = await this.loadInternal(action.key);
76
+ return Object.assign(action.stateConstructor(), data[chatId]);
73
77
  });
74
78
  }
75
79
  async saveActionExecutionResult(action, chatId, transactionResult) {
76
- await this.lock(async () => {
80
+ await this.lock(action.key, async () => {
77
81
  const data = await this.loadInternal(action.key);
78
82
  if (transactionResult.shouldUpdate) {
79
83
  data[chatId] = transactionResult.data;
@@ -82,14 +86,16 @@ class JsonFileStorage {
82
86
  });
83
87
  }
84
88
  async close() {
85
- await this.semaphore.acquire();
89
+ for (const lock of this.locks.values()) {
90
+ await lock.acquire();
91
+ }
86
92
  }
87
- async updateStateFor(sourceActionKey, chatId, update) {
88
- await this.lock(async () => {
89
- const data = await this.loadInternal(sourceActionKey);
90
- const state = Object.assign(new actionStateBase_1.ActionStateBase(), data[chatId]);
93
+ async updateStateFor(action, chatId, update) {
94
+ await this.lock(action.key, async () => {
95
+ const data = await this.loadInternal(action.key);
96
+ const state = Object.assign(action.stateConstructor(), data[chatId]);
91
97
  await update(state);
92
- await this.save(data, sourceActionKey);
98
+ await this.save(data, action.key);
93
99
  });
94
100
  }
95
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../services/logger.ts"],"names":[],"mappings":"AAAA,cAAM,UAAU;IAEZ,OAAO,CAAC,cAAc;IAQtB,cAAc,CACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM;IAKhB,gBAAgB,CAAC,KAAK,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EACjB,SAAS,CAAC,EAAE,KAAK,GAAG,SAAS;CAYpC;AAED,eAAO,MAAM,MAAM,YAAmB,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../services/logger.ts"],"names":[],"mappings":"AAAA,cAAM,UAAU;IAEZ,OAAO,CAAC,cAAc;IAQtB,cAAc,CACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM;IAOhB,gBAAgB,CAAC,KAAK,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EACjB,SAAS,CAAC,EAAE,KAAK,GAAG,SAAS;CAQpC;AAED,eAAO,MAAM,MAAM,YAAmB,CAAC"}
@@ -11,16 +11,10 @@ class JsonLogger {
11
11
  return JSON.stringify(plainObject);
12
12
  }
13
13
  logWithTraceId(botName, traceId, chatName, text) {
14
- console.log(JSON.stringify({ botName, traceId, chatName, text }));
14
+ console.log(`{"botName":"${botName}","traceId":"${traceId}","chatName":"${chatName}","text":"${text}"}`);
15
15
  }
16
16
  errorWithTraceId(botName, traceId, chatName, errorObj, extraData) {
17
- console.error(JSON.stringify({
18
- botName,
19
- traceId,
20
- chatName,
21
- error: this.serializeError(errorObj),
22
- extraData
23
- }));
17
+ console.error(`{"botName":"${botName}","traceId":"${traceId}","chatName":"${chatName}","error":${this.serializeError(errorObj)}${extraData ? `,"extraData":${JSON.stringify(extraData)}` : ''}}`);
24
18
  }
25
19
  }
26
20
  exports.Logger = new JsonLogger();
@@ -1 +1 @@
1
- {"version":3,"file":"taskScheduler.d.ts","sourceRoot":"","sources":["../../services/taskScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,cAAM,aAAa;IACf,WAAW,EAAE,UAAU,EAAE,CAAM;IAE/B,OAAO;IAMP,UAAU,CACN,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,IAAI,EAClB,QAAQ,EAAE,YAAY,EACtB,gBAAgB,EAAE,OAAO,EACzB,SAAS,EAAE,MAAM;IAoBrB,iBAAiB,CACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,IAAI,EAClB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM;CAoBxB;AAED,eAAO,MAAM,SAAS,eAAsB,CAAC"}
1
+ {"version":3,"file":"taskScheduler.d.ts","sourceRoot":"","sources":["../../services/taskScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,cAAM,aAAa;IACf,WAAW,EAAE,UAAU,EAAE,CAAM;IAE/B,OAAO;IAMP,UAAU,CACN,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,IAAI,EAClB,QAAQ,EAAE,YAAY,EACtB,gBAAgB,EAAE,OAAO,EACzB,SAAS,EAAE,MAAM;IAmBrB,iBAAiB,CACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,IAAI,EAClB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM;CAoBxB;AAED,eAAO,MAAM,SAAS,eAAsB,CAAC"}
@@ -13,7 +13,6 @@ class TaskScheduler {
13
13
  });
14
14
  }
15
15
  createTask(name, action, interval, executeRightAway, ownerName) {
16
- executeRightAway = executeRightAway ?? false;
17
16
  const taskId = setInterval(action, interval);
18
17
  const task = new taskRecord_1.TaskRecord(name, taskId, interval);
19
18
  if (executeRightAway) {
@@ -14,7 +14,7 @@ import { IActionState } from '../types/actionState';
14
14
  import { CommandAction } from '../entities/actions/commandAction';
15
15
  export declare class TelegramApiService {
16
16
  isFlushing: boolean;
17
- messageQueue: Array<BotResponse>;
17
+ messageQueue: BotResponse[];
18
18
  botName: string;
19
19
  telegram: Telegram;
20
20
  chats: Record<number, string>;
@@ -1 +1 @@
1
- {"version":3,"file":"telegramApi.d.ts","sourceRoot":"","sources":["../../services/telegramApi.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAiB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAElE,qBAAa,kBAAkB;IAC3B,UAAU,UAAS;IACnB,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,CAAM;IAEtC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,cAAc,CAAC;gBAGpB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQ3B,cAAc;YA2BN,WAAW;YAqBX,eAAe;IA2E7B,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,eAAe;IAQvB,uBAAuB,CAAC,YAAY,SAAS,YAAY,EACrD,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC;IAsBxC,oBAAoB,CAAC,YAAY,SAAS,YAAY,EAClD,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,eAAe,CAAC,YAAY,CAAC;CAYrD;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,KAAK,IAAI,CAAC;IACvE,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,CAAC,YAAY,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD"}
1
+ {"version":3,"file":"telegramApi.d.ts","sourceRoot":"","sources":["../../services/telegramApi.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAiB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAIlE,qBAAa,kBAAkB;IAC3B,UAAU,UAAS;IACnB,YAAY,EAAE,WAAW,EAAE,CAAM;IAEjC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,cAAc,CAAC;gBAGpB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQ3B,cAAc;YA2BN,WAAW;YAqBX,eAAe;IA2E7B,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,eAAe;IAQvB,uBAAuB,CAAC,YAAY,SAAS,YAAY,EACrD,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC;IAWxC,oBAAoB,CAAC,YAAY,SAAS,YAAY,EAClD,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,eAAe,CAAC,YAAY,CAAC;CAYrD;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,CAAC,QAAQ,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,KAAK,IAAI,CAAC;IACvE,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,CAAC,YAAY,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD"}
@@ -6,6 +6,7 @@ const messageContext_1 = require("../entities/context/messageContext");
6
6
  const reverseRecord_1 = require("../helpers/reverseRecord");
7
7
  const logger_1 = require("./logger");
8
8
  const promises_1 = require("timers/promises");
9
+ const TELEGRAM_RATELIMIT_DELAY = 35;
9
10
  class TelegramApiService {
10
11
  constructor(botName, telegram, storage, chats) {
11
12
  this.isFlushing = false;
@@ -25,7 +26,7 @@ class TelegramApiService {
25
26
  break;
26
27
  try {
27
28
  await this.processResponse(message);
28
- await (0, promises_1.setTimeout)(100);
29
+ await (0, promises_1.setTimeout)(TELEGRAM_RATELIMIT_DELAY);
29
30
  }
30
31
  catch (error) {
31
32
  logger_1.Logger.errorWithTraceId(this.botName, message.traceId, this.chats[message.chatId], error, message);
@@ -36,7 +37,7 @@ class TelegramApiService {
36
37
  async pinIfShould(response, sentMessage) {
37
38
  if (response.shouldPin) {
38
39
  await this.telegram.pinChatMessage(response.chatId, sentMessage.message_id, { disable_notification: true });
39
- await this.storage.updateStateFor(response.sourceActionKey, response.chatId, async (state) => {
40
+ await this.storage.updateStateFor(response.action, response.chatId, async (state) => {
40
41
  state.pinnedMessages.push(sentMessage.message_id);
41
42
  });
42
43
  }
@@ -77,7 +78,7 @@ class TelegramApiService {
77
78
  return;
78
79
  case 'unpin':
79
80
  await this.telegram.unpinChatMessage(response.chatId, response.messageId);
80
- await this.storage.updateStateFor(response.sourceActionKey, response.chatId, async (state) => {
81
+ await this.storage.updateStateFor(response.action, response.chatId, async (state) => {
81
82
  state.pinnedMessages = state.pinnedMessages.filter((x) => x != response.messageId);
82
83
  });
83
84
  break;
@@ -94,14 +95,10 @@ class TelegramApiService {
94
95
  };
95
96
  }
96
97
  createContextForMessage(incomingMessage, command) {
97
- const firstName = incomingMessage.from?.first_name ?? 'Unknown user';
98
- const lastName = incomingMessage.from?.last_name
99
- ? ` ${incomingMessage.from?.last_name}`
100
- : '';
101
- return new messageContext_1.MessageContext(this.botName, command.key, this.getInteractions(), incomingMessage.chat.id, incomingMessage.chatName, incomingMessage.message_id, incomingMessage.text, incomingMessage.from?.id, incomingMessage.traceId, firstName + lastName, this.storage);
98
+ return new messageContext_1.MessageContext(this.botName, command, this.getInteractions(), incomingMessage, this.storage);
102
99
  }
103
100
  createContextForChat(chatId, scheduledAction) {
104
- return new chatContext_1.ChatContext(this.botName, scheduledAction.key, this.getInteractions(), chatId, this.chats[chatId], `Scheduled:${scheduledAction.key}:${chatId}`, this.storage);
101
+ return new chatContext_1.ChatContext(this.botName, scheduledAction, this.getInteractions(), chatId, this.chats[chatId], `Scheduled:${scheduledAction.key}:${chatId}`, this.storage);
105
102
  }
106
103
  }
107
104
  exports.TelegramApiService = TelegramApiService;
@@ -1,6 +1,9 @@
1
1
  import { IActionState } from './actionState';
2
+ export type ActionKey = string & {
3
+ __brand: 'actionKey';
4
+ };
2
5
  export interface IActionWithState {
3
- key: string;
6
+ key: ActionKey;
4
7
  stateConstructor: () => IActionState;
5
8
  }
6
9
  //# sourceMappingURL=actionWithState.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"actionWithState.d.ts","sourceRoot":"","sources":["../../types/actionWithState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,YAAY,CAAC;CACxC"}
1
+ {"version":3,"file":"actionWithState.d.ts","sourceRoot":"","sources":["../../types/actionWithState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,gBAAgB,EAAE,MAAM,YAAY,CAAC;CACxC"}
@@ -3,6 +3,7 @@ import { Reaction } from '../entities/responses/reaction';
3
3
  import { TextMessage } from '../entities/responses/textMessage';
4
4
  import { UnpinResponse } from '../entities/responses/unpin';
5
5
  import { VideoMessage } from '../entities/responses/videoMessage';
6
+ import { IActionWithState } from './actionWithState';
6
7
  export declare const BotResponseTypes: {
7
8
  readonly unpin: "unpin";
8
9
  readonly text: "text";
@@ -15,7 +16,7 @@ export interface IChatResponse {
15
16
  kind: keyof typeof BotResponseTypes;
16
17
  chatId: number;
17
18
  traceId: number | string;
18
- sourceActionKey: string;
19
+ action: IActionWithState;
19
20
  }
20
21
  export interface IReplyMessage<TType> extends IChatResponse {
21
22
  content: TType;
@@ -1 +1 @@
1
- {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../types/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAElE,eAAO,MAAM,gBAAgB;;;;;;CAMnB,CAAC;AAEX,MAAM,MAAM,WAAW,GACjB,aAAa,GACb,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,YAAY,CAAC;AAEnB,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,OAAO,gBAAgB,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,CAAE,SAAQ,aAAa;IACvD,OAAO,EAAE,KAAK,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;CACtB"}
1
+ {"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../types/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,gBAAgB;;;;;;CAMnB,CAAC;AAEX,MAAM,MAAM,WAAW,GACjB,aAAa,GACb,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,YAAY,CAAC;AAEnB,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,OAAO,gBAAgB,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB,MAAM,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,CAAE,SAAQ,aAAa;IACvD,OAAO,EAAE,KAAK,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;CACtB"}
@@ -1,12 +1,12 @@
1
1
  import { ActionExecutionResult } from '../entities/actionExecutionResult';
2
2
  import { IActionState } from './actionState';
3
- import { IActionWithState } from './actionWithState';
3
+ import { ActionKey, IActionWithState } from './actionWithState';
4
4
  export interface IStorageClient {
5
- updateStateFor<TActionState extends IActionState>(sourceActionKey: string, chatId: number, update: (state: TActionState) => Promise<void>): Promise<void>;
5
+ updateStateFor<TActionState extends IActionState>(action: IActionWithState, chatId: number, update: (state: TActionState) => Promise<void>): Promise<void>;
6
6
  close(): Promise<void>;
7
- load<TActionState extends IActionState>(key: string): Promise<Record<number, TActionState>>;
7
+ load<TActionState extends IActionState>(key: ActionKey): Promise<Record<number, TActionState>>;
8
8
  saveMetadata(actions: IActionWithState[], botName: string): Promise<void>;
9
- getActionState<TActionState extends IActionState>(entity: IActionWithState, chatId: number): Promise<TActionState>;
9
+ getActionState<TActionState extends IActionState>(action: IActionWithState, chatId: number): Promise<TActionState>;
10
10
  saveActionExecutionResult(action: IActionWithState, chatId: number, transactionResult: ActionExecutionResult): Promise<void>;
11
11
  }
12
12
  //# sourceMappingURL=storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,cAAc;IAC3B,cAAc,CAAC,YAAY,SAAS,YAAY,EAC5C,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,YAAY,SAAS,YAAY,EAClC,GAAG,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IACzC,YAAY,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,cAAc,CAAC,YAAY,SAAS,YAAY,EAC5C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CAAC;IACzB,yBAAyB,CACrB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,qBAAqB,GACzC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC3B,cAAc,CAAC,YAAY,SAAS,YAAY,EAC5C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,YAAY,SAAS,YAAY,EAClC,GAAG,EAAE,SAAS,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IACzC,YAAY,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,cAAc,CAAC,YAAY,SAAS,YAAY,EAC5C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CAAC;IACzB,yBAAyB,CACrB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,qBAAqB,GACzC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB"}
@@ -5,7 +5,7 @@ import { Seconds } from '../../types/timeValues';
5
5
  import { secondsToMilliseconds } from '../../helpers/timeConvertions';
6
6
  import { toArray } from '../../helpers/toArray';
7
7
  import { IActionState } from '../../types/actionState';
8
- import { IActionWithState } from '../../types/actionWithState';
8
+ import { IActionWithState, ActionKey } from '../../types/actionWithState';
9
9
  import { CommandTriggerCheckResult } from '../commandTriggerCheckResult';
10
10
  import { MessageContext } from '../context/messageContext';
11
11
  import { Logger } from '../../services/logger';
@@ -23,16 +23,16 @@ export class CommandAction<TActionState extends IActionState>
23
23
  allowedUsers: number[];
24
24
  condition: CommandCondition<TActionState>;
25
25
  stateConstructor: () => TActionState;
26
- key: string;
26
+ key: ActionKey;
27
27
 
28
28
  constructor(
29
- trigger: string | RegExp | Array<string> | Array<RegExp>,
29
+ trigger: string | RegExp | string[] | RegExp[],
30
30
  handler: CommandHandler<TActionState>,
31
31
  name: string,
32
32
  active: boolean,
33
33
  cooldown: Seconds,
34
- chatsBlacklist: Array<number>,
35
- allowedUsers: Array<number>,
34
+ chatsBlacklist: number[],
35
+ allowedUsers: number[],
36
36
  condition: CommandCondition<TActionState>,
37
37
  stateConstructor: () => TActionState
38
38
  ) {
@@ -46,7 +46,7 @@ export class CommandAction<TActionState extends IActionState>
46
46
  this.condition = condition;
47
47
  this.stateConstructor = stateConstructor;
48
48
 
49
- this.key = `command:${this.name.replace('.', '-')}`;
49
+ this.key = `command:${this.name.replace('.', '-')}` as ActionKey;
50
50
  }
51
51
 
52
52
  async exec(ctx: MessageContext<TActionState>) {
@@ -122,14 +122,22 @@ export class CommandAction<TActionState extends IActionState>
122
122
  if (typeof trigger == 'string') {
123
123
  shouldTrigger = ctx.messageText.toLowerCase() == trigger;
124
124
  } else {
125
- let matchCount = ctx.messageText.match(trigger)?.length ?? 0;
126
- if (matchCount > 0) {
127
- for (; matchCount > 0; matchCount--) {
128
- const execResult = trigger.exec(ctx.messageText);
129
- if (execResult) matchResults.push(execResult);
125
+ trigger.lastIndex = 0;
126
+
127
+ const execResult = trigger.exec(ctx.messageText);
128
+ if (execResult != null) {
129
+ matchResults.push(execResult);
130
+
131
+ if (trigger.global) {
132
+ while (true) {
133
+ const nextResult = trigger.exec(ctx.messageText);
134
+ if (nextResult == null) break;
135
+ matchResults.push(nextResult);
136
+ }
130
137
  }
131
- shouldTrigger = true;
132
138
  }
139
+
140
+ shouldTrigger = matchResults.length > 0;
133
141
  }
134
142
  }
135
143
 
@@ -4,7 +4,7 @@ import { ScheduledHandler } from '../../types/handlers';
4
4
  import { hoursToMilliseconds } from '../../helpers/timeConvertions';
5
5
  import { HoursOfDay } from '../../types/timeValues';
6
6
  import { IActionState } from '../../types/actionState';
7
- import { IActionWithState } from '../../types/actionWithState';
7
+ import { IActionWithState, ActionKey } from '../../types/actionWithState';
8
8
  import { CachedStateFactory } from '../cachedStateFactory';
9
9
  import { ChatContext } from '../context/chatContext';
10
10
  import { ActionExecutionResult } from '../actionExecutionResult';
@@ -20,7 +20,7 @@ export class ScheduledAction<TActionState extends IActionState>
20
20
  timeinHours: HoursOfDay;
21
21
  active: boolean;
22
22
  chatsWhitelist: number[];
23
- key: string;
23
+ key: ActionKey;
24
24
 
25
25
  cachedState = new Map<string, unknown>();
26
26
  stateConstructor: () => TActionState;
@@ -42,7 +42,7 @@ export class ScheduledAction<TActionState extends IActionState>
42
42
  this.active = active;
43
43
  this.chatsWhitelist = whitelist;
44
44
  this.cachedStateFactories = cachedStateFactories;
45
- this.key = `scheduled:${this.name.replace('.', '-')}`;
45
+ this.key = `scheduled:${this.name.replace('.', '-')}` as ActionKey;
46
46
  this.stateConstructor = stateConstructor;
47
47
  }
48
48
 
@@ -35,6 +35,8 @@ export class BotInstance {
35
35
  this.scheduled = options.scheduled;
36
36
  this.chats = options.chats;
37
37
 
38
+ const actions = [...this.commands, ...this.scheduled];
39
+
38
40
  Logger.logWithTraceId(
39
41
  this.name,
40
42
  `System:Bot-${this.name}-Start`,
@@ -44,7 +46,7 @@ export class BotInstance {
44
46
  this.telegraf = new Telegraf(options.token);
45
47
  this.storage =
46
48
  options.storageClient ??
47
- new JsonFileStorage(options.name, options.storagePath);
49
+ new JsonFileStorage(options.name, actions, options.storagePath);
48
50
  this.api = new TelegramApiService(
49
51
  this.name,
50
52
  this.telegraf.telegram,
@@ -55,10 +57,7 @@ export class BotInstance {
55
57
  this.initializeMessageProcessing();
56
58
  this.initializeScheduledProcessing();
57
59
 
58
- this.storage.saveMetadata(
59
- [...this.commands, ...this.scheduled],
60
- this.name
61
- );
60
+ this.storage.saveMetadata(actions, this.name);
62
61
 
63
62
  this.telegraf.launch();
64
63
  }
@@ -9,12 +9,13 @@ import {
9
9
  MessageSendingOptions,
10
10
  TextMessageSendingOptions
11
11
  } from '../../types/messageSendingOptions';
12
+ import { IActionWithState } from '../../types/actionWithState';
12
13
 
13
14
  /**
14
15
  * Context of action executed in chat.
15
16
  */
16
17
  export class ChatContext<TActionState> {
17
- protected actionKey: string;
18
+ protected action: IActionWithState;
18
19
  protected interactions: IBotApiInteractions;
19
20
  updateActions: Array<(state: TActionState) => void> = [];
20
21
  /** Trace id of a action execution. */
@@ -30,7 +31,7 @@ export class ChatContext<TActionState> {
30
31
 
31
32
  constructor(
32
33
  botName: string,
33
- actionKey: string,
34
+ action: IActionWithState,
34
35
  interactions: IBotApiInteractions,
35
36
  chatId: number,
36
37
  chatName: string,
@@ -38,7 +39,7 @@ export class ChatContext<TActionState> {
38
39
  storage: IStorageClient
39
40
  ) {
40
41
  this.botName = botName;
41
- this.actionKey = actionKey;
42
+ this.action = action;
42
43
  this.interactions = interactions;
43
44
  this.chatId = chatId;
44
45
  this.chatName = chatName;
@@ -51,9 +52,7 @@ export class ChatContext<TActionState> {
51
52
  * @param stateUpdateAction Function that will modify state.
52
53
  */
53
54
  updateState(stateUpdateAction: (state: TActionState) => void) {
54
- this.updateActions.push(
55
- stateUpdateAction as (state: TActionState) => void
56
- );
55
+ this.updateActions.push(stateUpdateAction);
57
56
  }
58
57
 
59
58
  /**
@@ -68,7 +67,7 @@ export class ChatContext<TActionState> {
68
67
  this.chatId,
69
68
  undefined,
70
69
  this.traceId,
71
- this.actionKey,
70
+ this.action,
72
71
  options
73
72
  )
74
73
  );
@@ -87,7 +86,7 @@ export class ChatContext<TActionState> {
87
86
  this.chatId,
88
87
  undefined,
89
88
  this.traceId,
90
- this.actionKey,
89
+ this.action,
91
90
  options
92
91
  )
93
92
  );
@@ -106,7 +105,7 @@ export class ChatContext<TActionState> {
106
105
  this.chatId,
107
106
  undefined,
108
107
  this.traceId,
109
- this.actionKey,
108
+ this.action,
110
109
  options
111
110
  )
112
111
  );
@@ -118,12 +117,7 @@ export class ChatContext<TActionState> {
118
117
  */
119
118
  unpinMessage(messageId: number) {
120
119
  this.interactions.unpin(
121
- new UnpinResponse(
122
- messageId,
123
- this.chatId,
124
- this.traceId,
125
- this.actionKey
126
- )
120
+ new UnpinResponse(messageId, this.chatId, this.traceId, this.action)
127
121
  );
128
122
  }
129
123
  }
@@ -9,10 +9,12 @@ import { TextMessage } from '../responses/textMessage';
9
9
  import { VideoMessage } from '../responses/videoMessage';
10
10
  import { ActionStateBase } from '../states/actionStateBase';
11
11
  import { ChatContext } from './chatContext';
12
+ import { IncomingMessage } from '../incomingMessage';
12
13
  import {
13
14
  MessageSendingOptions,
14
15
  TextMessageSendingOptions
15
16
  } from '../../types/messageSendingOptions';
17
+ import { IActionWithState, ActionKey } from '../../types/actionWithState';
16
18
 
17
19
  /**
18
20
  * Context of action executed in chat, in response to a message
@@ -35,31 +37,27 @@ export class MessageContext<
35
37
 
36
38
  constructor(
37
39
  botName: string,
38
- scheduledKey: string,
40
+ action: IActionWithState,
39
41
  interactions: IBotApiInteractions,
40
- chatId: number,
41
- chatName: string,
42
- messageId: number,
43
- messageText: string,
44
- fromUserId: number | undefined,
45
- traceId: number | string,
46
- fromUserName: string,
42
+ message: IncomingMessage,
47
43
  storage: IStorageClient
48
44
  ) {
49
45
  super(
50
46
  botName,
51
- scheduledKey,
47
+ action,
52
48
  interactions,
53
- chatId,
54
- chatName,
55
- traceId,
49
+ message.chat.id,
50
+ message.chatName,
51
+ message.traceId,
56
52
  storage
57
53
  );
58
54
 
59
- this.messageId = messageId;
60
- this.messageText = messageText;
61
- this.fromUserId = fromUserId;
62
- this.fromUserName = fromUserName;
55
+ this.messageId = message.message_id;
56
+ this.messageText = message.text ?? '';
57
+ this.fromUserId = message.from?.id;
58
+ this.fromUserName =
59
+ (message.from?.first_name ?? 'Unknown user') +
60
+ (message.from?.last_name ? ` ${message.from.last_name}` : '');
63
61
  }
64
62
 
65
63
  /**
@@ -70,13 +68,18 @@ export class MessageContext<
70
68
  async loadStateOf<TAnotherActionState extends IActionState>(
71
69
  commandName: string
72
70
  ): Promise<TAnotherActionState> {
73
- return (
74
- ((
75
- await this.storage.load(
76
- `command:${commandName.replace('.', '-')}`
77
- )
78
- )[this.chatId] as TAnotherActionState) ?? new ActionStateBase()
79
- );
71
+ const storageKey = `command:${commandName.replace(
72
+ '.',
73
+ '-'
74
+ )}` as ActionKey;
75
+ const allStates = await this.storage.load(storageKey);
76
+ const stateForChat = allStates[this.chatId];
77
+
78
+ if (!stateForChat) {
79
+ return new ActionStateBase() as TAnotherActionState;
80
+ }
81
+
82
+ return stateForChat as TAnotherActionState;
80
83
  }
81
84
 
82
85
  /**
@@ -91,7 +94,7 @@ export class MessageContext<
91
94
  this.chatId,
92
95
  this.messageId,
93
96
  this.traceId,
94
- this.actionKey,
97
+ this.action,
95
98
  options
96
99
  )
97
100
  );
@@ -110,7 +113,7 @@ export class MessageContext<
110
113
  this.chatId,
111
114
  this.messageId,
112
115
  this.traceId,
113
- this.actionKey,
116
+ this.action,
114
117
  options
115
118
  )
116
119
  );
@@ -129,7 +132,7 @@ export class MessageContext<
129
132
  this.chatId,
130
133
  this.messageId,
131
134
  this.traceId,
132
- this.actionKey,
135
+ this.action,
133
136
  options
134
137
  )
135
138
  );
@@ -146,7 +149,7 @@ export class MessageContext<
146
149
  this.chatId,
147
150
  this.messageId,
148
151
  emoji,
149
- this.actionKey
152
+ this.action
150
153
  )
151
154
  );
152
155
  }
@@ -1,6 +1,7 @@
1
1
  import { InputFile } from 'telegraf/types';
2
2
  import { BotResponseTypes, IReplyMessage } from '../../types/response';
3
3
  import { MessageSendingOptions } from '../../types/messageSendingOptions';
4
+ import { IActionWithState } from '../../types/actionWithState';
4
5
 
5
6
  export class ImageMessage implements IReplyMessage<InputFile> {
6
7
  kind = BotResponseTypes.image;
@@ -11,14 +12,14 @@ export class ImageMessage implements IReplyMessage<InputFile> {
11
12
  traceId: string | number;
12
13
  disableWebPreview = false;
13
14
  shouldPin: boolean;
14
- sourceActionKey: string;
15
+ action: IActionWithState;
15
16
 
16
17
  constructor(
17
18
  image: InputFile,
18
19
  chatId: number,
19
20
  replyId: number | undefined,
20
21
  traceId: number | string,
21
- sourceActionKey: string,
22
+ action: IActionWithState,
22
23
  options?: MessageSendingOptions
23
24
  ) {
24
25
  this.content = image;
@@ -26,6 +27,6 @@ export class ImageMessage implements IReplyMessage<InputFile> {
26
27
  this.replyId = replyId;
27
28
  this.traceId = traceId;
28
29
  this.shouldPin = options?.pin ?? false;
29
- this.sourceActionKey = sourceActionKey;
30
+ this.action = action;
30
31
  }
31
32
  }