chz-telegram-bot 0.7.13 → 0.7.15
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/propertyProviderSets.d.ts +0 -1
- package/dist/dtos/propertyProviderSets.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.d.ts +1 -1
- package/dist/entities/actions/commandAction.d.ts.map +1 -1
- package/dist/entities/actions/commandAction.js +2 -2
- package/dist/helpers/builders/commandActionBuilder.d.ts.map +1 -1
- package/dist/helpers/builders/commandActionBuilder.js +2 -3
- package/package.json +4 -1
- package/eslint.config.ts +0 -62
- package/src/builtin/helpAction.ts +0 -17
- package/src/dtos/chatHistoryMessage.ts +0 -22
- package/src/dtos/chatInfo.ts +0 -12
- package/src/dtos/commandTriggerCheckResult.ts +0 -40
- package/src/dtos/cooldownInfo.ts +0 -10
- package/src/dtos/incomingMessage.ts +0 -71
- package/src/dtos/incomingQuery.ts +0 -14
- package/src/dtos/messageInfo.ts +0 -15
- package/src/dtos/propertyProviderSets.ts +0 -21
- package/src/dtos/replyInfo.ts +0 -9
- package/src/dtos/responses/delay.ts +0 -28
- package/src/dtos/responses/imageMessage.ts +0 -41
- package/src/dtos/responses/inlineQueryResponse.ts +0 -26
- package/src/dtos/responses/reaction.ts +0 -30
- package/src/dtos/responses/textMessage.ts +0 -44
- package/src/dtos/responses/unpin.ts +0 -27
- package/src/dtos/responses/videoMessage.ts +0 -41
- package/src/dtos/userInfo.ts +0 -8
- package/src/entities/actions/commandAction.ts +0 -275
- package/src/entities/actions/inlineQueryAction.ts +0 -83
- package/src/entities/actions/replyCaptureAction.ts +0 -110
- package/src/entities/actions/scheduledAction.ts +0 -182
- package/src/entities/botInstance.ts +0 -92
- package/src/entities/cachedStateFactory.ts +0 -14
- package/src/entities/context/baseContext.ts +0 -111
- package/src/entities/context/chatContext.ts +0 -135
- package/src/entities/context/inlineQueryContext.ts +0 -63
- package/src/entities/context/messageContext.ts +0 -250
- package/src/entities/context/replyContext.ts +0 -260
- package/src/entities/states/actionStateBase.ts +0 -6
- package/src/entities/taskRecord.ts +0 -11
- package/src/helpers/builders/commandActionBuilder.ts +0 -214
- package/src/helpers/builders/inlineQueryActionBuilder.ts +0 -71
- package/src/helpers/builders/scheduledActionBuilder.ts +0 -143
- package/src/helpers/mapUtils.ts +0 -28
- package/src/helpers/noop.ts +0 -20
- package/src/helpers/objectFromEntries.ts +0 -7
- package/src/helpers/timeConvertions.ts +0 -13
- package/src/helpers/toArray.ts +0 -3
- package/src/helpers/traceFactory.ts +0 -11
- package/src/index.ts +0 -33
- package/src/main.ts +0 -76
- package/src/services/actionProcessingService.ts +0 -125
- package/src/services/actionProcessors/baseProcessor.ts +0 -67
- package/src/services/actionProcessors/commandActionProcessor.ts +0 -231
- package/src/services/actionProcessors/inlineQueryActionProcessor.ts +0 -165
- package/src/services/actionProcessors/scheduledActionProcessor.ts +0 -136
- package/src/services/jsonFileStorage.ts +0 -181
- package/src/services/nodeTimeoutScheduler.ts +0 -79
- package/src/services/responseProcessingQueue.ts +0 -57
- package/src/services/telegramApi.ts +0 -278
- package/src/types/action.ts +0 -15
- package/src/types/actionState.ts +0 -4
- package/src/types/cachedValueAccessor.ts +0 -1
- package/src/types/capture.ts +0 -33
- package/src/types/commandCondition.ts +0 -9
- package/src/types/commandTrigger.ts +0 -1
- package/src/types/events.ts +0 -286
- package/src/types/externalAliases.ts +0 -18
- package/src/types/handlers.ts +0 -26
- package/src/types/inputFile.ts +0 -4
- package/src/types/messageSendingOptions.ts +0 -10
- package/src/types/messageTypes.ts +0 -21
- package/src/types/propertyProvider.ts +0 -14
- package/src/types/response.ts +0 -51
- package/src/types/scheduler.ts +0 -20
- package/src/types/storage.ts +0 -23
- package/src/types/timeValues.ts +0 -33
- package/src/types/trace.ts +0 -5
- package/tests/dtos/commandTriggerCheckResult.test.ts +0 -301
- package/tests/entities/actions/inlineQueryAction.test.ts +0 -359
- package/tests/entities/actions/replyCaptureAction.test.ts +0 -501
- package/tests/entities/cachedStateFactory.test.ts +0 -98
- package/tests/entities/context/chatContext.test.ts +0 -606
- package/tests/entities/context/messageContext.test.ts +0 -370
- package/tests/entities/states/actionStateBase.test.ts +0 -138
- package/tests/entities/taskRecord.test.ts +0 -195
- package/tests/helpers/mapUtils.test.ts +0 -163
- package/tests/helpers/timeConvertions.test.ts +0 -129
- package/tests/services/actionProcessors/baseActionProcessor.test.ts +0 -359
- package/tests/services/actionProcessors/commandActionProcessor.test.ts +0 -268
- package/tests/services/actionProcessors/inlineQueryActionProcessor.test.ts +0 -616
- package/tests/services/actionProcessors/processorTestHelpers.ts +0 -147
- package/tests/services/actionProcessors/scheduledActionProcessor.test.ts +0 -153
- package/tests/services/jsonFileStorage.test.ts +0 -927
- package/tests/services/nodeTimeoutScheduler.test.ts +0 -421
- package/tests/services/responseProcessingQueue.test.ts +0 -388
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -118
package/src/dtos/userInfo.ts
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
import moment from 'moment';
|
|
2
|
-
import { CommandHandler } from '../../types/handlers';
|
|
3
|
-
import { CommandCondition } from '../../types/commandCondition';
|
|
4
|
-
import { secondsToMilliseconds } from '../../helpers/timeConvertions';
|
|
5
|
-
import { toArray } from '../../helpers/toArray';
|
|
6
|
-
import { IActionState } from '../../types/actionState';
|
|
7
|
-
import { IActionWithState, ActionKey } from '../../types/action';
|
|
8
|
-
import { CommandTriggerCheckResult } from '../../dtos/commandTriggerCheckResult';
|
|
9
|
-
import { MessageContextInternal } from '../context/messageContext';
|
|
10
|
-
import { CommandTrigger } from '../../types/commandTrigger';
|
|
11
|
-
import { Noop } from '../../helpers/noop';
|
|
12
|
-
import { MessageType } from '../../types/messageTypes';
|
|
13
|
-
import { Sema as Semaphore } from 'async-sema';
|
|
14
|
-
import { getOrSetIfNotExists } from '../../helpers/mapUtils';
|
|
15
|
-
import { CooldownInfo } from '../../dtos/cooldownInfo';
|
|
16
|
-
import { TextMessage } from '../../dtos/responses/textMessage';
|
|
17
|
-
import { ReplyInfo } from '../../dtos/replyInfo';
|
|
18
|
-
import { Seconds } from '../../types/timeValues';
|
|
19
|
-
import { CommandActionPropertyProvider } from '../../types/propertyProvider';
|
|
20
|
-
import { CommandActionProviders } from '../../dtos/propertyProviderSets';
|
|
21
|
-
import { BotResponse } from '../../types/response';
|
|
22
|
-
import { BotEventType } from '../../types/events';
|
|
23
|
-
|
|
24
|
-
export class CommandAction<
|
|
25
|
-
TActionState extends IActionState
|
|
26
|
-
> implements IActionWithState<TActionState> {
|
|
27
|
-
private readonly cooldownInfoProvider: CommandActionPropertyProvider<CooldownInfo>;
|
|
28
|
-
private readonly isActiveProvider: CommandActionPropertyProvider<boolean>;
|
|
29
|
-
private readonly chatsBlacklistProvider: CommandActionPropertyProvider<
|
|
30
|
-
number[]
|
|
31
|
-
>;
|
|
32
|
-
private readonly chatsWhitelistProvider: CommandActionPropertyProvider<
|
|
33
|
-
number[]
|
|
34
|
-
>;
|
|
35
|
-
private readonly usersWhitelistProvider: CommandActionPropertyProvider<
|
|
36
|
-
number[]
|
|
37
|
-
>;
|
|
38
|
-
|
|
39
|
-
readonly key: ActionKey;
|
|
40
|
-
readonly name: string;
|
|
41
|
-
readonly ratelimitSemaphores = new Map<number, Semaphore>();
|
|
42
|
-
readonly maxAllowedSimultaniousExecutions: number;
|
|
43
|
-
|
|
44
|
-
readonly triggers: CommandTrigger[];
|
|
45
|
-
|
|
46
|
-
readonly handler: CommandHandler<TActionState>;
|
|
47
|
-
readonly condition: CommandCondition<TActionState>;
|
|
48
|
-
readonly stateConstructor: () => TActionState;
|
|
49
|
-
readonly readmeFactory: (botName: string) => string;
|
|
50
|
-
|
|
51
|
-
private lastCustomCooldown: Seconds | undefined;
|
|
52
|
-
|
|
53
|
-
constructor(
|
|
54
|
-
trigger: CommandTrigger | CommandTrigger[],
|
|
55
|
-
handler: CommandHandler<TActionState>,
|
|
56
|
-
name: string,
|
|
57
|
-
providers: CommandActionProviders,
|
|
58
|
-
condition: CommandCondition<TActionState>,
|
|
59
|
-
stateConstructor: () => TActionState,
|
|
60
|
-
readmeFactory: (botName: string) => string
|
|
61
|
-
) {
|
|
62
|
-
this.triggers = toArray(trigger);
|
|
63
|
-
this.name = name;
|
|
64
|
-
|
|
65
|
-
this.cooldownInfoProvider = providers.cooldownProvider;
|
|
66
|
-
this.isActiveProvider = providers.isActiveProvider;
|
|
67
|
-
this.chatsBlacklistProvider = providers.chatsBlacklistProvider;
|
|
68
|
-
this.chatsWhitelistProvider = providers.chatsWhitelistProvider;
|
|
69
|
-
this.usersWhitelistProvider = providers.usersWhitelistProvider;
|
|
70
|
-
|
|
71
|
-
this.handler = handler;
|
|
72
|
-
this.condition = condition;
|
|
73
|
-
this.stateConstructor = stateConstructor;
|
|
74
|
-
this.readmeFactory = readmeFactory;
|
|
75
|
-
|
|
76
|
-
this.maxAllowedSimultaniousExecutions =
|
|
77
|
-
providers.maxAllowedSimultaniousExecutions;
|
|
78
|
-
|
|
79
|
-
this.key = `command:${this.name.replace('.', '-')}` as ActionKey;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async exec(
|
|
83
|
-
ctx: MessageContextInternal<TActionState>
|
|
84
|
-
): Promise<BotResponse[]> {
|
|
85
|
-
let lock: Semaphore | undefined;
|
|
86
|
-
if (this.maxAllowedSimultaniousExecutions != 0) {
|
|
87
|
-
lock = getOrSetIfNotExists(
|
|
88
|
-
this.ratelimitSemaphores,
|
|
89
|
-
ctx.chatInfo.id,
|
|
90
|
-
new Semaphore(this.maxAllowedSimultaniousExecutions)
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
await lock.acquire();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
const state = ctx.storage.getActionState<TActionState>(
|
|
98
|
-
this,
|
|
99
|
-
ctx.chatInfo.id
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const { shouldExecute, matchResults, skipCooldown, reason } =
|
|
103
|
-
this.triggers
|
|
104
|
-
.map((x) => this.checkIfShouldBeExecuted(ctx, x, state))
|
|
105
|
-
.reduce(
|
|
106
|
-
(acc, curr) => acc.mergeWith(curr),
|
|
107
|
-
CommandTriggerCheckResult.DoNotTrigger('Other')
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
if (!shouldExecute) {
|
|
111
|
-
if (reason == 'OnCooldown') {
|
|
112
|
-
const cooldownMessage =
|
|
113
|
-
this.cooldownInfoProvider(ctx).message;
|
|
114
|
-
|
|
115
|
-
return cooldownMessage
|
|
116
|
-
? [
|
|
117
|
-
new TextMessage(
|
|
118
|
-
cooldownMessage,
|
|
119
|
-
ctx.chatInfo,
|
|
120
|
-
ctx.observability.traceId,
|
|
121
|
-
this,
|
|
122
|
-
new ReplyInfo(ctx.messageInfo.id)
|
|
123
|
-
)
|
|
124
|
-
]
|
|
125
|
-
: Noop.NoResponse;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return Noop.NoResponse;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
ctx.observability.eventEmitter.emit(
|
|
132
|
-
BotEventType.commandActionExecuting,
|
|
133
|
-
{
|
|
134
|
-
action: this,
|
|
135
|
-
ctx,
|
|
136
|
-
state,
|
|
137
|
-
traceId: ctx.observability.traceId
|
|
138
|
-
}
|
|
139
|
-
);
|
|
140
|
-
ctx.matchResults = matchResults;
|
|
141
|
-
|
|
142
|
-
await this.handler(ctx, state);
|
|
143
|
-
|
|
144
|
-
if (skipCooldown) {
|
|
145
|
-
ctx.startCooldown = false;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (ctx.startCooldown) {
|
|
149
|
-
this.lastCustomCooldown = ctx.customCooldown;
|
|
150
|
-
|
|
151
|
-
state.lastExecutedDate = moment().valueOf();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
await ctx.storage.saveActionExecutionResult(
|
|
155
|
-
this,
|
|
156
|
-
ctx.chatInfo.id,
|
|
157
|
-
state
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
ctx.observability.eventEmitter.emit(
|
|
161
|
-
BotEventType.commandActionExecuted,
|
|
162
|
-
{
|
|
163
|
-
action: this,
|
|
164
|
-
ctx,
|
|
165
|
-
state,
|
|
166
|
-
traceId: ctx.observability.traceId
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
return ctx.responses;
|
|
170
|
-
} finally {
|
|
171
|
-
lock?.release();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
private checkIfShouldBeExecuted(
|
|
176
|
-
ctx: MessageContextInternal<TActionState>,
|
|
177
|
-
trigger: CommandTrigger,
|
|
178
|
-
state: TActionState
|
|
179
|
-
) {
|
|
180
|
-
if (!this.isActiveProvider(ctx))
|
|
181
|
-
return CommandTriggerCheckResult.DontTriggerAndSkipCooldown(
|
|
182
|
-
'ActionDisabled'
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const chatsBlacklist = this.chatsBlacklistProvider(ctx);
|
|
186
|
-
const chatsWhitelist = this.chatsWhitelistProvider(ctx);
|
|
187
|
-
|
|
188
|
-
const isChatInBlacklist = chatsBlacklist.includes(ctx.chatInfo.id);
|
|
189
|
-
const isChatInWhitelist =
|
|
190
|
-
chatsWhitelist.length == 0 ||
|
|
191
|
-
chatsWhitelist.includes(ctx.chatInfo.id);
|
|
192
|
-
|
|
193
|
-
if (isChatInBlacklist || !isChatInWhitelist)
|
|
194
|
-
return CommandTriggerCheckResult.DontTriggerAndSkipCooldown(
|
|
195
|
-
'ChatForbidden'
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
const triggerCheckResult = this.checkTrigger(ctx, trigger);
|
|
199
|
-
|
|
200
|
-
if (!triggerCheckResult.shouldExecute) return triggerCheckResult;
|
|
201
|
-
|
|
202
|
-
if (!ctx.userInfo.id)
|
|
203
|
-
return CommandTriggerCheckResult.DontTriggerAndSkipCooldown(
|
|
204
|
-
'UserIdMissing'
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
const usersWhitelist = this.usersWhitelistProvider(ctx);
|
|
208
|
-
const isUserAllowed =
|
|
209
|
-
usersWhitelist.length == 0 ||
|
|
210
|
-
usersWhitelist.includes(ctx.userInfo.id);
|
|
211
|
-
|
|
212
|
-
if (!isUserAllowed)
|
|
213
|
-
return CommandTriggerCheckResult.DontTriggerAndSkipCooldown(
|
|
214
|
-
'UserForbidden'
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
const lastExecutedDate = moment(state.lastExecutedDate);
|
|
218
|
-
const cooldownInMilliseconds = secondsToMilliseconds(
|
|
219
|
-
this.lastCustomCooldown ?? this.cooldownInfoProvider(ctx).cooldown
|
|
220
|
-
);
|
|
221
|
-
const onCooldown =
|
|
222
|
-
moment().diff(lastExecutedDate) < cooldownInMilliseconds;
|
|
223
|
-
|
|
224
|
-
if (onCooldown)
|
|
225
|
-
return CommandTriggerCheckResult.DoNotTrigger('OnCooldown');
|
|
226
|
-
|
|
227
|
-
const isCustomConditionMet = this.condition(ctx, state);
|
|
228
|
-
if (!isCustomConditionMet)
|
|
229
|
-
return CommandTriggerCheckResult.DontTriggerAndSkipCooldown(
|
|
230
|
-
'CustomConditionNotMet'
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
return triggerCheckResult;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
private checkTrigger(
|
|
237
|
-
ctx: MessageContextInternal<TActionState>,
|
|
238
|
-
trigger: CommandTrigger
|
|
239
|
-
) {
|
|
240
|
-
if (trigger == MessageType.Any || trigger == ctx.messageInfo.type)
|
|
241
|
-
return CommandTriggerCheckResult.Trigger();
|
|
242
|
-
|
|
243
|
-
if (typeof trigger == 'string')
|
|
244
|
-
return ctx.messageInfo.text.toLowerCase() == trigger.toLowerCase()
|
|
245
|
-
? CommandTriggerCheckResult.Trigger()
|
|
246
|
-
: CommandTriggerCheckResult.DoNotTrigger('TriggerNotSatisfied');
|
|
247
|
-
|
|
248
|
-
const matchResults: RegExpExecArray[] = [];
|
|
249
|
-
|
|
250
|
-
trigger.lastIndex = 0;
|
|
251
|
-
|
|
252
|
-
const execResult = trigger.exec(ctx.messageInfo.text);
|
|
253
|
-
if (execResult != null) {
|
|
254
|
-
let regexMatchLimit = 100;
|
|
255
|
-
matchResults.push(execResult);
|
|
256
|
-
|
|
257
|
-
if (trigger.global) {
|
|
258
|
-
while (regexMatchLimit > 0) {
|
|
259
|
-
const nextResult = trigger.exec(ctx.messageInfo.text);
|
|
260
|
-
|
|
261
|
-
if (nextResult == null) break;
|
|
262
|
-
|
|
263
|
-
matchResults.push(nextResult);
|
|
264
|
-
regexMatchLimit -= 1;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return new CommandTriggerCheckResult(
|
|
270
|
-
matchResults.length > 0,
|
|
271
|
-
matchResults,
|
|
272
|
-
false
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { Noop } from '../../helpers/noop';
|
|
2
|
-
import { ActionKey, IAction } from '../../types/action';
|
|
3
|
-
import { InlineQueryContextInternal } from '../context/inlineQueryContext';
|
|
4
|
-
import { InlineQueryHandler } from '../../types/handlers';
|
|
5
|
-
import { InlineActionPropertyProvider } from '../../types/propertyProvider';
|
|
6
|
-
import { BotEventType } from '../../types/events';
|
|
7
|
-
import { InlineQueryResponse } from '../../dtos/responses/inlineQueryResponse';
|
|
8
|
-
|
|
9
|
-
export class InlineQueryAction implements IAction {
|
|
10
|
-
readonly key: ActionKey;
|
|
11
|
-
readonly isActiveProvider: InlineActionPropertyProvider<boolean>;
|
|
12
|
-
readonly handler: InlineQueryHandler;
|
|
13
|
-
readonly name: string;
|
|
14
|
-
readonly pattern: RegExp;
|
|
15
|
-
|
|
16
|
-
constructor(
|
|
17
|
-
handler: InlineQueryHandler,
|
|
18
|
-
name: string,
|
|
19
|
-
activeProvider: InlineActionPropertyProvider<boolean>,
|
|
20
|
-
pattern: RegExp
|
|
21
|
-
) {
|
|
22
|
-
this.handler = handler;
|
|
23
|
-
this.name = name;
|
|
24
|
-
this.isActiveProvider = activeProvider;
|
|
25
|
-
this.pattern = pattern;
|
|
26
|
-
|
|
27
|
-
this.key = `inline:${this.name.replace('.', '-')}` as ActionKey;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async exec(ctx: InlineQueryContextInternal) {
|
|
31
|
-
if (!this.isActiveProvider(ctx)) return Noop.NoResponse;
|
|
32
|
-
|
|
33
|
-
const matchResults: RegExpExecArray[] = [];
|
|
34
|
-
|
|
35
|
-
this.pattern.lastIndex = 0;
|
|
36
|
-
|
|
37
|
-
const execResult = this.pattern.exec(ctx.queryText);
|
|
38
|
-
if (execResult != null) {
|
|
39
|
-
let regexMatchLimit = 100;
|
|
40
|
-
matchResults.push(execResult);
|
|
41
|
-
|
|
42
|
-
if (this.pattern.global) {
|
|
43
|
-
while (regexMatchLimit > 0) {
|
|
44
|
-
const nextResult = this.pattern.exec(ctx.queryText);
|
|
45
|
-
|
|
46
|
-
if (nextResult == null) break;
|
|
47
|
-
|
|
48
|
-
matchResults.push(nextResult);
|
|
49
|
-
regexMatchLimit -= 1;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (matchResults.length == 0) return Noop.NoResponse;
|
|
55
|
-
|
|
56
|
-
ctx.matchResults = matchResults;
|
|
57
|
-
|
|
58
|
-
ctx.observability.eventEmitter.emit(
|
|
59
|
-
BotEventType.inlineActionExecuting,
|
|
60
|
-
{
|
|
61
|
-
action: this,
|
|
62
|
-
ctx,
|
|
63
|
-
traceId: ctx.observability.traceId
|
|
64
|
-
}
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
await this.handler(ctx);
|
|
68
|
-
|
|
69
|
-
ctx.observability.eventEmitter.emit(BotEventType.inlineActionExecuted, {
|
|
70
|
-
action: this,
|
|
71
|
-
ctx,
|
|
72
|
-
traceId: ctx.observability.traceId
|
|
73
|
-
});
|
|
74
|
-
return [
|
|
75
|
-
new InlineQueryResponse(
|
|
76
|
-
ctx.queryResults,
|
|
77
|
-
ctx.queryId,
|
|
78
|
-
ctx.observability.traceId,
|
|
79
|
-
ctx.action
|
|
80
|
-
)
|
|
81
|
-
];
|
|
82
|
-
}
|
|
83
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { CommandTriggerCheckResult } from '../../dtos/commandTriggerCheckResult';
|
|
2
|
-
import { Noop } from '../../helpers/noop';
|
|
3
|
-
import { IActionState } from '../../types/actionState';
|
|
4
|
-
import { CommandTrigger } from '../../types/commandTrigger';
|
|
5
|
-
import { ActionKey, IAction } from '../../types/action';
|
|
6
|
-
import { ReplyContextInternal } from '../context/replyContext';
|
|
7
|
-
import { BotEventType } from '../../types/events';
|
|
8
|
-
|
|
9
|
-
export class ReplyCaptureAction<
|
|
10
|
-
TParentActionState extends IActionState
|
|
11
|
-
> implements IAction {
|
|
12
|
-
readonly parentMessageId: number;
|
|
13
|
-
readonly key: ActionKey;
|
|
14
|
-
readonly handler: (
|
|
15
|
-
replyContext: ReplyContextInternal<TParentActionState>
|
|
16
|
-
) => Promise<void>;
|
|
17
|
-
readonly triggers: CommandTrigger[];
|
|
18
|
-
readonly abortController: AbortController;
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
parentMessageId: number,
|
|
22
|
-
parentAction: IAction,
|
|
23
|
-
handler: (
|
|
24
|
-
replyContext: ReplyContextInternal<TParentActionState>
|
|
25
|
-
) => Promise<void>,
|
|
26
|
-
triggers: CommandTrigger[],
|
|
27
|
-
abortController: AbortController
|
|
28
|
-
) {
|
|
29
|
-
this.parentMessageId = parentMessageId;
|
|
30
|
-
this.handler = handler;
|
|
31
|
-
this.triggers = triggers;
|
|
32
|
-
this.abortController = abortController;
|
|
33
|
-
|
|
34
|
-
this.key = `capture:${parentAction.key}:${Math.random()
|
|
35
|
-
.toString()
|
|
36
|
-
.replace('.', '')}` as ActionKey;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async exec(ctx: ReplyContextInternal<TParentActionState>) {
|
|
40
|
-
const { shouldExecute, matchResults } = this.triggers
|
|
41
|
-
.map((x) => this.checkIfShouldBeExecuted(ctx, x))
|
|
42
|
-
.reduce(
|
|
43
|
-
(acc, curr) => acc.mergeWith(curr),
|
|
44
|
-
CommandTriggerCheckResult.DoNotTrigger('Other')
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
if (!shouldExecute) return Noop.NoResponse;
|
|
48
|
-
|
|
49
|
-
ctx.observability.eventEmitter.emit(BotEventType.replyActionExecuting, {
|
|
50
|
-
action: this,
|
|
51
|
-
ctx,
|
|
52
|
-
traceId: ctx.observability.traceId
|
|
53
|
-
});
|
|
54
|
-
ctx.matchResults = matchResults;
|
|
55
|
-
|
|
56
|
-
await this.handler(ctx);
|
|
57
|
-
|
|
58
|
-
ctx.observability.eventEmitter.emit(BotEventType.replyActionExecuted, {
|
|
59
|
-
action: this,
|
|
60
|
-
ctx,
|
|
61
|
-
traceId: ctx.observability.traceId
|
|
62
|
-
});
|
|
63
|
-
return ctx.responses;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private checkIfShouldBeExecuted(
|
|
67
|
-
ctx: ReplyContextInternal<TParentActionState>,
|
|
68
|
-
trigger: CommandTrigger
|
|
69
|
-
) {
|
|
70
|
-
if (ctx.replyMessageId != this.parentMessageId)
|
|
71
|
-
return CommandTriggerCheckResult.DoNotTrigger(
|
|
72
|
-
'TriggerNotSatisfied'
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
if (trigger == ctx.messageInfo.type)
|
|
76
|
-
return CommandTriggerCheckResult.Trigger();
|
|
77
|
-
|
|
78
|
-
if (typeof trigger == 'string')
|
|
79
|
-
return ctx.messageInfo.text.toLowerCase() == trigger.toLowerCase()
|
|
80
|
-
? CommandTriggerCheckResult.Trigger()
|
|
81
|
-
: CommandTriggerCheckResult.DoNotTrigger('TriggerNotSatisfied');
|
|
82
|
-
|
|
83
|
-
const matchResults: RegExpExecArray[] = [];
|
|
84
|
-
|
|
85
|
-
trigger.lastIndex = 0;
|
|
86
|
-
|
|
87
|
-
const execResult = trigger.exec(ctx.messageInfo.text);
|
|
88
|
-
if (execResult != null) {
|
|
89
|
-
let regexMatchLimit = 100;
|
|
90
|
-
matchResults.push(execResult);
|
|
91
|
-
|
|
92
|
-
if (trigger.global) {
|
|
93
|
-
while (regexMatchLimit > 0) {
|
|
94
|
-
const nextResult = trigger.exec(ctx.messageInfo.text);
|
|
95
|
-
|
|
96
|
-
if (nextResult == null) break;
|
|
97
|
-
|
|
98
|
-
matchResults.push(nextResult);
|
|
99
|
-
regexMatchLimit -= 1;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return new CommandTriggerCheckResult(
|
|
105
|
-
matchResults.length > 0,
|
|
106
|
-
matchResults,
|
|
107
|
-
false
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import moment from 'moment';
|
|
2
|
-
import { Sema as Semaphore } from 'async-sema';
|
|
3
|
-
import { ScheduledHandler } from '../../types/handlers';
|
|
4
|
-
import { hoursToMilliseconds } from '../../helpers/timeConvertions';
|
|
5
|
-
import { HoursOfDay } from '../../types/timeValues';
|
|
6
|
-
import { IActionState } from '../../types/actionState';
|
|
7
|
-
import { IActionWithState, ActionKey } from '../../types/action';
|
|
8
|
-
import { CachedStateFactory } from '../cachedStateFactory';
|
|
9
|
-
import { ChatContextInternal } from '../context/chatContext';
|
|
10
|
-
import { Noop } from '../../helpers/noop';
|
|
11
|
-
import { getOrSetIfNotExists, getOrThrow } from '../../helpers/mapUtils';
|
|
12
|
-
import { ScheduledActionPropertyProvider } from '../../types/propertyProvider';
|
|
13
|
-
import { ScheduledActionProviders } from '../../dtos/propertyProviderSets';
|
|
14
|
-
import { BotEventType } from '../../types/events';
|
|
15
|
-
|
|
16
|
-
export class ScheduledAction<
|
|
17
|
-
TActionState extends IActionState
|
|
18
|
-
> implements IActionWithState<TActionState> {
|
|
19
|
-
static readonly locks = new Map<string, Semaphore>();
|
|
20
|
-
|
|
21
|
-
readonly name: string;
|
|
22
|
-
readonly key: ActionKey;
|
|
23
|
-
|
|
24
|
-
private readonly timeinHoursProvider: ScheduledActionPropertyProvider<HoursOfDay>;
|
|
25
|
-
private readonly activeProvider: ScheduledActionPropertyProvider<boolean>;
|
|
26
|
-
private readonly chatsWhitelistProvider: ScheduledActionPropertyProvider<
|
|
27
|
-
number[]
|
|
28
|
-
>;
|
|
29
|
-
|
|
30
|
-
readonly cachedState = new Map<string, unknown>();
|
|
31
|
-
readonly stateConstructor: () => TActionState;
|
|
32
|
-
readonly cachedStateFactories: Map<string, CachedStateFactory>;
|
|
33
|
-
readonly handler: ScheduledHandler<TActionState>;
|
|
34
|
-
|
|
35
|
-
constructor(
|
|
36
|
-
name: string,
|
|
37
|
-
handler: ScheduledHandler<TActionState>,
|
|
38
|
-
providers: ScheduledActionProviders,
|
|
39
|
-
cachedStateFactories: Map<string, CachedStateFactory>,
|
|
40
|
-
stateConstructor: () => TActionState
|
|
41
|
-
) {
|
|
42
|
-
this.name = name;
|
|
43
|
-
this.key = `scheduled:${this.name.replace('.', '-')}` as ActionKey;
|
|
44
|
-
|
|
45
|
-
this.timeinHoursProvider = providers.timeinHoursProvider;
|
|
46
|
-
this.activeProvider = providers.isActiveProvider;
|
|
47
|
-
this.chatsWhitelistProvider = providers.chatsWhitelistProvider;
|
|
48
|
-
|
|
49
|
-
this.cachedStateFactories = cachedStateFactories;
|
|
50
|
-
this.stateConstructor = stateConstructor;
|
|
51
|
-
this.handler = handler;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async exec(ctx: ChatContextInternal<TActionState>) {
|
|
55
|
-
if (
|
|
56
|
-
!this.activeProvider(ctx) ||
|
|
57
|
-
!this.chatsWhitelistProvider(ctx).includes(ctx.chatInfo.id)
|
|
58
|
-
)
|
|
59
|
-
return Noop.NoResponse;
|
|
60
|
-
|
|
61
|
-
const state = ctx.storage.getActionState<TActionState>(
|
|
62
|
-
this,
|
|
63
|
-
ctx.chatInfo.id
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
const isAllowedToTrigger = this.checkIfShouldBeExecuted(state, ctx);
|
|
67
|
-
if (!isAllowedToTrigger) return Noop.NoResponse;
|
|
68
|
-
|
|
69
|
-
ctx.observability.eventEmitter.emit(
|
|
70
|
-
BotEventType.scheduledActionExecuting,
|
|
71
|
-
{
|
|
72
|
-
action: this,
|
|
73
|
-
ctx,
|
|
74
|
-
state,
|
|
75
|
-
traceId: ctx.observability.traceId
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
await this.handler(
|
|
80
|
-
ctx,
|
|
81
|
-
<TResult>(key: string) => this.getCachedValue<TResult>(key, ctx),
|
|
82
|
-
state
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
state.lastExecutedDate = moment().valueOf();
|
|
86
|
-
|
|
87
|
-
await ctx.storage.saveActionExecutionResult(
|
|
88
|
-
this,
|
|
89
|
-
ctx.chatInfo.id,
|
|
90
|
-
state
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
ctx.observability.eventEmitter.emit(
|
|
94
|
-
BotEventType.scheduledActionExecuted,
|
|
95
|
-
{
|
|
96
|
-
action: this,
|
|
97
|
-
ctx,
|
|
98
|
-
state,
|
|
99
|
-
traceId: ctx.observability.traceId
|
|
100
|
-
}
|
|
101
|
-
);
|
|
102
|
-
return ctx.responses;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
private async getCachedValue<TResult>(
|
|
106
|
-
key: string,
|
|
107
|
-
ctx: ChatContextInternal<TActionState>
|
|
108
|
-
): Promise<TResult> {
|
|
109
|
-
const cachedItemFactory = getOrThrow(
|
|
110
|
-
this.cachedStateFactories,
|
|
111
|
-
key,
|
|
112
|
-
`No shared cache was set up for the key [${key}] in action '${this.name}'`
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
const semaphoreKey = `${this.key}_cached:${key}`;
|
|
116
|
-
const semaphore = getOrSetIfNotExists(
|
|
117
|
-
ScheduledAction.locks,
|
|
118
|
-
semaphoreKey,
|
|
119
|
-
new Semaphore(1)
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
await semaphore.acquire();
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
if (this.cachedState.has(key)) {
|
|
126
|
-
return this.cachedState.get(key) as TResult;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
ctx.observability.eventEmitter.emit(
|
|
130
|
-
BotEventType.scheduledActionCacheValueCreating,
|
|
131
|
-
{
|
|
132
|
-
action: this,
|
|
133
|
-
ctx,
|
|
134
|
-
key,
|
|
135
|
-
traceId: ctx.observability.traceId
|
|
136
|
-
}
|
|
137
|
-
);
|
|
138
|
-
const value = await cachedItemFactory.getValue();
|
|
139
|
-
|
|
140
|
-
this.cachedState.set(key, value);
|
|
141
|
-
|
|
142
|
-
ctx.scheduler.createOnetimeTask(
|
|
143
|
-
`Drop cached value [${this.name} : ${key}]`,
|
|
144
|
-
() => this.cachedState.delete(key),
|
|
145
|
-
hoursToMilliseconds(
|
|
146
|
-
cachedItemFactory.invalidationTimeoutInHours
|
|
147
|
-
),
|
|
148
|
-
ctx.botName
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
return value as TResult;
|
|
152
|
-
} finally {
|
|
153
|
-
ctx.observability.eventEmitter.emit(
|
|
154
|
-
BotEventType.scheduledActionCacheValueReturned,
|
|
155
|
-
{
|
|
156
|
-
action: this,
|
|
157
|
-
ctx,
|
|
158
|
-
key,
|
|
159
|
-
traceId: ctx.observability.traceId
|
|
160
|
-
}
|
|
161
|
-
);
|
|
162
|
-
semaphore.release();
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
private checkIfShouldBeExecuted(
|
|
167
|
-
state: IActionState,
|
|
168
|
-
ctx: ChatContextInternal<TActionState>
|
|
169
|
-
): boolean {
|
|
170
|
-
const startOfToday = moment().startOf('day').valueOf();
|
|
171
|
-
const lastExecutedDate = moment(state.lastExecutedDate);
|
|
172
|
-
const currentTime = moment();
|
|
173
|
-
const scheduledTime = moment()
|
|
174
|
-
.startOf('day')
|
|
175
|
-
.add(this.timeinHoursProvider(ctx), 'hours');
|
|
176
|
-
|
|
177
|
-
const isAllowedToTrigger = currentTime.isSameOrAfter(scheduledTime);
|
|
178
|
-
const hasTriggeredToday = lastExecutedDate.isAfter(startOfToday);
|
|
179
|
-
|
|
180
|
-
return isAllowedToTrigger && !hasTriggeredToday;
|
|
181
|
-
}
|
|
182
|
-
}
|