chz-telegram-bot 0.0.26 → 0.0.27
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/entities/actions/commandAction.d.ts +2 -2
- package/dist/entities/actions/commandAction.d.ts.map +1 -1
- package/dist/entities/actions/scheduledAction.d.ts +2 -2
- package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
- package/dist/entities/botInstance.d.ts.map +1 -1
- package/dist/entities/botInstance.js +3 -2
- package/dist/entities/context/chatContext.d.ts +3 -2
- package/dist/entities/context/chatContext.d.ts.map +1 -1
- package/dist/entities/context/chatContext.js +6 -6
- package/dist/entities/context/messageContext.d.ts +2 -1
- package/dist/entities/context/messageContext.d.ts.map +1 -1
- package/dist/entities/context/messageContext.js +6 -6
- package/dist/entities/responses/imageMessage.d.ts +3 -2
- package/dist/entities/responses/imageMessage.d.ts.map +1 -1
- package/dist/entities/responses/imageMessage.js +2 -2
- package/dist/entities/responses/reaction.d.ts +3 -2
- package/dist/entities/responses/reaction.d.ts.map +1 -1
- package/dist/entities/responses/reaction.js +2 -2
- package/dist/entities/responses/textMessage.d.ts +3 -2
- package/dist/entities/responses/textMessage.d.ts.map +1 -1
- package/dist/entities/responses/textMessage.js +2 -2
- package/dist/entities/responses/unpin.d.ts +3 -2
- package/dist/entities/responses/unpin.d.ts.map +1 -1
- package/dist/entities/responses/unpin.js +2 -2
- package/dist/entities/responses/videoMessage.d.ts +3 -2
- package/dist/entities/responses/videoMessage.d.ts.map +1 -1
- package/dist/entities/responses/videoMessage.js +2 -2
- package/dist/services/jsonFileStorage.d.ts +6 -7
- package/dist/services/jsonFileStorage.d.ts.map +1 -1
- package/dist/services/jsonFileStorage.js +29 -23
- package/dist/services/telegramApi.js +4 -4
- package/dist/types/actionWithState.d.ts +4 -1
- package/dist/types/actionWithState.d.ts.map +1 -1
- package/dist/types/response.d.ts +2 -1
- package/dist/types/response.d.ts.map +1 -1
- package/dist/types/storage.d.ts +4 -4
- package/dist/types/storage.d.ts.map +1 -1
- package/entities/actions/commandAction.ts +3 -3
- package/entities/actions/scheduledAction.ts +3 -3
- package/entities/botInstance.ts +4 -5
- package/entities/context/chatContext.ts +8 -12
- package/entities/context/messageContext.ts +11 -7
- package/entities/responses/imageMessage.ts +4 -3
- package/entities/responses/reaction.ts +4 -3
- package/entities/responses/textMessage.ts +4 -3
- package/entities/responses/unpin.ts +4 -3
- package/entities/responses/videoMessage.ts +4 -3
- package/package.json +1 -1
- package/services/jsonFileStorage.ts +41 -27
- package/services/telegramApi.ts +4 -4
- package/types/actionWithState.ts +3 -1
- package/types/response.ts +3 -1
- package/types/storage.ts +4 -4
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { IActionState } from './actionState';
|
|
2
|
+
export type ActionKey = string & {
|
|
3
|
+
__brand: 'actionKey';
|
|
4
|
+
};
|
|
2
5
|
export interface IActionWithState {
|
|
3
|
-
key:
|
|
6
|
+
key: ActionKey;
|
|
4
7
|
stateConstructor: () => IActionState;
|
|
5
8
|
}
|
|
6
9
|
//# sourceMappingURL=actionWithState.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actionWithState.d.ts","sourceRoot":"","sources":["../../types/actionWithState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"actionWithState.d.ts","sourceRoot":"","sources":["../../types/actionWithState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,gBAAgB,EAAE,MAAM,YAAY,CAAC;CACxC"}
|
package/dist/types/response.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Reaction } from '../entities/responses/reaction';
|
|
|
3
3
|
import { TextMessage } from '../entities/responses/textMessage';
|
|
4
4
|
import { UnpinResponse } from '../entities/responses/unpin';
|
|
5
5
|
import { VideoMessage } from '../entities/responses/videoMessage';
|
|
6
|
+
import { IActionWithState } from './actionWithState';
|
|
6
7
|
export declare const BotResponseTypes: {
|
|
7
8
|
readonly unpin: "unpin";
|
|
8
9
|
readonly text: "text";
|
|
@@ -15,7 +16,7 @@ export interface IChatResponse {
|
|
|
15
16
|
kind: keyof typeof BotResponseTypes;
|
|
16
17
|
chatId: number;
|
|
17
18
|
traceId: number | string;
|
|
18
|
-
|
|
19
|
+
action: IActionWithState;
|
|
19
20
|
}
|
|
20
21
|
export interface IReplyMessage<TType> extends IChatResponse {
|
|
21
22
|
content: TType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../types/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../types/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,gBAAgB;;;;;;CAMnB,CAAC;AAEX,MAAM,MAAM,WAAW,GACjB,aAAa,GACb,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,YAAY,CAAC;AAEnB,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,OAAO,gBAAgB,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB,MAAM,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,CAAE,SAAQ,aAAa;IACvD,OAAO,EAAE,KAAK,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;CACtB"}
|
package/dist/types/storage.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ActionExecutionResult } from '../entities/actionExecutionResult';
|
|
2
2
|
import { IActionState } from './actionState';
|
|
3
|
-
import { IActionWithState } from './actionWithState';
|
|
3
|
+
import { ActionKey, IActionWithState } from './actionWithState';
|
|
4
4
|
export interface IStorageClient {
|
|
5
|
-
updateStateFor<TActionState extends IActionState>(
|
|
5
|
+
updateStateFor<TActionState extends IActionState>(action: IActionWithState, chatId: number, update: (state: TActionState) => Promise<void>): Promise<void>;
|
|
6
6
|
close(): Promise<void>;
|
|
7
|
-
load<TActionState extends IActionState>(key:
|
|
7
|
+
load<TActionState extends IActionState>(key: ActionKey): Promise<Record<number, TActionState>>;
|
|
8
8
|
saveMetadata(actions: IActionWithState[], botName: string): Promise<void>;
|
|
9
|
-
getActionState<TActionState extends IActionState>(
|
|
9
|
+
getActionState<TActionState extends IActionState>(action: IActionWithState, chatId: number): Promise<TActionState>;
|
|
10
10
|
saveActionExecutionResult(action: IActionWithState, chatId: number, transactionResult: ActionExecutionResult): Promise<void>;
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../types/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC3B,cAAc,CAAC,YAAY,SAAS,YAAY,EAC5C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CAAC,YAAY,SAAS,YAAY,EAClC,GAAG,EAAE,SAAS,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;IACzC,YAAY,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,cAAc,CAAC,YAAY,SAAS,YAAY,EAC5C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,YAAY,CAAC,CAAC;IACzB,yBAAyB,CACrB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,iBAAiB,EAAE,qBAAqB,GACzC,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB"}
|
|
@@ -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 } from '../../types/actionWithState';
|
|
8
|
+
import { IActionWithState, ActionKey } from '../../types/actionWithState';
|
|
9
9
|
import { CommandTriggerCheckResult } from '../commandTriggerCheckResult';
|
|
10
10
|
import { MessageContext } from '../context/messageContext';
|
|
11
11
|
import { Logger } from '../../services/logger';
|
|
@@ -23,7 +23,7 @@ export class CommandAction<TActionState extends IActionState>
|
|
|
23
23
|
allowedUsers: number[];
|
|
24
24
|
condition: CommandCondition<TActionState>;
|
|
25
25
|
stateConstructor: () => TActionState;
|
|
26
|
-
key:
|
|
26
|
+
key: ActionKey;
|
|
27
27
|
|
|
28
28
|
constructor(
|
|
29
29
|
trigger: string | RegExp | string[] | RegExp[],
|
|
@@ -46,7 +46,7 @@ export class CommandAction<TActionState extends IActionState>
|
|
|
46
46
|
this.condition = condition;
|
|
47
47
|
this.stateConstructor = stateConstructor;
|
|
48
48
|
|
|
49
|
-
this.key = `command:${this.name.replace('.', '-')}
|
|
49
|
+
this.key = `command:${this.name.replace('.', '-')}` as ActionKey;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
async exec(ctx: MessageContext<TActionState>) {
|
|
@@ -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 } from '../../types/actionWithState';
|
|
7
|
+
import { IActionWithState, ActionKey } from '../../types/actionWithState';
|
|
8
8
|
import { CachedStateFactory } from '../cachedStateFactory';
|
|
9
9
|
import { ChatContext } from '../context/chatContext';
|
|
10
10
|
import { ActionExecutionResult } from '../actionExecutionResult';
|
|
@@ -20,7 +20,7 @@ export class ScheduledAction<TActionState extends IActionState>
|
|
|
20
20
|
timeinHours: HoursOfDay;
|
|
21
21
|
active: boolean;
|
|
22
22
|
chatsWhitelist: number[];
|
|
23
|
-
key:
|
|
23
|
+
key: ActionKey;
|
|
24
24
|
|
|
25
25
|
cachedState = new Map<string, unknown>();
|
|
26
26
|
stateConstructor: () => TActionState;
|
|
@@ -42,7 +42,7 @@ export class ScheduledAction<TActionState extends IActionState>
|
|
|
42
42
|
this.active = active;
|
|
43
43
|
this.chatsWhitelist = whitelist;
|
|
44
44
|
this.cachedStateFactories = cachedStateFactories;
|
|
45
|
-
this.key = `scheduled:${this.name.replace('.', '-')}
|
|
45
|
+
this.key = `scheduled:${this.name.replace('.', '-')}` as ActionKey;
|
|
46
46
|
this.stateConstructor = stateConstructor;
|
|
47
47
|
}
|
|
48
48
|
|
package/entities/botInstance.ts
CHANGED
|
@@ -35,6 +35,8 @@ export class BotInstance {
|
|
|
35
35
|
this.scheduled = options.scheduled;
|
|
36
36
|
this.chats = options.chats;
|
|
37
37
|
|
|
38
|
+
const actions = [...this.commands, ...this.scheduled];
|
|
39
|
+
|
|
38
40
|
Logger.logWithTraceId(
|
|
39
41
|
this.name,
|
|
40
42
|
`System:Bot-${this.name}-Start`,
|
|
@@ -44,7 +46,7 @@ export class BotInstance {
|
|
|
44
46
|
this.telegraf = new Telegraf(options.token);
|
|
45
47
|
this.storage =
|
|
46
48
|
options.storageClient ??
|
|
47
|
-
new JsonFileStorage(options.name, options.storagePath);
|
|
49
|
+
new JsonFileStorage(options.name, actions, options.storagePath);
|
|
48
50
|
this.api = new TelegramApiService(
|
|
49
51
|
this.name,
|
|
50
52
|
this.telegraf.telegram,
|
|
@@ -55,10 +57,7 @@ export class BotInstance {
|
|
|
55
57
|
this.initializeMessageProcessing();
|
|
56
58
|
this.initializeScheduledProcessing();
|
|
57
59
|
|
|
58
|
-
this.storage.saveMetadata(
|
|
59
|
-
[...this.commands, ...this.scheduled],
|
|
60
|
-
this.name
|
|
61
|
-
);
|
|
60
|
+
this.storage.saveMetadata(actions, this.name);
|
|
62
61
|
|
|
63
62
|
this.telegraf.launch();
|
|
64
63
|
}
|
|
@@ -9,12 +9,13 @@ import {
|
|
|
9
9
|
MessageSendingOptions,
|
|
10
10
|
TextMessageSendingOptions
|
|
11
11
|
} from '../../types/messageSendingOptions';
|
|
12
|
+
import { IActionWithState } from '../../types/actionWithState';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Context of action executed in chat.
|
|
15
16
|
*/
|
|
16
17
|
export class ChatContext<TActionState> {
|
|
17
|
-
protected
|
|
18
|
+
protected action: IActionWithState;
|
|
18
19
|
protected interactions: IBotApiInteractions;
|
|
19
20
|
updateActions: Array<(state: TActionState) => void> = [];
|
|
20
21
|
/** Trace id of a action execution. */
|
|
@@ -30,7 +31,7 @@ export class ChatContext<TActionState> {
|
|
|
30
31
|
|
|
31
32
|
constructor(
|
|
32
33
|
botName: string,
|
|
33
|
-
|
|
34
|
+
action: IActionWithState,
|
|
34
35
|
interactions: IBotApiInteractions,
|
|
35
36
|
chatId: number,
|
|
36
37
|
chatName: string,
|
|
@@ -38,7 +39,7 @@ export class ChatContext<TActionState> {
|
|
|
38
39
|
storage: IStorageClient
|
|
39
40
|
) {
|
|
40
41
|
this.botName = botName;
|
|
41
|
-
this.
|
|
42
|
+
this.action = action;
|
|
42
43
|
this.interactions = interactions;
|
|
43
44
|
this.chatId = chatId;
|
|
44
45
|
this.chatName = chatName;
|
|
@@ -66,7 +67,7 @@ export class ChatContext<TActionState> {
|
|
|
66
67
|
this.chatId,
|
|
67
68
|
undefined,
|
|
68
69
|
this.traceId,
|
|
69
|
-
this.
|
|
70
|
+
this.action,
|
|
70
71
|
options
|
|
71
72
|
)
|
|
72
73
|
);
|
|
@@ -85,7 +86,7 @@ export class ChatContext<TActionState> {
|
|
|
85
86
|
this.chatId,
|
|
86
87
|
undefined,
|
|
87
88
|
this.traceId,
|
|
88
|
-
this.
|
|
89
|
+
this.action,
|
|
89
90
|
options
|
|
90
91
|
)
|
|
91
92
|
);
|
|
@@ -104,7 +105,7 @@ export class ChatContext<TActionState> {
|
|
|
104
105
|
this.chatId,
|
|
105
106
|
undefined,
|
|
106
107
|
this.traceId,
|
|
107
|
-
this.
|
|
108
|
+
this.action,
|
|
108
109
|
options
|
|
109
110
|
)
|
|
110
111
|
);
|
|
@@ -116,12 +117,7 @@ export class ChatContext<TActionState> {
|
|
|
116
117
|
*/
|
|
117
118
|
unpinMessage(messageId: number) {
|
|
118
119
|
this.interactions.unpin(
|
|
119
|
-
new UnpinResponse(
|
|
120
|
-
messageId,
|
|
121
|
-
this.chatId,
|
|
122
|
-
this.traceId,
|
|
123
|
-
this.actionKey
|
|
124
|
-
)
|
|
120
|
+
new UnpinResponse(messageId, this.chatId, this.traceId, this.action)
|
|
125
121
|
);
|
|
126
122
|
}
|
|
127
123
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
MessageSendingOptions,
|
|
15
15
|
TextMessageSendingOptions
|
|
16
16
|
} from '../../types/messageSendingOptions';
|
|
17
|
+
import { IActionWithState, ActionKey } from '../../types/actionWithState';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Context of action executed in chat, in response to a message
|
|
@@ -36,14 +37,14 @@ export class MessageContext<
|
|
|
36
37
|
|
|
37
38
|
constructor(
|
|
38
39
|
botName: string,
|
|
39
|
-
|
|
40
|
+
action: IActionWithState,
|
|
40
41
|
interactions: IBotApiInteractions,
|
|
41
42
|
message: IncomingMessage,
|
|
42
43
|
storage: IStorageClient
|
|
43
44
|
) {
|
|
44
45
|
super(
|
|
45
46
|
botName,
|
|
46
|
-
|
|
47
|
+
action,
|
|
47
48
|
interactions,
|
|
48
49
|
message.chat.id,
|
|
49
50
|
message.chatName,
|
|
@@ -67,7 +68,10 @@ export class MessageContext<
|
|
|
67
68
|
async loadStateOf<TAnotherActionState extends IActionState>(
|
|
68
69
|
commandName: string
|
|
69
70
|
): Promise<TAnotherActionState> {
|
|
70
|
-
const storageKey = `command:${commandName.replace(
|
|
71
|
+
const storageKey = `command:${commandName.replace(
|
|
72
|
+
'.',
|
|
73
|
+
'-'
|
|
74
|
+
)}` as ActionKey;
|
|
71
75
|
const allStates = await this.storage.load(storageKey);
|
|
72
76
|
const stateForChat = allStates[this.chatId];
|
|
73
77
|
|
|
@@ -90,7 +94,7 @@ export class MessageContext<
|
|
|
90
94
|
this.chatId,
|
|
91
95
|
this.messageId,
|
|
92
96
|
this.traceId,
|
|
93
|
-
this.
|
|
97
|
+
this.action,
|
|
94
98
|
options
|
|
95
99
|
)
|
|
96
100
|
);
|
|
@@ -109,7 +113,7 @@ export class MessageContext<
|
|
|
109
113
|
this.chatId,
|
|
110
114
|
this.messageId,
|
|
111
115
|
this.traceId,
|
|
112
|
-
this.
|
|
116
|
+
this.action,
|
|
113
117
|
options
|
|
114
118
|
)
|
|
115
119
|
);
|
|
@@ -128,7 +132,7 @@ export class MessageContext<
|
|
|
128
132
|
this.chatId,
|
|
129
133
|
this.messageId,
|
|
130
134
|
this.traceId,
|
|
131
|
-
this.
|
|
135
|
+
this.action,
|
|
132
136
|
options
|
|
133
137
|
)
|
|
134
138
|
);
|
|
@@ -145,7 +149,7 @@ export class MessageContext<
|
|
|
145
149
|
this.chatId,
|
|
146
150
|
this.messageId,
|
|
147
151
|
emoji,
|
|
148
|
-
this.
|
|
152
|
+
this.action
|
|
149
153
|
)
|
|
150
154
|
);
|
|
151
155
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { InputFile } from 'telegraf/types';
|
|
2
2
|
import { BotResponseTypes, IReplyMessage } from '../../types/response';
|
|
3
3
|
import { MessageSendingOptions } from '../../types/messageSendingOptions';
|
|
4
|
+
import { IActionWithState } from '../../types/actionWithState';
|
|
4
5
|
|
|
5
6
|
export class ImageMessage implements IReplyMessage<InputFile> {
|
|
6
7
|
kind = BotResponseTypes.image;
|
|
@@ -11,14 +12,14 @@ export class ImageMessage implements IReplyMessage<InputFile> {
|
|
|
11
12
|
traceId: string | number;
|
|
12
13
|
disableWebPreview = false;
|
|
13
14
|
shouldPin: boolean;
|
|
14
|
-
|
|
15
|
+
action: IActionWithState;
|
|
15
16
|
|
|
16
17
|
constructor(
|
|
17
18
|
image: InputFile,
|
|
18
19
|
chatId: number,
|
|
19
20
|
replyId: number | undefined,
|
|
20
21
|
traceId: number | string,
|
|
21
|
-
|
|
22
|
+
action: IActionWithState,
|
|
22
23
|
options?: MessageSendingOptions
|
|
23
24
|
) {
|
|
24
25
|
this.content = image;
|
|
@@ -26,6 +27,6 @@ export class ImageMessage implements IReplyMessage<InputFile> {
|
|
|
26
27
|
this.replyId = replyId;
|
|
27
28
|
this.traceId = traceId;
|
|
28
29
|
this.shouldPin = options?.pin ?? false;
|
|
29
|
-
this.
|
|
30
|
+
this.action = action;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TelegramEmoji } from 'telegraf/types';
|
|
2
2
|
import { BotResponseTypes, IChatResponse } from '../../types/response';
|
|
3
|
+
import { IActionWithState } from '../../types/actionWithState';
|
|
3
4
|
|
|
4
5
|
export class Reaction implements IChatResponse {
|
|
5
6
|
kind = BotResponseTypes.react;
|
|
@@ -8,19 +9,19 @@ export class Reaction implements IChatResponse {
|
|
|
8
9
|
messageId: number;
|
|
9
10
|
traceId: number | string;
|
|
10
11
|
emoji: TelegramEmoji;
|
|
11
|
-
|
|
12
|
+
action: IActionWithState;
|
|
12
13
|
|
|
13
14
|
constructor(
|
|
14
15
|
traceId: number | string,
|
|
15
16
|
chatId: number,
|
|
16
17
|
messageId: number,
|
|
17
18
|
emoji: TelegramEmoji,
|
|
18
|
-
|
|
19
|
+
action: IActionWithState
|
|
19
20
|
) {
|
|
20
21
|
this.chatId = chatId;
|
|
21
22
|
this.messageId = messageId;
|
|
22
23
|
this.emoji = emoji;
|
|
23
24
|
this.traceId = traceId;
|
|
24
|
-
this.
|
|
25
|
+
this.action = action;
|
|
25
26
|
}
|
|
26
27
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TextMessageSendingOptions } from '../../types/messageSendingOptions';
|
|
2
2
|
import { BotResponseTypes, IReplyMessage } from '../../types/response';
|
|
3
|
+
import { IActionWithState } from '../../types/actionWithState';
|
|
3
4
|
|
|
4
5
|
export class TextMessage implements IReplyMessage<string> {
|
|
5
6
|
kind = BotResponseTypes.text;
|
|
@@ -10,14 +11,14 @@ export class TextMessage implements IReplyMessage<string> {
|
|
|
10
11
|
traceId: string | number;
|
|
11
12
|
disableWebPreview: boolean;
|
|
12
13
|
shouldPin: boolean;
|
|
13
|
-
|
|
14
|
+
action: IActionWithState;
|
|
14
15
|
|
|
15
16
|
constructor(
|
|
16
17
|
text: string,
|
|
17
18
|
chatId: number,
|
|
18
19
|
replyId: number | undefined,
|
|
19
20
|
traceId: string | number,
|
|
20
|
-
|
|
21
|
+
action: IActionWithState,
|
|
21
22
|
options?: TextMessageSendingOptions
|
|
22
23
|
) {
|
|
23
24
|
this.content = text;
|
|
@@ -26,6 +27,6 @@ export class TextMessage implements IReplyMessage<string> {
|
|
|
26
27
|
this.traceId = traceId;
|
|
27
28
|
this.disableWebPreview = options?.disableWebPreview ?? false;
|
|
28
29
|
this.shouldPin = options?.pin ?? false;
|
|
29
|
-
this.
|
|
30
|
+
this.action = action;
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BotResponseTypes, IChatResponse } from '../../types/response';
|
|
2
|
+
import { IActionWithState } from '../../types/actionWithState';
|
|
2
3
|
|
|
3
4
|
export class UnpinResponse implements IChatResponse {
|
|
4
5
|
kind = BotResponseTypes.unpin;
|
|
@@ -6,17 +7,17 @@ export class UnpinResponse implements IChatResponse {
|
|
|
6
7
|
messageId: number;
|
|
7
8
|
chatId: number;
|
|
8
9
|
traceId: number | string;
|
|
9
|
-
|
|
10
|
+
action: IActionWithState;
|
|
10
11
|
|
|
11
12
|
constructor(
|
|
12
13
|
messageId: number,
|
|
13
14
|
chatId: number,
|
|
14
15
|
traceId: number | string,
|
|
15
|
-
|
|
16
|
+
action: IActionWithState
|
|
16
17
|
) {
|
|
17
18
|
this.messageId = messageId;
|
|
18
19
|
this.chatId = chatId;
|
|
19
20
|
this.traceId = traceId;
|
|
20
|
-
this.
|
|
21
|
+
this.action = action;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { InputFile } from 'telegraf/types';
|
|
2
2
|
import { BotResponseTypes, IReplyMessage } from '../../types/response';
|
|
3
3
|
import { MessageSendingOptions } from '../../types/messageSendingOptions';
|
|
4
|
+
import { IActionWithState } from '../../types/actionWithState';
|
|
4
5
|
|
|
5
6
|
export class VideoMessage implements IReplyMessage<InputFile> {
|
|
6
7
|
kind = BotResponseTypes.video;
|
|
@@ -11,14 +12,14 @@ export class VideoMessage implements IReplyMessage<InputFile> {
|
|
|
11
12
|
traceId: string | number;
|
|
12
13
|
disableWebPreview = false;
|
|
13
14
|
shouldPin: boolean;
|
|
14
|
-
|
|
15
|
+
action: IActionWithState;
|
|
15
16
|
|
|
16
17
|
constructor(
|
|
17
18
|
video: InputFile,
|
|
18
19
|
chatId: number,
|
|
19
20
|
replyId: number | undefined,
|
|
20
21
|
traceId: number | string,
|
|
21
|
-
|
|
22
|
+
action: IActionWithState,
|
|
22
23
|
options?: MessageSendingOptions
|
|
23
24
|
) {
|
|
24
25
|
this.content = video;
|
|
@@ -26,6 +27,6 @@ export class VideoMessage implements IReplyMessage<InputFile> {
|
|
|
26
27
|
this.replyId = replyId;
|
|
27
28
|
this.traceId = traceId;
|
|
28
29
|
this.shouldPin = options?.pin ?? false;
|
|
29
|
-
this.
|
|
30
|
+
this.action = action;
|
|
30
31
|
}
|
|
31
32
|
}
|
package/package.json
CHANGED
|
@@ -6,15 +6,15 @@ import { IStorageClient } from '../types/storage';
|
|
|
6
6
|
import { ActionStateBase } from '../entities/states/actionStateBase';
|
|
7
7
|
import { ActionExecutionResult } from '../entities/actionExecutionResult';
|
|
8
8
|
import { IActionState } from '../types/actionState';
|
|
9
|
-
import { IActionWithState } from '../types/actionWithState';
|
|
9
|
+
import { IActionWithState, ActionKey } from '../types/actionWithState';
|
|
10
10
|
|
|
11
11
|
export class JsonFileStorage implements IStorageClient {
|
|
12
|
-
|
|
12
|
+
private locks = new Map<ActionKey, Semaphore>();
|
|
13
13
|
private cache: Map<string, Record<number, IActionState>>;
|
|
14
14
|
private storagePath: string;
|
|
15
15
|
private botName: string;
|
|
16
16
|
|
|
17
|
-
constructor(botName: string, path?: string) {
|
|
17
|
+
constructor(botName: string, actions: IActionWithState[], path?: string) {
|
|
18
18
|
this.cache = new Map<string, Record<number, IActionState>>();
|
|
19
19
|
this.botName = botName;
|
|
20
20
|
this.storagePath = path ?? 'storage';
|
|
@@ -24,19 +24,31 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
24
24
|
recursive: true
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
for (const action of actions) {
|
|
29
|
+
this.locks.set(action.key, new Semaphore(1));
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
private async lock<TType>(action: () => Promise<TType>) {
|
|
30
|
-
|
|
33
|
+
private async lock<TType>(key: ActionKey, action: () => Promise<TType>) {
|
|
34
|
+
const lock = this.locks.get(key);
|
|
35
|
+
|
|
36
|
+
if (!lock) {
|
|
37
|
+
throw new Error(`Lock for action ${key} not found`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await lock.acquire();
|
|
31
41
|
|
|
32
42
|
try {
|
|
33
43
|
return await action();
|
|
34
44
|
} finally {
|
|
35
|
-
|
|
45
|
+
lock.release();
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
48
|
|
|
39
|
-
private async loadInternal<TActionState extends IActionState>(
|
|
49
|
+
private async loadInternal<TActionState extends IActionState>(
|
|
50
|
+
key: ActionKey
|
|
51
|
+
) {
|
|
40
52
|
if (!this.cache.has(key)) {
|
|
41
53
|
const targetPath = this.buidPathFromKey(key);
|
|
42
54
|
if (!existsSync(targetPath)) {
|
|
@@ -55,7 +67,7 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
55
67
|
return (this.cache.get(key) ?? {}) as Record<number, TActionState>;
|
|
56
68
|
}
|
|
57
69
|
|
|
58
|
-
private async save(data: Record<number, ActionStateBase>, key:
|
|
70
|
+
private async save(data: Record<number, ActionStateBase>, key: ActionKey) {
|
|
59
71
|
this.cache.delete(key);
|
|
60
72
|
|
|
61
73
|
const targetPath = this.buidPathFromKey(key);
|
|
@@ -68,38 +80,38 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
68
80
|
await writeFile(targetPath, JSON.stringify(data), { flag: 'w+' });
|
|
69
81
|
}
|
|
70
82
|
|
|
71
|
-
private buidPathFromKey(key:
|
|
83
|
+
private buidPathFromKey(key: ActionKey) {
|
|
72
84
|
return `${this.storagePath}/${this.botName}/${key.replaceAll(
|
|
73
85
|
':',
|
|
74
86
|
'/'
|
|
75
87
|
)}.json`;
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
async load<TActionState extends IActionState>(key:
|
|
79
|
-
return await this.lock(async () => {
|
|
90
|
+
async load<TActionState extends IActionState>(key: ActionKey) {
|
|
91
|
+
return await this.lock(key, async () => {
|
|
80
92
|
return this.loadInternal<TActionState>(key);
|
|
81
93
|
});
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
async saveMetadata(actions: IActionWithState[], botName: string) {
|
|
85
|
-
|
|
86
|
-
|
|
97
|
+
const targetPath = this.buidPathFromKey(
|
|
98
|
+
`Metadata-${botName}` as ActionKey
|
|
99
|
+
);
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
101
|
+
await writeFile(targetPath, JSON.stringify(actions), {
|
|
102
|
+
flag: 'w+'
|
|
91
103
|
});
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
async getActionState<TActionState extends IActionState>(
|
|
95
|
-
|
|
107
|
+
action: IActionWithState,
|
|
96
108
|
chatId: number
|
|
97
109
|
) {
|
|
98
|
-
return await this.lock(async () => {
|
|
99
|
-
const data = await this.loadInternal(
|
|
110
|
+
return await this.lock(action.key, async () => {
|
|
111
|
+
const data = await this.loadInternal(action.key);
|
|
100
112
|
|
|
101
113
|
return Object.assign(
|
|
102
|
-
|
|
114
|
+
action.stateConstructor(),
|
|
103
115
|
data[chatId]
|
|
104
116
|
) as TActionState;
|
|
105
117
|
});
|
|
@@ -110,7 +122,7 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
110
122
|
chatId: number,
|
|
111
123
|
transactionResult: ActionExecutionResult
|
|
112
124
|
) {
|
|
113
|
-
await this.lock(async () => {
|
|
125
|
+
await this.lock(action.key, async () => {
|
|
114
126
|
const data = await this.loadInternal(action.key);
|
|
115
127
|
|
|
116
128
|
if (transactionResult.shouldUpdate) {
|
|
@@ -121,24 +133,26 @@ export class JsonFileStorage implements IStorageClient {
|
|
|
121
133
|
}
|
|
122
134
|
|
|
123
135
|
async close(): Promise<void> {
|
|
124
|
-
|
|
136
|
+
for (const lock of this.locks.values()) {
|
|
137
|
+
await lock.acquire();
|
|
138
|
+
}
|
|
125
139
|
}
|
|
126
140
|
|
|
127
141
|
async updateStateFor<TActionState extends IActionState>(
|
|
128
|
-
|
|
142
|
+
action: IActionWithState,
|
|
129
143
|
chatId: number,
|
|
130
144
|
update: (state: TActionState) => Promise<void>
|
|
131
145
|
) {
|
|
132
|
-
await this.lock(async () => {
|
|
133
|
-
const data = await this.loadInternal(
|
|
146
|
+
await this.lock(action.key, async () => {
|
|
147
|
+
const data = await this.loadInternal(action.key);
|
|
134
148
|
const state = Object.assign(
|
|
135
|
-
|
|
149
|
+
action.stateConstructor(),
|
|
136
150
|
data[chatId]
|
|
137
151
|
) as TActionState;
|
|
138
152
|
|
|
139
153
|
await update(state);
|
|
140
154
|
|
|
141
|
-
await this.save(data,
|
|
155
|
+
await this.save(data, action.key);
|
|
142
156
|
});
|
|
143
157
|
}
|
|
144
158
|
}
|
package/services/telegramApi.ts
CHANGED
|
@@ -80,7 +80,7 @@ export class TelegramApiService {
|
|
|
80
80
|
);
|
|
81
81
|
|
|
82
82
|
await this.storage.updateStateFor(
|
|
83
|
-
response.
|
|
83
|
+
response.action,
|
|
84
84
|
response.chatId,
|
|
85
85
|
async (state) => {
|
|
86
86
|
state.pinnedMessages.push(sentMessage.message_id);
|
|
@@ -152,7 +152,7 @@ export class TelegramApiService {
|
|
|
152
152
|
);
|
|
153
153
|
|
|
154
154
|
await this.storage.updateStateFor(
|
|
155
|
-
response.
|
|
155
|
+
response.action,
|
|
156
156
|
response.chatId,
|
|
157
157
|
async (state) => {
|
|
158
158
|
state.pinnedMessages = state.pinnedMessages.filter(
|
|
@@ -182,7 +182,7 @@ export class TelegramApiService {
|
|
|
182
182
|
) {
|
|
183
183
|
return new MessageContext<TActionState>(
|
|
184
184
|
this.botName,
|
|
185
|
-
command
|
|
185
|
+
command,
|
|
186
186
|
this.getInteractions(),
|
|
187
187
|
incomingMessage,
|
|
188
188
|
this.storage
|
|
@@ -195,7 +195,7 @@ export class TelegramApiService {
|
|
|
195
195
|
) {
|
|
196
196
|
return new ChatContext<TActionState>(
|
|
197
197
|
this.botName,
|
|
198
|
-
scheduledAction
|
|
198
|
+
scheduledAction,
|
|
199
199
|
this.getInteractions(),
|
|
200
200
|
chatId,
|
|
201
201
|
this.chats[chatId],
|
package/types/actionWithState.ts
CHANGED