chz-telegram-bot 0.0.50 → 0.0.52

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 (117) hide show
  1. package/dist/dtos/actionExecutionResult.d.ts +7 -0
  2. package/dist/dtos/actionExecutionResult.d.ts.map +1 -0
  3. package/dist/dtos/actionExecutionResult.js +10 -0
  4. package/dist/dtos/chatInfo.d.ts +8 -0
  5. package/dist/dtos/chatInfo.d.ts.map +1 -0
  6. package/dist/dtos/chatInfo.js +10 -0
  7. package/dist/dtos/commandTriggerCheckResult.d.ts +10 -0
  8. package/dist/dtos/commandTriggerCheckResult.d.ts.map +1 -0
  9. package/dist/dtos/commandTriggerCheckResult.js +20 -0
  10. package/dist/dtos/incomingMessage.d.ts +14 -0
  11. package/dist/dtos/incomingMessage.d.ts.map +1 -0
  12. package/dist/dtos/incomingMessage.js +44 -0
  13. package/dist/dtos/responses/delay.d.ts +14 -0
  14. package/dist/dtos/responses/delay.d.ts.map +1 -0
  15. package/dist/dtos/responses/delay.js +14 -0
  16. package/dist/dtos/responses/imageMessage.d.ts +18 -0
  17. package/dist/dtos/responses/imageMessage.d.ts.map +1 -0
  18. package/dist/dtos/responses/imageMessage.js +17 -0
  19. package/dist/dtos/responses/reaction.d.ts +15 -0
  20. package/dist/dtos/responses/reaction.d.ts.map +1 -0
  21. package/dist/dtos/responses/reaction.js +15 -0
  22. package/dist/dtos/responses/textMessage.d.ts +17 -0
  23. package/dist/dtos/responses/textMessage.d.ts.map +1 -0
  24. package/dist/dtos/responses/textMessage.js +17 -0
  25. package/dist/dtos/responses/unpin.d.ts +13 -0
  26. package/dist/dtos/responses/unpin.d.ts.map +1 -0
  27. package/dist/dtos/responses/unpin.js +14 -0
  28. package/dist/dtos/responses/videoMessage.d.ts +18 -0
  29. package/dist/dtos/responses/videoMessage.d.ts.map +1 -0
  30. package/dist/dtos/responses/videoMessage.js +17 -0
  31. package/dist/entities/actions/commandAction.d.ts +11 -10
  32. package/dist/entities/actions/commandAction.d.ts.map +1 -1
  33. package/dist/entities/actions/commandAction.js +41 -40
  34. package/dist/entities/actions/scheduledAction.d.ts +11 -11
  35. package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
  36. package/dist/entities/actions/scheduledAction.js +14 -18
  37. package/dist/entities/botInstance.d.ts +16 -8
  38. package/dist/entities/botInstance.d.ts.map +1 -1
  39. package/dist/entities/botInstance.js +26 -18
  40. package/dist/entities/cachedStateFactory.d.ts +2 -2
  41. package/dist/entities/cachedStateFactory.d.ts.map +1 -1
  42. package/dist/entities/context/chatContext.d.ts +13 -14
  43. package/dist/entities/context/chatContext.d.ts.map +1 -1
  44. package/dist/entities/context/chatContext.js +16 -24
  45. package/dist/entities/context/messageContext.d.ts +5 -3
  46. package/dist/entities/context/messageContext.d.ts.map +1 -1
  47. package/dist/entities/context/messageContext.js +13 -13
  48. package/dist/entities/taskRecord.d.ts +3 -3
  49. package/dist/entities/taskRecord.d.ts.map +1 -1
  50. package/dist/helpers/noop.d.ts +4 -2
  51. package/dist/helpers/noop.d.ts.map +1 -1
  52. package/dist/helpers/noop.js +3 -2
  53. package/dist/index.d.ts +3 -1
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/main.d.ts +10 -2
  56. package/dist/main.d.ts.map +1 -1
  57. package/dist/main.js +6 -11
  58. package/dist/services/jsonFileStorage.d.ts +5 -6
  59. package/dist/services/jsonFileStorage.d.ts.map +1 -1
  60. package/dist/services/jsonFileStorage.js +3 -5
  61. package/dist/services/jsonLogger.d.ts +8 -0
  62. package/dist/services/jsonLogger.d.ts.map +1 -0
  63. package/dist/services/jsonLogger.js +28 -0
  64. package/dist/services/nodeTimeoutScheduler.d.ts +13 -0
  65. package/dist/services/nodeTimeoutScheduler.d.ts.map +1 -0
  66. package/dist/services/nodeTimeoutScheduler.js +33 -0
  67. package/dist/services/taskScheduler.d.ts +1 -1
  68. package/dist/services/taskScheduler.d.ts.map +1 -1
  69. package/dist/services/telegramApi.d.ts +7 -14
  70. package/dist/services/telegramApi.d.ts.map +1 -1
  71. package/dist/services/telegramApi.js +12 -20
  72. package/dist/types/actionWithState.d.ts +2 -2
  73. package/dist/types/actionWithState.d.ts.map +1 -1
  74. package/dist/types/commandCondition.d.ts +1 -1
  75. package/dist/types/commandCondition.d.ts.map +1 -1
  76. package/dist/types/logger.d.ts +6 -0
  77. package/dist/types/logger.d.ts.map +1 -0
  78. package/dist/types/logger.js +2 -0
  79. package/dist/types/response.d.ts +15 -14
  80. package/dist/types/response.d.ts.map +1 -1
  81. package/dist/types/scheduler.d.ts +7 -0
  82. package/dist/types/scheduler.d.ts.map +1 -0
  83. package/dist/types/scheduler.js +2 -0
  84. package/dist/types/storage.d.ts +1 -2
  85. package/dist/types/storage.d.ts.map +1 -1
  86. package/dtos/chatInfo.ts +11 -0
  87. package/{entities → dtos}/commandTriggerCheckResult.ts +10 -10
  88. package/{entities → dtos}/incomingMessage.ts +13 -12
  89. package/{entities → dtos}/responses/delay.ts +8 -7
  90. package/{entities → dtos}/responses/imageMessage.ts +11 -10
  91. package/{entities → dtos}/responses/reaction.ts +9 -8
  92. package/{entities → dtos}/responses/textMessage.ts +11 -10
  93. package/{entities → dtos}/responses/unpin.ts +8 -7
  94. package/{entities → dtos}/responses/videoMessage.ts +11 -10
  95. package/entities/actions/commandAction.ts +64 -56
  96. package/entities/actions/scheduledAction.ts +40 -43
  97. package/entities/botInstance.ts +64 -35
  98. package/entities/cachedStateFactory.ts +2 -2
  99. package/entities/context/chatContext.ts +40 -34
  100. package/entities/context/messageContext.ts +21 -18
  101. package/entities/taskRecord.ts +3 -3
  102. package/helpers/noop.ts +5 -2
  103. package/index.ts +3 -1
  104. package/main.ts +16 -15
  105. package/package.json +1 -1
  106. package/services/jsonFileStorage.ts +8 -10
  107. package/services/{logger.ts → jsonLogger.ts} +3 -3
  108. package/services/{taskScheduler.ts → nodeTimeoutScheduler.ts} +12 -8
  109. package/services/telegramApi.ts +19 -54
  110. package/types/actionWithState.ts +2 -2
  111. package/types/commandCondition.ts +1 -1
  112. package/types/logger.ts +24 -0
  113. package/types/response.ts +15 -14
  114. package/types/scheduler.ts +20 -0
  115. package/types/storage.ts +1 -2
  116. package/entities/actionExecutionResult.ts +0 -11
  117. package/helpers/inverseRecord.ts +0 -7
@@ -10,21 +10,27 @@ import { TelegramApiService } from '../services/telegramApi';
10
10
  import { IActionState } from '../types/actionState';
11
11
  import { CommandAction } from './actions/commandAction';
12
12
  import { ScheduledAction } from './actions/scheduledAction';
13
- import { Logger } from '../services/logger';
14
- import { Scheduler } from '../services/taskScheduler';
15
- import { IncomingMessage } from './incomingMessage';
13
+ import { JsonLogger } from '../services/jsonLogger';
14
+ import { IncomingMessage } from '../dtos/incomingMessage';
16
15
  import moment from 'moment';
17
16
  import { ChatContext } from './context/chatContext';
18
17
  import { MessageContext } from './context/messageContext';
18
+ import { ChatInfo } from '../dtos/chatInfo';
19
+ import { ILogger } from '../types/logger';
20
+ import { IScheduler } from '../types/scheduler';
21
+ import { NodeTimeoutScheduler } from '../services/nodeTimeoutScheduler';
19
22
 
20
23
  export class BotInstance {
21
- name: string;
22
- private api: TelegramApiService;
23
- private telegraf: Telegraf;
24
- private commands: CommandAction<IActionState>[];
25
- private scheduled: ScheduledAction<IActionState>[];
26
- private chats: Record<string, number>;
27
- storage: IStorageClient;
24
+ private readonly api: TelegramApiService;
25
+ private readonly storage: IStorageClient;
26
+ private readonly scheduler: IScheduler;
27
+ private readonly logger: ILogger;
28
+
29
+ readonly name: string;
30
+ private readonly telegraf: Telegraf;
31
+ private readonly commands: CommandAction<IActionState>[];
32
+ private readonly scheduled: ScheduledAction<IActionState>[];
33
+ private readonly chats: Record<string, number>;
28
34
 
29
35
  constructor(options: {
30
36
  name: string;
@@ -32,10 +38,14 @@ export class BotInstance {
32
38
  commands: CommandAction<IActionState>[];
33
39
  scheduled: ScheduledAction<IActionState>[];
34
40
  chats: Record<string, number>;
35
- storageClient?: IStorageClient;
36
41
  storagePath?: string;
37
42
  scheduledPeriod?: Seconds;
38
43
  verboseLoggingForIncomingMessage?: boolean;
44
+ services?: {
45
+ storageClient?: IStorageClient;
46
+ logger?: ILogger;
47
+ scheduler?: IScheduler;
48
+ };
39
49
  }) {
40
50
  this.name = options.name;
41
51
  this.commands = options.commands;
@@ -43,22 +53,19 @@ export class BotInstance {
43
53
  this.chats = options.chats;
44
54
 
45
55
  const actions = [...this.commands, ...this.scheduled];
46
-
47
- Logger.logWithTraceId(
48
- this.name,
49
- `System:Bot-${this.name}-Start`,
50
- 'System',
51
- 'Starting bot...'
52
- );
53
56
  this.telegraf = new Telegraf(options.token);
57
+ this.logger = options.services?.logger ?? new JsonLogger();
58
+ this.scheduler =
59
+ options.services?.scheduler ??
60
+ new NodeTimeoutScheduler(this.logger);
54
61
  this.storage =
55
- options.storageClient ??
62
+ options.services?.storageClient ??
56
63
  new JsonFileStorage(options.name, actions, options.storagePath);
57
64
  this.api = new TelegramApiService(
58
65
  this.name,
59
66
  this.telegraf.telegram,
60
67
  this.storage,
61
- this.chats
68
+ this.logger
62
69
  );
63
70
 
64
71
  this.initializeMessageProcessing(
@@ -70,6 +77,12 @@ export class BotInstance {
70
77
 
71
78
  this.storage.saveMetadata(actions, this.name);
72
79
 
80
+ this.logger.logWithTraceId(
81
+ this.name,
82
+ `System:Bot-${this.name}-Start`,
83
+ 'System',
84
+ 'Starting bot...'
85
+ );
73
86
  this.telegraf.launch();
74
87
  }
75
88
 
@@ -78,7 +91,7 @@ export class BotInstance {
78
91
  const now = moment();
79
92
 
80
93
  if (now.minute() == 0 && now.second() == 0) {
81
- Scheduler.createTask(
94
+ this.scheduler.createTask(
82
95
  'ScheduledProcessing',
83
96
  async () => {
84
97
  await this.runScheduled();
@@ -98,10 +111,10 @@ export class BotInstance {
98
111
 
99
112
  const delay = nextExecutionTime.diff(now);
100
113
 
101
- Scheduler.createOnetimeTask(
114
+ this.scheduler.createOnetimeTask(
102
115
  'ScheduledProcessing_OneTime',
103
116
  async () => {
104
- Scheduler.createTask(
117
+ this.scheduler.createTask(
105
118
  'ScheduledProcessing',
106
119
  async () => {
107
120
  await this.runScheduled();
@@ -129,17 +142,17 @@ export class BotInstance {
129
142
  const messageFromId = msg.from?.id ?? 'Unknown';
130
143
 
131
144
  if (verboseLoggingForIncomingMessage) {
132
- Logger.logObjectWithTraceId(
145
+ this.logger.logObjectWithTraceId(
133
146
  this.name,
134
147
  msg.traceId,
135
- msg.chatName,
148
+ msg.chatInfo.name,
136
149
  ctx.update.message
137
150
  );
138
151
  } else {
139
- Logger.logWithTraceId(
152
+ this.logger.logWithTraceId(
140
153
  this.name,
141
154
  msg.traceId,
142
- msg.chatName,
155
+ msg.chatInfo.name,
143
156
  `${messageFromName} (${messageFromId}): ${messageContent}`
144
157
  );
145
158
  }
@@ -150,29 +163,40 @@ export class BotInstance {
150
163
  }
151
164
 
152
165
  async stop(code: string) {
153
- Logger.logWithTraceId(
166
+ this.logger.logWithTraceId(
154
167
  this.name,
155
168
  `System:Bot-${this.name}-Stop`,
156
169
  'System',
157
170
  'Stopping bot...'
158
171
  );
159
172
 
173
+ this.scheduler.stopAll();
160
174
  await this.storage.close();
161
175
  this.telegraf.stop(code);
162
176
  }
163
177
 
164
178
  private async runScheduled() {
165
- const ctx = new ChatContext<IActionState>();
179
+ const ctx = new ChatContext<IActionState>(
180
+ this.storage,
181
+ this.logger,
182
+ this.scheduler
183
+ );
166
184
 
167
185
  for (const [chatName, chatId] of Object.entries(this.chats)) {
168
186
  for (const scheduledAction of this.scheduled) {
169
- this.api.initializeContextForChat(ctx, chatId, scheduledAction);
187
+ ctx.initializeChatContext(
188
+ this.name,
189
+ scheduledAction,
190
+ new ChatInfo(chatId, chatName),
191
+ `Scheduled:${scheduledAction.key}:${chatId}`
192
+ );
170
193
 
171
194
  try {
172
195
  const responses = await scheduledAction.exec(ctx);
173
196
  this.api.enqueueBatchedResponses(responses);
197
+ ctx.isInitialized = false;
174
198
  } catch (error) {
175
- Logger.errorWithTraceId(
199
+ this.logger.errorWithTraceId(
176
200
  ctx.botName,
177
201
  ctx.traceId,
178
202
  chatName,
@@ -187,19 +211,24 @@ export class BotInstance {
187
211
  }
188
212
 
189
213
  private async processMessage(msg: IncomingMessage) {
190
- const ctx = new MessageContext<IActionState>();
214
+ const ctx = new MessageContext<IActionState>(
215
+ this.storage,
216
+ this.logger,
217
+ this.scheduler
218
+ );
191
219
 
192
220
  for (const commandAction of this.commands) {
193
- this.api.initializeContextForMessage(ctx, msg, commandAction);
221
+ ctx.initializeMessageContext(this.name, commandAction, msg);
194
222
 
195
223
  try {
196
224
  const responses = await commandAction.exec(ctx);
197
225
  this.api.enqueueBatchedResponses(responses);
226
+ ctx.isInitialized = false;
198
227
  } catch (error) {
199
- Logger.errorWithTraceId(
228
+ this.logger.errorWithTraceId(
200
229
  ctx.botName,
201
230
  ctx.traceId,
202
- ctx.chatName,
231
+ ctx.chatInfo.name,
203
232
  error,
204
233
  ctx
205
234
  );
@@ -1,8 +1,8 @@
1
1
  import { Hours } from '../types/timeValues';
2
2
 
3
3
  export class CachedStateFactory {
4
- getValue: () => Promise<unknown>;
5
- invalidationTimeoutInHours: Hours;
4
+ readonly getValue: () => Promise<unknown>;
5
+ readonly invalidationTimeoutInHours: Hours;
6
6
 
7
7
  constructor(
8
8
  itemFactory: () => Promise<unknown>,
@@ -1,9 +1,9 @@
1
1
  import { resolve } from 'path';
2
2
  import { IStorageClient } from '../../types/storage';
3
- import { ImageMessage } from '../responses/imageMessage';
4
- import { TextMessage } from '../responses/textMessage';
5
- import { VideoMessage } from '../responses/videoMessage';
6
- import { UnpinResponse } from '../responses/unpin';
3
+ import { ImageMessage } from '../../dtos/responses/imageMessage';
4
+ import { TextMessage } from '../../dtos/responses/textMessage';
5
+ import { VideoMessage } from '../../dtos/responses/videoMessage';
6
+ import { UnpinResponse } from '../../dtos/responses/unpin';
7
7
  import {
8
8
  MessageSendingOptions,
9
9
  TextMessageSendingOptions
@@ -12,61 +12,62 @@ import { IActionWithState } from '../../types/actionWithState';
12
12
  import { IActionState } from '../../types/actionState';
13
13
  import { BotResponse } from '../../types/response';
14
14
  import { Milliseconds } from '../../types/timeValues';
15
- import { DelayResponse } from '../responses/delay';
15
+ import { DelayResponse } from '../../dtos/responses/delay';
16
+ import { ChatInfo } from '../../dtos/chatInfo';
17
+ import { ILogger } from '../../types/logger';
18
+ import { IScheduler } from '../../types/scheduler';
16
19
 
17
20
  /**
18
21
  * Context of action executed in chat.
19
22
  */
20
23
  export class ChatContext<TActionState extends IActionState> {
21
24
  protected action!: IActionWithState<TActionState>;
22
- updateActions: Array<(state: TActionState) => void> = [];
25
+
26
+ /** Storage client instance for the bot executing this action. */
27
+ readonly storage!: IStorageClient;
28
+ /** Logger instance for the bot executing this action */
29
+ readonly logger!: ILogger;
30
+ /** Scheduler instance for the bot executing this action */
31
+ readonly scheduler!: IScheduler;
32
+
23
33
  /** Trace id of a action execution. */
24
34
  traceId!: number | string;
25
35
  /** Name of a bot that executes this action. */
26
36
  botName!: string;
27
- /** Id of a chat that action is executed in. */
28
- chatId!: number;
29
- /** Name of a chat that action is executed in. */
30
- chatName!: string;
31
- /** Storage client instance for this bot. */
32
- storage!: IStorageClient;
37
+ /** Chat information. */
38
+ chatInfo!: ChatInfo;
33
39
  /** Ordered collection of responses to be processed */
34
40
  responses: BotResponse[] = [];
35
41
 
36
42
  isInitialized = false;
37
43
 
38
- constructor() {}
44
+ constructor(
45
+ storage: IStorageClient,
46
+ logger: ILogger,
47
+ scheduler: IScheduler
48
+ ) {
49
+ this.storage = storage;
50
+ this.logger = logger;
51
+ this.scheduler = scheduler;
52
+ }
39
53
 
40
54
  initializeChatContext(
41
55
  botName: string,
42
56
  action: IActionWithState<TActionState>,
43
- chatId: number,
44
- chatName: string,
45
- traceId: number | string,
46
- storage: IStorageClient
57
+ chatInfo: ChatInfo,
58
+ traceId: number | string
47
59
  ) {
48
60
  this.botName = botName;
49
61
  this.action = action;
50
- this.chatId = chatId;
51
- this.chatName = chatName;
62
+ this.chatInfo = chatInfo;
52
63
  this.traceId = traceId;
53
- this.storage = storage;
54
64
 
55
- this.updateActions = [];
56
65
  this.isInitialized = true;
57
66
  this.responses = [];
58
67
 
59
68
  return this;
60
69
  }
61
70
 
62
- /**
63
- * Manually update the state of an action.
64
- * @param stateUpdateAction Function that will modify state.
65
- */
66
- updateState(stateUpdateAction: (state: TActionState) => void) {
67
- this.updateActions.push(stateUpdateAction);
68
- }
69
-
70
71
  /**
71
72
  * Sends text message to chat after action execution is finished.
72
73
  * If multiple responses are sent, they will be sent in the order they were added, with delay of at least 35ms as per Telegram rate-limit.
@@ -77,7 +78,7 @@ export class ChatContext<TActionState extends IActionState> {
77
78
  this.responses.push(
78
79
  new TextMessage(
79
80
  text,
80
- this.chatId,
81
+ this.chatInfo,
81
82
  undefined,
82
83
  this.traceId,
83
84
  this.action,
@@ -97,7 +98,7 @@ export class ChatContext<TActionState extends IActionState> {
97
98
  this.responses.push(
98
99
  new ImageMessage(
99
100
  { source: resolve(filePath) },
100
- this.chatId,
101
+ this.chatInfo,
101
102
  undefined,
102
103
  this.traceId,
103
104
  this.action,
@@ -117,7 +118,7 @@ export class ChatContext<TActionState extends IActionState> {
117
118
  this.responses.push(
118
119
  new VideoMessage(
119
120
  { source: resolve(filePath) },
120
- this.chatId,
121
+ this.chatInfo,
121
122
  undefined,
122
123
  this.traceId,
123
124
  this.action,
@@ -133,7 +134,12 @@ export class ChatContext<TActionState extends IActionState> {
133
134
  */
134
135
  unpinMessage(messageId: number) {
135
136
  this.responses.push(
136
- new UnpinResponse(messageId, this.chatId, this.traceId, this.action)
137
+ new UnpinResponse(
138
+ messageId,
139
+ this.chatInfo,
140
+ this.traceId,
141
+ this.action
142
+ )
137
143
  );
138
144
  }
139
145
 
@@ -143,7 +149,7 @@ export class ChatContext<TActionState extends IActionState> {
143
149
  */
144
150
  delayNextResponse(delay: Milliseconds) {
145
151
  this.responses.push(
146
- new DelayResponse(delay, this.chatId, this.traceId, this.action)
152
+ new DelayResponse(delay, this.chatInfo, this.traceId, this.action)
147
153
  );
148
154
  }
149
155
  }
@@ -2,19 +2,21 @@ import { resolve } from 'path';
2
2
  import { TelegramEmoji } from 'telegraf/types';
3
3
  import { IStorageClient } from '../../types/storage';
4
4
  import { IActionState } from '../../types/actionState';
5
- import { ImageMessage } from '../responses/imageMessage';
6
- import { Reaction } from '../responses/reaction';
7
- import { TextMessage } from '../responses/textMessage';
8
- import { VideoMessage } from '../responses/videoMessage';
5
+ import { ImageMessage } from '../../dtos/responses/imageMessage';
6
+ import { Reaction } from '../../dtos/responses/reaction';
7
+ import { TextMessage } from '../../dtos/responses/textMessage';
8
+ import { VideoMessage } from '../../dtos/responses/videoMessage';
9
9
  import { ActionStateBase } from '../states/actionStateBase';
10
10
  import { ChatContext } from './chatContext';
11
- import { IncomingMessage } from '../incomingMessage';
11
+ import { IncomingMessage } from '../../dtos/incomingMessage';
12
12
  import {
13
13
  MessageSendingOptions,
14
14
  TextMessageSendingOptions
15
15
  } from '../../types/messageSendingOptions';
16
16
  import { IActionWithState, ActionKey } from '../../types/actionWithState';
17
17
  import { MessageTypeValue } from '../../types/messageTypes';
18
+ import { ILogger } from '../../types/logger';
19
+ import { IScheduler } from '../../types/scheduler';
18
20
  /**
19
21
  * Context of action executed in chat, in response to a message
20
22
  */
@@ -36,15 +38,18 @@ export class MessageContext<
36
38
  /** Type of message being received */
37
39
  messageType!: MessageTypeValue;
38
40
 
39
- constructor() {
40
- super();
41
+ constructor(
42
+ storage: IStorageClient,
43
+ logger: ILogger,
44
+ scheduler: IScheduler
45
+ ) {
46
+ super(storage, logger, scheduler);
41
47
  }
42
48
 
43
49
  initializeMessageContext(
44
50
  botName: string,
45
51
  action: IActionWithState<TActionState>,
46
- message: IncomingMessage,
47
- storage: IStorageClient
52
+ message: IncomingMessage
48
53
  ) {
49
54
  this.messageId = message.message_id;
50
55
  this.messageText = message.text ?? '';
@@ -60,10 +65,8 @@ export class MessageContext<
60
65
  return this.initializeChatContext(
61
66
  botName,
62
67
  action,
63
- message.chat.id,
64
- message.chatName,
65
- message.traceId,
66
- storage
68
+ message.chatInfo,
69
+ message.traceId
67
70
  );
68
71
  }
69
72
 
@@ -80,7 +83,7 @@ export class MessageContext<
80
83
  '-'
81
84
  )}` as ActionKey;
82
85
  const allStates = await this.storage.load(storageKey);
83
- const stateForChat = allStates[this.chatId];
86
+ const stateForChat = allStates[this.chatInfo.id];
84
87
 
85
88
  if (!stateForChat) {
86
89
  return new ActionStateBase() as TAnotherActionState;
@@ -99,7 +102,7 @@ export class MessageContext<
99
102
  this.responses.push(
100
103
  new TextMessage(
101
104
  text,
102
- this.chatId,
105
+ this.chatInfo,
103
106
  this.messageId,
104
107
  this.traceId,
105
108
  this.action,
@@ -119,7 +122,7 @@ export class MessageContext<
119
122
  this.responses.push(
120
123
  new ImageMessage(
121
124
  { source: resolve(filePath) },
122
- this.chatId,
125
+ this.chatInfo,
123
126
  this.messageId,
124
127
  this.traceId,
125
128
  this.action,
@@ -139,7 +142,7 @@ export class MessageContext<
139
142
  this.responses.push(
140
143
  new VideoMessage(
141
144
  { source: resolve(filePath) },
142
- this.chatId,
145
+ this.chatInfo,
143
146
  this.messageId,
144
147
  this.traceId,
145
148
  this.action,
@@ -157,7 +160,7 @@ export class MessageContext<
157
160
  this.responses.push(
158
161
  new Reaction(
159
162
  this.traceId,
160
- this.chatId,
163
+ this.chatInfo,
161
164
  this.messageId,
162
165
  emoji,
163
166
  this.action
@@ -1,9 +1,9 @@
1
1
  import { Milliseconds } from '../types/timeValues';
2
2
 
3
3
  export class TaskRecord {
4
- name: string;
5
- taskId: NodeJS.Timeout;
6
- interval: Milliseconds;
4
+ readonly name: string;
5
+ readonly taskId: NodeJS.Timeout;
6
+ readonly interval: Milliseconds;
7
7
 
8
8
  constructor(name: string, taskId: NodeJS.Timeout, interval: Milliseconds) {
9
9
  this.name = name;
package/helpers/noop.ts CHANGED
@@ -1,9 +1,12 @@
1
+ import { BotResponse } from '../types/response';
2
+
1
3
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
4
  export class Noop {
3
- static async true<T1>(arg1: T1) {
5
+ static NoResponse: BotResponse[] = [];
6
+ static true<T1>(arg1: T1) {
4
7
  return true;
5
8
  }
6
- static async false<T1>(arg1: T1) {
9
+ static false<T1>(arg1: T1) {
7
10
  return false;
8
11
  }
9
12
  static async call<T1, T2>(arg1: T1, arg2: T2): Promise<void>;
package/index.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { startBot, stopBots } from './main';
2
2
  export { CommandActionBuilder } from './helpers/builders/commandActionBuilder';
3
3
  export { CommandActionBuilderWithState } from './helpers/builders/commandActionBuilder';
4
- export * from './helpers/builders/scheduledActionBuilder';
5
4
  export { IStorageClient } from './types/storage';
5
+ export { ILogger } from './types/logger';
6
+ export { IScheduler } from './types/scheduler';
7
+ export * from './helpers/builders/scheduledActionBuilder';
6
8
  export * from './types/actionState';
7
9
  export * from './entities/states/actionStateBase';
8
10
  export { Hours, Milliseconds, Seconds } from './types/timeValues';
package/main.ts CHANGED
@@ -1,19 +1,15 @@
1
1
  import { readFile } from 'fs/promises';
2
2
  import { IStorageClient } from './types/storage';
3
- import { Logger } from './services/logger';
4
3
  import { CommandAction } from './entities/actions/commandAction';
5
4
  import { ScheduledAction } from './entities/actions/scheduledAction';
6
5
  import { IActionState } from './types/actionState';
7
- import { Scheduler } from './services/taskScheduler';
8
6
  import { BotInstance } from './entities/botInstance';
9
7
  import { Seconds } from './types/timeValues';
8
+ import { IScheduler } from './types/scheduler';
9
+ import { ILogger } from './types/logger';
10
10
 
11
11
  const bots: BotInstance[] = [];
12
12
 
13
- function log(text: string) {
14
- Logger.logWithTraceId('ALL BOTS', 'System:Bot', 'System', text);
15
- }
16
-
17
13
  /**
18
14
  * Starts bot
19
15
  */
@@ -28,14 +24,20 @@ async function startBot(options: {
28
24
  scheduled: ScheduledAction<IActionState>[];
29
25
  /** Object containing chat name and chat id pairs. Used for logging and execution of scheduled action. */
30
26
  chats: Record<string, number>;
31
- /** Storage client for bot state storage. If not provided, default `JsonFileStorage` will be used. */
32
- storageClient?: IStorageClient;
33
27
  /** 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.*/
34
28
  storagePath?: string;
35
29
  /** Period of time between execution of scheduled actions. */
36
30
  scheduledPeriod?: Seconds;
37
31
  /** If true, telegram API objects will be logged instead of message content. */
38
32
  verboseLoggingForIncomingMessage?: boolean;
33
+ services?: {
34
+ /** Storage client for bot state storage. If not provided, default `JsonFileStorage` will be used. */
35
+ storageClient?: IStorageClient;
36
+ /** Logger client for bot logging. If not provided, default `JsonFileStorage` will be used. */
37
+ logger?: ILogger;
38
+ /** Scheduler client for bot scheduling. If not provided, default `NodeTimeoutScheduler` will be used. */
39
+ scheduler?: IScheduler;
40
+ };
39
41
  }) {
40
42
  const token = await readFile(options.tokenFilePath, 'utf8');
41
43
  const bot = new BotInstance({
@@ -44,11 +46,15 @@ async function startBot(options: {
44
46
  commands: options.commands,
45
47
  scheduled: options.scheduled,
46
48
  chats: options.chats,
47
- storageClient: options.storageClient,
48
49
  storagePath: options.storagePath,
49
50
  scheduledPeriod: options.scheduledPeriod,
50
51
  verboseLoggingForIncomingMessage:
51
- options.verboseLoggingForIncomingMessage
52
+ options.verboseLoggingForIncomingMessage,
53
+ services: {
54
+ storageClient: options.services?.storageClient,
55
+ logger: options.services?.logger,
56
+ scheduler: options.services?.scheduler
57
+ }
52
58
  });
53
59
  bots.push(bot);
54
60
 
@@ -59,11 +65,6 @@ async function startBot(options: {
59
65
  * Terminates all scheduled tasks, closes storage connections and stops all bots.
60
66
  */
61
67
  async function stopBots(reason: string) {
62
- log(`Recieved termination code: ${reason}`);
63
- Scheduler.stopAll();
64
- log('Acquiring storage semaphore...');
65
-
66
- log('Stopping bots...');
67
68
  for (const bot of bots) {
68
69
  await bot.stop(reason);
69
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chz-telegram-bot",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "async-sema": "^3.1.1",
@@ -3,15 +3,14 @@ import { dirname } from 'path';
3
3
  import { mkdir, readFile, writeFile } from 'fs/promises';
4
4
  import { Sema as Semaphore } from 'async-sema';
5
5
  import { IStorageClient } from '../types/storage';
6
- import { ActionExecutionResult } from '../entities/actionExecutionResult';
7
6
  import { IActionState } from '../types/actionState';
8
7
  import { IActionWithState, ActionKey } from '../types/actionWithState';
9
8
 
10
9
  export class JsonFileStorage implements IStorageClient {
11
- private locks = new Map<ActionKey, Semaphore>();
12
- private cache: Map<string, Record<number, IActionState>>;
13
- private storagePath: string;
14
- private botName: string;
10
+ private readonly locks = new Map<ActionKey, Semaphore>();
11
+ private readonly cache: Map<string, Record<number, IActionState>>;
12
+ private readonly storagePath: string;
13
+ private readonly botName: string;
15
14
 
16
15
  constructor(
17
16
  botName: string,
@@ -129,15 +128,14 @@ export class JsonFileStorage implements IStorageClient {
129
128
  async saveActionExecutionResult<TActionState extends IActionState>(
130
129
  action: IActionWithState<TActionState>,
131
130
  chatId: number,
132
- transactionResult: ActionExecutionResult<TActionState>
131
+ state: TActionState
133
132
  ) {
134
133
  await this.lock(action.key, async () => {
135
134
  const data = await this.loadInternal<TActionState>(action.key);
136
135
 
137
- if (transactionResult.shouldUpdate) {
138
- data[chatId] = transactionResult.data;
139
- await this.save(data, action.key);
140
- }
136
+ data[chatId] = state;
137
+
138
+ await this.save(data, action.key);
141
139
  });
142
140
  }
143
141
 
@@ -1,4 +1,6 @@
1
- class JsonLogger {
1
+ import { ILogger } from '../types/logger';
2
+
3
+ export class JsonLogger implements ILogger {
2
4
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
5
  private serializeError(error: any) {
4
6
  const plainObject: Record<string, unknown> = {};
@@ -46,5 +48,3 @@ class JsonLogger {
46
48
  );
47
49
  }
48
50
  }
49
-
50
- export const Logger = new JsonLogger();