@andrey4emk/npm-app-back-b24 3.1.0 → 3.2.0

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 (2) hide show
  1. package/logs/logs.ts +50 -46
  2. package/package.json +1 -1
package/logs/logs.ts CHANGED
@@ -19,6 +19,17 @@ interface LogLevelConfig {
19
19
  /** Полная конфигурация логирования */
20
20
  type LogConfig = Record<LogLevel, LogLevelConfig>;
21
21
 
22
+ // ==================== Константы B24 Chat ====================
23
+
24
+ /** ID бота в B24 для отправки error-логов */
25
+ const B24_BOT_ID = 66600;
26
+
27
+ /** CLIENT_ID бота в B24 */
28
+ const B24_CLIENT_ID = "6rf74zyu842h6op1z186b08k7aavqq7z";
29
+
30
+ /** Лимит длины сообщения для B24 чата (безопасный, с запасом на BB-обёртку) */
31
+ const B24_TEXT_LIMIT = 4000;
32
+
22
33
  // ==================== Настройки ====================
23
34
 
24
35
  const configDir: string = process.env.CONFIG_DIR || "../config";
@@ -62,19 +73,19 @@ class LogsAPI {
62
73
 
63
74
  private readonly resetColor = "\x1b[0m";
64
75
 
65
- /** Токен Telegram-бота для отправки error-логов */
66
- private readonly tgBotToken: string | undefined;
76
+ /** Webhook URL для отправки error-логов в B24 чат */
77
+ private readonly b24WebhookUrl: string | undefined;
67
78
 
68
- /** ID чата Telegram для отправки error-логов */
69
- private readonly tgChatId: string | undefined;
79
+ /** ID чата B24 для отправки error-логов (формат: chatXXXXXX) */
80
+ private readonly b24ChatId: string | undefined;
70
81
 
71
82
  /** Имя приложения для заголовка сообщения */
72
83
  private readonly appName: string;
73
84
 
74
85
  constructor() {
75
86
  this.initConfig();
76
- this.tgBotToken = process.env.LOG_TG_BOT_TOKEN;
77
- this.tgChatId = process.env.LOG_TG_CHAT_ID;
87
+ this.b24WebhookUrl = process.env.LOG_B24_WEBHOOK_URL;
88
+ this.b24ChatId = process.env.LOG_B24_CHAT_ID;
78
89
  this.appName = process.env.APP_NAME || "Unknown App";
79
90
  }
80
91
 
@@ -89,6 +100,12 @@ class LogsAPI {
89
100
 
90
101
  /**
91
102
  * Добавляет сообщение в лог
103
+ *
104
+ * Важно: при уровне `error` сообщение отправляется в B24 чат (через webhook)
105
+ * независимо от настройки `error.enabled` в `log.json`. Настройка `enabled`
106
+ * управляет только выводом в консоль. Это сделано намеренно, чтобы критические
107
+ * ошибки всегда доходили до команды даже при отключённом консольном логировании.
108
+ *
92
109
  * @param message - Текст сообщения или объект для вывода
93
110
  * @param level - Уровень логирования (trace, debug, info, warn, error)
94
111
  * @param jsonData - Дополнительные данные для вывода (опционально)
@@ -97,9 +114,9 @@ class LogsAPI {
97
114
  const levelStr: LogLevel = level || "info";
98
115
  const messageText = typeof message === "string" ? message : JSON.stringify(message);
99
116
 
100
- // Отправляем error-логи в Telegram независимо от настроек консольного вывода
117
+ // Отправляем error-логи в B24 чат независимо от настроек консольного вывода
101
118
  if (levelStr === "error") {
102
- this.sendToTelegram(messageText, jsonData);
119
+ this.sendToB24Chat(messageText, jsonData);
103
120
  }
104
121
 
105
122
  // Проверяем конфиг, и если логирование отключено, то выходим
@@ -123,22 +140,26 @@ class LogsAPI {
123
140
  }
124
141
  }
125
142
 
126
- /** Лимит длины текста для Telegram (с запасом на HTML-обёртку) */
127
- private readonly TG_TEXT_LIMIT = 4000;
143
+ /**
144
+ * Убирает BB-теги для plain text отправки (при обрезке текста)
145
+ */
146
+ private stripBbCodes(text: string): string {
147
+ return text.replace(/\[\/?(b|code)\]/g, "");
148
+ }
128
149
 
129
150
  /**
130
- * Отправляет сообщение в Telegram-чат (fire-and-forget)
151
+ * Отправляет error-сообщение в чат B24 через webhook (fire-and-forget)
131
152
  * Использует нативный fetch, а не fetchRetry — во избежание циклической зависимости
132
153
  * Ошибки не пробрасываются, только console.error
133
154
  */
134
- private sendToTelegram(message: string, jsonData?: unknown): void {
135
- if (!this.tgBotToken || !this.tgChatId) return;
155
+ private sendToB24Chat(message: string, jsonData?: unknown): void {
156
+ if (!this.b24WebhookUrl || !this.b24ChatId) return;
136
157
 
137
- const url = `https://api.telegram.org/bot${this.tgBotToken}/sendMessage`;
158
+ // Нормализуем URL — гарантируем trailing slash
159
+ const baseUrl = this.b24WebhookUrl.endsWith("/") ? this.b24WebhookUrl : `${this.b24WebhookUrl}/`;
160
+ const url = `${baseUrl}imbot.message.add.json`;
138
161
 
139
- // Экранируем HTML-спецсимволы в тексте сообщения
140
- const escapedMessage = this.escapeHtml(message);
141
- let text = `<b>ERROR</b> | ${this.escapeHtml(this.appName)}\n${escapedMessage}`;
162
+ let text = `[b]ERROR[/b] | ${this.appName}\n${message}`;
142
163
 
143
164
  // Добавляем jsonData если есть
144
165
  if (jsonData !== undefined) {
@@ -149,59 +170,42 @@ class LogsAPI {
149
170
  } catch {
150
171
  jsonStr = "[Не удалось сериализовать jsonData]";
151
172
  }
152
- text += `\n<pre>${this.escapeHtml(jsonStr)}</pre>`;
173
+ text += `\n[code]${jsonStr}[/code]`;
153
174
  }
154
175
 
155
- // Обрезаем до лимита Telegram
176
+ // Обрезаем до лимита B24
156
177
  let isTruncated = false;
157
- if (text.length > this.TG_TEXT_LIMIT) {
178
+ if (text.length > B24_TEXT_LIMIT) {
158
179
  const truncatedSuffix = "\n...(обрезано)";
159
- text = text.slice(0, this.TG_TEXT_LIMIT - truncatedSuffix.length) + truncatedSuffix;
180
+ text = text.slice(0, B24_TEXT_LIMIT - truncatedSuffix.length) + truncatedSuffix;
160
181
  isTruncated = true;
161
182
  }
162
183
 
163
- // Если текст обрезан — отправляем как plain text, чтобы не разорвать HTML-теги
184
+ // Если текст обрезан — отправляем без BB-тегов, чтобы не разорвать [code]...[/code]
164
185
  if (isTruncated) {
165
- text = this.stripHtml(text);
186
+ text = this.stripBbCodes(text);
166
187
  }
167
188
 
168
189
  fetch(url, {
169
190
  method: "POST",
170
191
  headers: { "Content-Type": "application/json" },
171
192
  body: JSON.stringify({
172
- chat_id: this.tgChatId,
173
- text,
174
- ...(isTruncated ? {} : { parse_mode: "HTML" }),
193
+ BOT_ID: B24_BOT_ID,
194
+ CLIENT_ID: B24_CLIENT_ID,
195
+ DIALOG_ID: this.b24ChatId,
196
+ MESSAGE: text,
175
197
  }),
176
198
  })
177
199
  .then((response) => {
178
200
  if (!response.ok) {
179
- console.error(`Logs Telegram: HTTP ${response.status}`);
201
+ console.error(`Logs B24 Chat: HTTP ${response.status}`);
180
202
  }
181
203
  })
182
204
  .catch((error: unknown) => {
183
205
  const errMsg = error instanceof Error ? error.message : String(error);
184
- console.error(`Logs Telegram: ошибка отправки — ${errMsg}`);
206
+ console.error(`Logs B24 Chat: ошибка отправки — ${errMsg}`);
185
207
  });
186
208
  }
187
-
188
- /** Экранирует HTML-спецсимволы для Telegram parse_mode HTML */
189
- private escapeHtml(text: string): string {
190
- return text
191
- .replace(/&/g, "&amp;")
192
- .replace(/</g, "&lt;")
193
- .replace(/>/g, "&gt;");
194
- }
195
-
196
- /** Убирает HTML-теги и декодирует HTML-entities для plain text отправки */
197
- private stripHtml(text: string): string {
198
- return text
199
- .replace(/<\/?b>/g, "")
200
- .replace(/<\/?pre>/g, "")
201
- .replace(/&lt;/g, "<")
202
- .replace(/&gt;/g, ">")
203
- .replace(/&amp;/g, "&");
204
- }
205
209
  }
206
210
 
207
211
  export const logs = new LogsAPI();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrey4emk/npm-app-back-b24",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Bitrix24 OAuth helpers for Node.js projects",
5
5
  "main": "index.ts",
6
6
  "type": "module",