chz-telegram-bot 0.0.51 → 0.0.53
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/commandTriggerCheckResult.d.ts +5 -5
- package/dist/dtos/commandTriggerCheckResult.d.ts.map +1 -1
- package/dist/dtos/commandTriggerCheckResult.js +3 -6
- package/dist/dtos/incomingMessage.d.ts +8 -7
- package/dist/dtos/incomingMessage.d.ts.map +1 -1
- package/dist/dtos/incomingMessage.js +3 -2
- package/dist/dtos/responses/delay.d.ts +3 -2
- package/dist/dtos/responses/delay.d.ts.map +1 -1
- package/dist/dtos/responses/imageMessage.d.ts +3 -2
- package/dist/dtos/responses/imageMessage.d.ts.map +1 -1
- package/dist/dtos/responses/reaction.d.ts +3 -2
- package/dist/dtos/responses/reaction.d.ts.map +1 -1
- package/dist/dtos/responses/textMessage.d.ts +3 -2
- package/dist/dtos/responses/textMessage.d.ts.map +1 -1
- package/dist/dtos/responses/unpin.d.ts +3 -2
- package/dist/dtos/responses/unpin.d.ts.map +1 -1
- package/dist/dtos/responses/videoMessage.d.ts +3 -2
- package/dist/dtos/responses/videoMessage.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.d.ts +1 -0
- package/dist/entities/actions/commandAction.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.js +38 -37
- package/dist/entities/actions/scheduledAction.d.ts +1 -1
- package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
- package/dist/entities/actions/scheduledAction.js +12 -16
- package/dist/entities/botInstance.d.ts +11 -3
- package/dist/entities/botInstance.d.ts.map +1 -1
- package/dist/entities/botInstance.js +26 -18
- package/dist/entities/context/chatContext.d.ts +12 -11
- package/dist/entities/context/chatContext.d.ts.map +1 -1
- package/dist/entities/context/chatContext.js +5 -12
- package/dist/entities/context/messageContext.d.ts +4 -2
- package/dist/entities/context/messageContext.d.ts.map +1 -1
- package/dist/entities/context/messageContext.js +4 -4
- 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/helpers/traceFactory.d.ts +3 -0
- package/dist/helpers/traceFactory.d.ts.map +1 -0
- package/dist/helpers/traceFactory.js +6 -0
- package/dist/index.d.ts +4 -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 +1 -2
- package/dist/services/jsonFileStorage.d.ts.map +1 -1
- package/dist/services/jsonFileStorage.js +3 -5
- package/dist/services/jsonLogger.d.ts +9 -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 +34 -0
- package/dist/services/telegramApi.d.ts +7 -5
- package/dist/services/telegramApi.d.ts.map +1 -1
- package/dist/services/telegramApi.js +4 -4
- package/dist/types/commandCondition.d.ts +1 -1
- package/dist/types/commandCondition.d.ts.map +1 -1
- package/dist/types/logger.d.ts +7 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/logger.js +2 -0
- package/dist/types/response.d.ts +2 -1
- 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/dist/types/trace.d.ts +2 -0
- package/dist/types/trace.d.ts.map +1 -0
- package/dist/types/trace.js +2 -0
- package/dtos/commandTriggerCheckResult.ts +10 -10
- package/dtos/incomingMessage.ts +17 -7
- package/dtos/responses/delay.ts +3 -2
- package/dtos/responses/imageMessage.ts +3 -2
- package/dtos/responses/reaction.ts +3 -2
- package/dtos/responses/textMessage.ts +3 -2
- package/dtos/responses/unpin.ts +3 -2
- package/dtos/responses/videoMessage.ts +3 -2
- package/entities/actions/commandAction.ts +48 -41
- package/entities/actions/scheduledAction.ts +28 -31
- package/entities/botInstance.ts +57 -34
- package/entities/context/chatContext.ts +22 -17
- package/entities/context/messageContext.ts +10 -6
- package/helpers/noop.ts +5 -2
- package/helpers/traceFactory.ts +9 -0
- package/index.ts +4 -1
- package/main.ts +16 -15
- package/package.json +1 -1
- package/services/jsonFileStorage.ts +4 -6
- package/services/{logger.ts → jsonLogger.ts} +7 -6
- package/services/{taskScheduler.ts → nodeTimeoutScheduler.ts} +15 -10
- package/services/telegramApi.ts +15 -8
- package/types/commandCondition.ts +1 -1
- package/types/logger.ts +26 -0
- package/types/response.ts +2 -1
- package/types/scheduler.ts +20 -0
- package/types/storage.ts +1 -2
- package/types/trace.ts +1 -0
- package/dtos/actionExecutionResult.ts +0 -11
package/entities/botInstance.ts
CHANGED
|
@@ -10,22 +10,28 @@ 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 { Scheduler } from '../services/taskScheduler';
|
|
13
|
+
import { JsonLogger } from '../services/jsonLogger';
|
|
15
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';
|
|
19
18
|
import { ChatInfo } from '../dtos/chatInfo';
|
|
19
|
+
import { ILogger } from '../types/logger';
|
|
20
|
+
import { IScheduler } from '../types/scheduler';
|
|
21
|
+
import { NodeTimeoutScheduler } from '../services/nodeTimeoutScheduler';
|
|
22
|
+
import { createTrace } from '../helpers/traceFactory';
|
|
20
23
|
|
|
21
24
|
export class BotInstance {
|
|
22
|
-
readonly name: string;
|
|
23
25
|
private readonly api: TelegramApiService;
|
|
26
|
+
private readonly storage: IStorageClient;
|
|
27
|
+
private readonly scheduler: IScheduler;
|
|
28
|
+
private readonly logger: ILogger;
|
|
29
|
+
|
|
30
|
+
readonly name: string;
|
|
24
31
|
private readonly telegraf: Telegraf;
|
|
25
32
|
private readonly commands: CommandAction<IActionState>[];
|
|
26
33
|
private readonly scheduled: ScheduledAction<IActionState>[];
|
|
27
34
|
private readonly chats: Record<string, number>;
|
|
28
|
-
readonly storage: IStorageClient;
|
|
29
35
|
|
|
30
36
|
constructor(options: {
|
|
31
37
|
name: string;
|
|
@@ -33,10 +39,14 @@ export class BotInstance {
|
|
|
33
39
|
commands: CommandAction<IActionState>[];
|
|
34
40
|
scheduled: ScheduledAction<IActionState>[];
|
|
35
41
|
chats: Record<string, number>;
|
|
36
|
-
storageClient?: IStorageClient;
|
|
37
42
|
storagePath?: string;
|
|
38
43
|
scheduledPeriod?: Seconds;
|
|
39
44
|
verboseLoggingForIncomingMessage?: boolean;
|
|
45
|
+
services?: {
|
|
46
|
+
storageClient?: IStorageClient;
|
|
47
|
+
logger?: ILogger;
|
|
48
|
+
scheduler?: IScheduler;
|
|
49
|
+
};
|
|
40
50
|
}) {
|
|
41
51
|
this.name = options.name;
|
|
42
52
|
this.commands = options.commands;
|
|
@@ -44,21 +54,19 @@ export class BotInstance {
|
|
|
44
54
|
this.chats = options.chats;
|
|
45
55
|
|
|
46
56
|
const actions = [...this.commands, ...this.scheduled];
|
|
47
|
-
|
|
48
|
-
Logger.logWithTraceId(
|
|
49
|
-
this.name,
|
|
50
|
-
`System:Bot-${this.name}-Start`,
|
|
51
|
-
'System',
|
|
52
|
-
'Starting bot...'
|
|
53
|
-
);
|
|
54
57
|
this.telegraf = new Telegraf(options.token);
|
|
58
|
+
this.logger = options.services?.logger ?? new JsonLogger();
|
|
59
|
+
this.scheduler =
|
|
60
|
+
options.services?.scheduler ??
|
|
61
|
+
new NodeTimeoutScheduler(this.logger);
|
|
55
62
|
this.storage =
|
|
56
|
-
options.storageClient ??
|
|
63
|
+
options.services?.storageClient ??
|
|
57
64
|
new JsonFileStorage(options.name, actions, options.storagePath);
|
|
58
65
|
this.api = new TelegramApiService(
|
|
59
66
|
this.name,
|
|
60
67
|
this.telegraf.telegram,
|
|
61
|
-
this.storage
|
|
68
|
+
this.storage,
|
|
69
|
+
this.logger
|
|
62
70
|
);
|
|
63
71
|
|
|
64
72
|
this.initializeMessageProcessing(
|
|
@@ -70,6 +78,12 @@ export class BotInstance {
|
|
|
70
78
|
|
|
71
79
|
this.storage.saveMetadata(actions, this.name);
|
|
72
80
|
|
|
81
|
+
this.logger.logWithTraceId(
|
|
82
|
+
this.name,
|
|
83
|
+
createTrace(this, this.name, 'Start'),
|
|
84
|
+
'System',
|
|
85
|
+
'Starting bot...'
|
|
86
|
+
);
|
|
73
87
|
this.telegraf.launch();
|
|
74
88
|
}
|
|
75
89
|
|
|
@@ -78,7 +92,7 @@ export class BotInstance {
|
|
|
78
92
|
const now = moment();
|
|
79
93
|
|
|
80
94
|
if (now.minute() == 0 && now.second() == 0) {
|
|
81
|
-
|
|
95
|
+
this.scheduler.createTask(
|
|
82
96
|
'ScheduledProcessing',
|
|
83
97
|
async () => {
|
|
84
98
|
await this.runScheduled();
|
|
@@ -98,10 +112,10 @@ export class BotInstance {
|
|
|
98
112
|
|
|
99
113
|
const delay = nextExecutionTime.diff(now);
|
|
100
114
|
|
|
101
|
-
|
|
115
|
+
this.scheduler.createOnetimeTask(
|
|
102
116
|
'ScheduledProcessing_OneTime',
|
|
103
117
|
async () => {
|
|
104
|
-
|
|
118
|
+
this.scheduler.createTask(
|
|
105
119
|
'ScheduledProcessing',
|
|
106
120
|
async () => {
|
|
107
121
|
await this.runScheduled();
|
|
@@ -121,7 +135,7 @@ export class BotInstance {
|
|
|
121
135
|
) {
|
|
122
136
|
if (this.commands.length > 0) {
|
|
123
137
|
this.telegraf.on('message', async (ctx) => {
|
|
124
|
-
const msg = new IncomingMessage(ctx.update.message);
|
|
138
|
+
const msg = new IncomingMessage(ctx.update.message, this.name);
|
|
125
139
|
const messageContent =
|
|
126
140
|
msg.text || `<non-text message: ${msg.type}>`;
|
|
127
141
|
|
|
@@ -129,14 +143,14 @@ export class BotInstance {
|
|
|
129
143
|
const messageFromId = msg.from?.id ?? 'Unknown';
|
|
130
144
|
|
|
131
145
|
if (verboseLoggingForIncomingMessage) {
|
|
132
|
-
|
|
146
|
+
this.logger.logObjectWithTraceId(
|
|
133
147
|
this.name,
|
|
134
148
|
msg.traceId,
|
|
135
149
|
msg.chatInfo.name,
|
|
136
150
|
ctx.update.message
|
|
137
151
|
);
|
|
138
152
|
} else {
|
|
139
|
-
|
|
153
|
+
this.logger.logWithTraceId(
|
|
140
154
|
this.name,
|
|
141
155
|
msg.traceId,
|
|
142
156
|
msg.chatInfo.name,
|
|
@@ -150,19 +164,24 @@ export class BotInstance {
|
|
|
150
164
|
}
|
|
151
165
|
|
|
152
166
|
async stop(code: string) {
|
|
153
|
-
|
|
167
|
+
this.logger.logWithTraceId(
|
|
154
168
|
this.name,
|
|
155
|
-
|
|
169
|
+
createTrace(this, this.name, 'Stop'),
|
|
156
170
|
'System',
|
|
157
171
|
'Stopping bot...'
|
|
158
172
|
);
|
|
159
173
|
|
|
174
|
+
this.scheduler.stopAll();
|
|
160
175
|
await this.storage.close();
|
|
161
176
|
this.telegraf.stop(code);
|
|
162
177
|
}
|
|
163
178
|
|
|
164
179
|
private async runScheduled() {
|
|
165
|
-
const ctx = new ChatContext<IActionState>(
|
|
180
|
+
const ctx = new ChatContext<IActionState>(
|
|
181
|
+
this.storage,
|
|
182
|
+
this.logger,
|
|
183
|
+
this.scheduler
|
|
184
|
+
);
|
|
166
185
|
|
|
167
186
|
for (const [chatName, chatId] of Object.entries(this.chats)) {
|
|
168
187
|
for (const scheduledAction of this.scheduled) {
|
|
@@ -170,15 +189,19 @@ export class BotInstance {
|
|
|
170
189
|
this.name,
|
|
171
190
|
scheduledAction,
|
|
172
191
|
new ChatInfo(chatId, chatName),
|
|
173
|
-
|
|
174
|
-
|
|
192
|
+
createTrace(
|
|
193
|
+
scheduledAction,
|
|
194
|
+
this.name,
|
|
195
|
+
`${scheduledAction.key}-${chatId}`
|
|
196
|
+
)
|
|
175
197
|
);
|
|
176
198
|
|
|
177
199
|
try {
|
|
178
200
|
const responses = await scheduledAction.exec(ctx);
|
|
179
201
|
this.api.enqueueBatchedResponses(responses);
|
|
202
|
+
ctx.isInitialized = false;
|
|
180
203
|
} catch (error) {
|
|
181
|
-
|
|
204
|
+
this.logger.errorWithTraceId(
|
|
182
205
|
ctx.botName,
|
|
183
206
|
ctx.traceId,
|
|
184
207
|
chatName,
|
|
@@ -193,21 +216,21 @@ export class BotInstance {
|
|
|
193
216
|
}
|
|
194
217
|
|
|
195
218
|
private async processMessage(msg: IncomingMessage) {
|
|
196
|
-
const ctx = new MessageContext<IActionState>(
|
|
219
|
+
const ctx = new MessageContext<IActionState>(
|
|
220
|
+
this.storage,
|
|
221
|
+
this.logger,
|
|
222
|
+
this.scheduler
|
|
223
|
+
);
|
|
197
224
|
|
|
198
225
|
for (const commandAction of this.commands) {
|
|
199
|
-
ctx.initializeMessageContext(
|
|
200
|
-
this.name,
|
|
201
|
-
commandAction,
|
|
202
|
-
msg,
|
|
203
|
-
this.storage
|
|
204
|
-
);
|
|
226
|
+
ctx.initializeMessageContext(this.name, commandAction, msg);
|
|
205
227
|
|
|
206
228
|
try {
|
|
207
229
|
const responses = await commandAction.exec(ctx);
|
|
208
230
|
this.api.enqueueBatchedResponses(responses);
|
|
231
|
+
ctx.isInitialized = false;
|
|
209
232
|
} catch (error) {
|
|
210
|
-
|
|
233
|
+
this.logger.errorWithTraceId(
|
|
211
234
|
ctx.botName,
|
|
212
235
|
ctx.traceId,
|
|
213
236
|
ctx.chatInfo.name,
|
|
@@ -14,56 +14,61 @@ import { BotResponse } from '../../types/response';
|
|
|
14
14
|
import { Milliseconds } from '../../types/timeValues';
|
|
15
15
|
import { DelayResponse } from '../../dtos/responses/delay';
|
|
16
16
|
import { ChatInfo } from '../../dtos/chatInfo';
|
|
17
|
+
import { ILogger } from '../../types/logger';
|
|
18
|
+
import { IScheduler } from '../../types/scheduler';
|
|
19
|
+
import { TraceId } from '../../types/trace';
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Context of action executed in chat.
|
|
20
23
|
*/
|
|
21
24
|
export class ChatContext<TActionState extends IActionState> {
|
|
22
25
|
protected action!: IActionWithState<TActionState>;
|
|
23
|
-
|
|
26
|
+
|
|
27
|
+
/** Storage client instance for the bot executing this action. */
|
|
28
|
+
readonly storage!: IStorageClient;
|
|
29
|
+
/** Logger instance for the bot executing this action */
|
|
30
|
+
readonly logger!: ILogger;
|
|
31
|
+
/** Scheduler instance for the bot executing this action */
|
|
32
|
+
readonly scheduler!: IScheduler;
|
|
33
|
+
|
|
24
34
|
/** Trace id of a action execution. */
|
|
25
|
-
traceId!:
|
|
35
|
+
traceId!: TraceId;
|
|
26
36
|
/** Name of a bot that executes this action. */
|
|
27
37
|
botName!: string;
|
|
28
38
|
/** Chat information. */
|
|
29
39
|
chatInfo!: ChatInfo;
|
|
30
|
-
/** Storage client instance for this bot. */
|
|
31
|
-
storage!: IStorageClient;
|
|
32
40
|
/** Ordered collection of responses to be processed */
|
|
33
41
|
responses: BotResponse[] = [];
|
|
34
42
|
|
|
35
43
|
isInitialized = false;
|
|
36
44
|
|
|
37
|
-
constructor(
|
|
45
|
+
constructor(
|
|
46
|
+
storage: IStorageClient,
|
|
47
|
+
logger: ILogger,
|
|
48
|
+
scheduler: IScheduler
|
|
49
|
+
) {
|
|
50
|
+
this.storage = storage;
|
|
51
|
+
this.logger = logger;
|
|
52
|
+
this.scheduler = scheduler;
|
|
53
|
+
}
|
|
38
54
|
|
|
39
55
|
initializeChatContext(
|
|
40
56
|
botName: string,
|
|
41
57
|
action: IActionWithState<TActionState>,
|
|
42
58
|
chatInfo: ChatInfo,
|
|
43
|
-
traceId:
|
|
44
|
-
storage: IStorageClient
|
|
59
|
+
traceId: TraceId
|
|
45
60
|
) {
|
|
46
61
|
this.botName = botName;
|
|
47
62
|
this.action = action;
|
|
48
63
|
this.chatInfo = chatInfo;
|
|
49
64
|
this.traceId = traceId;
|
|
50
|
-
this.storage = storage;
|
|
51
65
|
|
|
52
|
-
this.updateActions = [];
|
|
53
66
|
this.isInitialized = true;
|
|
54
67
|
this.responses = [];
|
|
55
68
|
|
|
56
69
|
return this;
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
/**
|
|
60
|
-
* Manually update the state of an action.
|
|
61
|
-
* @param stateUpdateAction Function that will modify state.
|
|
62
|
-
*/
|
|
63
|
-
updateState(stateUpdateAction: (state: TActionState) => void) {
|
|
64
|
-
this.updateActions.push(stateUpdateAction);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
72
|
/**
|
|
68
73
|
* Sends text message to chat after action execution is finished.
|
|
69
74
|
* 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.
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
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 ?? '';
|
|
@@ -61,8 +66,7 @@ export class MessageContext<
|
|
|
61
66
|
botName,
|
|
62
67
|
action,
|
|
63
68
|
message.chatInfo,
|
|
64
|
-
message.traceId
|
|
65
|
-
storage
|
|
69
|
+
message.traceId
|
|
66
70
|
);
|
|
67
71
|
}
|
|
68
72
|
|
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,9 +1,12 @@
|
|
|
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';
|
|
9
11
|
export { MessageType } from './types/messageTypes';
|
|
12
|
+
export { TraceId } from './types/trace';
|
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,7 +3,6 @@ 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 '../dtos/actionExecutionResult';
|
|
7
6
|
import { IActionState } from '../types/actionState';
|
|
8
7
|
import { IActionWithState, ActionKey } from '../types/actionWithState';
|
|
9
8
|
|
|
@@ -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,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { ILogger } from '../types/logger';
|
|
2
|
+
import { TraceId } from '../types/trace';
|
|
3
|
+
|
|
4
|
+
export class JsonLogger implements ILogger {
|
|
2
5
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
6
|
private serializeError(error: any) {
|
|
4
7
|
const plainObject: Record<string, unknown> = {};
|
|
@@ -10,7 +13,7 @@ class JsonLogger {
|
|
|
10
13
|
|
|
11
14
|
logObjectWithTraceId(
|
|
12
15
|
botName: string,
|
|
13
|
-
traceId:
|
|
16
|
+
traceId: TraceId,
|
|
14
17
|
chatName: string,
|
|
15
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
19
|
data: any
|
|
@@ -23,7 +26,7 @@ class JsonLogger {
|
|
|
23
26
|
|
|
24
27
|
logWithTraceId(
|
|
25
28
|
botName: string,
|
|
26
|
-
traceId:
|
|
29
|
+
traceId: TraceId,
|
|
27
30
|
chatName: string,
|
|
28
31
|
text: string
|
|
29
32
|
) {
|
|
@@ -34,7 +37,7 @@ class JsonLogger {
|
|
|
34
37
|
|
|
35
38
|
errorWithTraceId<TData>(
|
|
36
39
|
botName: string,
|
|
37
|
-
traceId:
|
|
40
|
+
traceId: TraceId,
|
|
38
41
|
chatName: string,
|
|
39
42
|
errorObj: unknown,
|
|
40
43
|
extraData?: TData | undefined
|
|
@@ -46,5 +49,3 @@ class JsonLogger {
|
|
|
46
49
|
);
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
|
-
|
|
50
|
-
export const Logger = new JsonLogger();
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { TaskRecord } from '../entities/taskRecord';
|
|
2
|
+
import { createTrace } from '../helpers/traceFactory';
|
|
3
|
+
import { ILogger } from '../types/logger';
|
|
4
|
+
import { IScheduler } from '../types/scheduler';
|
|
2
5
|
import { Milliseconds } from '../types/timeValues';
|
|
3
|
-
import { Logger } from './logger';
|
|
4
6
|
|
|
5
|
-
class
|
|
7
|
+
export class NodeTimeoutScheduler implements IScheduler {
|
|
8
|
+
private readonly logger!: ILogger;
|
|
6
9
|
readonly activeTasks: TaskRecord[] = [];
|
|
7
10
|
|
|
11
|
+
constructor(logger: ILogger) {
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
stopAll() {
|
|
9
16
|
this.activeTasks.forEach((task) => {
|
|
10
17
|
clearInterval(task.taskId);
|
|
@@ -25,9 +32,9 @@ class TaskScheduler {
|
|
|
25
32
|
setImmediate(action);
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
this.logger.logWithTraceId(
|
|
29
36
|
ownerName,
|
|
30
|
-
|
|
37
|
+
createTrace(this, ownerName, name),
|
|
31
38
|
'System',
|
|
32
39
|
`Created task [${taskId}]${name}, that will run every ${interval}ms.`
|
|
33
40
|
);
|
|
@@ -42,9 +49,9 @@ class TaskScheduler {
|
|
|
42
49
|
ownerName: string
|
|
43
50
|
) {
|
|
44
51
|
const actionWrapper = () => {
|
|
45
|
-
|
|
52
|
+
this.logger.logWithTraceId(
|
|
46
53
|
ownerName,
|
|
47
|
-
|
|
54
|
+
createTrace(this, ownerName, name),
|
|
48
55
|
'System',
|
|
49
56
|
`Executing delayed oneshot [${taskId}]${name}`
|
|
50
57
|
);
|
|
@@ -52,13 +59,11 @@ class TaskScheduler {
|
|
|
52
59
|
};
|
|
53
60
|
const taskId = setTimeout(actionWrapper, delay);
|
|
54
61
|
|
|
55
|
-
|
|
62
|
+
this.logger.logWithTraceId(
|
|
56
63
|
ownerName,
|
|
57
|
-
|
|
64
|
+
createTrace(this, ownerName, name),
|
|
58
65
|
'System',
|
|
59
66
|
`Created oneshot task [${taskId}]${name}, that will run in ${delay}ms.`
|
|
60
67
|
);
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
|
-
|
|
64
|
-
export const Scheduler = new TaskScheduler();
|
package/services/telegramApi.ts
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
import { Message } from 'telegraf/types';
|
|
2
2
|
import { IStorageClient } from '../types/storage';
|
|
3
|
-
import { Logger } from './logger';
|
|
4
3
|
import { BotResponse, IReplyMessage } from '../types/response';
|
|
5
4
|
import { Telegram } from 'telegraf/typings/telegram';
|
|
6
5
|
import { setTimeout } from 'timers/promises';
|
|
7
6
|
import { Milliseconds } from '../types/timeValues';
|
|
7
|
+
import { ILogger } from '../types/logger';
|
|
8
8
|
|
|
9
9
|
const TELEGRAM_RATELIMIT_DELAY = 35 as Milliseconds;
|
|
10
10
|
|
|
11
11
|
export class TelegramApiService {
|
|
12
|
-
|
|
13
|
-
readonly
|
|
12
|
+
private readonly telegram: Telegram;
|
|
13
|
+
private readonly storage: IStorageClient;
|
|
14
|
+
private readonly logger: ILogger;
|
|
14
15
|
|
|
15
|
-
readonly botName: string;
|
|
16
|
-
readonly
|
|
17
|
-
|
|
16
|
+
private readonly botName: string;
|
|
17
|
+
private readonly messageQueue: BotResponse[] = [];
|
|
18
|
+
isFlushing = false;
|
|
18
19
|
|
|
19
|
-
constructor(
|
|
20
|
+
constructor(
|
|
21
|
+
botName: string,
|
|
22
|
+
telegram: Telegram,
|
|
23
|
+
storage: IStorageClient,
|
|
24
|
+
logger: ILogger
|
|
25
|
+
) {
|
|
20
26
|
this.telegram = telegram;
|
|
21
27
|
this.botName = botName;
|
|
22
28
|
this.storage = storage;
|
|
29
|
+
this.logger = logger;
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
enqueueBatchedResponses(responses: BotResponse[]) {
|
|
@@ -42,7 +49,7 @@ export class TelegramApiService {
|
|
|
42
49
|
await this.processResponse(message);
|
|
43
50
|
await setTimeout(TELEGRAM_RATELIMIT_DELAY);
|
|
44
51
|
} catch (error) {
|
|
45
|
-
|
|
52
|
+
this.logger.errorWithTraceId(
|
|
46
53
|
this.botName,
|
|
47
54
|
message.traceId,
|
|
48
55
|
message.chatInfo.name,
|
package/types/logger.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TraceId } from './trace';
|
|
2
|
+
|
|
3
|
+
export interface ILogger {
|
|
4
|
+
logObjectWithTraceId(
|
|
5
|
+
botName: string,
|
|
6
|
+
traceId: TraceId,
|
|
7
|
+
chatName: string,
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
data: any
|
|
10
|
+
): void;
|
|
11
|
+
|
|
12
|
+
logWithTraceId(
|
|
13
|
+
botName: string,
|
|
14
|
+
traceId: TraceId,
|
|
15
|
+
chatName: string,
|
|
16
|
+
text: string
|
|
17
|
+
): void;
|
|
18
|
+
|
|
19
|
+
errorWithTraceId<TData>(
|
|
20
|
+
botName: string,
|
|
21
|
+
traceId: TraceId,
|
|
22
|
+
chatName: string,
|
|
23
|
+
errorObj: unknown,
|
|
24
|
+
extraData?: TData | undefined
|
|
25
|
+
): void;
|
|
26
|
+
}
|