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.
- package/dist/dtos/actionExecutionResult.d.ts +7 -0
- package/dist/dtos/actionExecutionResult.d.ts.map +1 -0
- package/dist/dtos/actionExecutionResult.js +10 -0
- package/dist/dtos/chatInfo.d.ts +8 -0
- package/dist/dtos/chatInfo.d.ts.map +1 -0
- package/dist/dtos/chatInfo.js +10 -0
- package/dist/dtos/commandTriggerCheckResult.d.ts +10 -0
- package/dist/dtos/commandTriggerCheckResult.d.ts.map +1 -0
- package/dist/dtos/commandTriggerCheckResult.js +20 -0
- package/dist/dtos/incomingMessage.d.ts +14 -0
- package/dist/dtos/incomingMessage.d.ts.map +1 -0
- package/dist/dtos/incomingMessage.js +44 -0
- package/dist/dtos/responses/delay.d.ts +14 -0
- package/dist/dtos/responses/delay.d.ts.map +1 -0
- package/dist/dtos/responses/delay.js +14 -0
- package/dist/dtos/responses/imageMessage.d.ts +18 -0
- package/dist/dtos/responses/imageMessage.d.ts.map +1 -0
- package/dist/dtos/responses/imageMessage.js +17 -0
- package/dist/dtos/responses/reaction.d.ts +15 -0
- package/dist/dtos/responses/reaction.d.ts.map +1 -0
- package/dist/dtos/responses/reaction.js +15 -0
- package/dist/dtos/responses/textMessage.d.ts +17 -0
- package/dist/dtos/responses/textMessage.d.ts.map +1 -0
- package/dist/dtos/responses/textMessage.js +17 -0
- package/dist/dtos/responses/unpin.d.ts +13 -0
- package/dist/dtos/responses/unpin.d.ts.map +1 -0
- package/dist/dtos/responses/unpin.js +14 -0
- package/dist/dtos/responses/videoMessage.d.ts +18 -0
- package/dist/dtos/responses/videoMessage.d.ts.map +1 -0
- package/dist/dtos/responses/videoMessage.js +17 -0
- package/dist/entities/actions/commandAction.d.ts +11 -10
- package/dist/entities/actions/commandAction.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.js +41 -40
- package/dist/entities/actions/scheduledAction.d.ts +11 -11
- package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
- package/dist/entities/actions/scheduledAction.js +14 -18
- package/dist/entities/botInstance.d.ts +16 -8
- package/dist/entities/botInstance.d.ts.map +1 -1
- package/dist/entities/botInstance.js +26 -18
- package/dist/entities/cachedStateFactory.d.ts +2 -2
- package/dist/entities/cachedStateFactory.d.ts.map +1 -1
- package/dist/entities/context/chatContext.d.ts +13 -14
- package/dist/entities/context/chatContext.d.ts.map +1 -1
- package/dist/entities/context/chatContext.js +16 -24
- package/dist/entities/context/messageContext.d.ts +5 -3
- package/dist/entities/context/messageContext.d.ts.map +1 -1
- package/dist/entities/context/messageContext.js +13 -13
- package/dist/entities/taskRecord.d.ts +3 -3
- package/dist/entities/taskRecord.d.ts.map +1 -1
- package/dist/helpers/noop.d.ts +4 -2
- package/dist/helpers/noop.d.ts.map +1 -1
- package/dist/helpers/noop.js +3 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/main.d.ts +10 -2
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +6 -11
- package/dist/services/jsonFileStorage.d.ts +5 -6
- package/dist/services/jsonFileStorage.d.ts.map +1 -1
- package/dist/services/jsonFileStorage.js +3 -5
- package/dist/services/jsonLogger.d.ts +8 -0
- package/dist/services/jsonLogger.d.ts.map +1 -0
- package/dist/services/jsonLogger.js +28 -0
- package/dist/services/nodeTimeoutScheduler.d.ts +13 -0
- package/dist/services/nodeTimeoutScheduler.d.ts.map +1 -0
- package/dist/services/nodeTimeoutScheduler.js +33 -0
- package/dist/services/taskScheduler.d.ts +1 -1
- package/dist/services/taskScheduler.d.ts.map +1 -1
- package/dist/services/telegramApi.d.ts +7 -14
- package/dist/services/telegramApi.d.ts.map +1 -1
- package/dist/services/telegramApi.js +12 -20
- package/dist/types/actionWithState.d.ts +2 -2
- package/dist/types/actionWithState.d.ts.map +1 -1
- package/dist/types/commandCondition.d.ts +1 -1
- package/dist/types/commandCondition.d.ts.map +1 -1
- package/dist/types/logger.d.ts +6 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/logger.js +2 -0
- package/dist/types/response.d.ts +15 -14
- package/dist/types/response.d.ts.map +1 -1
- package/dist/types/scheduler.d.ts +7 -0
- package/dist/types/scheduler.d.ts.map +1 -0
- package/dist/types/scheduler.js +2 -0
- package/dist/types/storage.d.ts +1 -2
- package/dist/types/storage.d.ts.map +1 -1
- package/dtos/chatInfo.ts +11 -0
- package/{entities → dtos}/commandTriggerCheckResult.ts +10 -10
- package/{entities → dtos}/incomingMessage.ts +13 -12
- package/{entities → dtos}/responses/delay.ts +8 -7
- package/{entities → dtos}/responses/imageMessage.ts +11 -10
- package/{entities → dtos}/responses/reaction.ts +9 -8
- package/{entities → dtos}/responses/textMessage.ts +11 -10
- package/{entities → dtos}/responses/unpin.ts +8 -7
- package/{entities → dtos}/responses/videoMessage.ts +11 -10
- package/entities/actions/commandAction.ts +64 -56
- package/entities/actions/scheduledAction.ts +40 -43
- package/entities/botInstance.ts +64 -35
- package/entities/cachedStateFactory.ts +2 -2
- package/entities/context/chatContext.ts +40 -34
- package/entities/context/messageContext.ts +21 -18
- package/entities/taskRecord.ts +3 -3
- package/helpers/noop.ts +5 -2
- package/index.ts +3 -1
- package/main.ts +16 -15
- package/package.json +1 -1
- package/services/jsonFileStorage.ts +8 -10
- package/services/{logger.ts → jsonLogger.ts} +3 -3
- package/services/{taskScheduler.ts → nodeTimeoutScheduler.ts} +12 -8
- package/services/telegramApi.ts +19 -54
- package/types/actionWithState.ts +2 -2
- package/types/commandCondition.ts +1 -1
- package/types/logger.ts +24 -0
- package/types/response.ts +15 -14
- package/types/scheduler.ts +20 -0
- package/types/storage.ts +1 -2
- package/entities/actionExecutionResult.ts +0 -11
- package/helpers/inverseRecord.ts +0 -7
package/entities/botInstance.ts
CHANGED
|
@@ -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 {
|
|
14
|
-
import {
|
|
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
|
-
|
|
22
|
-
private
|
|
23
|
-
private
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
114
|
+
this.scheduler.createOnetimeTask(
|
|
102
115
|
'ScheduledProcessing_OneTime',
|
|
103
116
|
async () => {
|
|
104
|
-
|
|
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
|
-
|
|
145
|
+
this.logger.logObjectWithTraceId(
|
|
133
146
|
this.name,
|
|
134
147
|
msg.traceId,
|
|
135
|
-
msg.
|
|
148
|
+
msg.chatInfo.name,
|
|
136
149
|
ctx.update.message
|
|
137
150
|
);
|
|
138
151
|
} else {
|
|
139
|
-
|
|
152
|
+
this.logger.logWithTraceId(
|
|
140
153
|
this.name,
|
|
141
154
|
msg.traceId,
|
|
142
|
-
msg.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
228
|
+
this.logger.errorWithTraceId(
|
|
200
229
|
ctx.botName,
|
|
201
230
|
ctx.traceId,
|
|
202
|
-
ctx.
|
|
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 '
|
|
4
|
-
import { TextMessage } from '
|
|
5
|
-
import { VideoMessage } from '
|
|
6
|
-
import { UnpinResponse } from '
|
|
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 '
|
|
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
|
-
|
|
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
|
-
/**
|
|
28
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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.
|
|
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 '
|
|
6
|
-
import { Reaction } from '
|
|
7
|
-
import { TextMessage } from '
|
|
8
|
-
import { VideoMessage } from '
|
|
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 '
|
|
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
|
-
|
|
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.
|
|
64
|
-
message.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
163
|
+
this.chatInfo,
|
|
161
164
|
this.messageId,
|
|
162
165
|
emoji,
|
|
163
166
|
this.action
|
package/entities/taskRecord.ts
CHANGED
|
@@ -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
|
|
5
|
+
static NoResponse: BotResponse[] = [];
|
|
6
|
+
static true<T1>(arg1: T1) {
|
|
4
7
|
return true;
|
|
5
8
|
}
|
|
6
|
-
static
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
136
|
+
data[chatId] = state;
|
|
137
|
+
|
|
138
|
+
await this.save(data, action.key);
|
|
141
139
|
});
|
|
142
140
|
}
|
|
143
141
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
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();
|