chz-telegram-bot 0.0.22 → 0.0.24

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 (40) hide show
  1. package/dist/entities/botInstance.d.ts +1 -1
  2. package/dist/entities/botInstance.d.ts.map +1 -1
  3. package/dist/entities/botInstance.js +24 -5
  4. package/dist/entities/context/chatContext.d.ts +30 -3
  5. package/dist/entities/context/chatContext.d.ts.map +1 -1
  6. package/dist/entities/context/chatContext.js +22 -0
  7. package/dist/entities/context/messageContext.d.ts +38 -1
  8. package/dist/entities/context/messageContext.d.ts.map +1 -1
  9. package/dist/entities/context/messageContext.js +34 -1
  10. package/dist/helpers/builders/commandActionBuilder.d.ts +38 -0
  11. package/dist/helpers/builders/commandActionBuilder.d.ts.map +1 -1
  12. package/dist/helpers/builders/commandActionBuilder.js +38 -0
  13. package/dist/helpers/builders/scheduledActionBuilder.d.ts +34 -0
  14. package/dist/helpers/builders/scheduledActionBuilder.d.ts.map +1 -1
  15. package/dist/helpers/builders/scheduledActionBuilder.js +34 -0
  16. package/dist/helpers/reverseRecord.d.ts +2 -0
  17. package/dist/helpers/reverseRecord.d.ts.map +1 -0
  18. package/dist/helpers/reverseRecord.js +6 -0
  19. package/dist/main.d.ts +14 -1
  20. package/dist/main.d.ts.map +1 -1
  21. package/dist/main.js +7 -1
  22. package/dist/services/taskScheduler.d.ts.map +1 -1
  23. package/dist/services/taskScheduler.js +1 -2
  24. package/dist/services/telegramApi.d.ts +2 -2
  25. package/dist/services/telegramApi.d.ts.map +1 -1
  26. package/dist/services/telegramApi.js +5 -4
  27. package/dist/types/handlers.d.ts +12 -2
  28. package/dist/types/handlers.d.ts.map +1 -1
  29. package/entities/botInstance.ts +44 -10
  30. package/entities/context/chatContext.ts +30 -3
  31. package/entities/context/messageContext.ts +38 -1
  32. package/helpers/builders/commandActionBuilder.ts +38 -0
  33. package/helpers/builders/scheduledActionBuilder.ts +34 -0
  34. package/helpers/reverseRecord.ts +7 -0
  35. package/main.ts +15 -2
  36. package/package.json +1 -1
  37. package/services/taskScheduler.ts +2 -3
  38. package/services/telegramApi.ts +8 -6
  39. package/types/handlers.ts +5 -0
  40. package/helpers/reverseMap.ts +0 -3
package/dist/main.d.ts CHANGED
@@ -3,15 +3,28 @@ import { CommandAction } from './entities/actions/commandAction';
3
3
  import { ScheduledAction } from './entities/actions/scheduledAction';
4
4
  import { IActionState } from './types/actionState';
5
5
  import { BotInstance } from './entities/botInstance';
6
+ /**
7
+ * Starts bot
8
+ */
6
9
  declare function startBot(options: {
10
+ /** Bot name, used in logging */
7
11
  name: string;
12
+ /** Path to file containing Telegram Bot token. */
8
13
  tokenFilePath: string;
14
+ /** Collection of actions that will be executed as a response to message from used. Created using `CommandActionBuilder`.*/
9
15
  commands: CommandAction<IActionState>[];
16
+ /** Collection of actions that will be executed on timer. Created using `ScheduledActionBuilder`.*/
10
17
  scheduled: ScheduledAction<IActionState>[];
11
- chats: Map<string, number>;
18
+ /** Object containing chat name and chat id pairs. Used for logging and execution of scheduled action. */
19
+ chats: Record<string, number>;
20
+ /** Storage client for bot state storage. If not provided, default `JsonFileStorage` will be used. */
12
21
  storageClient?: IStorageClient;
22
+ /** Storage path for default `JsonFileStorage` client. Will be used only if `storageClient` is not provided. If not provided, default value of `./storage/` will be used.*/
13
23
  storagePath?: string;
14
24
  }): Promise<BotInstance>;
25
+ /**
26
+ * Terminates all scheduled tasks, closes storage connections and stops all bots.
27
+ */
15
28
  declare function stopBots(reason: string): Promise<void>;
16
29
  export { startBot, stopBots };
17
30
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQrD,iBAAe,QAAQ,CAAC,OAAO,EAAE;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,EAAE,CAAC;IACxC,SAAS,EAAE,eAAe,CAAC,YAAY,CAAC,EAAE,CAAC;IAC3C,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,wBAcA;AAED,iBAAe,QAAQ,CAAC,MAAM,EAAE,MAAM,iBASrC;AAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../main.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQrD;;GAEG;AACH,iBAAe,QAAQ,CAAC,OAAO,EAAE;IAC7B,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,aAAa,EAAE,MAAM,CAAC;IACtB,2HAA2H;IAC3H,QAAQ,EAAE,aAAa,CAAC,YAAY,CAAC,EAAE,CAAC;IACxC,mGAAmG;IACnG,SAAS,EAAE,eAAe,CAAC,YAAY,CAAC,EAAE,CAAC;IAC3C,yGAAyG;IACzG,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,qGAAqG;IACrG,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,2KAA2K;IAC3K,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,wBAcA;AAED;;GAEG;AACH,iBAAe,QAAQ,CAAC,MAAM,EAAE,MAAM,iBASrC;AAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC"}
package/dist/main.js CHANGED
@@ -10,6 +10,9 @@ const bots = [];
10
10
  function log(text) {
11
11
  logger_1.Logger.logWithTraceId('ALL BOTS', 'System:Bot', 'System', text);
12
12
  }
13
+ /**
14
+ * Starts bot
15
+ */
13
16
  async function startBot(options) {
14
17
  const token = await (0, promises_1.readFile)(options.tokenFilePath, 'utf8');
15
18
  const bot = new botInstance_1.BotInstance({
@@ -24,12 +27,15 @@ async function startBot(options) {
24
27
  bots.push(bot);
25
28
  return bot;
26
29
  }
30
+ /**
31
+ * Terminates all scheduled tasks, closes storage connections and stops all bots.
32
+ */
27
33
  async function stopBots(reason) {
28
34
  log(`Recieved termination code: ${reason}`);
29
35
  taskScheduler_1.Scheduler.stopAll();
30
36
  log('Acquiring storage semaphore...');
31
37
  log('Stopping bots...');
32
38
  for (const bot of bots) {
33
- bot.stop(reason);
39
+ await bot.stop(reason);
34
40
  }
35
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"taskScheduler.d.ts","sourceRoot":"","sources":["../../services/taskScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAW,MAAM,qBAAqB,CAAC;AAG5D,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;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"}
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Scheduler = void 0;
4
4
  const taskRecord_1 = require("../entities/taskRecord");
5
- const timeConvertions_1 = require("../helpers/timeConvertions");
6
5
  const logger_1 = require("./logger");
7
6
  class TaskScheduler {
8
7
  constructor() {
@@ -18,7 +17,7 @@ class TaskScheduler {
18
17
  const taskId = setInterval(action, interval);
19
18
  const task = new taskRecord_1.TaskRecord(name, taskId, interval);
20
19
  if (executeRightAway) {
21
- setTimeout(action, (0, timeConvertions_1.secondsToMilliseconds)(1));
20
+ setImmediate(action);
22
21
  }
23
22
  logger_1.Logger.logWithTraceId(ownerName, `System:TaskScheduler-${ownerName}-${name}`, 'System', `Created task [${taskId}]${name}, that will run every ${interval}ms.`);
24
23
  this.activeTasks.push(task);
@@ -14,9 +14,9 @@ export declare class TelegramApiService {
14
14
  messageQueue: Array<BotResponse>;
15
15
  botName: string;
16
16
  telegram: Telegram;
17
- chats: Map<number, string>;
17
+ chats: Record<number, string>;
18
18
  storage: IStorageClient;
19
- constructor(botName: string, telegram: Telegram, storage: IStorageClient, chats: Map<string, number>);
19
+ constructor(botName: string, telegram: Telegram, storage: IStorageClient, chats: Record<string, number>);
20
20
  flushResponses(): Promise<void>;
21
21
  private pinIfShould;
22
22
  private processResponse;
@@ -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;AAIrD,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,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,OAAO,EAAE,cAAc,CAAC;gBAGpB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAQxB,cAAc;YAyBN,WAAW;YAqBX,eAAe;IA2E7B,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,eAAe;IAQvB,uBAAuB,CACnB,eAAe,EAAE,eAAe,EAChC,UAAU,EAAE,MAAM;IAsBtB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAW5D;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;AAIrD,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,CACnB,eAAe,EAAE,eAAe,EAChC,UAAU,EAAE,MAAM;IAsBtB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;CAW5D;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"}
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TelegramApiService = void 0;
4
4
  const chatContext_1 = require("../entities/context/chatContext");
5
5
  const messageContext_1 = require("../entities/context/messageContext");
6
- const reverseMap_1 = require("../helpers/reverseMap");
6
+ const reverseRecord_1 = require("../helpers/reverseRecord");
7
7
  const logger_1 = require("./logger");
8
8
  const promises_1 = require("timers/promises");
9
9
  class TelegramApiService {
@@ -12,12 +12,13 @@ class TelegramApiService {
12
12
  this.messageQueue = [];
13
13
  this.telegram = telegram;
14
14
  this.botName = botName;
15
- this.chats = (0, reverseMap_1.reverseMap)(chats);
15
+ this.chats = (0, reverseRecord_1.reverseRecord)(chats);
16
16
  this.storage = storage;
17
17
  }
18
18
  async flushResponses() {
19
19
  if (this.isFlushing)
20
20
  return;
21
+ this.isFlushing = true;
21
22
  while (this.messageQueue.length) {
22
23
  const message = this.messageQueue.pop();
23
24
  if (!message)
@@ -27,7 +28,7 @@ class TelegramApiService {
27
28
  await (0, promises_1.setTimeout)(100);
28
29
  }
29
30
  catch (error) {
30
- logger_1.Logger.errorWithTraceId(this.botName, message.traceId, this.chats.get(message.chatId), error, message);
31
+ logger_1.Logger.errorWithTraceId(this.botName, message.traceId, this.chats[message.chatId], error, message);
31
32
  }
32
33
  }
33
34
  this.isFlushing = false;
@@ -100,7 +101,7 @@ class TelegramApiService {
100
101
  return new messageContext_1.MessageContext(this.botName, commandKey, this.getInteractions(), incomingMessage.chat.id, incomingMessage.chatName, incomingMessage.message_id, incomingMessage.text, incomingMessage.from?.id, incomingMessage.traceId, firstName + lastName, this.storage);
101
102
  }
102
103
  createContextForChat(chatId, scheduledKey) {
103
- return new chatContext_1.ChatContext(this.botName, scheduledKey, this.getInteractions(), chatId, this.chats.get(chatId), `Scheduled:${scheduledKey}:${chatId}`, this.storage);
104
+ return new chatContext_1.ChatContext(this.botName, scheduledKey, this.getInteractions(), chatId, this.chats[chatId], `Scheduled:${scheduledKey}:${chatId}`, this.storage);
104
105
  }
105
106
  }
106
107
  exports.TelegramApiService = TelegramApiService;
@@ -2,6 +2,16 @@ import { ChatContext } from '../entities/context/chatContext';
2
2
  import { MessageContext } from '../entities/context/messageContext';
3
3
  import { IActionState } from './actionState';
4
4
  import { CachedValueAccessor } from './cachedValueAccessor';
5
- export type CommandHandler<TActionState extends IActionState> = (ctx: MessageContext<TActionState>, state: TActionState) => Promise<void>;
6
- export type ScheduledHandler<TActionState extends IActionState> = (ctx: ChatContext, getCached: CachedValueAccessor, state: TActionState) => Promise<void>;
5
+ export type CommandHandler<TActionState extends IActionState> = (
6
+ /** Context of action executed in chat, in response to a message. */
7
+ ctx: MessageContext<TActionState>,
8
+ /** State of an action being executed. */
9
+ state: TActionState) => Promise<void>;
10
+ export type ScheduledHandler<TActionState extends IActionState> = (
11
+ /** Context of action executed in chat. */
12
+ ctx: ChatContext,
13
+ /** Function that will attempt to get value from cache. If there is no value found, corresponding cached state factory will be called. */
14
+ getCached: CachedValueAccessor,
15
+ /** State of an action being executed. */
16
+ state: TActionState) => Promise<void>;
7
17
  //# sourceMappingURL=handlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../types/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,cAAc,CAAC,YAAY,SAAS,YAAY,IAAI,CAC5D,GAAG,EAAE,cAAc,CAAC,YAAY,CAAC,EACjC,KAAK,EAAE,YAAY,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,MAAM,gBAAgB,CAAC,YAAY,SAAS,YAAY,IAAI,CAC9D,GAAG,EAAE,WAAW,EAChB,SAAS,EAAE,mBAAmB,EAC9B,KAAK,EAAE,YAAY,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../types/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,MAAM,cAAc,CAAC,YAAY,SAAS,YAAY,IAAI;AAC5D,oEAAoE;AACpE,GAAG,EAAE,cAAc,CAAC,YAAY,CAAC;AACjC,yCAAyC;AACzC,KAAK,EAAE,YAAY,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,MAAM,gBAAgB,CAAC,YAAY,SAAS,YAAY,IAAI;AAC9D,0CAA0C;AAC1C,GAAG,EAAE,WAAW;AAChB,yIAAyI;AACzI,SAAS,EAAE,mBAAmB;AAC9B,yCAAyC;AACzC,KAAK,EAAE,YAAY,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { Telegraf } from 'telegraf';
2
2
  import { hoursToMilliseconds } from '../helpers/timeConvertions';
3
- import { Hours } from '../types/timeValues';
3
+ import { Hours, Milliseconds } from '../types/timeValues';
4
4
  import { IStorageClient } from '../types/storage';
5
5
  import { JsonFileStorage } from '../services/jsonFileStorage';
6
6
  import { TelegramApiService } from '../services/telegramApi';
@@ -10,6 +10,7 @@ import { ScheduledAction } from './actions/scheduledAction';
10
10
  import { Logger } from '../services/logger';
11
11
  import { Scheduler } from '../services/taskScheduler';
12
12
  import { IncomingMessage } from './incomingMessage';
13
+ import moment from 'moment';
13
14
 
14
15
  export class BotInstance {
15
16
  name: string;
@@ -17,7 +18,7 @@ export class BotInstance {
17
18
  private telegraf: Telegraf;
18
19
  private commands: CommandAction<IActionState>[];
19
20
  private scheduled: ScheduledAction<IActionState>[];
20
- private chats: Map<string, number>;
21
+ private chats: Record<string, number>;
21
22
  storage: IStorageClient;
22
23
 
23
24
  constructor(options: {
@@ -25,7 +26,7 @@ export class BotInstance {
25
26
  token: string;
26
27
  commands: CommandAction<IActionState>[];
27
28
  scheduled: ScheduledAction<IActionState>[];
28
- chats: Map<string, number>;
29
+ chats: Record<string, number>;
29
30
  storageClient?: IStorageClient;
30
31
  storagePath?: string;
31
32
  }) {
@@ -64,13 +65,44 @@ export class BotInstance {
64
65
 
65
66
  private initializeScheduledProcessing() {
66
67
  if (this.scheduled.length > 0) {
67
- Scheduler.createTask(
68
- 'ScheduledProcessing',
68
+ const now = moment();
69
+
70
+ let nextExecutionTime = now.clone().startOf('hour');
71
+
72
+ if (now.minute() == 0 && now.second() == 0) {
73
+ Scheduler.createTask(
74
+ 'ScheduledProcessing',
75
+ async () => {
76
+ await this.runScheduled();
77
+ },
78
+ hoursToMilliseconds(1 as Hours),
79
+ true,
80
+ this.name
81
+ );
82
+
83
+ return;
84
+ }
85
+
86
+ if (now.minute() > 0 || now.second() > 0) {
87
+ nextExecutionTime = nextExecutionTime.add(1, 'hour');
88
+ }
89
+
90
+ const delay = nextExecutionTime.diff(now);
91
+
92
+ Scheduler.createOnetimeTask(
93
+ 'ScheduledProcessing_OneTime',
69
94
  async () => {
70
- await this.runScheduled();
95
+ Scheduler.createTask(
96
+ 'ScheduledProcessing',
97
+ async () => {
98
+ await this.runScheduled();
99
+ },
100
+ hoursToMilliseconds(1 as Hours),
101
+ true,
102
+ this.name
103
+ );
71
104
  },
72
- hoursToMilliseconds(0.5 as Hours),
73
- true,
105
+ delay as Milliseconds,
74
106
  this.name
75
107
  );
76
108
  }
@@ -111,7 +143,7 @@ export class BotInstance {
111
143
  }
112
144
 
113
145
  private async runScheduled() {
114
- for (const [chatName, chatId] of this.chats.entries()) {
146
+ for (const [chatName, chatId] of Object.entries(this.chats)) {
115
147
  for (const trig of this.scheduled) {
116
148
  const ctx = this.api.createContextForChat(chatId, trig.key);
117
149
 
@@ -128,6 +160,8 @@ export class BotInstance {
128
160
  }
129
161
  }
130
162
  }
163
+
164
+ await this.api.flushResponses();
131
165
  }
132
166
 
133
167
  private async processMessage(msg: IncomingMessage) {
@@ -147,6 +181,6 @@ export class BotInstance {
147
181
  }
148
182
  }
149
183
 
150
- this.api.flushResponses();
184
+ await this.api.flushResponses();
151
185
  }
152
186
  }
@@ -10,13 +10,21 @@ import {
10
10
  TextMessageSendingOptions
11
11
  } from '../../types/messageSendingOptions';
12
12
 
13
+ /**
14
+ * Context of action executed in chat.
15
+ */
13
16
  export class ChatContext {
17
+ protected actionKey: string;
18
+ protected interactions: IBotApiInteractions;
19
+ /** Trace id of a action execution. */
20
+ traceId: number | string;
21
+ /** Name of a bot that executes this action. */
14
22
  botName: string;
15
- actionKey: string;
16
- interactions: IBotApiInteractions;
23
+ /** Id of a chat that action is executed in. */
17
24
  chatId: number;
25
+ /** Name of a chat that action is executed in. */
18
26
  chatName: string;
19
- traceId: number | string;
27
+ /** Storage client instance for this bot. */
20
28
  storage: IStorageClient;
21
29
 
22
30
  constructor(
@@ -37,6 +45,11 @@ export class ChatContext {
37
45
  this.storage = storage;
38
46
  }
39
47
 
48
+ /**
49
+ * Sends text message to chat.
50
+ * @param text Message contents.
51
+ * @param options Message sending option.
52
+ */
40
53
  sendTextToChat(text: string, options?: TextMessageSendingOptions) {
41
54
  this.interactions.respond(
42
55
  new TextMessage(
@@ -50,6 +63,11 @@ export class ChatContext {
50
63
  );
51
64
  }
52
65
 
66
+ /**
67
+ * Sends image message to chat.
68
+ * @param name Message contents.
69
+ * @param options Message sending option.
70
+ */
53
71
  sendImageToChat(name: string, options?: MessageSendingOptions) {
54
72
  const filePath = `./content/${name}.png`;
55
73
  this.interactions.respond(
@@ -64,6 +82,11 @@ export class ChatContext {
64
82
  );
65
83
  }
66
84
 
85
+ /**
86
+ * Sends video/gif message to chat.
87
+ * @param name Message contents.
88
+ * @param options Message sending option.
89
+ */
67
90
  sendVideoToChat(name: string, options?: MessageSendingOptions) {
68
91
  const filePath = `./content/${name}.mp4`;
69
92
  this.interactions.respond(
@@ -78,6 +101,10 @@ export class ChatContext {
78
101
  );
79
102
  }
80
103
 
104
+ /**
105
+ * Unpins message.
106
+ * @param messageId Message id.
107
+ */
81
108
  unpinMessage(messageId: number) {
82
109
  this.interactions.unpin(
83
110
  new UnpinResponse(
@@ -14,15 +14,24 @@ import {
14
14
  TextMessageSendingOptions
15
15
  } from '../../types/messageSendingOptions';
16
16
 
17
+ /**
18
+ * Context of action executed in chat, in response to a message
19
+ */
17
20
  export class MessageContext<
18
21
  TActionState extends IActionState
19
22
  > extends ChatContext {
23
+ updateActions: Array<(state: TActionState) => void> = [];
24
+ /** Id of a message that triggered this action. */
20
25
  messageId: number;
26
+ /** Text of a message that triggered this action. */
21
27
  messageText: string;
28
+ /** Collection of Regexp match results on a message that triggered this action. Will be empty if trigger is not a Regexp. */
22
29
  matchResults: RegExpMatchArray[] = [];
30
+ /** Id of a user that sent a message that triggered this action. */
23
31
  fromUserId: number | undefined;
32
+ /** Indicates if cooldown should be started after action is executed. Set to `true` by default. */
24
33
  startCooldown: boolean = true;
25
- updateActions: Array<(state: TActionState) => void> = [];
34
+ /** Name of a user that sent a message that triggered this action. */
26
35
  fromUserName: string;
27
36
 
28
37
  constructor(
@@ -54,6 +63,11 @@ export class MessageContext<
54
63
  this.fromUserName = fromUserName;
55
64
  }
56
65
 
66
+ /**
67
+ * Loads state of another action. Changes to the loaded state will no affect actual state of other action.
68
+ * @param commandName Name of an action to load state of.
69
+ * @template TAnotherActionState - Type of a state that is used by another action.
70
+ */
57
71
  async loadStateOf<TAnotherActionState extends IActionState>(
58
72
  commandName: string
59
73
  ): Promise<TAnotherActionState> {
@@ -66,12 +80,21 @@ export class MessageContext<
66
80
  );
67
81
  }
68
82
 
83
+ /**
84
+ * Manually update the state of an action.
85
+ * @param stateUpdateAction Function that will modify state.
86
+ */
69
87
  updateState(stateUpdateAction: (state: TActionState) => void) {
70
88
  this.updateActions.push(
71
89
  stateUpdateAction as (state: TActionState) => void
72
90
  );
73
91
  }
74
92
 
93
+ /**
94
+ * Reply with text message to message that triggered this action.
95
+ * @param text Message contents.
96
+ * @param options Message sending option.
97
+ */
75
98
  replyWithText(text: string, options?: TextMessageSendingOptions) {
76
99
  this.interactions.respond(
77
100
  new TextMessage(
@@ -85,6 +108,11 @@ export class MessageContext<
85
108
  );
86
109
  }
87
110
 
111
+ /**
112
+ * Reply with image message to message that triggered this action.
113
+ * @param text Message contents.
114
+ * @param options Message sending option.
115
+ */
88
116
  replyWithImage(name: string, options?: MessageSendingOptions) {
89
117
  const filePath = `./content/${name}.png`;
90
118
  this.interactions.respond(
@@ -99,6 +127,11 @@ export class MessageContext<
99
127
  );
100
128
  }
101
129
 
130
+ /**
131
+ * Reply with video/gif message to message that triggered this action.
132
+ * @param text Message contents.
133
+ * @param options Message sending option.
134
+ */
102
135
  replyWithVideo(name: string, options?: MessageSendingOptions) {
103
136
  const filePath = `./content/${name}.mp4`;
104
137
  this.interactions.respond(
@@ -113,6 +146,10 @@ export class MessageContext<
113
146
  );
114
147
  }
115
148
 
149
+ /**
150
+ * React to the message that triggered this action.
151
+ * @param emoji Telegram emoji to react with.
152
+ */
116
153
  react(emoji: TelegramEmoji) {
117
154
  this.interactions.react(
118
155
  new Reaction(
@@ -7,6 +7,9 @@ import { IActionState } from '../../types/actionState';
7
7
  import { toArray } from '../toArray';
8
8
  import { Noop } from '../noop';
9
9
 
10
+ /**
11
+ * Builder for `CommandAction` with state represented by `TActionState`
12
+ */
10
13
  export class CommandActionBuilderWithState<TActionState extends IActionState> {
11
14
  name: string;
12
15
  trigger: string | RegExp | Array<string> | Array<RegExp> = [];
@@ -19,53 +22,82 @@ export class CommandActionBuilderWithState<TActionState extends IActionState> {
19
22
  handler: CommandHandler<TActionState> = Noop.call;
20
23
  condition: CommandCondition<TActionState> = Noop.true;
21
24
 
25
+ /**
26
+ * Builder for `CommandAction` with state represented by `TActionState`
27
+ * @param name Action name, will be used for logging and storage
28
+ * @param stateConstructor Function that creates default state object
29
+ */
22
30
  constructor(name: string, stateConstructor: () => TActionState) {
23
31
  this.name = name;
24
32
  this.stateConstructor = stateConstructor;
25
33
  }
26
34
 
35
+ /**
36
+ * Defines action trigger
37
+ * @param trigger If `string` or `string[]` is provided, will be triggered only on exact message match.
38
+ *
39
+ * If `RegExp` or `RegExp[]` is provided, will be triggered on successful match.
40
+ */
27
41
  on(trigger: string | RegExp | Array<string> | Array<RegExp>) {
28
42
  this.trigger = trigger;
29
43
 
30
44
  return this;
31
45
  }
32
46
 
47
+ /** Defines id (or ids) of users that are allowed to trigger this action.
48
+ * @param id User id or ids
49
+ */
33
50
  from(id: number | Array<number>) {
34
51
  this.allowedUsers = toArray(id);
35
52
 
36
53
  return this;
37
54
  }
38
55
 
56
+ /** Defines action logic itself, will be executed on trigger.
57
+ * @param handler Callback that will be called on trigger
58
+ */
39
59
  do(handler: CommandHandler<TActionState>) {
40
60
  this.handler = handler;
41
61
 
42
62
  return this;
43
63
  }
44
64
 
65
+ /** Defines condition that will be checked before trigger match check is executed.
66
+ * @param condition Condition check predicate
67
+ */
45
68
  when(condition: CommandCondition<TActionState>) {
46
69
  this.condition = condition;
47
70
 
48
71
  return this;
49
72
  }
50
73
 
74
+ /** If called during building, action is marked as disabled and never checked. */
51
75
  disabled() {
52
76
  this.active = false;
53
77
 
54
78
  return this;
55
79
  }
56
80
 
81
+ /** Sets action cooldown.
82
+ * @param seconds Cooldown in seconds.
83
+ */
57
84
  cooldown(seconds: Seconds) {
58
85
  this.cooldownSeconds = seconds;
59
86
 
60
87
  return this;
61
88
  }
62
89
 
90
+ /**
91
+ * Adds a chat to ignore list for this action.
92
+ * @param chatId Chat id to ignore.
93
+ */
63
94
  ignoreChat(chatId: number) {
64
95
  this.blacklist.push(chatId);
65
96
 
66
97
  return this;
67
98
  }
68
99
 
100
+ /** Builds action */
69
101
  build() {
70
102
  return new CommandAction(
71
103
  this.trigger,
@@ -81,7 +113,13 @@ export class CommandActionBuilderWithState<TActionState extends IActionState> {
81
113
  }
82
114
  }
83
115
 
116
+ /**
117
+ * Builder for `CommandAction` with state represented by default state (containing only last execution date).
118
+ */
84
119
  export class CommandActionBuilder extends CommandActionBuilderWithState<ActionStateBase> {
120
+ /**
121
+ * Builder for `CommandAction` with state represented by default state (containing only last execution date).
122
+ */
85
123
  constructor(name: string) {
86
124
  super(name, () => new ActionStateBase());
87
125
  }
@@ -6,6 +6,9 @@ import { ScheduledHandler } from '../../types/handlers';
6
6
  import { Hours, HoursOfDay } from '../../types/timeValues';
7
7
  import { Noop } from '../noop';
8
8
 
9
+ /**
10
+ * Builder for `ScheduledAction` with state represented by `TActionState`
11
+ */
9
12
  export class ScheduledActionBuilderWithState<
10
13
  TActionState extends IActionState
11
14
  > {
@@ -18,29 +21,52 @@ export class ScheduledActionBuilderWithState<
18
21
 
19
22
  name: string;
20
23
 
24
+ /**
25
+ * Builder for `ScheduledAction` with state represented by `TActionState`
26
+ * @param name Action name, will be used for logging and storage
27
+ * @param stateConstructor Function that creates default state object
28
+ */
21
29
  constructor(name: string, stateConstructor: () => TActionState) {
22
30
  this.name = name;
23
31
  this.stateConstructor = stateConstructor;
24
32
  }
25
33
 
34
+ /**
35
+ * Adds a chat to whitelist for this action.
36
+ * @param chatId Chat id to execute in.
37
+ */
26
38
  allowIn(chatId: number) {
27
39
  this.whitelist.push(chatId);
28
40
 
29
41
  return this;
30
42
  }
31
43
 
44
+ /**
45
+ * Defines time for scheduled item execution.
46
+ * @param time Time of day (0 - 23) to execute action.
47
+ */
32
48
  runAt(time: HoursOfDay) {
33
49
  this.time = time;
34
50
 
35
51
  return this;
36
52
  }
37
53
 
54
+ /** Defines action logic itself, will be executed on timer.
55
+ * @param handler Callback that will be called on timer.
56
+ */
38
57
  do(handler: ScheduledHandler<TActionState>) {
39
58
  this.handler = handler;
40
59
 
41
60
  return this;
42
61
  }
43
62
 
63
+ /**
64
+ * Defines process-wide cache, that is shared by all actions of this type (even in different bot instances).
65
+ * Can be used for fetch request de-duping, etc.
66
+ * @param key Key that will be used to retrieve value from cache.
67
+ * @param itemFactory Callback that will be executed once to create cached value.
68
+ * @param invalidationTimeoutInHours Timeout for cache invalidation.
69
+ */
44
70
  withSharedCache(
45
71
  key: string,
46
72
  itemFactory: () => Promise<unknown>,
@@ -54,12 +80,14 @@ export class ScheduledActionBuilderWithState<
54
80
  return this;
55
81
  }
56
82
 
83
+ /** If called during building, action is marked as disabled and never checked. */
57
84
  disabled() {
58
85
  this.active = false;
59
86
 
60
87
  return this;
61
88
  }
62
89
 
90
+ /** Builds action */
63
91
  build() {
64
92
  return new ScheduledAction<TActionState>(
65
93
  this.name,
@@ -73,7 +101,13 @@ export class ScheduledActionBuilderWithState<
73
101
  }
74
102
  }
75
103
 
104
+ /**
105
+ * Builder for `ScheduledAction` with state represented by default state (containing only last execution date).
106
+ */
76
107
  export class ScheduledActionBuilder extends ScheduledActionBuilderWithState<ActionStateBase> {
108
+ /**
109
+ * Builder for `ScheduledAction` with state represented by default state (containing only last execution date).
110
+ */
77
111
  constructor(name: string) {
78
112
  super(name, () => new ActionStateBase());
79
113
  }
@@ -0,0 +1,7 @@
1
+ export function reverseRecord<T extends PropertyKey, U extends PropertyKey>(
2
+ input: Record<T, U>
3
+ ) {
4
+ return Object.fromEntries(
5
+ Object.entries(input).map(([key, value]) => [value, key])
6
+ ) as Record<U, T>;
7
+ }