chz-telegram-bot 0.1.3 → 0.1.4
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/README.md +74 -12
- package/dist/dtos/incomingQuery.d.ts +9 -0
- package/dist/dtos/incomingQuery.d.ts.map +1 -0
- package/dist/dtos/incomingQuery.js +12 -0
- package/dist/dtos/responses/delay.d.ts +1 -1
- package/dist/dtos/responses/delay.d.ts.map +1 -1
- package/dist/dtos/responses/imageMessage.d.ts +1 -1
- package/dist/dtos/responses/imageMessage.d.ts.map +1 -1
- package/dist/dtos/responses/inlineQueryResponse.d.ts +13 -0
- package/dist/dtos/responses/inlineQueryResponse.d.ts.map +1 -0
- package/dist/dtos/responses/inlineQueryResponse.js +15 -0
- package/dist/dtos/responses/reaction.d.ts +1 -1
- package/dist/dtos/responses/reaction.d.ts.map +1 -1
- package/dist/dtos/responses/textMessage.d.ts +1 -1
- package/dist/dtos/responses/textMessage.d.ts.map +1 -1
- package/dist/dtos/responses/unpin.d.ts +1 -1
- package/dist/dtos/responses/unpin.d.ts.map +1 -1
- package/dist/dtos/responses/videoMessage.d.ts +1 -1
- package/dist/dtos/responses/videoMessage.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/inlineQueryAction.d.ts +14 -0
- package/dist/entities/actions/inlineQueryAction.d.ts.map +1 -0
- package/dist/entities/actions/inlineQueryAction.js +40 -0
- package/dist/entities/actions/scheduledAction.d.ts +1 -1
- package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
- package/dist/entities/botInstance.d.ts +8 -2
- package/dist/entities/botInstance.d.ts.map +1 -1
- package/dist/entities/botInstance.js +34 -2
- package/dist/entities/context/chatContext.d.ts +1 -1
- package/dist/entities/context/chatContext.d.ts.map +1 -1
- package/dist/entities/context/inlineQueryContext.d.ts +38 -0
- package/dist/entities/context/inlineQueryContext.d.ts.map +1 -0
- package/dist/entities/context/inlineQueryContext.js +40 -0
- package/dist/entities/context/messageContext.d.ts +1 -1
- package/dist/entities/context/messageContext.d.ts.map +1 -1
- package/dist/helpers/builders/inlineQueryActionBuilder.d.ts +34 -0
- package/dist/helpers/builders/inlineQueryActionBuilder.d.ts.map +1 -0
- package/dist/helpers/builders/inlineQueryActionBuilder.js +48 -0
- package/dist/helpers/noop.d.ts +1 -0
- package/dist/helpers/noop.d.ts.map +1 -1
- package/dist/helpers/noop.js +1 -1
- package/dist/helpers/traceFactory.d.ts +1 -1
- package/dist/helpers/traceFactory.d.ts.map +1 -1
- package/dist/helpers/traceFactory.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/main.d.ts +43 -34
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +34 -31
- package/dist/services/jsonFileStorage.d.ts +1 -1
- package/dist/services/jsonFileStorage.d.ts.map +1 -1
- package/dist/services/telegramApi.d.ts.map +1 -1
- package/dist/services/telegramApi.js +6 -1
- package/dist/types/handlers.d.ts +4 -0
- package/dist/types/handlers.d.ts.map +1 -1
- package/dist/types/response.d.ts +4 -2
- package/dist/types/response.d.ts.map +1 -1
- package/dist/types/response.js +2 -1
- package/dist/types/statefulAction.d.ts +9 -0
- package/dist/types/statefulAction.d.ts.map +1 -0
- package/dist/types/statefulAction.js +2 -0
- package/dist/types/statelessAction.d.ts +5 -0
- package/dist/types/statelessAction.d.ts.map +1 -0
- package/dist/types/statelessAction.js +2 -0
- package/dist/types/storage.d.ts +1 -1
- package/dist/types/storage.d.ts.map +1 -1
- package/dtos/incomingQuery.ts +20 -0
- package/dtos/responses/delay.ts +1 -1
- package/dtos/responses/imageMessage.ts +1 -1
- package/dtos/responses/inlineQueryResponse.ts +26 -0
- package/dtos/responses/reaction.ts +1 -1
- package/dtos/responses/textMessage.ts +1 -1
- package/dtos/responses/unpin.ts +1 -1
- package/dtos/responses/videoMessage.ts +1 -1
- package/entities/actions/commandAction.ts +1 -1
- package/entities/actions/inlineQueryAction.ts +68 -0
- package/entities/actions/scheduledAction.ts +1 -1
- package/entities/botInstance.ts +86 -4
- package/entities/context/chatContext.ts +4 -4
- package/entities/context/inlineQueryContext.ts +83 -0
- package/entities/context/messageContext.ts +1 -1
- package/helpers/builders/inlineQueryActionBuilder.ts +62 -0
- package/helpers/noop.ts +3 -1
- package/helpers/traceFactory.ts +4 -2
- package/index.ts +1 -1
- package/main.ts +64 -57
- package/package.json +1 -1
- package/services/jsonFileStorage.ts +1 -1
- package/services/telegramApi.ts +10 -1
- package/types/handlers.ts +6 -0
- package/types/response.ts +5 -2
- package/types/statelessAction.ts +5 -0
- package/types/storage.ts +1 -1
- /package/types/{actionWithState.ts → statefulAction.ts} +0 -0
|
@@ -5,7 +5,7 @@ import { Seconds } from '../../types/timeValues';
|
|
|
5
5
|
import { secondsToMilliseconds } from '../../helpers/timeConvertions';
|
|
6
6
|
import { toArray } from '../../helpers/toArray';
|
|
7
7
|
import { IActionState } from '../../types/actionState';
|
|
8
|
-
import { IActionWithState, ActionKey } from '../../types/
|
|
8
|
+
import { IActionWithState, ActionKey } from '../../types/statefulAction';
|
|
9
9
|
import { CommandTriggerCheckResult } from '../../dtos/commandTriggerCheckResult';
|
|
10
10
|
import { MessageContext } from '../context/messageContext';
|
|
11
11
|
import { CommandTrigger } from '../../types/commandTrigger';
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Noop } from '../../helpers/noop';
|
|
2
|
+
import { ActionKey } from '../../types/statefulAction';
|
|
3
|
+
import { InlineQueryContext } from '../context/inlineQueryContext';
|
|
4
|
+
import { InlineQueryHandler } from '../../types/handlers';
|
|
5
|
+
import { IActionWithoutState } from '../../types/statelessAction';
|
|
6
|
+
|
|
7
|
+
export class InlineQueryAction implements IActionWithoutState {
|
|
8
|
+
readonly key: ActionKey;
|
|
9
|
+
readonly active: boolean;
|
|
10
|
+
readonly handler: InlineQueryHandler;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly pattern: RegExp;
|
|
13
|
+
|
|
14
|
+
constructor(
|
|
15
|
+
handler: InlineQueryHandler,
|
|
16
|
+
name: string,
|
|
17
|
+
active: boolean,
|
|
18
|
+
pattern: RegExp
|
|
19
|
+
) {
|
|
20
|
+
this.handler = handler;
|
|
21
|
+
this.name = name;
|
|
22
|
+
this.active = active;
|
|
23
|
+
this.pattern = pattern;
|
|
24
|
+
|
|
25
|
+
this.key = `inline:${this.name.replace('.', '-')}` as ActionKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async exec(ctx: InlineQueryContext) {
|
|
29
|
+
if (!ctx.isInitialized)
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Context for ${this.key} is not initialized or already consumed`
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (!this.active) return Noop.NoResponse;
|
|
35
|
+
|
|
36
|
+
const matchResults: RegExpExecArray[] = [];
|
|
37
|
+
|
|
38
|
+
this.pattern.lastIndex = 0;
|
|
39
|
+
|
|
40
|
+
const execResult = this.pattern.exec(ctx.queryText);
|
|
41
|
+
if (execResult != null) {
|
|
42
|
+
matchResults.push(execResult);
|
|
43
|
+
|
|
44
|
+
if (this.pattern.global) {
|
|
45
|
+
while (true) {
|
|
46
|
+
const nextResult = this.pattern.exec(ctx.queryText);
|
|
47
|
+
if (nextResult == null) break;
|
|
48
|
+
matchResults.push(nextResult);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (matchResults.length == 0) return Noop.NoResponse;
|
|
54
|
+
|
|
55
|
+
ctx.matchResults = matchResults;
|
|
56
|
+
|
|
57
|
+
ctx.logger.logWithTraceId(
|
|
58
|
+
ctx.botName,
|
|
59
|
+
ctx.traceId,
|
|
60
|
+
'Unknown',
|
|
61
|
+
` - Executing [${this.name}]`
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
await this.handler(ctx);
|
|
65
|
+
|
|
66
|
+
return ctx.responses;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -4,7 +4,7 @@ import { ScheduledHandler } from '../../types/handlers';
|
|
|
4
4
|
import { hoursToMilliseconds } from '../../helpers/timeConvertions';
|
|
5
5
|
import { HoursOfDay } from '../../types/timeValues';
|
|
6
6
|
import { IActionState } from '../../types/actionState';
|
|
7
|
-
import { IActionWithState, ActionKey } from '../../types/
|
|
7
|
+
import { IActionWithState, ActionKey } from '../../types/statefulAction';
|
|
8
8
|
import { CachedStateFactory } from '../cachedStateFactory';
|
|
9
9
|
import { ChatContext } from '../context/chatContext';
|
|
10
10
|
import { Noop } from '../../helpers/noop';
|
package/entities/botInstance.ts
CHANGED
|
@@ -20,6 +20,9 @@ import { ILogger } from '../types/logger';
|
|
|
20
20
|
import { IScheduler } from '../types/scheduler';
|
|
21
21
|
import { NodeTimeoutScheduler } from '../services/nodeTimeoutScheduler';
|
|
22
22
|
import { createTrace } from '../helpers/traceFactory';
|
|
23
|
+
import { InlineQueryAction } from './actions/inlineQueryAction';
|
|
24
|
+
import { IncomingInlineQuery } from '../dtos/incomingQuery';
|
|
25
|
+
import { InlineQueryContext } from './context/inlineQueryContext';
|
|
23
26
|
|
|
24
27
|
export class BotInstance {
|
|
25
28
|
private readonly api: TelegramApiService;
|
|
@@ -31,13 +34,17 @@ export class BotInstance {
|
|
|
31
34
|
private readonly telegraf: Telegraf;
|
|
32
35
|
private readonly commands: CommandAction<IActionState>[];
|
|
33
36
|
private readonly scheduled: ScheduledAction<IActionState>[];
|
|
37
|
+
private readonly inlineQueries: InlineQueryAction[];
|
|
34
38
|
private readonly chats: Record<string, number>;
|
|
35
39
|
|
|
36
40
|
constructor(options: {
|
|
37
41
|
name: string;
|
|
38
42
|
token: string;
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
actions: {
|
|
44
|
+
commands: CommandAction<IActionState>[];
|
|
45
|
+
scheduled: ScheduledAction<IActionState>[];
|
|
46
|
+
inlineQueries: InlineQueryAction[];
|
|
47
|
+
};
|
|
41
48
|
chats: Record<string, number>;
|
|
42
49
|
storagePath?: string;
|
|
43
50
|
scheduledPeriod?: Seconds;
|
|
@@ -49,8 +56,9 @@ export class BotInstance {
|
|
|
49
56
|
};
|
|
50
57
|
}) {
|
|
51
58
|
this.name = options.name;
|
|
52
|
-
this.commands = options.commands;
|
|
53
|
-
this.scheduled = options.scheduled;
|
|
59
|
+
this.commands = options.actions.commands;
|
|
60
|
+
this.scheduled = options.actions.scheduled;
|
|
61
|
+
this.inlineQueries = options.actions.inlineQueries;
|
|
54
62
|
this.chats = options.chats;
|
|
55
63
|
|
|
56
64
|
const actions = [...this.commands, ...this.scheduled];
|
|
@@ -72,6 +80,7 @@ export class BotInstance {
|
|
|
72
80
|
this.initializeMessageProcessing(
|
|
73
81
|
options.verboseLoggingForIncomingMessage ?? false
|
|
74
82
|
);
|
|
83
|
+
this.initializeInlineQueryProcessing(500 as Milliseconds);
|
|
75
84
|
this.initializeScheduledProcessing(
|
|
76
85
|
options.scheduledPeriod ?? hoursToSeconds(1 as Hours)
|
|
77
86
|
);
|
|
@@ -130,6 +139,79 @@ export class BotInstance {
|
|
|
130
139
|
);
|
|
131
140
|
}
|
|
132
141
|
}
|
|
142
|
+
|
|
143
|
+
private initializeInlineQueryProcessing(period: Milliseconds) {
|
|
144
|
+
let pendingInlineQueries: IncomingInlineQuery[] = [];
|
|
145
|
+
|
|
146
|
+
if (this.inlineQueries.length > 0) {
|
|
147
|
+
this.telegraf.on('inline_query', async (ctx) => {
|
|
148
|
+
const query = new IncomingInlineQuery(
|
|
149
|
+
ctx.inlineQuery.id,
|
|
150
|
+
ctx.inlineQuery.query,
|
|
151
|
+
ctx.inlineQuery.from.id,
|
|
152
|
+
createTrace('InlineQuery', this.name, ctx.inlineQuery.id)
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
this.logger.logWithTraceId(
|
|
156
|
+
this.name,
|
|
157
|
+
query.traceId,
|
|
158
|
+
'Query',
|
|
159
|
+
`${ctx.inlineQuery.from.username} (${ctx.inlineQuery.from.id}): Query for ${ctx.inlineQuery.query}`
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
pendingInlineQueries = pendingInlineQueries.filter(
|
|
163
|
+
(q) => q.userId != query.userId
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
pendingInlineQueries.push(query);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
this.scheduler.createTask(
|
|
170
|
+
'InlineQueryProcessing',
|
|
171
|
+
async () => {
|
|
172
|
+
const ctx = new InlineQueryContext(
|
|
173
|
+
this.storage,
|
|
174
|
+
this.logger,
|
|
175
|
+
this.scheduler
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
for (const inlineQuery of pendingInlineQueries) {
|
|
179
|
+
for (const inlineQueryAction of this.inlineQueries) {
|
|
180
|
+
ctx.initializeContext(
|
|
181
|
+
inlineQuery.query,
|
|
182
|
+
inlineQuery.queryId,
|
|
183
|
+
this.name,
|
|
184
|
+
inlineQueryAction,
|
|
185
|
+
inlineQuery.traceId
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const responses = await inlineQueryAction.exec(
|
|
190
|
+
ctx
|
|
191
|
+
);
|
|
192
|
+
this.api.enqueueBatchedResponses(responses);
|
|
193
|
+
ctx.isInitialized = false;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this.logger.errorWithTraceId(
|
|
196
|
+
ctx.botName,
|
|
197
|
+
ctx.traceId,
|
|
198
|
+
'Unknown',
|
|
199
|
+
error,
|
|
200
|
+
ctx
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.api.flushResponses();
|
|
207
|
+
},
|
|
208
|
+
period,
|
|
209
|
+
false,
|
|
210
|
+
this.name
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
133
215
|
private initializeMessageProcessing(
|
|
134
216
|
verboseLoggingForIncomingMessage: boolean
|
|
135
217
|
) {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
MessageSendingOptions,
|
|
9
9
|
TextMessageSendingOptions
|
|
10
10
|
} from '../../types/messageSendingOptions';
|
|
11
|
-
import { IActionWithState } from '../../types/
|
|
11
|
+
import { IActionWithState } from '../../types/statefulAction';
|
|
12
12
|
import { IActionState } from '../../types/actionState';
|
|
13
13
|
import { BotResponse } from '../../types/response';
|
|
14
14
|
import { Milliseconds } from '../../types/timeValues';
|
|
@@ -25,11 +25,11 @@ export class ChatContext<TActionState extends IActionState> {
|
|
|
25
25
|
protected action!: IActionWithState<TActionState>;
|
|
26
26
|
|
|
27
27
|
/** Storage client instance for the bot executing this action. */
|
|
28
|
-
readonly storage
|
|
28
|
+
readonly storage: IStorageClient;
|
|
29
29
|
/** Logger instance for the bot executing this action */
|
|
30
|
-
readonly logger
|
|
30
|
+
readonly logger: ILogger;
|
|
31
31
|
/** Scheduler instance for the bot executing this action */
|
|
32
|
-
readonly scheduler
|
|
32
|
+
readonly scheduler: IScheduler;
|
|
33
33
|
|
|
34
34
|
/** Trace id of a action execution. */
|
|
35
35
|
traceId!: TraceId;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { InlineQueryResult } from 'telegraf/types';
|
|
2
|
+
import { ILogger } from '../../types/logger';
|
|
3
|
+
import { BotResponse } from '../../types/response';
|
|
4
|
+
import { IScheduler } from '../../types/scheduler';
|
|
5
|
+
import { IStorageClient } from '../../types/storage';
|
|
6
|
+
import { TraceId } from '../../types/trace';
|
|
7
|
+
import { InlineQueryAction } from '../actions/inlineQueryAction';
|
|
8
|
+
import { InlineQueryResponse } from '../../dtos/responses/inlineQueryResponse';
|
|
9
|
+
|
|
10
|
+
export class InlineQueryContext {
|
|
11
|
+
private action!: InlineQueryAction;
|
|
12
|
+
private queryResults: InlineQueryResult[] = [];
|
|
13
|
+
|
|
14
|
+
/** Storage client instance for the bot executing this action. */
|
|
15
|
+
readonly storage: IStorageClient;
|
|
16
|
+
/** Logger instance for the bot executing this action */
|
|
17
|
+
readonly logger: ILogger;
|
|
18
|
+
/** Scheduler instance for the bot executing this action */
|
|
19
|
+
readonly scheduler: IScheduler;
|
|
20
|
+
|
|
21
|
+
/** Trace id of a action execution. */
|
|
22
|
+
traceId!: TraceId;
|
|
23
|
+
/** Name of a bot that executes this action. */
|
|
24
|
+
botName!: string;
|
|
25
|
+
|
|
26
|
+
/** Ordered collection of responses to be processed */
|
|
27
|
+
get responses(): BotResponse[] {
|
|
28
|
+
return [
|
|
29
|
+
new InlineQueryResponse(
|
|
30
|
+
this.queryResults,
|
|
31
|
+
this.queryId,
|
|
32
|
+
this.traceId,
|
|
33
|
+
this.action
|
|
34
|
+
)
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
/** Inline query text */
|
|
38
|
+
queryText!: string;
|
|
39
|
+
/** Internal query id */
|
|
40
|
+
queryId!: string;
|
|
41
|
+
/** Collection of Regexp match results on a message that triggered this action. Will be empty if trigger is not a Regexp. */
|
|
42
|
+
matchResults: RegExpMatchArray[] = [];
|
|
43
|
+
|
|
44
|
+
isInitialized = false;
|
|
45
|
+
|
|
46
|
+
constructor(
|
|
47
|
+
storage: IStorageClient,
|
|
48
|
+
logger: ILogger,
|
|
49
|
+
scheduler: IScheduler
|
|
50
|
+
) {
|
|
51
|
+
this.storage = storage;
|
|
52
|
+
this.logger = logger;
|
|
53
|
+
this.scheduler = scheduler;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
initializeContext(
|
|
57
|
+
queryText: string,
|
|
58
|
+
queryId: string,
|
|
59
|
+
botName: string,
|
|
60
|
+
action: InlineQueryAction,
|
|
61
|
+
traceId: TraceId
|
|
62
|
+
) {
|
|
63
|
+
this.queryText = queryText;
|
|
64
|
+
this.queryId = queryId;
|
|
65
|
+
this.botName = botName;
|
|
66
|
+
this.action = action;
|
|
67
|
+
this.traceId = traceId;
|
|
68
|
+
|
|
69
|
+
this.isInitialized = true;
|
|
70
|
+
this.queryResults = [];
|
|
71
|
+
this.matchResults = [];
|
|
72
|
+
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* This result will be shown to user as a response to inline query.
|
|
78
|
+
* @param queryResult Inline query result to be shown to user.
|
|
79
|
+
*/
|
|
80
|
+
showInlineQueryResult(queryResult: InlineQueryResult) {
|
|
81
|
+
this.queryResults.push(queryResult);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
MessageSendingOptions,
|
|
14
14
|
TextMessageSendingOptions
|
|
15
15
|
} from '../../types/messageSendingOptions';
|
|
16
|
-
import { IActionWithState, ActionKey } from '../../types/
|
|
16
|
+
import { IActionWithState, ActionKey } from '../../types/statefulAction';
|
|
17
17
|
import { MessageTypeValue } from '../../types/messageTypes';
|
|
18
18
|
import { ILogger } from '../../types/logger';
|
|
19
19
|
import { IScheduler } from '../../types/scheduler';
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { InlineQueryHandler } from '../../types/handlers';
|
|
2
|
+
import { Seconds } from '../../types/timeValues';
|
|
3
|
+
import { Noop } from '../noop';
|
|
4
|
+
import { InlineQueryAction } from '../../entities/actions/inlineQueryAction';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builder for `InlineQueryAction`
|
|
8
|
+
*/
|
|
9
|
+
export class InlineQueryActionBuilder {
|
|
10
|
+
name: string;
|
|
11
|
+
pattern: RegExp = /.+/gi;
|
|
12
|
+
|
|
13
|
+
active = true;
|
|
14
|
+
cooldownSeconds: Seconds = 0 as Seconds;
|
|
15
|
+
blacklist: number[] = [];
|
|
16
|
+
allowedUsers: number[] = [];
|
|
17
|
+
handler: InlineQueryHandler = Noop.call;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Builder for `InlineQueryAction`
|
|
21
|
+
* @param name Action name, will be used for logging and storage
|
|
22
|
+
*/
|
|
23
|
+
constructor(name: string) {
|
|
24
|
+
this.name = name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Defines action pattern to check if action should be executed, if not setup, check will default to true
|
|
29
|
+
* @param trigger RegExp to check
|
|
30
|
+
*/
|
|
31
|
+
on(pattern: RegExp) {
|
|
32
|
+
this.pattern = pattern;
|
|
33
|
+
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Defines action logic itself, will be executed.
|
|
38
|
+
* @param handler Callback that will be called
|
|
39
|
+
*/
|
|
40
|
+
do(handler: InlineQueryHandler) {
|
|
41
|
+
this.handler = handler;
|
|
42
|
+
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** If called during building, action is marked as disabled and never checked. */
|
|
47
|
+
disabled() {
|
|
48
|
+
this.active = false;
|
|
49
|
+
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Builds action */
|
|
54
|
+
build() {
|
|
55
|
+
return new InlineQueryAction(
|
|
56
|
+
this.handler,
|
|
57
|
+
this.name,
|
|
58
|
+
this.active,
|
|
59
|
+
this.pattern
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
package/helpers/noop.ts
CHANGED
|
@@ -9,6 +9,8 @@ export class Noop {
|
|
|
9
9
|
static false<T1>(arg1: T1) {
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
static async call<T1>(arg1: T1): Promise<void>;
|
|
12
14
|
static async call<T1, T2>(arg1: T1, arg2: T2): Promise<void>;
|
|
13
|
-
static async call
|
|
15
|
+
static async call(arg1: unknown, arg2?: unknown) {}
|
|
14
16
|
}
|
package/helpers/traceFactory.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { TraceId } from '../types/trace';
|
|
2
2
|
|
|
3
3
|
export function createTrace<T extends object>(
|
|
4
|
-
traceOwner: T,
|
|
4
|
+
traceOwner: T | string,
|
|
5
5
|
botName: string,
|
|
6
6
|
traceName: string
|
|
7
7
|
) {
|
|
8
|
-
return `${
|
|
8
|
+
return `${
|
|
9
|
+
typeof traceOwner == 'string' ? traceOwner : traceOwner.constructor.name
|
|
10
|
+
}:${botName}-${traceName}` as TraceId;
|
|
9
11
|
}
|
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { botOrchestrator } from './main';
|
|
2
2
|
export { CommandActionBuilder } from './helpers/builders/commandActionBuilder';
|
|
3
3
|
export { CommandActionBuilderWithState } from './helpers/builders/commandActionBuilder';
|
|
4
4
|
export { IStorageClient } from './types/storage';
|
package/main.ts
CHANGED
|
@@ -7,67 +7,74 @@ import { Seconds } from './types/timeValues';
|
|
|
7
7
|
import { IScheduler } from './types/scheduler';
|
|
8
8
|
import { ILogger } from './types/logger';
|
|
9
9
|
import { ActionStateBase } from './entities/states/actionStateBase';
|
|
10
|
+
import { InlineQueryAction } from './entities/actions/inlineQueryAction';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
class BotOrchestrator {
|
|
13
|
+
bots: BotInstance[] = [];
|
|
12
14
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
async
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
options.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Starts bot
|
|
17
|
+
*/
|
|
18
|
+
async startBot(options: {
|
|
19
|
+
/** Bot name, used in logging */
|
|
20
|
+
name: string;
|
|
21
|
+
/** Path to file containing Telegram Bot token. */
|
|
22
|
+
tokenFilePath: string;
|
|
23
|
+
actions: {
|
|
24
|
+
/** Collection of actions that will be executed as a response to message from used. Created using `CommandActionBuilder`.*/
|
|
25
|
+
commands: CommandAction<ActionStateBase>[];
|
|
26
|
+
/** Collection of actions that will be executed on timer. Created using `ScheduledActionBuilder`.*/
|
|
27
|
+
scheduled: ScheduledAction<ActionStateBase>[];
|
|
28
|
+
/** Collection of actions that will handle inline queries */
|
|
29
|
+
inlineQueries: InlineQueryAction[];
|
|
30
|
+
};
|
|
31
|
+
/** Object containing chat name and chat id pairs. Used for logging and execution of scheduled action. */
|
|
32
|
+
chats: Record<string, number>;
|
|
33
|
+
/** 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
|
+
storagePath?: string;
|
|
35
|
+
/** Period of time between execution of scheduled actions. */
|
|
36
|
+
scheduledPeriod?: Seconds;
|
|
37
|
+
/** If true, telegram API objects will be logged instead of message content. */
|
|
38
|
+
verboseLoggingForIncomingMessage?: boolean;
|
|
39
|
+
services?: {
|
|
40
|
+
/** Storage client for bot state storage. If not provided, default `JsonFileStorage` will be used. */
|
|
41
|
+
storageClient?: IStorageClient;
|
|
42
|
+
/** Logger client for bot logging. If not provided, default `JsonFileStorage` will be used. */
|
|
43
|
+
logger?: ILogger;
|
|
44
|
+
/** Scheduler client for bot scheduling. If not provided, default `NodeTimeoutScheduler` will be used. */
|
|
45
|
+
scheduler?: IScheduler;
|
|
46
|
+
};
|
|
47
|
+
}) {
|
|
48
|
+
const token = await readFile(options.tokenFilePath, 'utf8');
|
|
49
|
+
const bot = new BotInstance({
|
|
50
|
+
name: options.name,
|
|
51
|
+
token,
|
|
52
|
+
actions: options.actions,
|
|
53
|
+
chats: options.chats,
|
|
54
|
+
storagePath: options.storagePath,
|
|
55
|
+
scheduledPeriod: options.scheduledPeriod,
|
|
56
|
+
verboseLoggingForIncomingMessage:
|
|
57
|
+
options.verboseLoggingForIncomingMessage,
|
|
58
|
+
services: {
|
|
59
|
+
storageClient: options.services?.storageClient,
|
|
60
|
+
logger: options.services?.logger,
|
|
61
|
+
scheduler: options.services?.scheduler
|
|
62
|
+
}
|
|
63
|
+
});
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
}
|
|
65
|
+
this.bots.push(bot);
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
return bot;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Terminates all scheduled tasks, closes storage connections and stops all bots.
|
|
72
|
+
*/
|
|
73
|
+
async stopBots(reason: string) {
|
|
74
|
+
for (const bot of this.bots) {
|
|
75
|
+
await bot.stop(reason);
|
|
76
|
+
}
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
79
|
|
|
73
|
-
export
|
|
80
|
+
export const botOrchestrator = new BotOrchestrator();
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
|
4
4
|
import { Sema as Semaphore } from 'async-sema';
|
|
5
5
|
import { IStorageClient } from '../types/storage';
|
|
6
6
|
import { IActionState } from '../types/actionState';
|
|
7
|
-
import { IActionWithState, ActionKey } from '../types/
|
|
7
|
+
import { IActionWithState, ActionKey } from '../types/statefulAction';
|
|
8
8
|
|
|
9
9
|
export class JsonFileStorage implements IStorageClient {
|
|
10
10
|
private readonly locks = new Map<ActionKey, Semaphore>();
|
package/services/telegramApi.ts
CHANGED
|
@@ -41,7 +41,9 @@ export class TelegramApiService {
|
|
|
41
41
|
this.logger.errorWithTraceId(
|
|
42
42
|
this.botName,
|
|
43
43
|
response.traceId,
|
|
44
|
-
response
|
|
44
|
+
'chatInfo' in response
|
|
45
|
+
? response.chatInfo.name
|
|
46
|
+
: 'Unknown',
|
|
45
47
|
error,
|
|
46
48
|
response
|
|
47
49
|
);
|
|
@@ -150,6 +152,13 @@ export class TelegramApiService {
|
|
|
150
152
|
}
|
|
151
153
|
);
|
|
152
154
|
break;
|
|
155
|
+
case 'inlineQuery':
|
|
156
|
+
await this.telegram.answerInlineQuery(
|
|
157
|
+
response.queryId,
|
|
158
|
+
response.queryResults,
|
|
159
|
+
{ cache_time: 3600 }
|
|
160
|
+
);
|
|
161
|
+
break;
|
|
153
162
|
case 'delay':
|
|
154
163
|
break;
|
|
155
164
|
}
|
package/types/handlers.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { ChatContext } from '../entities/context/chatContext';
|
|
2
|
+
import { InlineQueryContext } from '../entities/context/inlineQueryContext';
|
|
2
3
|
import { MessageContext } from '../entities/context/messageContext';
|
|
3
4
|
import { IActionState } from './actionState';
|
|
4
5
|
import { CachedValueAccessor } from './cachedValueAccessor';
|
|
5
6
|
|
|
7
|
+
export type InlineQueryHandler = (
|
|
8
|
+
/** Context of inline query executed in chat, in response to a message. */
|
|
9
|
+
ctx: InlineQueryContext
|
|
10
|
+
) => Promise<void>;
|
|
11
|
+
|
|
6
12
|
export type CommandHandler<TActionState extends IActionState> = (
|
|
7
13
|
/** Context of action executed in chat, in response to a message. */
|
|
8
14
|
ctx: MessageContext<TActionState>,
|
package/types/response.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ChatInfo } from '../dtos/chatInfo';
|
|
2
2
|
import { DelayResponse } from '../dtos/responses/delay';
|
|
3
3
|
import { ImageMessage } from '../dtos/responses/imageMessage';
|
|
4
|
+
import { InlineQueryResponse } from '../dtos/responses/inlineQueryResponse';
|
|
4
5
|
import { Reaction } from '../dtos/responses/reaction';
|
|
5
6
|
import { TextMessage } from '../dtos/responses/textMessage';
|
|
6
7
|
import { UnpinResponse } from '../dtos/responses/unpin';
|
|
7
8
|
import { VideoMessage } from '../dtos/responses/videoMessage';
|
|
8
9
|
import { IActionState } from './actionState';
|
|
9
|
-
import { IActionWithState } from './
|
|
10
|
+
import { IActionWithState } from './statefulAction';
|
|
10
11
|
import { TraceId } from './trace';
|
|
11
12
|
|
|
12
13
|
export const BotResponseTypes = {
|
|
@@ -15,7 +16,8 @@ export const BotResponseTypes = {
|
|
|
15
16
|
image: 'image',
|
|
16
17
|
video: 'video',
|
|
17
18
|
react: 'react',
|
|
18
|
-
delay: 'delay'
|
|
19
|
+
delay: 'delay',
|
|
20
|
+
inlineQuery: 'inlineQuery'
|
|
19
21
|
} as const;
|
|
20
22
|
|
|
21
23
|
export type BotResponse =
|
|
@@ -24,6 +26,7 @@ export type BotResponse =
|
|
|
24
26
|
| TextMessage
|
|
25
27
|
| VideoMessage
|
|
26
28
|
| DelayResponse
|
|
29
|
+
| InlineQueryResponse
|
|
27
30
|
| ImageMessage;
|
|
28
31
|
|
|
29
32
|
export interface IChatResponse {
|