chz-telegram-bot 0.3.9 → 0.3.11
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.map +1 -1
- package/dist/entities/actions/commandAction.js +14 -1
- 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.js +10 -8
- 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 +11 -14
- 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 +5 -7
- package/dist/services/actionProcessors/scheduledActionProcessor.js +7 -5
- 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 +1 -0
- package/dist/services/responseProcessingQueue.d.ts.map +1 -1
- package/dist/services/responseProcessingQueue.js +11 -10
- 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 +5 -1
- 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/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 +6 -18
- package/services/actionProcessors/inlineQueryActionProcessor.ts +6 -16
- package/services/actionProcessors/scheduledActionProcessor.ts +5 -5
- package/services/jsonFileStorage.ts +39 -21
- package/services/jsonLogger.ts +12 -8
- package/services/nodeTimeoutScheduler.ts +6 -6
- package/services/responseProcessingQueue.ts +13 -7
- 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
|
@@ -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(
|
|
@@ -101,7 +91,7 @@ export class InlineQueryActionProcessor extends BaseActionProcessor {
|
|
|
101
91
|
inlineQuery.traceId
|
|
102
92
|
);
|
|
103
93
|
|
|
104
|
-
this.executeAction(
|
|
94
|
+
await this.executeAction(
|
|
105
95
|
inlineQueryAction,
|
|
106
96
|
ctx,
|
|
107
97
|
(error, ctx) => {
|
|
@@ -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,
|
|
@@ -96,7 +96,7 @@ export class ScheduledActionProcessor extends BaseActionProcessor {
|
|
|
96
96
|
)
|
|
97
97
|
);
|
|
98
98
|
|
|
99
|
-
this.executeAction(scheduledAction, ctx);
|
|
99
|
+
await this.executeAction(scheduledAction, ctx);
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -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
|
}
|
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
import { setTimeout } from 'timers/promises';
|
|
2
1
|
import { Milliseconds } from '../types/timeValues';
|
|
2
|
+
import { RateLimit } from 'async-sema';
|
|
3
3
|
|
|
4
4
|
export type QueueItem = {
|
|
5
5
|
priority: number;
|
|
6
6
|
callback: () => Promise<void>;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
function notEmpty<T>(arr: T[]): arr is [T, ...T[]] {
|
|
10
|
+
return arr.length > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
const TELEGRAM_RATELIMIT_DELAY = 35 as Milliseconds;
|
|
10
14
|
|
|
11
15
|
export class ResponseProcessingQueue {
|
|
16
|
+
rateLimiter = RateLimit(1, { timeUnit: TELEGRAM_RATELIMIT_DELAY });
|
|
12
17
|
items: QueueItem[] = [];
|
|
13
18
|
isFlushing = false;
|
|
14
19
|
|
|
15
20
|
enqueue(item: QueueItem) {
|
|
16
21
|
if (
|
|
17
22
|
this.items.length === 0 ||
|
|
18
|
-
item.priority >= this.items[this.items.length - 1]
|
|
23
|
+
item.priority >= this.items[this.items.length - 1].priority
|
|
19
24
|
) {
|
|
20
25
|
this.items.push(item);
|
|
21
26
|
return;
|
|
@@ -24,7 +29,7 @@ export class ResponseProcessingQueue {
|
|
|
24
29
|
let insertIndex = this.items.length;
|
|
25
30
|
while (
|
|
26
31
|
insertIndex > 0 &&
|
|
27
|
-
this.items[insertIndex - 1]
|
|
32
|
+
this.items[insertIndex - 1].priority > item.priority
|
|
28
33
|
) {
|
|
29
34
|
insertIndex--;
|
|
30
35
|
}
|
|
@@ -36,13 +41,14 @@ export class ResponseProcessingQueue {
|
|
|
36
41
|
|
|
37
42
|
this.isFlushing = true;
|
|
38
43
|
|
|
39
|
-
while (this.items
|
|
44
|
+
while (notEmpty(this.items)) {
|
|
40
45
|
if (Date.now() >= this.items[0].priority) {
|
|
41
|
-
|
|
46
|
+
await this.rateLimiter();
|
|
47
|
+
|
|
48
|
+
const [item] = this.items;
|
|
49
|
+
this.items.shift();
|
|
42
50
|
|
|
43
51
|
await item.callback();
|
|
44
|
-
} else {
|
|
45
|
-
await setTimeout(TELEGRAM_RATELIMIT_DELAY);
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
54
|
|
package/services/telegramApi.ts
CHANGED
|
@@ -101,7 +101,7 @@ export class TelegramApiService {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
flushResponses() {
|
|
104
|
-
this.queue.flushReadyItems();
|
|
104
|
+
void this.queue.flushReadyItems();
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
private async pinIfShould(response: IReplyResponse, sentMessage: Message) {
|
|
@@ -115,7 +115,7 @@ export class TelegramApiService {
|
|
|
115
115
|
await this.storage.updateStateFor(
|
|
116
116
|
response.action as IActionWithState<IActionState>,
|
|
117
117
|
response.chatInfo.id,
|
|
118
|
-
|
|
118
|
+
(state) => {
|
|
119
119
|
state.pinnedMessages.push(sentMessage.message_id);
|
|
120
120
|
}
|
|
121
121
|
);
|
|
@@ -150,9 +150,10 @@ export class TelegramApiService {
|
|
|
150
150
|
sentMessage = await this.telegram.sendPhoto(
|
|
151
151
|
response.chatInfo.id,
|
|
152
152
|
response.content,
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
153
154
|
response.replyInfo?.id
|
|
154
155
|
? ({
|
|
155
|
-
reply_to_message_id: response.replyInfo
|
|
156
|
+
reply_to_message_id: response.replyInfo.id // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
156
157
|
} as any)
|
|
157
158
|
: undefined
|
|
158
159
|
);
|
|
@@ -161,9 +162,10 @@ export class TelegramApiService {
|
|
|
161
162
|
sentMessage = await this.telegram.sendVideo(
|
|
162
163
|
response.chatInfo.id,
|
|
163
164
|
response.content,
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
164
166
|
response.replyInfo?.id
|
|
165
167
|
? ({
|
|
166
|
-
reply_to_message_id: response.replyInfo
|
|
168
|
+
reply_to_message_id: response.replyInfo.id // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
167
169
|
} as any)
|
|
168
170
|
: undefined
|
|
169
171
|
);
|
|
@@ -191,7 +193,7 @@ export class TelegramApiService {
|
|
|
191
193
|
await this.storage.updateStateFor(
|
|
192
194
|
response.action,
|
|
193
195
|
response.chatInfo.id,
|
|
194
|
-
|
|
196
|
+
(state) => {
|
|
195
197
|
state.pinnedMessages = state.pinnedMessages.filter(
|
|
196
198
|
(x) => x != response.messageId
|
|
197
199
|
);
|
package/tsconfig.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
|
12
12
|
|
|
13
13
|
/* Language and Environment */
|
|
14
|
-
"target": "
|
|
14
|
+
"target": "es2024" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
|
15
15
|
"lib": [
|
|
16
16
|
"es2021",
|
|
17
17
|
"DOM",
|
package/types/commandTrigger.ts
CHANGED
package/types/handlers.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type CommandHandler<TActionState extends IActionState> = (
|
|
|
14
14
|
ctx: MessageContext<TActionState>,
|
|
15
15
|
/** State of an action being executed. */
|
|
16
16
|
state: TActionState
|
|
17
|
-
) => Promise<void
|
|
17
|
+
) => Promise<void> | void;
|
|
18
18
|
|
|
19
19
|
export type ScheduledHandler<TActionState extends IActionState> = (
|
|
20
20
|
/** Context of action executed in chat. */
|
package/types/logger.ts
CHANGED
|
@@ -8,10 +8,7 @@ export interface IScopedLogger {
|
|
|
8
8
|
|
|
9
9
|
logWithTraceId(text: string): void;
|
|
10
10
|
|
|
11
|
-
errorWithTraceId
|
|
12
|
-
errorObj: unknown,
|
|
13
|
-
extraData?: TData | undefined
|
|
14
|
-
): void;
|
|
11
|
+
errorWithTraceId(errorObj: unknown, extraData?: unknown): void;
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
export interface ILogger {
|
|
@@ -36,11 +33,11 @@ export interface ILogger {
|
|
|
36
33
|
text: string
|
|
37
34
|
): void;
|
|
38
35
|
|
|
39
|
-
errorWithTraceId
|
|
36
|
+
errorWithTraceId(
|
|
40
37
|
botName: string,
|
|
41
38
|
traceId: TraceId,
|
|
42
39
|
chatName: string,
|
|
43
40
|
errorObj: unknown,
|
|
44
|
-
extraData?:
|
|
41
|
+
extraData?: unknown
|
|
45
42
|
): void;
|
|
46
43
|
}
|
package/types/scheduler.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface IScheduler {
|
|
|
5
5
|
|
|
6
6
|
createTask(
|
|
7
7
|
name: string,
|
|
8
|
-
action: () =>
|
|
8
|
+
action: () => unknown,
|
|
9
9
|
interval: Milliseconds,
|
|
10
10
|
executeRightAway: boolean,
|
|
11
11
|
ownerName: string
|
|
@@ -13,7 +13,7 @@ export interface IScheduler {
|
|
|
13
13
|
|
|
14
14
|
createOnetimeTask(
|
|
15
15
|
name: string,
|
|
16
|
-
action: () =>
|
|
16
|
+
action: () => unknown,
|
|
17
17
|
delay: Milliseconds,
|
|
18
18
|
ownerName: string
|
|
19
19
|
): void;
|
package/types/storage.ts
CHANGED
|
@@ -5,12 +5,12 @@ export interface IStorageClient {
|
|
|
5
5
|
updateStateFor<TActionState extends IActionState>(
|
|
6
6
|
action: IActionWithState<TActionState>,
|
|
7
7
|
chatId: number,
|
|
8
|
-
update: (state: TActionState) => Promise<void>
|
|
8
|
+
update: (state: TActionState) => Promise<void> | void
|
|
9
9
|
): Promise<void>;
|
|
10
10
|
close(): Promise<void>;
|
|
11
11
|
load<TActionState extends IActionState>(
|
|
12
12
|
key: ActionKey
|
|
13
|
-
): Promise<Record<number, TActionState>>;
|
|
13
|
+
): Promise<Record<number, TActionState | undefined>>;
|
|
14
14
|
saveMetadata<TActionState extends IActionState>(
|
|
15
15
|
actions: IActionWithState<TActionState>[]
|
|
16
16
|
): Promise<void>;
|
package/types/timeValues.ts
CHANGED
|
@@ -2,9 +2,9 @@ declare const millisecondsSymbol: unique symbol;
|
|
|
2
2
|
declare const secondsSymbol: unique symbol;
|
|
3
3
|
declare const hoursSymbol: unique symbol;
|
|
4
4
|
|
|
5
|
-
export type Milliseconds = number & { [millisecondsSymbol]:
|
|
6
|
-
export type Seconds = number & { [secondsSymbol]:
|
|
7
|
-
export type Hours = number & { [hoursSymbol]:
|
|
5
|
+
export type Milliseconds = number & { [millisecondsSymbol]: never };
|
|
6
|
+
export type Seconds = number & { [secondsSymbol]: never };
|
|
7
|
+
export type Hours = number & { [hoursSymbol]: never };
|
|
8
8
|
|
|
9
9
|
export type HoursOfDay =
|
|
10
10
|
| 0
|
package/types/trace.ts
CHANGED