chz-telegram-bot 0.3.10 → 0.3.12
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/builtin/helpAction.ts +1 -1
- package/dist/builtin/helpAction.js +1 -1
- package/dist/dtos/chatInfo.js +4 -0
- package/dist/dtos/commandTriggerCheckResult.js +3 -0
- package/dist/dtos/incomingMessage.js +9 -1
- package/dist/dtos/incomingQuery.js +5 -0
- package/dist/dtos/replyInfo.js +2 -0
- package/dist/dtos/responses/delay.js +6 -2
- package/dist/dtos/responses/imageMessage.js +10 -4
- package/dist/dtos/responses/inlineQueryResponse.js +6 -2
- package/dist/dtos/responses/reaction.js +7 -2
- package/dist/dtos/responses/textMessage.js +10 -3
- package/dist/dtos/responses/unpin.js +6 -2
- package/dist/dtos/responses/videoMessage.js +10 -4
- package/dist/entities/actions/commandAction.d.ts +4 -1
- package/dist/entities/actions/commandAction.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.js +46 -17
- package/dist/entities/actions/inlineQueryAction.d.ts.map +1 -1
- package/dist/entities/actions/inlineQueryAction.js +8 -1
- package/dist/entities/actions/replyCaptureAction.d.ts.map +1 -1
- package/dist/entities/actions/replyCaptureAction.js +8 -1
- package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
- package/dist/entities/actions/scheduledAction.js +13 -14
- package/dist/entities/botInstance.js +6 -1
- package/dist/entities/cachedStateFactory.js +2 -0
- package/dist/entities/context/baseContext.js +14 -2
- package/dist/entities/context/chatContext.d.ts +0 -3
- package/dist/entities/context/chatContext.d.ts.map +1 -1
- package/dist/entities/context/chatContext.js +38 -41
- package/dist/entities/context/inlineQueryContext.d.ts +0 -3
- package/dist/entities/context/inlineQueryContext.d.ts.map +1 -1
- package/dist/entities/context/inlineQueryContext.js +12 -6
- package/dist/entities/context/messageContext.d.ts +0 -3
- package/dist/entities/context/messageContext.d.ts.map +1 -1
- package/dist/entities/context/messageContext.js +77 -68
- package/dist/entities/context/replyContext.d.ts +0 -3
- package/dist/entities/context/replyContext.d.ts.map +1 -1
- package/dist/entities/context/replyContext.js +90 -65
- package/dist/entities/states/actionStateBase.js +2 -4
- package/dist/entities/taskRecord.js +3 -0
- package/dist/helpers/builders/commandActionBuilder.d.ts +3 -0
- package/dist/helpers/builders/commandActionBuilder.d.ts.map +1 -1
- package/dist/helpers/builders/commandActionBuilder.js +18 -9
- package/dist/helpers/builders/inlineQueryActionBuilder.js +7 -6
- package/dist/helpers/builders/scheduledActionBuilder.js +7 -5
- package/dist/helpers/mapUtils.d.ts +3 -0
- package/dist/helpers/mapUtils.d.ts.map +1 -0
- package/dist/helpers/mapUtils.js +17 -0
- package/dist/helpers/noop.d.ts +3 -4
- package/dist/helpers/noop.d.ts.map +1 -1
- package/dist/helpers/noop.js +2 -1
- package/dist/helpers/traceFactory.d.ts +1 -1
- package/dist/helpers/traceFactory.d.ts.map +1 -1
- package/dist/main.js +1 -3
- package/dist/services/actionProcessingService.d.ts.map +1 -1
- package/dist/services/actionProcessingService.js +15 -3
- package/dist/services/actionProcessors/baseProcessor.d.ts +1 -3
- package/dist/services/actionProcessors/baseProcessor.d.ts.map +1 -1
- package/dist/services/actionProcessors/baseProcessor.js +6 -2
- package/dist/services/actionProcessors/commandActionProcessor.d.ts +0 -4
- package/dist/services/actionProcessors/commandActionProcessor.d.ts.map +1 -1
- package/dist/services/actionProcessors/commandActionProcessor.js +10 -13
- package/dist/services/actionProcessors/inlineQueryActionProcessor.d.ts +0 -4
- package/dist/services/actionProcessors/inlineQueryActionProcessor.d.ts.map +1 -1
- package/dist/services/actionProcessors/inlineQueryActionProcessor.js +4 -6
- package/dist/services/actionProcessors/scheduledActionProcessor.js +6 -4
- package/dist/services/jsonFileStorage.d.ts +4 -3
- package/dist/services/jsonFileStorage.d.ts.map +1 -1
- package/dist/services/jsonFileStorage.js +22 -14
- package/dist/services/jsonLogger.d.ts +1 -1
- package/dist/services/jsonLogger.d.ts.map +1 -1
- package/dist/services/jsonLogger.js +9 -3
- package/dist/services/nodeTimeoutScheduler.d.ts +2 -2
- package/dist/services/nodeTimeoutScheduler.d.ts.map +1 -1
- package/dist/services/nodeTimeoutScheduler.js +6 -5
- package/dist/services/responseProcessingQueue.d.ts.map +1 -1
- package/dist/services/responseProcessingQueue.js +9 -7
- package/dist/services/telegramApi.d.ts.map +1 -1
- package/dist/services/telegramApi.js +17 -8
- package/dist/types/commandTrigger.d.ts +1 -2
- package/dist/types/commandTrigger.d.ts.map +1 -1
- package/dist/types/handlers.d.ts +1 -1
- package/dist/types/handlers.d.ts.map +1 -1
- package/dist/types/logger.d.ts +2 -2
- package/dist/types/logger.d.ts.map +1 -1
- package/dist/types/scheduler.d.ts +2 -2
- package/dist/types/scheduler.d.ts.map +1 -1
- package/dist/types/storage.d.ts +2 -2
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/types/timeValues.d.ts +3 -3
- package/dist/types/timeValues.d.ts.map +1 -1
- package/dist/types/trace.d.ts +1 -1
- package/dist/types/trace.d.ts.map +1 -1
- package/dtos/incomingMessage.ts +1 -1
- package/entities/actions/commandAction.ts +55 -28
- package/entities/actions/inlineQueryAction.ts +5 -1
- package/entities/actions/replyCaptureAction.ts +5 -1
- package/entities/actions/scheduledAction.ts +11 -13
- package/entities/botInstance.ts +7 -7
- package/entities/context/chatContext.ts +0 -6
- package/entities/context/inlineQueryContext.ts +0 -6
- package/entities/context/messageContext.ts +0 -6
- package/entities/context/replyContext.ts +18 -15
- package/eslint.config.js +26 -1
- package/helpers/builders/commandActionBuilder.ts +10 -0
- package/helpers/mapUtils.ts +15 -0
- package/helpers/noop.ts +4 -4
- package/helpers/traceFactory.ts +2 -2
- package/package.json +1 -1
- package/services/actionProcessingService.ts +8 -4
- package/services/actionProcessors/baseProcessor.ts +5 -8
- package/services/actionProcessors/commandActionProcessor.ts +5 -17
- package/services/actionProcessors/inlineQueryActionProcessor.ts +5 -15
- package/services/actionProcessors/scheduledActionProcessor.ts +4 -4
- package/services/jsonFileStorage.ts +39 -21
- package/services/jsonLogger.ts +12 -8
- package/services/nodeTimeoutScheduler.ts +6 -6
- package/services/responseProcessingQueue.ts +9 -4
- package/services/telegramApi.ts +7 -5
- package/tsconfig.json +1 -1
- package/types/commandTrigger.ts +1 -3
- package/types/handlers.ts +1 -1
- package/types/logger.ts +3 -6
- package/types/scheduler.ts +2 -2
- package/types/storage.ts +2 -2
- package/types/timeValues.ts +3 -3
- package/types/trace.ts +3 -1
|
@@ -13,8 +13,6 @@ import {
|
|
|
13
13
|
MessageTypeValue,
|
|
14
14
|
TelegrafContextMessage
|
|
15
15
|
} from '../../types/messageTypes';
|
|
16
|
-
import { IScheduler } from '../../types/scheduler';
|
|
17
|
-
import { IStorageClient } from '../../types/storage';
|
|
18
16
|
import { ReplyCaptureAction } from '../actions/replyCaptureAction';
|
|
19
17
|
import { resolve } from 'path';
|
|
20
18
|
import { BaseContext } from './baseContext';
|
|
@@ -41,10 +39,6 @@ export class ReplyContext<
|
|
|
41
39
|
|
|
42
40
|
isInitialized = false;
|
|
43
41
|
|
|
44
|
-
constructor(storage: IStorageClient, scheduler: IScheduler) {
|
|
45
|
-
super(storage, scheduler);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
42
|
private getQuotePart(quote: boolean | string) {
|
|
49
43
|
return typeof quote == 'boolean'
|
|
50
44
|
? this.matchResults.length != 0
|
|
@@ -136,7 +130,9 @@ export class ReplyContext<
|
|
|
136
130
|
text: string,
|
|
137
131
|
quote?: string,
|
|
138
132
|
options?: TextMessageSendingOptions
|
|
139
|
-
) =>
|
|
133
|
+
) => {
|
|
134
|
+
this.replyWithText(text, quote ?? true, options);
|
|
135
|
+
},
|
|
140
136
|
/**
|
|
141
137
|
* Reply with image message to message that triggered this action after action execution is finished.
|
|
142
138
|
* 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.
|
|
@@ -147,7 +143,9 @@ export class ReplyContext<
|
|
|
147
143
|
name: string,
|
|
148
144
|
quote?: string,
|
|
149
145
|
options?: MessageSendingOptions
|
|
150
|
-
) =>
|
|
146
|
+
) => {
|
|
147
|
+
this.replyWithImage(name, quote ?? true, options);
|
|
148
|
+
},
|
|
151
149
|
|
|
152
150
|
/**
|
|
153
151
|
* Reply with video/gif message to message that triggered this action after action execution is finished.
|
|
@@ -159,7 +157,9 @@ export class ReplyContext<
|
|
|
159
157
|
name: string,
|
|
160
158
|
quote?: string,
|
|
161
159
|
options?: MessageSendingOptions
|
|
162
|
-
) =>
|
|
160
|
+
) => {
|
|
161
|
+
this.replyWithVideo(name, quote ?? true, options);
|
|
162
|
+
}
|
|
163
163
|
},
|
|
164
164
|
|
|
165
165
|
/**
|
|
@@ -168,16 +168,18 @@ export class ReplyContext<
|
|
|
168
168
|
* @param text Message contents.
|
|
169
169
|
* @param options Message sending option.
|
|
170
170
|
*/
|
|
171
|
-
withText: (text: string, options?: TextMessageSendingOptions) =>
|
|
172
|
-
this.replyWithText(text, false, options)
|
|
171
|
+
withText: (text: string, options?: TextMessageSendingOptions) => {
|
|
172
|
+
this.replyWithText(text, false, options);
|
|
173
|
+
},
|
|
173
174
|
/**
|
|
174
175
|
* Reply with image message to message that triggered this action after action execution is finished.
|
|
175
176
|
* 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.
|
|
176
177
|
* @param text Message contents.
|
|
177
178
|
* @param options Message sending option.
|
|
178
179
|
*/
|
|
179
|
-
withImage: (name: string, options?: MessageSendingOptions) =>
|
|
180
|
-
this.replyWithImage(name, false, options)
|
|
180
|
+
withImage: (name: string, options?: MessageSendingOptions) => {
|
|
181
|
+
this.replyWithImage(name, false, options);
|
|
182
|
+
},
|
|
181
183
|
|
|
182
184
|
/**
|
|
183
185
|
* Reply with video/gif message to message that triggered this action after action execution is finished.
|
|
@@ -185,8 +187,9 @@ export class ReplyContext<
|
|
|
185
187
|
* @param text Message contents.
|
|
186
188
|
* @param options Message sending option.
|
|
187
189
|
*/
|
|
188
|
-
withVideo: (name: string, options?: MessageSendingOptions) =>
|
|
189
|
-
this.replyWithVideo(name, false, options)
|
|
190
|
+
withVideo: (name: string, options?: MessageSendingOptions) => {
|
|
191
|
+
this.replyWithVideo(name, false, options);
|
|
192
|
+
},
|
|
190
193
|
|
|
191
194
|
/**
|
|
192
195
|
* React to the message that triggered this action after action execution is finished.
|
package/eslint.config.js
CHANGED
|
@@ -3,8 +3,14 @@ import tseslint from 'typescript-eslint';
|
|
|
3
3
|
|
|
4
4
|
export default tseslint.config(
|
|
5
5
|
eslint.configs.recommended,
|
|
6
|
-
...tseslint.configs.
|
|
6
|
+
...tseslint.configs.strictTypeChecked,
|
|
7
7
|
{
|
|
8
|
+
languageOptions: {
|
|
9
|
+
parserOptions: {
|
|
10
|
+
project: './tsconfig.json',
|
|
11
|
+
sourceType: 'module'
|
|
12
|
+
}
|
|
13
|
+
},
|
|
8
14
|
rules: {
|
|
9
15
|
'@typescript-eslint/no-unused-vars': [
|
|
10
16
|
'error',
|
|
@@ -17,6 +23,25 @@ export default tseslint.config(
|
|
|
17
23
|
varsIgnorePattern: '^_',
|
|
18
24
|
ignoreRestSiblings: true
|
|
19
25
|
}
|
|
26
|
+
],
|
|
27
|
+
'@typescript-eslint/no-misused-promises': [
|
|
28
|
+
'error',
|
|
29
|
+
{
|
|
30
|
+
checksVoidReturn: true,
|
|
31
|
+
checksConditionals: true
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
'@typescript-eslint/restrict-template-expressions': [
|
|
35
|
+
'error',
|
|
36
|
+
{
|
|
37
|
+
allowNumber: true
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
'@typescript-eslint/unbound-method': [
|
|
41
|
+
'error',
|
|
42
|
+
{
|
|
43
|
+
ignoreStatic: true
|
|
44
|
+
}
|
|
20
45
|
]
|
|
21
46
|
},
|
|
22
47
|
ignores: ['dist/*']
|
|
@@ -23,6 +23,7 @@ export class CommandActionBuilderWithState<TActionState extends IActionState> {
|
|
|
23
23
|
stateConstructor: () => TActionState;
|
|
24
24
|
handler: CommandHandler<TActionState> = Noop.call;
|
|
25
25
|
condition: CommandCondition<TActionState> = Noop.true;
|
|
26
|
+
maxAllowedSimultaniousExecutions: number = 0;
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Builder for `CommandAction` with state represented by `TActionState`
|
|
@@ -86,6 +87,14 @@ export class CommandActionBuilderWithState<TActionState extends IActionState> {
|
|
|
86
87
|
return this;
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/** Sets maximum number of simultaniously executing handlers for this command per chat. 0 is treated as unlimited. */
|
|
91
|
+
ratelimit(maxAllowedSimultaniousExecutions: number) {
|
|
92
|
+
this.maxAllowedSimultaniousExecutions =
|
|
93
|
+
maxAllowedSimultaniousExecutions;
|
|
94
|
+
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
89
98
|
/** Sets action cooldown.
|
|
90
99
|
* @param seconds Cooldown in seconds.
|
|
91
100
|
*/
|
|
@@ -115,6 +124,7 @@ export class CommandActionBuilderWithState<TActionState extends IActionState> {
|
|
|
115
124
|
this.cooldownSeconds,
|
|
116
125
|
this.blacklist,
|
|
117
126
|
this.allowedUsers,
|
|
127
|
+
this.maxAllowedSimultaniousExecutions,
|
|
118
128
|
this.condition,
|
|
119
129
|
this.stateConstructor,
|
|
120
130
|
this.readmeFactory != null ? this.readmeFactory : (_) => ''
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function getOrSetIfNotExists<K, V>(map: Map<K, V>, key: K, fallback: V) {
|
|
2
|
+
const existingValue = map.get(key);
|
|
3
|
+
if (existingValue) return existingValue;
|
|
4
|
+
|
|
5
|
+
map.set(key, fallback);
|
|
6
|
+
|
|
7
|
+
return fallback;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getOrThrow<K, V>(map: Map<K, V>, key: K, error: string) {
|
|
11
|
+
const existingValue = map.get(key);
|
|
12
|
+
if (existingValue) return existingValue;
|
|
13
|
+
|
|
14
|
+
throw new Error(error);
|
|
15
|
+
}
|
package/helpers/noop.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { BotResponse } from '../types/response';
|
|
2
2
|
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
4
5
|
export class Noop {
|
|
5
6
|
static NoResponse: BotResponse[] = [];
|
|
6
|
-
static true
|
|
7
|
+
static true(arg1: unknown) {
|
|
7
8
|
return true;
|
|
8
9
|
}
|
|
9
|
-
static false
|
|
10
|
+
static false(arg1: unknown) {
|
|
10
11
|
return false;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
static async call
|
|
14
|
-
static async call<T1, T2>(arg1: T1, arg2: T2): Promise<void>;
|
|
14
|
+
static async call(arg1: unknown): Promise<void>;
|
|
15
15
|
static async call(arg1: unknown, arg2?: unknown) {}
|
|
16
16
|
}
|
package/helpers/traceFactory.ts
CHANGED
package/package.json
CHANGED
|
@@ -75,13 +75,14 @@ export class ActionProcessingService {
|
|
|
75
75
|
this.telegraf.telegram,
|
|
76
76
|
this.storage,
|
|
77
77
|
this.logger,
|
|
78
|
-
(capture, id, chatInfo, traceId) =>
|
|
78
|
+
(capture, id, chatInfo, traceId) => {
|
|
79
79
|
this.commandProcessor.captureRegistrationCallback(
|
|
80
80
|
capture,
|
|
81
81
|
id,
|
|
82
82
|
chatInfo,
|
|
83
83
|
traceId
|
|
84
|
-
)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
85
86
|
);
|
|
86
87
|
|
|
87
88
|
const botInfo = await this.telegraf.telegram.getMe();
|
|
@@ -116,9 +117,12 @@ export class ActionProcessingService {
|
|
|
116
117
|
scheduledPeriod ?? hoursToSeconds(1 as Hours)
|
|
117
118
|
);
|
|
118
119
|
|
|
119
|
-
this.storage.saveMetadata([
|
|
120
|
+
void this.storage.saveMetadata([
|
|
121
|
+
...actions.scheduled,
|
|
122
|
+
...commandActions
|
|
123
|
+
]);
|
|
120
124
|
|
|
121
|
-
this.telegraf.launch();
|
|
125
|
+
void this.telegraf.launch();
|
|
122
126
|
}
|
|
123
127
|
|
|
124
128
|
stop(code: string) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Telegraf } from 'telegraf';
|
|
2
1
|
import { ILogger } from '../../types/logger';
|
|
3
2
|
import { IScheduler } from '../../types/scheduler';
|
|
4
3
|
import { IStorageClient } from '../../types/storage';
|
|
@@ -14,7 +13,6 @@ export abstract class BaseActionProcessor {
|
|
|
14
13
|
protected readonly botName: string;
|
|
15
14
|
|
|
16
15
|
protected api!: TelegramApiService;
|
|
17
|
-
protected telegraf!: Telegraf;
|
|
18
16
|
|
|
19
17
|
constructor(
|
|
20
18
|
botName: string,
|
|
@@ -29,16 +27,15 @@ export abstract class BaseActionProcessor {
|
|
|
29
27
|
this.botName = botName;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
private defaultErrorHandler<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
private defaultErrorHandler<TAction extends IAction>(
|
|
31
|
+
error: Error,
|
|
32
|
+
ctx: BaseContext<TAction>
|
|
33
|
+
) {
|
|
36
34
|
ctx.logger.errorWithTraceId(error, ctx);
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
initializeDependencies(api: TelegramApiService
|
|
37
|
+
initializeDependencies(api: TelegramApiService) {
|
|
40
38
|
this.api = api;
|
|
41
|
-
this.telegraf = telegraf;
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
async executeAction<
|
|
@@ -5,9 +5,6 @@ import { ReplyCaptureAction } from '../../entities/actions/replyCaptureAction';
|
|
|
5
5
|
import { MessageContext } from '../../entities/context/messageContext';
|
|
6
6
|
import { ReplyContext } from '../../entities/context/replyContext';
|
|
7
7
|
import { IActionState } from '../../types/actionState';
|
|
8
|
-
import { ILogger } from '../../types/logger';
|
|
9
|
-
import { IScheduler } from '../../types/scheduler';
|
|
10
|
-
import { IStorageClient } from '../../types/storage';
|
|
11
8
|
import { TelegramApiService } from '../telegramApi';
|
|
12
9
|
import { IReplyCapture } from '../../types/capture';
|
|
13
10
|
import { TraceId } from '../../types/trace';
|
|
@@ -29,22 +26,13 @@ export class CommandActionProcessor extends BaseActionProcessor {
|
|
|
29
26
|
])
|
|
30
27
|
);
|
|
31
28
|
|
|
32
|
-
constructor(
|
|
33
|
-
botName: string,
|
|
34
|
-
storage: IStorageClient,
|
|
35
|
-
scheduler: IScheduler,
|
|
36
|
-
logger: ILogger
|
|
37
|
-
) {
|
|
38
|
-
super(botName, storage, scheduler, logger);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
29
|
initialize(
|
|
42
30
|
api: TelegramApiService,
|
|
43
31
|
telegraf: Telegraf,
|
|
44
32
|
commands: CommandAction<IActionState>[],
|
|
45
33
|
verboseLoggingForIncomingMessage: boolean
|
|
46
34
|
) {
|
|
47
|
-
this.initializeDependencies(api
|
|
35
|
+
this.initializeDependencies(api);
|
|
48
36
|
|
|
49
37
|
for (const msgType of Object.values(MessageType)) {
|
|
50
38
|
if (msgType == MessageType.Text) {
|
|
@@ -72,7 +60,7 @@ export class CommandActionProcessor extends BaseActionProcessor {
|
|
|
72
60
|
}
|
|
73
61
|
|
|
74
62
|
if (commands.length > 0) {
|
|
75
|
-
|
|
63
|
+
telegraf.on('message', (ctx) => {
|
|
76
64
|
const msg = new IncomingMessage(
|
|
77
65
|
ctx.update.message,
|
|
78
66
|
this.botName
|
|
@@ -94,7 +82,7 @@ export class CommandActionProcessor extends BaseActionProcessor {
|
|
|
94
82
|
);
|
|
95
83
|
}
|
|
96
84
|
|
|
97
|
-
|
|
85
|
+
void this.processMessage(msg);
|
|
98
86
|
});
|
|
99
87
|
}
|
|
100
88
|
}
|
|
@@ -174,7 +162,7 @@ export class CommandActionProcessor extends BaseActionProcessor {
|
|
|
174
162
|
) {
|
|
175
163
|
ctx.replyMessageId = message.replyToMessageId;
|
|
176
164
|
ctx.messageId = message.messageId;
|
|
177
|
-
ctx.messageText = message.text
|
|
165
|
+
ctx.messageText = message.text;
|
|
178
166
|
ctx.messageType = message.type;
|
|
179
167
|
ctx.fromUserId = message.from?.id;
|
|
180
168
|
ctx.fromUserName =
|
|
@@ -202,7 +190,7 @@ export class CommandActionProcessor extends BaseActionProcessor {
|
|
|
202
190
|
message: IncomingMessage
|
|
203
191
|
) {
|
|
204
192
|
ctx.messageId = message.messageId;
|
|
205
|
-
ctx.messageText = message.text
|
|
193
|
+
ctx.messageText = message.text;
|
|
206
194
|
ctx.messageType = message.type;
|
|
207
195
|
ctx.fromUserId = message.from?.id;
|
|
208
196
|
ctx.fromUserName =
|
|
@@ -3,9 +3,6 @@ import { IncomingInlineQuery } from '../../dtos/incomingQuery';
|
|
|
3
3
|
import { InlineQueryAction } from '../../entities/actions/inlineQueryAction';
|
|
4
4
|
import { InlineQueryContext } from '../../entities/context/inlineQueryContext';
|
|
5
5
|
import { createTrace } from '../../helpers/traceFactory';
|
|
6
|
-
import { ILogger } from '../../types/logger';
|
|
7
|
-
import { IScheduler } from '../../types/scheduler';
|
|
8
|
-
import { IStorageClient } from '../../types/storage';
|
|
9
6
|
import { Milliseconds } from '../../types/timeValues';
|
|
10
7
|
import { TraceId } from '../../types/trace';
|
|
11
8
|
import { TelegramApiService } from '../telegramApi';
|
|
@@ -14,22 +11,13 @@ import { BaseActionProcessor } from './baseProcessor';
|
|
|
14
11
|
export class InlineQueryActionProcessor extends BaseActionProcessor {
|
|
15
12
|
private inlineQueries!: InlineQueryAction[];
|
|
16
13
|
|
|
17
|
-
constructor(
|
|
18
|
-
botName: string,
|
|
19
|
-
storage: IStorageClient,
|
|
20
|
-
scheduler: IScheduler,
|
|
21
|
-
logger: ILogger
|
|
22
|
-
) {
|
|
23
|
-
super(botName, storage, scheduler, logger);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
14
|
initialize(
|
|
27
15
|
api: TelegramApiService,
|
|
28
16
|
telegraf: Telegraf,
|
|
29
17
|
inlineQueries: InlineQueryAction[],
|
|
30
18
|
period: Milliseconds
|
|
31
19
|
) {
|
|
32
|
-
this.initializeDependencies(api
|
|
20
|
+
this.initializeDependencies(api);
|
|
33
21
|
this.inlineQueries = inlineQueries;
|
|
34
22
|
|
|
35
23
|
let pendingInlineQueries: IncomingInlineQuery[] = [];
|
|
@@ -37,7 +25,7 @@ export class InlineQueryActionProcessor extends BaseActionProcessor {
|
|
|
37
25
|
const queriesInProcessing = new Map<number, IncomingInlineQuery>();
|
|
38
26
|
|
|
39
27
|
if (this.inlineQueries.length > 0) {
|
|
40
|
-
|
|
28
|
+
telegraf.on('inline_query', (ctx) => {
|
|
41
29
|
const query = new IncomingInlineQuery(
|
|
42
30
|
ctx.inlineQuery.id,
|
|
43
31
|
ctx.inlineQuery.query,
|
|
@@ -52,7 +40,9 @@ export class InlineQueryActionProcessor extends BaseActionProcessor {
|
|
|
52
40
|
);
|
|
53
41
|
|
|
54
42
|
logger.logWithTraceId(
|
|
55
|
-
`${ctx.inlineQuery.from.username} (${
|
|
43
|
+
`${ctx.inlineQuery.from.username ?? 'Unknown'} (${
|
|
44
|
+
ctx.inlineQuery.from.id
|
|
45
|
+
}): Query for ${ctx.inlineQuery.query}`
|
|
56
46
|
);
|
|
57
47
|
|
|
58
48
|
const queryBeingProcessed = queriesInProcessing.get(
|
|
@@ -34,7 +34,7 @@ export class ScheduledActionProcessor extends BaseActionProcessor {
|
|
|
34
34
|
scheduled: ScheduledAction<IActionState>[],
|
|
35
35
|
period: Seconds
|
|
36
36
|
) {
|
|
37
|
-
this.initializeDependencies(api
|
|
37
|
+
this.initializeDependencies(api);
|
|
38
38
|
this.scheduled = scheduled;
|
|
39
39
|
|
|
40
40
|
if (this.scheduled.length > 0) {
|
|
@@ -63,11 +63,11 @@ export class ScheduledActionProcessor extends BaseActionProcessor {
|
|
|
63
63
|
|
|
64
64
|
this.scheduler.createOnetimeTask(
|
|
65
65
|
'ScheduledProcessing_OneTime',
|
|
66
|
-
|
|
66
|
+
() => {
|
|
67
67
|
this.scheduler.createTask(
|
|
68
68
|
'ScheduledProcessing',
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
() => {
|
|
70
|
+
void this.runScheduled();
|
|
71
71
|
},
|
|
72
72
|
secondsToMilliseconds(period),
|
|
73
73
|
true,
|
|
@@ -4,6 +4,7 @@ import { Sema as Semaphore } from 'async-sema';
|
|
|
4
4
|
import { IStorageClient } from '../types/storage';
|
|
5
5
|
import { IActionState } from '../types/actionState';
|
|
6
6
|
import { IActionWithState, ActionKey } from '../types/action';
|
|
7
|
+
import { getOrSetIfNotExists } from '../helpers/mapUtils';
|
|
7
8
|
|
|
8
9
|
function buildPath(storagePath: string, botName: string, actionKey: string) {
|
|
9
10
|
return `${storagePath}/${botName}/${actionKey.replaceAll(':', '/')}.json`;
|
|
@@ -40,10 +41,21 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
private
|
|
44
|
-
|
|
44
|
+
private backfillEmptyActionStates<TActionState extends IActionState>(
|
|
45
|
+
action: IActionWithState<TActionState>,
|
|
46
|
+
data: Record<number, TActionState | undefined>
|
|
47
|
+
): data is Record<number, TActionState> {
|
|
48
|
+
for (const [stringKey, value] of Object.entries(data)) {
|
|
49
|
+
if (value) continue;
|
|
50
|
+
|
|
51
|
+
data[parseInt(stringKey)] = action.stateConstructor();
|
|
52
|
+
}
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async lock<TType>(key: ActionKey, action: () => Promise<TType>) {
|
|
58
|
+
const lock = getOrSetIfNotExists(this.locks, key, new Semaphore(1));
|
|
47
59
|
|
|
48
60
|
await lock.acquire();
|
|
49
61
|
|
|
@@ -61,12 +73,11 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
61
73
|
private async loadFromFile<TActionState extends IActionState>(
|
|
62
74
|
key: ActionKey
|
|
63
75
|
) {
|
|
64
|
-
|
|
65
|
-
this.filePaths
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const targetPath = this.filePaths.get(key)!;
|
|
76
|
+
const targetPath = getOrSetIfNotExists(
|
|
77
|
+
this.filePaths,
|
|
78
|
+
key,
|
|
79
|
+
buildPath(this.storagePath, this.botName, key)
|
|
80
|
+
);
|
|
70
81
|
|
|
71
82
|
const fileContent = await readFile(targetPath, {
|
|
72
83
|
encoding: 'utf-8',
|
|
@@ -74,12 +85,18 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
74
85
|
});
|
|
75
86
|
|
|
76
87
|
if (fileContent) {
|
|
77
|
-
const data = JSON.parse(fileContent)
|
|
88
|
+
const data = JSON.parse(fileContent) as Record<
|
|
89
|
+
number,
|
|
90
|
+
TActionState
|
|
91
|
+
>;
|
|
78
92
|
|
|
79
93
|
this.cache.set(key, data);
|
|
80
94
|
}
|
|
81
95
|
|
|
82
|
-
return (this.cache.get(key) ?? {}) as Record<
|
|
96
|
+
return (this.cache.get(key) ?? {}) as Record<
|
|
97
|
+
number,
|
|
98
|
+
TActionState | undefined
|
|
99
|
+
>;
|
|
83
100
|
}
|
|
84
101
|
|
|
85
102
|
private async updateCacheAndSaveToFile<TActionState extends IActionState>(
|
|
@@ -88,12 +105,11 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
88
105
|
) {
|
|
89
106
|
this.cache.set(key, data);
|
|
90
107
|
|
|
91
|
-
|
|
92
|
-
this.filePaths
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const targetPath = this.filePaths.get(key)!;
|
|
108
|
+
const targetPath = getOrSetIfNotExists(
|
|
109
|
+
this.filePaths,
|
|
110
|
+
key,
|
|
111
|
+
buildPath(this.storagePath, this.botName, key)
|
|
112
|
+
);
|
|
97
113
|
|
|
98
114
|
await writeFile(targetPath, JSON.stringify(data), { flag: 'w+' });
|
|
99
115
|
}
|
|
@@ -133,14 +149,15 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
133
149
|
chatId: number,
|
|
134
150
|
state: TActionState
|
|
135
151
|
) {
|
|
136
|
-
|
|
152
|
+
await this.lock(action.key, async () => {
|
|
137
153
|
const data =
|
|
138
154
|
this.tryGetFromCache<TActionState>(action.key) ??
|
|
139
155
|
(await this.loadFromFile<TActionState>(action.key));
|
|
140
156
|
|
|
141
157
|
data[chatId] = state;
|
|
142
158
|
|
|
143
|
-
|
|
159
|
+
if (this.backfillEmptyActionStates(action, data))
|
|
160
|
+
await this.updateCacheAndSaveToFile(data, action.key);
|
|
144
161
|
});
|
|
145
162
|
}
|
|
146
163
|
|
|
@@ -153,7 +170,7 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
153
170
|
async updateStateFor<TActionState extends IActionState>(
|
|
154
171
|
action: IActionWithState<TActionState>,
|
|
155
172
|
chatId: number,
|
|
156
|
-
update: (state: TActionState) => Promise<void>
|
|
173
|
+
update: (state: TActionState) => Promise<void> | void
|
|
157
174
|
) {
|
|
158
175
|
await this.lock(action.key, async () => {
|
|
159
176
|
const data =
|
|
@@ -167,7 +184,8 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
167
184
|
|
|
168
185
|
await update(state);
|
|
169
186
|
|
|
170
|
-
|
|
187
|
+
if (this.backfillEmptyActionStates(action, data))
|
|
188
|
+
await this.updateCacheAndSaveToFile(data, action.key);
|
|
171
189
|
});
|
|
172
190
|
}
|
|
173
191
|
}
|
package/services/jsonLogger.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
1
2
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
3
|
import { ILogger, IScopedLogger } from '../types/logger';
|
|
3
4
|
import { TraceId } from '../types/trace';
|
|
@@ -13,18 +14,21 @@ export class JsonLogger implements ILogger {
|
|
|
13
14
|
|
|
14
15
|
createScope(botName: string, traceId: TraceId, chatName: string) {
|
|
15
16
|
return {
|
|
16
|
-
logObjectWithTraceId: (data: any) =>
|
|
17
|
-
this.logObjectWithTraceId(botName, traceId, chatName, data)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
logObjectWithTraceId: (data: any) => {
|
|
18
|
+
this.logObjectWithTraceId(botName, traceId, chatName, data);
|
|
19
|
+
},
|
|
20
|
+
logWithTraceId: (text: string) => {
|
|
21
|
+
this.logWithTraceId(botName, traceId, chatName, text);
|
|
22
|
+
},
|
|
23
|
+
errorWithTraceId: (errorObj: unknown, extraData?: unknown) => {
|
|
21
24
|
this.errorWithTraceId(
|
|
22
25
|
botName,
|
|
23
26
|
traceId,
|
|
24
27
|
chatName,
|
|
25
28
|
errorObj,
|
|
26
29
|
extraData
|
|
27
|
-
)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
28
32
|
} as IScopedLogger;
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -51,12 +55,12 @@ export class JsonLogger implements ILogger {
|
|
|
51
55
|
);
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
errorWithTraceId
|
|
58
|
+
errorWithTraceId(
|
|
55
59
|
botName: string,
|
|
56
60
|
traceId: TraceId,
|
|
57
61
|
chatName: string,
|
|
58
62
|
errorObj: unknown,
|
|
59
|
-
extraData?:
|
|
63
|
+
extraData?: unknown
|
|
60
64
|
) {
|
|
61
65
|
console.error(
|
|
62
66
|
`{"botName":"${botName}","traceId":"${traceId}","chatName":"${chatName}","error":${this.serializeError(
|
|
@@ -20,7 +20,7 @@ export class NodeTimeoutScheduler implements IScheduler {
|
|
|
20
20
|
|
|
21
21
|
createTask(
|
|
22
22
|
name: string,
|
|
23
|
-
action: () =>
|
|
23
|
+
action: () => unknown,
|
|
24
24
|
interval: Milliseconds,
|
|
25
25
|
executeRightAway: boolean,
|
|
26
26
|
ownerName: string
|
|
@@ -36,7 +36,7 @@ export class NodeTimeoutScheduler implements IScheduler {
|
|
|
36
36
|
ownerName,
|
|
37
37
|
createTrace(this, ownerName, name),
|
|
38
38
|
'System',
|
|
39
|
-
`Created task
|
|
39
|
+
`Created task ${name}, that will run every ${interval}ms.`
|
|
40
40
|
);
|
|
41
41
|
|
|
42
42
|
this.activeTasks.push(task);
|
|
@@ -44,7 +44,7 @@ export class NodeTimeoutScheduler implements IScheduler {
|
|
|
44
44
|
|
|
45
45
|
createOnetimeTask(
|
|
46
46
|
name: string,
|
|
47
|
-
action: () =>
|
|
47
|
+
action: () => unknown,
|
|
48
48
|
delay: Milliseconds,
|
|
49
49
|
ownerName: string
|
|
50
50
|
) {
|
|
@@ -53,17 +53,17 @@ export class NodeTimeoutScheduler implements IScheduler {
|
|
|
53
53
|
ownerName,
|
|
54
54
|
createTrace(this, ownerName, name),
|
|
55
55
|
'System',
|
|
56
|
-
`Executing delayed oneshot
|
|
56
|
+
`Executing delayed oneshot ${name}`
|
|
57
57
|
);
|
|
58
58
|
action();
|
|
59
59
|
};
|
|
60
|
-
|
|
60
|
+
setTimeout(actionWrapper, delay);
|
|
61
61
|
|
|
62
62
|
this.logger.logWithTraceId(
|
|
63
63
|
ownerName,
|
|
64
64
|
createTrace(this, ownerName, name),
|
|
65
65
|
'System',
|
|
66
|
-
`Created oneshot task
|
|
66
|
+
`Created oneshot task ${name}, that will run in ${delay}ms.`
|
|
67
67
|
);
|
|
68
68
|
}
|
|
69
69
|
}
|