chz-telegram-bot 0.5.5 → 0.6.9

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.
Files changed (74) hide show
  1. package/README.md +41 -31
  2. package/dist/entities/actions/commandAction.d.ts.map +1 -1
  3. package/dist/entities/actions/commandAction.js +11 -1
  4. package/dist/entities/actions/inlineQueryAction.d.ts.map +1 -1
  5. package/dist/entities/actions/inlineQueryAction.js +9 -1
  6. package/dist/entities/actions/replyCaptureAction.d.ts.map +1 -1
  7. package/dist/entities/actions/replyCaptureAction.js +9 -1
  8. package/dist/entities/actions/scheduledAction.d.ts.map +1 -1
  9. package/dist/entities/actions/scheduledAction.js +24 -4
  10. package/dist/entities/botInstance.d.ts +3 -4
  11. package/dist/entities/botInstance.d.ts.map +1 -1
  12. package/dist/entities/botInstance.js +13 -11
  13. package/dist/entities/context/baseContext.d.ts +4 -4
  14. package/dist/entities/context/baseContext.d.ts.map +1 -1
  15. package/dist/entities/context/baseContext.js +3 -2
  16. package/dist/eslint.config.d.ts +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/main.d.ts +0 -3
  21. package/dist/main.d.ts.map +1 -1
  22. package/dist/main.js +1 -2
  23. package/dist/services/actionProcessingService.d.ts +4 -4
  24. package/dist/services/actionProcessingService.d.ts.map +1 -1
  25. package/dist/services/actionProcessingService.js +10 -10
  26. package/dist/services/actionProcessors/baseProcessor.d.ts +3 -3
  27. package/dist/services/actionProcessors/baseProcessor.d.ts.map +1 -1
  28. package/dist/services/actionProcessors/baseProcessor.js +9 -5
  29. package/dist/services/actionProcessors/commandActionProcessor.d.ts +2 -3
  30. package/dist/services/actionProcessors/commandActionProcessor.d.ts.map +1 -1
  31. package/dist/services/actionProcessors/commandActionProcessor.js +32 -18
  32. package/dist/services/actionProcessors/inlineQueryActionProcessor.d.ts.map +1 -1
  33. package/dist/services/actionProcessors/inlineQueryActionProcessor.js +16 -8
  34. package/dist/services/actionProcessors/scheduledActionProcessor.d.ts +2 -2
  35. package/dist/services/actionProcessors/scheduledActionProcessor.d.ts.map +1 -1
  36. package/dist/services/actionProcessors/scheduledActionProcessor.js +4 -4
  37. package/dist/services/jsonFileStorage.d.ts +3 -1
  38. package/dist/services/jsonFileStorage.d.ts.map +1 -1
  39. package/dist/services/jsonFileStorage.js +26 -2
  40. package/dist/services/nodeTimeoutScheduler.d.ts +3 -3
  41. package/dist/services/nodeTimeoutScheduler.d.ts.map +1 -1
  42. package/dist/services/nodeTimeoutScheduler.js +27 -8
  43. package/dist/services/telegramApi.d.ts +4 -4
  44. package/dist/services/telegramApi.d.ts.map +1 -1
  45. package/dist/services/telegramApi.js +39 -9
  46. package/dist/types/events.d.ts +193 -0
  47. package/dist/types/events.d.ts.map +1 -0
  48. package/dist/types/events.js +69 -0
  49. package/dist/types/logger.d.ts +0 -12
  50. package/dist/types/logger.d.ts.map +1 -1
  51. package/dist/types/logger.js +1 -1
  52. package/entities/actions/commandAction.ts +11 -3
  53. package/entities/actions/inlineQueryAction.ts +9 -1
  54. package/entities/actions/replyCaptureAction.ts +9 -3
  55. package/entities/actions/scheduledAction.ts +31 -10
  56. package/entities/botInstance.ts +18 -25
  57. package/entities/context/baseContext.ts +9 -4
  58. package/index.ts +1 -1
  59. package/main.ts +1 -10
  60. package/package.json +1 -1
  61. package/services/actionProcessingService.ts +11 -15
  62. package/services/actionProcessors/baseProcessor.ts +9 -9
  63. package/services/actionProcessors/commandActionProcessor.ts +35 -46
  64. package/services/actionProcessors/inlineQueryActionProcessor.ts +24 -20
  65. package/services/actionProcessors/scheduledActionProcessor.ts +7 -10
  66. package/services/jsonFileStorage.ts +27 -1
  67. package/services/nodeTimeoutScheduler.ts +22 -22
  68. package/services/telegramApi.ts +53 -23
  69. package/types/events.ts +285 -0
  70. package/dist/services/jsonLogger.d.ts +0 -11
  71. package/dist/services/jsonLogger.d.ts.map +0 -1
  72. package/dist/services/jsonLogger.js +0 -66
  73. package/services/jsonLogger.ts +0 -112
  74. package/types/logger.ts +0 -39
@@ -5,12 +5,14 @@ import { IStorageClient } from '../types/storage';
5
5
  import { IActionState } from '../types/actionState';
6
6
  import { IActionWithState, ActionKey } from '../types/action';
7
7
  import { getOrSetIfNotExists } from '../helpers/mapUtils';
8
+ import { BotEventType, TypedEventEmitter } from '../types/events';
8
9
 
9
10
  function buildPath(storagePath: string, botName: string, actionKey: string) {
10
11
  return `${storagePath}/${botName}/${actionKey.replaceAll(':', '/')}.json`;
11
12
  }
12
13
 
13
14
  export class JsonFileStorage implements IStorageClient {
15
+ private readonly eventEmitter: TypedEventEmitter;
14
16
  private readonly filePaths = new Map<ActionKey, string>();
15
17
  private readonly locks = new Map<ActionKey, Semaphore>();
16
18
  private readonly cache: Map<string, Record<number, IActionState>>;
@@ -20,8 +22,10 @@ export class JsonFileStorage implements IStorageClient {
20
22
  constructor(
21
23
  botName: string,
22
24
  actions: IActionWithState<IActionState>[],
25
+ eventEmitter: TypedEventEmitter,
23
26
  path?: string
24
27
  ) {
28
+ this.eventEmitter = eventEmitter;
25
29
  this.cache = new Map<string, Record<number, IActionState>>();
26
30
  this.botName = botName;
27
31
  this.storagePath = path ?? 'storage';
@@ -57,12 +61,15 @@ export class JsonFileStorage implements IStorageClient {
57
61
  private async lock<TType>(key: ActionKey, action: () => Promise<TType>) {
58
62
  const lock = getOrSetIfNotExists(this.locks, key, new Semaphore(1));
59
63
 
64
+ this.eventEmitter.emit(BotEventType.storageLockAcquiring, key);
60
65
  await lock.acquire();
66
+ this.eventEmitter.emit(BotEventType.storageLockAcquired, key);
61
67
 
62
68
  try {
63
69
  return await action();
64
70
  } finally {
65
71
  lock.release();
72
+ this.eventEmitter.emit(BotEventType.storageLockReleased, key);
66
73
  }
67
74
  }
68
75
 
@@ -103,6 +110,10 @@ export class JsonFileStorage implements IStorageClient {
103
110
  data: Record<number, TActionState>,
104
111
  key: ActionKey
105
112
  ) {
113
+ this.eventEmitter.emit(BotEventType.storageStateSaving, {
114
+ data,
115
+ key
116
+ });
106
117
  this.cache.set(key, data);
107
118
 
108
119
  const targetPath = getOrSetIfNotExists(
@@ -112,6 +123,10 @@ export class JsonFileStorage implements IStorageClient {
112
123
  );
113
124
 
114
125
  await writeFile(targetPath, JSON.stringify(data), { flag: 'w+' });
126
+ this.eventEmitter.emit(BotEventType.storageStateSaved, {
127
+ data,
128
+ key
129
+ });
115
130
  }
116
131
 
117
132
  async load<TActionState extends IActionState>(key: ActionKey) {
@@ -135,13 +150,24 @@ export class JsonFileStorage implements IStorageClient {
135
150
  action: IActionWithState<TActionState>,
136
151
  chatId: number
137
152
  ) {
153
+ this.eventEmitter.emit(BotEventType.storageStateLoading, {
154
+ action,
155
+ chatId
156
+ });
138
157
  const value =
139
158
  this.tryGetFromCache<TActionState>(action.key) ??
140
159
  (await this.lock(action.key, async () => {
141
160
  return await this.loadFromFile<TActionState>(action.key);
142
161
  }));
143
162
 
144
- return Object.assign(action.stateConstructor(), value[chatId]);
163
+ const result = Object.assign(action.stateConstructor(), value[chatId]);
164
+
165
+ this.eventEmitter.emit(BotEventType.storageStateLoaded, {
166
+ action,
167
+ chatId,
168
+ state: result
169
+ });
170
+ return result;
145
171
  }
146
172
 
147
173
  async saveActionExecutionResult<TActionState extends IActionState>(
@@ -1,16 +1,12 @@
1
1
  import { TaskRecord } from '../entities/taskRecord';
2
- import { createTrace } from '../helpers/traceFactory';
3
- import { ILogger } from '../types/logger';
2
+ import { BotEventType, TypedEventEmitter } from '../types/events';
4
3
  import { IScheduler } from '../types/scheduler';
5
4
  import { Milliseconds } from '../types/timeValues';
6
5
 
7
6
  export class NodeTimeoutScheduler implements IScheduler {
8
- private readonly logger!: ILogger;
9
7
  readonly activeTasks: TaskRecord[] = [];
10
8
 
11
- constructor(logger: ILogger) {
12
- this.logger = logger;
13
- }
9
+ constructor(readonly eventEmitter: TypedEventEmitter) {}
14
10
 
15
11
  stopAll() {
16
12
  for (const task of this.activeTasks) {
@@ -25,19 +21,25 @@ export class NodeTimeoutScheduler implements IScheduler {
25
21
  executeRightAway: boolean,
26
22
  ownerName: string
27
23
  ) {
28
- const taskId = setInterval(action, interval);
24
+ const taskId = setInterval(() => {
25
+ action();
26
+ this.eventEmitter.emit(BotEventType.taskRun, {
27
+ name,
28
+ ownerName,
29
+ interval
30
+ });
31
+ }, interval);
29
32
  const task = new TaskRecord(name, taskId, interval);
30
33
 
31
34
  if (executeRightAway) {
32
35
  setImmediate(action);
33
36
  }
34
37
 
35
- this.logger.logWithTraceId(
38
+ this.eventEmitter.emit(BotEventType.taskCreated, {
39
+ name,
36
40
  ownerName,
37
- createTrace(this, ownerName, name),
38
- 'System',
39
- `Created task ${name}, that will run every ${interval}ms.`
40
- );
41
+ interval
42
+ });
41
43
 
42
44
  this.activeTasks.push(task);
43
45
  }
@@ -49,21 +51,19 @@ export class NodeTimeoutScheduler implements IScheduler {
49
51
  ownerName: string
50
52
  ) {
51
53
  const actionWrapper = () => {
52
- this.logger.logWithTraceId(
54
+ this.eventEmitter.emit(BotEventType.taskRun, {
55
+ name,
53
56
  ownerName,
54
- createTrace(this, ownerName, name),
55
- 'System',
56
- `Executing delayed oneshot ${name}`
57
- );
57
+ delay
58
+ });
58
59
  action();
59
60
  };
60
61
  setTimeout(actionWrapper, delay);
61
62
 
62
- this.logger.logWithTraceId(
63
+ this.eventEmitter.emit(BotEventType.taskCreated, {
64
+ name,
63
65
  ownerName,
64
- createTrace(this, ownerName, name),
65
- 'System',
66
- `Created oneshot task ${name}, that will run in ${delay}ms.`
67
- );
66
+ delay
67
+ });
68
68
  }
69
69
  }
@@ -1,6 +1,9 @@
1
1
  import { IStorageClient } from '../types/storage';
2
- import { BotResponse, IReplyResponse } from '../types/response';
3
- import { ILogger } from '../types/logger';
2
+ import {
3
+ BotResponse,
4
+ BotResponseTypes,
5
+ IReplyResponse
6
+ } from '../types/response';
4
7
  import { QueueItem, ResponseProcessingQueue } from './responseProcessingQueue';
5
8
  import { IReplyCapture } from '../types/capture';
6
9
  import { IActionWithState } from '../types/action';
@@ -8,6 +11,7 @@ import { IActionState } from '../types/actionState';
8
11
  import { TraceId } from '../types/trace';
9
12
  import { ChatInfo } from '../dtos/chatInfo';
10
13
  import { TelegramApiClient, TelegramMessage } from '../types/externalAliases';
14
+ import { BotEventType, TypedEventEmitter } from '../types/events';
11
15
 
12
16
  export const TELEGRAM_ERROR_QUOTE_INVALID = 'QUOTE_TEXT_INVALID';
13
17
 
@@ -15,7 +19,7 @@ export class TelegramApiService {
15
19
  private readonly queue = new ResponseProcessingQueue();
16
20
  private readonly telegram: TelegramApiClient;
17
21
  private readonly storage: IStorageClient;
18
- private readonly logger: ILogger;
22
+ private readonly eventEmitter: TypedEventEmitter;
19
23
  private readonly captureRegistrationCallback: (
20
24
  capture: IReplyCapture,
21
25
  parentMessageId: number,
@@ -25,22 +29,35 @@ export class TelegramApiService {
25
29
 
26
30
  private readonly botName: string;
27
31
 
32
+ private readonly methodMap: Record<
33
+ 'pin' | keyof typeof BotResponseTypes,
34
+ string | null
35
+ > = {
36
+ inlineQuery: 'answerInlineQuery',
37
+ text: 'sendMessage',
38
+ react: 'setMessageReaction',
39
+ unpin: 'unpinChatMessage',
40
+ pin: 'pinChatMessage',
41
+ image: 'sendPhoto',
42
+ video: 'sendVideo',
43
+ delay: null
44
+ };
45
+
28
46
  constructor(
29
47
  botName: string,
30
48
  telegram: TelegramApiClient,
31
49
  storage: IStorageClient,
32
- logger: ILogger,
50
+ eventEmitter: TypedEventEmitter,
33
51
  captureRegistrationCallback: (
34
52
  capture: IReplyCapture,
35
53
  parentMessageId: number,
36
- chatInfo: ChatInfo,
37
- traceId: TraceId
54
+ chatInfo: ChatInfo
38
55
  ) => void
39
56
  ) {
40
57
  this.telegram = telegram;
41
58
  this.botName = botName;
42
59
  this.storage = storage;
43
- this.logger = logger;
60
+ this.eventEmitter = eventEmitter;
44
61
  this.captureRegistrationCallback = captureRegistrationCallback;
45
62
  }
46
63
 
@@ -54,14 +71,6 @@ export class TelegramApiService {
54
71
 
55
72
  const queueItem: QueueItem = {
56
73
  callback: async () => {
57
- const scopedLogger = this.logger.createScope(
58
- this.botName,
59
- response.traceId,
60
- 'chatInfo' in response
61
- ? response.chatInfo.name
62
- : 'Unknown'
63
- );
64
-
65
74
  try {
66
75
  await this.processResponse(response);
67
76
  } catch (error) {
@@ -75,23 +84,27 @@ export class TelegramApiService {
75
84
  TELEGRAM_ERROR_QUOTE_INVALID
76
85
  )
77
86
  ) {
78
- scopedLogger.logWithTraceId(
79
- 'Quote error recieved, retrying without quote'
80
- );
87
+ this.eventEmitter.emit(BotEventType.error, {
88
+ error: new Error(
89
+ 'Quote error recieved, retrying without quote'
90
+ )
91
+ });
92
+
81
93
  try {
82
94
  await this.processResponse(response, true);
83
95
  } catch (error) {
84
- scopedLogger.errorWithTraceId(
85
- error,
86
- response
87
- );
96
+ this.eventEmitter.emit(BotEventType.error, {
97
+ error: error as Error
98
+ });
88
99
  }
89
100
 
90
101
  return;
91
102
  }
92
103
  }
93
104
 
94
- scopedLogger.errorWithTraceId(error, response);
105
+ this.eventEmitter.emit(BotEventType.error, {
106
+ error: error as Error
107
+ });
95
108
  }
96
109
  },
97
110
  priority: response.createdAt + offset
@@ -109,11 +122,19 @@ export class TelegramApiService {
109
122
  message: TelegramMessage
110
123
  ) {
111
124
  if (response.shouldPin) {
125
+ this.eventEmitter.emit(BotEventType.apiRequestSending, {
126
+ response: null,
127
+ telegramMethod: this.methodMap['pin']
128
+ });
112
129
  await this.telegram.pinChatMessage(
113
130
  response.chatInfo.id,
114
131
  message.message_id,
115
132
  { disable_notification: true }
116
133
  );
134
+ this.eventEmitter.emit(BotEventType.apiRequestSent, {
135
+ response: null,
136
+ telegramMethod: this.methodMap['pin']
137
+ });
117
138
 
118
139
  await this.storage.updateStateFor(
119
140
  response.action as IActionWithState<IActionState>,
@@ -127,6 +148,10 @@ export class TelegramApiService {
127
148
 
128
149
  private async processResponse(response: BotResponse, ignoreQuote = false) {
129
150
  const sentMessage = await this.sendApiRequest(response, ignoreQuote);
151
+ this.eventEmitter.emit(BotEventType.apiRequestSent, {
152
+ response,
153
+ telegramMethod: this.methodMap[response.kind]
154
+ });
130
155
 
131
156
  if (sentMessage && 'content' in response) {
132
157
  await this.pinIfShould(response, sentMessage);
@@ -146,6 +171,11 @@ export class TelegramApiService {
146
171
  response: BotResponse,
147
172
  ignoreQuote: boolean
148
173
  ): Promise<TelegramMessage | null> {
174
+ this.eventEmitter.emit(BotEventType.apiRequestSending, {
175
+ response,
176
+ telegramMethod: this.methodMap[response.kind]
177
+ });
178
+
149
179
  switch (response.kind) {
150
180
  case 'text':
151
181
  return await this.telegram.sendMessage(
@@ -0,0 +1,285 @@
1
+ import { ChatInfo } from '../dtos/chatInfo';
2
+ import { IncomingMessage } from '../dtos/incomingMessage';
3
+ import { IncomingInlineQuery } from '../dtos/incomingQuery';
4
+ import { CommandAction } from '../entities/actions/commandAction';
5
+ import { ChatContext } from '../entities/context/chatContext';
6
+ import { InlineQueryContext } from '../entities/context/inlineQueryContext';
7
+ import { MessageContext } from '../entities/context/messageContext';
8
+ import { ReplyContext } from '../entities/context/replyContext';
9
+ import { ActionKey, IAction, IActionWithState } from './action';
10
+ import { IActionState } from './actionState';
11
+ import { BotInfo } from './externalAliases';
12
+ import { BotResponse } from './response';
13
+ import { Milliseconds } from './timeValues';
14
+
15
+ export const BotEventType = {
16
+ error: 'error.generic',
17
+
18
+ messageRecieved: 'message.recieved',
19
+ messageProcessingStarted: 'message.processingStarted',
20
+ messageProcessingFinished: 'message.processingFinished',
21
+ beforeActionsExecuting: 'message.beforeActionsExecuting',
22
+
23
+ commandActionExecuting: 'command.actionExecuting',
24
+ commandActionExecuted: 'command.actionExecuted',
25
+ commandActionCaptureStarted: 'command.captionStarted',
26
+ commandActionCaptureAborted: 'command.captionAborted',
27
+
28
+ replyActionExecuting: 'reply.actionExecuting',
29
+ replyActionExecuted: 'reply.actionExecuted',
30
+
31
+ inlineActionExecuting: 'inline.actionExecuting',
32
+ inlineActionExecuted: 'inline.actionExecuted',
33
+ inlineProcessingStarted: 'inline.processingStarted',
34
+ inlineProcessingAborting: 'inline.processingAborting',
35
+ inlineProcessingAborted: 'inline.processingAborted',
36
+
37
+ scheduledActionExecuting: 'scheduled.actionExecuting',
38
+ scheduledActionExecuted: 'scheduled.actionExecuted',
39
+ scheduledActionCacheValueReturned: 'scheduled.cachedValueReturned',
40
+ scheduledActionCacheValueCreating: 'scheduled.cachedValueCreating',
41
+
42
+ apiRequestSending: 'api.requestSending',
43
+ apiRequestSent: 'api.requestSent',
44
+
45
+ storageLockAcquiring: 'storage.lockAcquiring',
46
+ storageLockAcquired: 'storage.lockAcquired',
47
+ storageLockReleased: 'storage.lockReleased',
48
+ storageStateSaving: 'storage.stateSaving',
49
+ storageStateSaved: 'storage.stateSaved',
50
+ storageStateLoading: 'storage.stateLoading',
51
+ storageStateLoaded: 'storage.stateLoaded',
52
+
53
+ taskCreated: 'task.created',
54
+ taskRun: 'task.run',
55
+
56
+ botStarting: 'bot.starting',
57
+ botStopping: 'bot.stopping'
58
+ } as const;
59
+
60
+ type BotEventTypeKeys = (typeof BotEventType)[keyof typeof BotEventType];
61
+ // Exhaustiveness validation
62
+ const _checkBotEventMapExhaustive: Record<BotEventTypeKeys, unknown> =
63
+ null as unknown as BotEventMap;
64
+
65
+ export type BotEventMap = {
66
+ [BotEventType.error]: {
67
+ error: Error;
68
+ };
69
+
70
+ [BotEventType.messageRecieved]: {
71
+ botInfo: BotInfo;
72
+ message: IncomingMessage;
73
+ };
74
+
75
+ [BotEventType.messageProcessingStarted]: {
76
+ botInfo: BotInfo;
77
+ message: IncomingMessage;
78
+ };
79
+
80
+ [BotEventType.messageProcessingFinished]: {
81
+ botInfo: BotInfo;
82
+ message: IncomingMessage;
83
+ };
84
+
85
+ [BotEventType.beforeActionsExecuting]: {
86
+ botInfo: BotInfo;
87
+ message: IncomingMessage;
88
+ commands: Set<CommandAction<IActionState>>;
89
+ };
90
+
91
+ [BotEventType.commandActionExecuting]: {
92
+ action: IActionWithState<IActionState>;
93
+ ctx: MessageContext<IActionState>;
94
+ state: IActionState;
95
+ };
96
+
97
+ [BotEventType.commandActionExecuted]: {
98
+ action: IActionWithState<IActionState>;
99
+ ctx: MessageContext<IActionState>;
100
+ state: IActionState;
101
+ };
102
+
103
+ [BotEventType.commandActionCaptureStarted]: {
104
+ parentMessageId: number;
105
+ chatInfo: ChatInfo;
106
+ };
107
+
108
+ [BotEventType.commandActionCaptureAborted]: {
109
+ parentMessageId: number;
110
+ chatInfo: ChatInfo;
111
+ };
112
+
113
+ [BotEventType.replyActionExecuting]: {
114
+ action: IAction;
115
+ ctx: ReplyContext<IActionState>;
116
+ };
117
+
118
+ [BotEventType.replyActionExecuted]: {
119
+ action: IAction;
120
+ ctx: ReplyContext<IActionState>;
121
+ };
122
+
123
+ [BotEventType.inlineActionExecuting]: {
124
+ action: IAction;
125
+ ctx: InlineQueryContext;
126
+ };
127
+
128
+ [BotEventType.inlineActionExecuted]: {
129
+ action: IAction;
130
+ ctx: InlineQueryContext;
131
+ };
132
+
133
+ [BotEventType.inlineProcessingStarted]: {
134
+ query: IncomingInlineQuery;
135
+ };
136
+
137
+ [BotEventType.inlineProcessingAborting]: {
138
+ abortedQuery: IncomingInlineQuery;
139
+ newQuery: IncomingInlineQuery;
140
+ };
141
+
142
+ [BotEventType.inlineProcessingAborted]: {
143
+ abortedQuery: IncomingInlineQuery;
144
+ };
145
+
146
+ [BotEventType.scheduledActionExecuting]: {
147
+ action: IAction;
148
+ ctx: ChatContext<IActionState>;
149
+ state: IActionState;
150
+ };
151
+
152
+ [BotEventType.scheduledActionExecuted]: {
153
+ action: IAction;
154
+ ctx: ChatContext<IActionState>;
155
+ state: IActionState;
156
+ };
157
+
158
+ [BotEventType.scheduledActionCacheValueCreating]: {
159
+ action: IAction;
160
+ ctx: ChatContext<IActionState>;
161
+ key: string;
162
+ };
163
+
164
+ [BotEventType.scheduledActionCacheValueReturned]: {
165
+ action: IAction;
166
+ ctx: ChatContext<IActionState>;
167
+ key: string;
168
+ };
169
+
170
+ [BotEventType.apiRequestSending]: {
171
+ response: BotResponse | null;
172
+ telegramMethod: string | null;
173
+ };
174
+
175
+ [BotEventType.apiRequestSent]: {
176
+ response: BotResponse | null;
177
+ telegramMethod: string | null;
178
+ };
179
+
180
+ [BotEventType.storageLockAcquiring]: ActionKey;
181
+ [BotEventType.storageLockAcquired]: ActionKey;
182
+ [BotEventType.storageLockReleased]: ActionKey;
183
+ [BotEventType.storageStateSaved]: {
184
+ key: ActionKey;
185
+ data: Record<number, unknown>;
186
+ };
187
+ [BotEventType.storageStateSaving]: {
188
+ key: ActionKey;
189
+ data: Record<number, unknown>;
190
+ };
191
+ [BotEventType.storageStateLoading]: {
192
+ action: IActionWithState<IActionState>;
193
+ chatId: number;
194
+ };
195
+ [BotEventType.storageStateLoaded]: {
196
+ action: IActionWithState<IActionState>;
197
+ chatId: number;
198
+ state: IActionState;
199
+ };
200
+
201
+ [BotEventType.taskCreated]: {
202
+ name: string;
203
+ ownerName: string;
204
+ delay?: Milliseconds;
205
+ interval?: Milliseconds;
206
+ };
207
+ [BotEventType.taskRun]: {
208
+ name: string;
209
+ ownerName: string;
210
+ delay?: Milliseconds;
211
+ interval?: Milliseconds;
212
+ };
213
+
214
+ [BotEventType.botStarting]: {
215
+ botName: string;
216
+ };
217
+
218
+ [BotEventType.botStopping]: {
219
+ botName: string;
220
+ };
221
+ };
222
+
223
+ type ListenerArgs<K extends keyof BotEventMap> =
224
+ BotEventMap[K] extends undefined ? [] : [BotEventMap[K]];
225
+
226
+ export type Listener<K extends keyof BotEventMap> = (
227
+ timestamp: number,
228
+ ...args: ListenerArgs<K>
229
+ ) => void;
230
+
231
+ export type EachListener = (
232
+ event: BotEventTypeKeys,
233
+ timestamp: number,
234
+ data: unknown
235
+ ) => void;
236
+
237
+ export class TypedEventEmitter {
238
+ private readonly listeners = new Map<
239
+ keyof BotEventMap | '*',
240
+ Set<Listener<keyof BotEventMap> | EachListener>
241
+ >();
242
+
243
+ on<K extends keyof BotEventMap>(event: K, fn: Listener<K>) {
244
+ const set = this.listeners.get(event) ?? new Set();
245
+ set.add(fn as Listener<keyof BotEventMap>);
246
+ this.listeners.set(event, set);
247
+ }
248
+
249
+ onEach(fn: EachListener) {
250
+ const event = '*';
251
+ const set = this.listeners.get(event) ?? new Set();
252
+ set.add(fn);
253
+ this.listeners.set(event, set);
254
+ }
255
+
256
+ emit<K extends keyof BotEventMap>(
257
+ event: K,
258
+ ...args: ListenerArgs<K>
259
+ ): void {
260
+ const timestamp = Date.now();
261
+ const specific = this.listeners.get(event);
262
+ if (specific) {
263
+ for (const fn of specific) {
264
+ (fn as Listener<K>)(timestamp, ...args);
265
+ }
266
+ }
267
+
268
+ const anySet = this.listeners.get('*');
269
+ if (anySet) {
270
+ for (const fn of anySet) {
271
+ (
272
+ fn as unknown as (
273
+ e: K,
274
+ t: number,
275
+ ...a: ListenerArgs<K>
276
+ ) => void
277
+ )(event, timestamp, ...args);
278
+ }
279
+ }
280
+ }
281
+
282
+ events(): (keyof BotEventMap | '*')[] {
283
+ return [...this.listeners.keys()];
284
+ }
285
+ }
@@ -1,11 +0,0 @@
1
- import { ILogger, IScopedLogger } from '../types/logger';
2
- import { TraceId } from '../types/trace';
3
- export declare class JsonLogger implements ILogger {
4
- private serializeError;
5
- private getCircularReplacer;
6
- createScope(botName: string, traceId: TraceId, chatName: string): IScopedLogger;
7
- logObjectWithTraceId(botName: string, traceId: TraceId, chatName: string, data: unknown): void;
8
- logWithTraceId(botName: string, traceId: TraceId, chatName: string, text: string): void;
9
- errorWithTraceId(botName: string, traceId: TraceId, chatName: string, errorObj: unknown, extraData?: unknown): void;
10
- }
11
- //# sourceMappingURL=jsonLogger.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"jsonLogger.d.ts","sourceRoot":"","sources":["../../services/jsonLogger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,qBAAa,UAAW,YAAW,OAAO;IACtC,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,mBAAmB;IAc3B,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAiBtD,aAAa;IAGtB,oBAAoB,CAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO;IAoBjB,cAAc,CACV,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM;IAOhB,gBAAgB,CACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EACjB,SAAS,CAAC,EAAE,OAAO;CAe1B"}
@@ -1,66 +0,0 @@
1
- export class JsonLogger {
2
- serializeError(error) {
3
- if (error instanceof Error) {
4
- const plainObject = {
5
- name: error.name,
6
- message: error.message,
7
- stack: error.stack
8
- };
9
- for (const [key, value] of Object.entries(error)) {
10
- plainObject[key] = value;
11
- }
12
- return JSON.stringify(plainObject);
13
- }
14
- return JSON.stringify({ error });
15
- }
16
- getCircularReplacer() {
17
- const cache = new Set();
18
- return (_, value) => {
19
- if (typeof value === 'object' && value !== null) {
20
- if (cache.has(value)) {
21
- return;
22
- }
23
- cache.add(value);
24
- }
25
- return value;
26
- };
27
- }
28
- createScope(botName, traceId, chatName) {
29
- return {
30
- logObjectWithTraceId: (data) => {
31
- this.logObjectWithTraceId(botName, traceId, chatName, data);
32
- },
33
- logWithTraceId: (text) => {
34
- this.logWithTraceId(botName, traceId, chatName, text);
35
- },
36
- errorWithTraceId: (errorObj, extraData) => {
37
- this.errorWithTraceId(botName, traceId, chatName, errorObj, extraData);
38
- }
39
- };
40
- }
41
- logObjectWithTraceId(botName, traceId, chatName, data) {
42
- const enrichedData = typeof data == 'object'
43
- ? {
44
- ...data,
45
- botName,
46
- traceId,
47
- chatName
48
- }
49
- : {
50
- botName,
51
- traceId,
52
- chatName,
53
- data
54
- };
55
- console.log(enrichedData);
56
- }
57
- logWithTraceId(botName, traceId, chatName, text) {
58
- console.log(`{"botName":"${botName}","traceId":"${traceId}","chatName":"${chatName}","text":"${text}"}`);
59
- }
60
- errorWithTraceId(botName, traceId, chatName, errorObj, extraData) {
61
- const dataString = extraData
62
- ? `,"extraData":${JSON.stringify(extraData, this.getCircularReplacer())}`
63
- : '';
64
- console.error(`{"botName":"${botName}","traceId":"${traceId}","chatName":"${chatName}","error":${this.serializeError(errorObj)}${dataString}}`);
65
- }
66
- }