@idoctor-devs/event-logger
Version:
NPM библиотека для отправки событий и сообщений в мессенджеры из веб-приложений
217 lines (216 loc) • 7.23 kB
JavaScript
function escapeMarkdownV2(text) {
const specialChars = /[().-]/g;
return text.replace(specialChars, "\\$&");
}
async function sendTelegramMessage(botToken, chatId, message, options = {}) {
const { timeout = 5e3, retryAttempts = 3 } = options;
const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
const payload = {
chat_id: chatId,
text: escapeMarkdownV2(message),
parse_mode: "MarkdownV2"
};
for (let attempt = 1; attempt <= retryAttempts; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload),
signal: controller.signal
});
clearTimeout(timeoutId);
if (response.ok) {
return true;
}
const errorData = await response.json().catch(() => ({}));
console.warn(`Telegram API error (attempt ${attempt}):`, {
status: response.status,
statusText: response.statusText,
error: errorData
});
if (attempt === retryAttempts) {
return false;
}
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
} catch (error) {
console.warn(`Network error (attempt ${attempt}):`, error);
if (attempt === retryAttempts) {
return false;
}
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
}
}
return false;
}
class BaseProvider {
}
class TelegramProvider extends BaseProvider {
constructor(config) {
super();
this.config = config;
}
async send(message, level, metadata) {
try {
const formattedMessage = this.formatMessage(message, level, metadata);
const success = await sendTelegramMessage(this.config.botToken, this.config.chatId, formattedMessage, {
timeout: this.config.timeout || 5e3,
retryAttempts: this.config.retryAttempts || 3
});
return success;
} catch (error) {
console.error("TelegramProvider send error:", error);
return false;
}
}
getBotToken() {
return this.config.botToken;
}
getChatId() {
return this.config.chatId;
}
formatDate(date = /* @__PURE__ */ new Date()) {
const data = typeof date === "string" ? new Date(date) : date;
const day = String(data.getDate()).padStart(2, "0");
const month = String(data.getMonth() + 1).padStart(2, "0");
const year = data.getFullYear();
const hours = String(data.getHours()).padStart(2, "0");
const minutes = String(data.getMinutes()).padStart(2, "0");
const seconds = String(data.getSeconds()).padStart(2, "0");
return `${day}.${month}.${year} ${hours}.${minutes}.${seconds}`;
}
formatMessage(message, level, metadata) {
const timestamp = this.formatDate();
const levelEmoji = this.getLevelEmoji(level);
let formattedMessage = `${levelEmoji} [${level.toUpperCase()}] ${timestamp}`;
if (metadata && Object.keys(metadata).length > 0) {
formattedMessage += "\n\n*METADATA*\n";
for (const [key, value] of Object.entries(metadata)) {
formattedMessage += `
*${key}*: ${value}`;
}
formattedMessage += "\n";
}
formattedMessage += `
${message}`;
return formattedMessage;
}
getLevelEmoji(level) {
const emojiMap = {
log: "📝",
info: "ℹ️",
warn: "⚠️",
error: "❌"
};
return emojiMap[level];
}
}
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
function validateEventLoggerConfig(config) {
if (!config) {
throw new ValidationError("Configuration is required");
}
if (!config.environment) {
throw new ValidationError("Environment is required");
}
if (config.environment !== "browser") {
throw new ValidationError('Only "browser" environment is supported');
}
if (!config.providers || !Array.isArray(config.providers)) {
throw new ValidationError("Providers array is required");
}
if (config.providers.length === 0) {
throw new ValidationError("At least one provider is required");
}
config.providers.forEach((provider, index) => {
validateTelegramProviderConfig(provider, index);
});
}
function validateTelegramProviderConfig(config, index) {
const providerPrefix = index !== void 0 ? `Provider[${index}]: ` : "";
if (!config) {
throw new ValidationError(`${providerPrefix}Provider configuration is required`);
}
if (config.type !== "telegram") {
throw new ValidationError(`${providerPrefix}Only "telegram" provider type is supported`);
}
if (!config.botToken || typeof config.botToken !== "string") {
throw new ValidationError(`${providerPrefix}Bot token is required and must be a string`);
}
if (!config.botToken.trim()) {
throw new ValidationError(`${providerPrefix}Bot token cannot be empty`);
}
if (!config.chatId || typeof config.chatId !== "string") {
throw new ValidationError(`${providerPrefix}Chat ID is required and must be a string`);
}
if (!config.chatId.trim()) {
throw new ValidationError(`${providerPrefix}Chat ID cannot be empty`);
}
if (config.timeout !== void 0) {
if (typeof config.timeout !== "number" || config.timeout <= 0) {
throw new ValidationError(`${providerPrefix}Timeout must be a positive number`);
}
}
if (config.retryAttempts !== void 0) {
if (typeof config.retryAttempts !== "number" || config.retryAttempts < 0) {
throw new ValidationError(`${providerPrefix}Retry attempts must be a non-negative number`);
}
}
}
class EventLogger {
constructor(config) {
this.providers = [];
validateEventLoggerConfig(config);
this.config = config;
this.initializeProviders();
}
initializeProviders() {
for (const providerConfig of this.config.providers) {
if (providerConfig.type === "telegram") {
const provider = new TelegramProvider(providerConfig);
this.providers.push(provider);
}
}
}
async log(message, metadata) {
await this.sendToAllProviders(message, "log", metadata);
}
async info(message, metadata) {
await this.sendToAllProviders(message, "info", metadata);
}
async warn(message, metadata) {
await this.sendToAllProviders(message, "warn", metadata);
}
async error(message, metadata) {
await this.sendToAllProviders(message, "error", metadata);
}
async sendToAllProviders(message, level, metadata) {
if ((!message || typeof message !== "string") && !metadata) {
console.warn("EventLogger: Message must be a non-empty string or metadata must be provided");
return;
}
const sendPromises = this.providers.map(async (provider) => {
try {
await provider.send(message, level, metadata);
} catch (error) {
console.error(`EventLogger: Failed to send message via provider:`, error);
}
});
await Promise.allSettled(sendPromises);
}
}
export {
BaseProvider,
EventLogger,
TelegramProvider,
ValidationError
};
//# sourceMappingURL=event-logger.es.js.map