kien-nguyen-support
Version:
šØ MultiChannelAlert - Ultimate multi-channel alert system supporting 15+ platforms (Telegram, Slack, Discord, Email, WhatsApp, Zalo, etc.) with zero dependencies. Advanced logging, notifications, and webhook management with enhanced formatting and smart
1,656 lines (1,393 loc) ⢠56.7 kB
Markdown
# šØ MultiChannelAlert - Kien Nguyen Support
<div align="center">
<h2>šÆ The Ultimate Multi-Channel Alert & Notification System</h2>
<p><strong>Send alerts to 15+ platforms simultaneously with zero dependencies!</strong></p>
</div>
## š± Supported Platforms
<div align="center">
<p>
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/slack/slack-original.svg" width="40" height="40" alt="Slack" title="Slack"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/8/82/Telegram_logo.svg" width="40" height="40" alt="Telegram" title="Telegram"/>
<img src="https://www.mattermost.com/wp-content/uploads/2022/02/icon.png" width="40" height="40" alt="Mattermost" title="Mattermost"/>
<img src="https://uxwing.com/wp-content/themes/uxwing/download/brands-and-social-media/discord-square-color-icon.png" width="40" height="40" alt="Discord" title="Discord"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/4/4e/Gmail_Icon.png" width="40" height="40" alt="Email" title="Email/Gmail"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/0/05/Facebook_Logo_%282019%29.png" width="40" height="40" alt="Messenger" title="Facebook Messenger"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/6/6b/WhatsApp.svg" width="40" height="40" alt="WhatsApp" title="WhatsApp"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg" width="40" height="40" alt="GitHub" title="GitHub (via N8n)"/>
</p>
<p>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Icon_of_Zalo.svg/100px-Icon_of_Zalo.svg.png" width="40" height="40" alt="Zalo" title="Zalo"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/4/41/LINE_logo.svg" width="40" height="40" alt="LINE" title="LINE"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/7/76/Viber.png?20210524155044" width="40" height="40" alt="Viber" title="Viber"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Skype_logo_%282019%E2%80%93present%29.svg/1187px-Skype_logo_%282019%E2%80%93present%29.svg.png" width="40" height="40" alt="Skype" title="Skype"/>
<img src="https://www.shareicon.net/data/128x128/2017/02/01/877517_media_512x512.png" width="40" height="40" alt="WeChat" title="WeChat"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/5/55/RocketChat_Logo_1024x1024.png?20160424202046" width="40" height="40" alt="RocketChat" title="RocketChat"/>
<img src="https://brandlogos.net/wp-content/uploads/2025/03/firebase_icon-logo_brandlogos.net_tcvck.png" width="40" height="40" alt="Firebase" title="Firebase"/>
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d9/N8n_logo.png?20210707053626" width="40" height="40" alt="N8n" title="N8n Webhooks"/>
</p>
</div>
---
A comprehensive **MultiChannelAlert system** and utilities package for logging, notifications, and webhook management with enhanced formatting and smart routing.
## Installation
```bash
npm install kien-nguyen-support
```
## š Why MultiChannelAlert?
<div align="center">
<table>
<tr>
<td align="center">
<h3>š Before</h3>
<p>Multiple libraries, complex setup</p>
<code>
telegram-bot-api<br/>
+ @slack/web-api<br/>
+ discord.js<br/>
+ nodemailer<br/>
+ whatsapp-web.js<br/>
= 50+ dependencies
</code>
</td>
<td align="center">
<h3>⨠After</h3>
<p>One simple import, zero dependencies</p>
<code>
const { MultiChannelAlert }<br/>
= require('kien-nguyen-support')<br/>
<br/>
// Send to 15+ platforms<br/>
multiAlert.error('Alert!')<br/>
= ZERO dependencies
</code>
</td>
</tr>
</table>
</div>
## šØ MultiChannelAlert - Core Features
<div align="center">
<h3>šÆ ONE API ā 15+ PLATFORMS</h3>
<p><em>"Write once, alert everywhere!"</em></p>
</div>
### š Key Highlights
- **šØ MultiChannelAlert**: Send alerts to **15+ platforms simultaneously** (Telegram, Slack, Mattermost, Discord, Email, WhatsApp, Zalo, LINE, Viber, Skype, WeChat, RocketChat, Firebase, N8n, Messenger)
- **šÆ Logger-Style API**: Clean constructor pattern with `.error()`, `.info()`, `.warn()`, `.success()` methods
- **š Smart ID Extraction**: Automatically extract and display IDs in copyable code blocks
- **š Enhanced Formatting**: Rich formatting with emojis, proper JSON display, platform-specific markdown, and code blocks for curl commands
- **āļø Conditional Channels**: Initialize only channels with available configurations
- **š Dynamic Management**: Add/remove channels at runtime with graceful error handling
- **šļø Individual StrictMode**: Filter data per channel with `strictMode` parameter for sensitive data protection
- **š Configuration Inheritance**: Channel-specific settings override global configurations
- **šØ Cross-Platform Formatting**: Consistent rich formatting across Telegram (Markdown), Mattermost (Markdown), Slack (Blocks), Discord (Embeds)
## ā” Performance & Optimization
- **Zero Dependencies**: No external runtime dependencies - uses only Node.js built-ins
- **85% Smaller Package**: Reduced from ~585KB to ~85KB by replacing axios & moment.js
- **Native HTTP Client**: Custom implementation with axios-compatible API
- **Custom DateTime Utils**: Native Date formatting without external libraries
- **Fast Installation**: No dependency resolution or security vulnerabilities
- **Production Ready**: Lightweight, secure, and maintainable
## š± Live Demo Screenshots
### Demo with Telegram and Mattermost
Real-time alerts with rich formatting, ID extraction, and smart routing:
<div align="center" style="padding: 0 20px;">
<img src="http://res.cloudinary.com/trungkien2022001/image/upload/v1759311010/upload/ccphpfenl4k5bdrgxw14.png" alt="Telegram Error Alert" width="350"/>
<img src="http://res.cloudinary.com/trungkien2022001/image/upload/v1759310992/upload/y59vvrocvng90elwuipr.png" alt="Telegram System Alert" width="350"/>
<br/>
<img src="http://res.cloudinary.com/trungkien2022001/image/upload/v1759310923/upload/brcgfxw2pud8nzkybegt.png" alt="Mattermost Alert Demo" width="350"/>
</div>
*Live examples showing enhanced formatting, automatic ID extraction, and multi-channel broadcasting*
## šÆ 4 Core Features
- šØ **MultiChannelAlert System**: Send notifications to **15+ platforms** simultaneously - Telegram, Slack, Mattermost, Discord, Email, WhatsApp, Zalo, LINE, Viber, Skype, WeChat, RocketChat, Firebase, N8n, Messenger
- š **Third-Party Log**: Save HTTP request/response logs and curl commands with provider-specific formatting
- š **Telegram Webhook**: Production webhook management with fallback and auto-recovery
- šÆ **Smart Routing**: Automatic thread/channel routing based on service and environment using nested object access
## ā” 30-Second Demo
```javascript
// šØ ONE LINE ā 15+ PLATFORMS
const multiAlert = new MultiChannelAlert({
channels: [
{ type: 'telegram', config: { /* telegram config */ } },
{ type: 'slack', config: { /* slack config */ } },
{ type: 'discord', config: { /* discord config */ } },
{ type: 'email', config: { /* email config */ } },
// ... add 11+ more platforms
]
})
// Send error alert to ALL channels simultaneously ā”
multiAlert.error('Payment system down!', {
userId: 'U123456',
amount: 999.99
})
// Result: 15+ notifications sent instantly! š
```
<div align="center">
<h4>š„ <strong>15+ Platforms</strong> ⢠<strong>Zero Dependencies</strong> ⢠<strong>One Simple API</strong> š„</h4>
</div>
## šļø Advanced Data Filtering & Formatting
### š StrictMode Data Filtering
Control exactly which data fields are sent to each platform:
```javascript
const multiAlert = new MultiChannelAlert({
channels: [
{
type: 'telegram',
config: {
botToken: 'your-token',
chatId: 'your-chat-id',
strictMode: true // Enable filtering for this channel
}
},
{
type: 'mattermost',
config: {
apiUrl: 'your-api-url',
token: 'your-token',
channelId: 'your-channel-id'
// No strictMode - will send all data
}
}
],
specific: [
{ key: 'user', title: 'š¤ User', markdown: true },
{ key: 'method', title: 'šØ Method', markdown: true },
{ key: 'status', title: 'š Status', markdown: true },
{ key: 'error_code', title: 'š“ Error Code', markdown: true },
{ key: 'curl', title: 'š» cURL Command', markdown: true }
],
strictMode: false // Global setting (can be overridden per channel)
})
// Full data object
const alertData = {
user: 'john_doe',
method: 'POST',
status: 500,
error_code: 'DB_CONNECTION_FAILED',
curl: 'curl -X POST https://api.example.com/users',
internal_debug: 'This sensitive data', // Won't be sent if strictMode=true
server_logs: 'Internal server information'
}
// Telegram will only receive: user, method, status, error_code, curl
// Mattermost will receive all fields (including internal_debug, server_logs)
await multiAlert.error(alertData)
```
### šØ Enhanced Message Formatting
Rich formatting with platform-specific optimizations:
**Telegram Output:**
```
**š¤ User:**
```
john_doe
```
**šØ Method:**
```
POST
```
**š» cURL Command:**
```
curl -X POST https://api.example.com/users
```
```
**Mattermost Output:**
```
------------------------
[From Mattermost Bot Alert]
šØ Environment: PRODUCTION
**š¤ User:** `john_doe`
**šØ Method:** `POST`
**š» cURL Command:**
```bash
curl -X POST https://api.example.com/users
```
------------------------
```
### š§ Individual Component StrictMode
Use individual alert components with their own filtering:
```javascript
// Individual components with their own strictMode
const telegramAlert = new TelegramAlert({
botToken: 'your-token',
chatId: 'your-chat-id',
strictMode: true // This component will filter data
})
const slackAlert = new SlackAlert({
webhookUrl: 'your-webhook-url'
// No strictMode - sends all data
})
// Same data, different filtering per component
const data = { user: 'john', secret: 'hidden', method: 'POST' }
const specific = [{ key: 'user', title: 'š¤ User' }, { key: 'method', title: 'šØ Method' }]
await telegramAlert.error(data) // Only sends: user, method (if specific is provided)
await slackAlert.error(data) // Sends all: user, secret, method
```
## Quick Start
```javascript
const {
MultiChannelAlert,
TelegramAlert,
SlackAlert,
saveProviderLog,
createTelegramWebhookClient
} = require('kien-nguyen-support')
// Feature 1: Multi-Channel Alerts (NEW in v2.0.0)
const multiAlert = new MultiChannelAlert({
channels: [
{
type: 'telegram',
config: {
botToken: process.env.TELEGRAM_BOT_TOKEN,
chatId: process.env.TELEGRAM_CHAT_ID,
service: 'hotel'
}
},
{
type: 'slack',
config: {
webhookUrl: process.env.SLACK_WEBHOOK_URL,
service: 'hotel'
}
}
],
environment: 'PRODUCTION'
})
// Send to all channels at once
await multiAlert.error({
user_id: '123',
booking_id: 'BK456',
error_code: 'PAYMENT_FAILED',
message: 'Payment processing failed'
})
// Feature 2: Individual Alert Channels
const telegram = new TelegramAlert({
botToken: process.env.TELEGRAM_BOT_TOKEN,
chatId: process.env.TELEGRAM_CHAT_ID,
service: 'hotel'
})
await telegram.error({ error_message: 'Hello from Telegram!' })
// Feature 3: Third-Party Logging
await saveProviderLog({
action: 'search',
req: requestData,
res: responseData,
code: 'EXPEDIA',
name: 'search-hotels'
})
// Feature 4: Telegram Webhook
const webhook = createTelegramWebhookClient({ /* config */ })
await webhook.setWebhook()
```
## Feature 1: Multi-Channel Alerts š
Send alerts to multiple platforms simultaneously with a single command.
### MultiChannelAlert Class (NEW)
```javascript
const { MultiChannelAlert } = require('kien-nguyen-support')
// Initialize with multiple channels
const multiAlert = new MultiChannelAlert({
channels: [
{
type: 'telegram',
config: {
botToken: 'your-bot-token',
chatId: 'your-chat-id',
service: 'hotel'
}
},
{
type: 'mattermost',
config: {
url: 'https://mattermost.example.com',
token: 'access-token',
chatId: 'channel-id',
service: 'hotel'
}
},
{
type: 'slack',
config: {
webhookUrl: 'https://hooks.slack.com/...',
service: 'hotel'
}
},
{
type: 'email',
config: {
host: 'smtp.gmail.com',
port: 587,
auth: {
user: 'alerts@company.com',
pass: 'app-password'
},
from: 'alerts@company.com',
to: 'team@company.com',
service: 'hotel'
}
}
],
service: 'hotel',
environment: 'PRODUCTION',
failSilently: true // Continue if some channels fail
})
// Send alerts using logger-style methods
await multiAlert.error({
user_id: '123',
booking_id: 'BK456',
error_code: 'PAYMENT_FAILED',
message: 'Payment processing failed',
amount: 150,
currency: 'USD'
})
await multiAlert.info({
deployment: 'v2.0.0',
status: 'success',
message: 'Deployment completed successfully'
})
await multiAlert.warn({
cpu_usage: '85%',
memory_usage: '78%',
message: 'High resource usage detected'
})
await multiAlert.success({
payment_id: 'PAY123',
amount: 150,
message: 'Payment processed successfully'
})
```
#### šÆ Configuration Inheritance & Data Filtering (NEW)
MultiChannelAlert now supports advanced configuration inheritance and data filtering:
```javascript
const multiAlert = new MultiChannelAlert({
// Global configuration
service: 'flight',
environment: 'PRODUCTION',
beauty: true,
specific: [
{ field: 'error', markdown: false },
{ field: 'message', markdown: true }
],
strictMode: true, // Enable data filtering
channels: [
{
type: 'telegram',
config: {
botToken: 'bot-token',
chatId: 'chat-id',
// Channel overrides: add timestamp field
specific: [
{ field: 'error', markdown: false },
{ field: 'message', markdown: true },
{ field: 'timestamp', markdown: false }
]
}
},
{
type: 'slack',
config: {
webhookUrl: 'webhook-url'
// Uses global specific: only error + message
}
}
]
})
// Test data with many fields
const alertData = {
error: 'Flight API timeout',
message: 'Booking system down',
timestamp: '2024-01-01T10:00:00Z',
userId: 'user123', // Will be filtered out
sessionId: 'session456', // Will be filtered out
metadata: { extra: 'info' } // Will be filtered out
}
await multiAlert.error(alertData)
// ā
Telegram receives: error, message, timestamp (3 fields)
// ā
Slack receives: error, message (2 fields)
// ā Other fields filtered out due to strictMode: true
```
**Configuration Inheritance Rules:**
- **Channel config** overrides **Global config** overrides **Default values**
- `strictMode: true` ā Only fields in `specific[]` are sent
- `strictMode: false` ā All fields are sent regardless of `specific[]`
- Channel-level `specific[]` overrides global `specific[]`
**Benefits:**
- šÆ **Selective Logging**: Only log relevant fields per channel
- š **Data Privacy**: Filter sensitive fields from certain channels
- ā” **Performance**: Reduce message size by filtering unnecessary data
- šļø **Flexibility**: Different field sets per channel type
### Conditional Channel Initialization
Only initialize channels that have available configuration:
```javascript
const channels = []
// Add Telegram if configured
if (process.env.TELEGRAM_BOT_TOKEN && process.env.TELEGRAM_CHAT_ID) {
channels.push({
type: 'telegram',
config: {
botToken: process.env.TELEGRAM_BOT_TOKEN,
chatId: process.env.TELEGRAM_CHAT_ID,
service: 'hotel'
}
})
}
// Add Slack if configured
if (process.env.SLACK_WEBHOOK_URL) {
channels.push({
type: 'slack',
config: {
webhookUrl: process.env.SLACK_WEBHOOK_URL,
service: 'hotel'
}
})
}
// Add Mattermost if configured
if (process.env.MATTERMOST_URL && process.env.MATTERMOST_TOKEN) {
channels.push({
type: 'mattermost',
config: {
url: process.env.MATTERMOST_URL,
token: process.env.MATTERMOST_TOKEN,
chatId: process.env.MATTERMOST_CHAT_ID,
service: 'hotel'
}
})
}
const multiAlert = new MultiChannelAlert({
channels,
service: 'hotel',
environment: 'PRODUCTION'
})
// This will only send to channels that were successfully initialized
const result = await multiAlert.error({ message: 'Test error' })
console.log(`Sent to ${result.summary.successful}/${result.summary.total} channels`)
```
#### šÆ Individual Component StrictMode (NEW)
Each alert component now supports individual `strictMode` for data filtering:
```javascript
const { TelegramAlert, SlackAlert, MattermostAlert } = require('kien-nguyen-support')
// TelegramAlert with strictMode
const telegramAlert = new TelegramAlert({
botToken: 'your-bot-token',
chatId: 'your-chat-id',
specific: [
{ field: 'error', markdown: false },
{ field: 'message', markdown: true }
],
strictMode: true // Enable data filtering
})
// SlackAlert with strictMode
const slackAlert = new SlackAlert({
webhookUrl: 'your-webhook-url',
specific: [
{ key: 'error', title: 'Error' },
{ key: 'user_id', title: 'User ID' }
],
strictMode: true // Enable data filtering
})
// MattermostAlert with strictMode
const mattermostAlert = new MattermostAlert({
apiUrl: 'your-mattermost-api-url',
token: 'your-token',
channelId: 'your-channel-id',
specific: [
{ field: 'error' },
{ field: 'message' },
{ field: 'user_id' }
],
strictMode: true // Enable data filtering
})
// Test data with many fields
const alertData = {
error: 'System failure',
message: 'Database error',
user_id: 'user_123',
session_id: 'session_456', // Will be filtered out
ip_address: '192.168.1.1', // Will be filtered out
metadata: { extra: 'info' } // Will be filtered out
}
await telegramAlert.error(alertData)
// ā
Sends only: error, message
await slackAlert.error(alertData)
// ā
Sends only: error, user_id
await mattermostAlert.error(alertData)
// ā
Sends only: error, message, user_id
```
**Benefits of Individual StrictMode:**
- šÆ **Selective Data**: Filter data without MultiChannelAlert
- š **Privacy Control**: Remove sensitive fields per component
- ā” **Performance**: Reduce message payload size
- šļø **Flexibility**: Different filtering per alert type
### Individual Alert Classes
You can also use individual alert classes directly:
#### 1. TelegramAlert
**Setup Requirements:**
1. Create a Telegram Bot:
- Message [@BotFather](https://t.me/botfather) on Telegram
- Send `/newbot` and follow instructions
- Get your `botToken`
2. Get Chat ID:
- Add your bot to a group/channel
- Send a message to the group
- Visit `https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates`
- Find `chat.id` in the response
```javascript
const { TelegramAlert } = require('kien-nguyen-support')
const telegram = new TelegramAlert({
botToken: 'your-bot-token', // From @BotFather
chatId: 'your-chat-id', // From getUpdates API
service: 'hotel', // For thread routing
environment: 'PRODUCTION', // Environment context
messageThreadIds: { // Optional: thread routing
'hotel.error.all': 30,
'hotel.info.all': 31,
general: 9
}
})
await telegram.error({ message: 'Error occurred' })
```
#### 2. SlackAlert
**Setup Requirements:**
1. Create a Slack App:
- Go to [api.slack.com/apps](https://api.slack.com/apps)
- Click "Create New App"
- Choose "From scratch"
2. Enable Incoming Webhooks:
- Go to "Incoming Webhooks" in your app settings
- Toggle "Activate Incoming Webhooks" to On
- Click "Add New Webhook to Workspace"
- Select channel and copy webhook URL
```javascript
const { SlackAlert } = require('kien-nguyen-support')
const slack = new SlackAlert({
webhookUrl: 'https://hooks.slack.com/services/...', // From Slack App
service: 'hotel', // For channel routing
environment: 'PRODUCTION', // Environment context
channels: { // Optional: channel routing
'hotel.error.all': '#hotel-errors',
'hotel.info.all': '#hotel-info',
general: '#general'
}
})
await slack.error({ message: 'Error occurred' })
```
#### 3. MattermostAlert
**Setup Requirements:**
1. Create Personal Access Token:
- Go to Mattermost ā Account Settings ā Security
- Click "Create New Token"
- Give it a description and copy the token
2. Get Channel ID:
- Go to channel ā View Info ā Copy channel ID
- Or use channel name (e.g., "town-square")
```javascript
const { MattermostAlert } = require('kien-nguyen-support')
const mattermost = new MattermostAlert({
url: 'https://your-mattermost.com', // Mattermost server URL
token: 'your-access-token', // Personal access token
chatId: 'channel-id-or-name', // Channel ID or name
service: 'hotel', // For channel routing
environment: 'PRODUCTION' // Environment context
})
await mattermost.error({ message: 'Error occurred' })
```
#### 4. EmailAlert
**Setup Requirements:**
1. SMTP Configuration (Gmail example):
- Enable 2FA on Gmail
- Generate App Password: Account ā Security ā App passwords
- Use app password (not your regular password)
```javascript
const { EmailAlert } = require('kien-nguyen-support')
const email = new EmailAlert({
host: 'smtp.gmail.com', // SMTP host
port: 587, // SMTP port
secure: false, // Use TLS
auth: {
user: 'your-email@gmail.com', // Your email
pass: 'your-app-password' // Gmail app password (not regular password)
},
from: 'alerts@company.com', // From address
to: 'team@company.com', // To address(es)
service: 'hotel', // For subject line
environment: 'PRODUCTION' // Environment context
})
await email.error({ message: 'Error occurred' })
```
#### 5. DiscordAlert
**Setup Requirements:**
1. Create Discord Webhook:
- Go to Discord channel ā Settings ā Integrations
- Click "Create Webhook"
- Copy webhook URL
```javascript
const { DiscordAlert } = require('kien-nguyen-support')
const discord = new DiscordAlert({
webhookUrl: 'https://discord.com/api/webhooks/...', // Discord webhook URL
service: 'hotel', // For embed title
environment: 'PRODUCTION' // Environment context
})
await discord.error({ message: 'Error occurred' })
```
#### 6. ZaloAlert
**Setup Requirements:**
1. Create Zalo OA (Official Account):
- Go to [developers.zalo.me](https://developers.zalo.me)
- Create Official Account
- Get access token from OA settings
2. Get User ID:
- User must follow your OA
- Use Zalo API to get follower user IDs
```javascript
const { ZaloAlert } = require('kien-nguyen-support')
const zalo = new ZaloAlert({
accessToken: 'your-oa-access-token', // OA access token
userId: 'user-id', // Zalo user ID
service: 'hotel', // For message context
environment: 'PRODUCTION' // Environment context
})
await zalo.error({ message: 'Error occurred' })
```
#### 7. MessengerAlert
**Setup Requirements:**
1. Create Facebook App:
- Go to [developers.facebook.com](https://developers.facebook.com)
- Create new app ā Business
- Add Messenger product
2. Get Page Access Token:
- Go to Messenger ā Settings
- Generate page access token
- Get recipient PSID from webhook events
```javascript
const { MessengerAlert } = require('kien-nguyen-support')
const messenger = new MessengerAlert({
pageAccessToken: 'your-page-access-token', // Facebook page access token
recipientId: 'user-psid', // User's PSID
service: 'hotel', // For message context
environment: 'PRODUCTION' // Environment context
})
await messenger.error({ message: 'Error occurred' })
```
#### 8. N8nAlert
**Setup Requirements:**
1. Create N8n Webhook:
- In N8n workflow, add "Webhook" node
- Set HTTP Method to POST
- Copy the webhook URL
```javascript
const { N8nAlert } = require('kien-nguyen-support')
const n8n = new N8nAlert({
webhookUrl: 'https://your-n8n.com/webhook/...', // N8n webhook URL
service: 'hotel', // For workflow context
environment: 'PRODUCTION' // Environment context
})
await n8n.error({ message: 'Error occurred' })
```
## Feature 2: Third-Party Log (HTTP Request Logging)
Save API request/response logs and curl commands to organized files:
```javascript
const { saveProviderLog, saveProviderCurl } = require('kien-nguyen-support')
// Save request/response logs
await saveProviderLog({
action: 'search', // Action name
req: requestData, // Request object/string (JSON or XML)
res: responseData, // Response object/string (JSON or XML)
code: 'EXPEDIA', // Provider/supplier code
name: 'search-hotels' // Operation name
})
// Save curl command for debugging
await saveProviderCurl({
name: 'search-hotels', // Operation name
request: { // Request object
method: 'POST',
url: 'https://api.expedia.com/search',
headers: { 'Content-Type': 'application/json' },
body: { destination: 'Bangkok' }
},
code: 'EXPEDIA', // Provider code
action: 'search' // Action name (optional)
})
```
**Files are automatically saved to:**
- Request/Response: `../logs/logs_YYYY-MM-DD/TIMESTAMP_CODE_NAME_ACTION_req.json`
- Curl commands: `../curl_logs/logs_YYYY-MM-DD/TIMESTAMP_CODE_NAME_ACTION_curl.txt`
## Feature 3: Telegram Bot (Legacy - Backward Compatibility)
### TelegramClient Class (Recommended)
Pre-configured client with smart thread routing:
```javascript
const { TelegramClient } = require('kien-nguyen-support')
/**
* Initialize TelegramClient
* Required config:
* - botToken: Telegram bot token
* - chatId: Chat ID to send messages to
*
* Optional config:
* - service: Default service type (hotel, flight, tour, transfer)
* - environment: Default environment (DEV, STAGING, PROD)
* - messageThreadIds: Thread routing configuration (nested object structure)
* - disableNotification: Disable notifications (default: false)
* - timeout: Request timeout in ms (default: 5000)
*/
const telegram = new TelegramClient({
botToken: process.env.TELEGRAM_BOT_TOKEN, // Required
chatId: process.env.TELEGRAM_CHAT_ID, // Required
service: 'hotel', // Optional
environment: 'PROD', // Optional
messageThreadIds: { // Optional - nested object structure
general: 17,
hotel: {
system: {
all: 17,
search: 19,
offer_search: 19,
prebook: 21,
book: 23,
cancel: 25
},
third_party: {
all: 28,
search: 30,
prebook: 32,
book: 34,
cancel: 36
}
},
flight: {
system: {
search: 5038,
'confirm-tax': 5039,
'generate-pnr': 5042
},
third_party: {
search: 3561,
'confirm-tax': 5050
}
},
flight: {
system: {
search: 5038,
'confirm-tax': 5039,
'generate-pnr': 5042
},
third_party: {
search: 3561,
'confirm-tax': 5050
}
}
},
disableNotification: false, // Optional
timeout: 10000 // Optional
})
/**
* Send message - options parameter structure
* Required: error_message
* Optional: All other fields for metadata and routing
*/
await telegram.sendMessage({
error_message: 'Hello World!' // Required - message text
})
/**
* Send error alert with auto thread routing and enhanced formatting
* Thread routing: service.type.metric ā nested object access ā general fallback
*
* ⨠NEW: Automatic ID extraction and rich formatting!
*/
await telegram.sendErrorAlert({
// Core error info
error_message: 'Failed to book hotel', // Required
error_code: 'BOOKING_FAILED', // Optional
// Routing (auto-detects thread using nested object access)
// Example: messageThreadIds.hotel.third_party.book = 34
type: 'third_party', // 'system' or 'third_party'
metric: 'book', // For thread routing
// Context (optional)
user_email: 'user@example.com',
supplier: {
code: 'EXPEDIA',
id: 123,
source_id: 'EXP001'
},
// š Enhanced request metadata with ID extraction
request_metadata: {
bookingId: 'ABC123',
search_id: 'd8e2048ba1974f568a807532752e452f', // Auto-extracted!
trace_id: 'trace-12345', // Auto-extracted!
log_id: '63b5c9faf56fc04fdf2509b3874cb6d4' // Auto-extracted!
},
error_stack: error.stack, // š Auto-truncated to 2 lines
metadata: { additional: 'data' } // Any extra metadata
})
```
### Factory Function (Alternative)
```javascript
const { createTelegramClient } = require('kien-nguyen-support')
const telegramClient = createTelegramClient({
botToken: process.env.TELEGRAM_BOT_TOKEN,
chatId: process.env.TELEGRAM_CHAT_ID,
messageThreadIds: { /* thread config */ }
})
await telegramClient.sendMessage({ error_message: 'Hello!' })
```
})
### šÆ Smart Thread Routing (v2.2.0)
The new nested object structure provides better organization and type safety:
```javascript
// Old underscore pattern (deprecated)
messageThreadIds: {
'hotel_system_search': 19,
'hotel_system_book': 23,
'hotel_third_party_search': 30
}
// New nested object structure (recommended)
messageThreadIds: {
general: 17, // Global fallback
hotel: {
system: {
all: 17, // Service-specific fallback
search: 19, // Specific metric
book: 23
},
third_party: {
all: 28,
search: 30,
book: 34
}
},
flight: {
system: { search: 5038 },
third_party: { search: 3561 }
}
}
```
**Routing Priority:**
1. `messageThreadIds.{service}.{type}.{metric}` - Most specific
2. `messageThreadIds.{service}.{type}.all` - Service + type fallback
3. `messageThreadIds.{service}.{type}.general` - Service + type general
4. `messageThreadIds.general` - Global fallback
**Example:** `service: 'hotel'`, `type: 'system'`, `metric: 'search'`
ā Looks for `messageThreadIds.hotel.system.search = 19`
## Feature 4: Telegram Webhook (Production Setup)
Manage webhooks for production deployment:
```javascript
const { createTelegramWebhookClient } = require('kien-nguyen-support')
/**
* Initialize webhook client
* Required config:
* - botToken: Telegram bot token
* - webhookUrl: URL for webhook endpoint
*
* Optional config:
* - secretToken: Secret token for verification
* - allowedUpdates: Array of update types to receive
* - maxConnections: Max simultaneous connections
* - timeout: Request timeout in ms
*/
const webhookClient = createTelegramWebhookClient({
botToken: process.env.TELEGRAM_BOT_TOKEN, // Required
webhookUrl: process.env.WEBHOOK_URL, // Required
secretToken: process.env.WEBHOOK_SECRET_TOKEN, // Optional but recommended
allowedUpdates: ['message', 'callback_query'], // Optional
maxConnections: 100, // Optional
timeout: 10000 // Optional
})
// Set webhook
await webhookClient.setWebhook()
// Get webhook info
const info = await webhookClient.getWebhookInfo()
console.log('Webhook status:', info.url)
// Delete webhook
await webhookClient.deleteWebhook()
// Verify incoming webhook requests
app.post('/telegram-webhook', (req, res) => {
const secretToken = req.headers['x-telegram-bot-api-secret-token']
if (!webhookClient.verifyRequest(secretToken)) {
return res.status(401).send('Unauthorized')
}
const update = webhookClient.parseUpdate(req.body)
if (update.message) {
console.log('Received message:', update.message.text)
}
res.sendStatus(200)
})
```
## Smart Thread Routing
Configure automatic message routing to specific Telegram threads:
```javascript
const messageThreadIds = {
// General fallback
general: 9,
// Hotel system
hotel_system_search: 19,
hotel_system_book: 23,
// Hotel third-party
hotel_third_party_search: 30,
hotel_third_party_book: 34,
// Flight system
flight_system_search: 41,
flight_system_book: 43,
// Tour/Transfer
tour_system_search: 51,
transfer_system_book: 73
}
```
**Routing Logic**: `{product}_{type}_{metric}` ā Falls back to `general`
Examples:
- `hotel` + `third_party` + `book` ā `hotel_third_party_book` (34)
- `flight` + `system` + `search` ā `flight_system_search` (41)
- Unknown combination ā `general` (9)
## Usage Patterns
### Pattern 1: Multi-Channel Alert System (Recommended)
Create a shared multi-channel client for your entire project:
```javascript
// config/alert-client.js
const { MultiChannelAlert } = require('kien-nguyen-support')
// Initialize channels based on available environment variables
const channels = []
if (process.env.TELEGRAM_BOT_TOKEN && process.env.TELEGRAM_CHAT_ID) {
channels.push({
type: 'telegram',
config: {
botToken: process.env.TELEGRAM_BOT_TOKEN,
chatId: process.env.TELEGRAM_CHAT_ID,
service: 'hotel'
}
})
}
if (process.env.SLACK_WEBHOOK_URL) {
channels.push({
type: 'slack',
config: {
webhookUrl: process.env.SLACK_WEBHOOK_URL,
service: 'hotel'
}
})
}
if (process.env.MATTERMOST_URL && process.env.MATTERMOST_TOKEN) {
channels.push({
type: 'mattermost',
config: {
url: process.env.MATTERMOST_URL,
token: process.env.MATTERMOST_TOKEN,
chatId: process.env.MATTERMOST_CHAT_ID,
service: 'hotel'
}
})
}
const alertClient = new MultiChannelAlert({
channels,
service: 'hotel',
environment: process.env.NODE_ENV || 'STAGING',
failSilently: true
})
module.exports = alertClient
// services/hotel-service.js
const alertClient = require('../config/alert-client')
async function bookHotel(hotelData) {
try {
const booking = await processHotelBooking(hotelData)
// Success alert to all channels
await alertClient.success({
booking_id: booking.id,
user_email: hotelData.userEmail,
amount: booking.amount,
currency: booking.currency,
message: `Hotel booking successful: ${booking.id}`
})
return booking
} catch (error) {
// Error alert to all channels
await alertClient.error({
error_code: 'BOOKING_FAILED',
message: error.message,
user_email: hotelData.userEmail,
booking_data: hotelData,
error_stack: error.stack
})
throw error
}
}
```
### Pattern 2: Service-Specific Alert Clients
```javascript
// services/flight-service.js
const { TelegramAlert, SlackAlert } = require('kien-nguyen-support')
// Flight-specific Telegram alerts
const flightTelegram = new TelegramAlert({
botToken: process.env.TELEGRAM_BOT_TOKEN,
chatId: process.env.FLIGHT_CHAT_ID,
service: 'flight',
environment: 'PRODUCTION',
messageThreadIds: {
'flight.error.all': 31,
'flight.info.all': 33,
general: 9
}
})
// Flight-specific Slack alerts
const flightSlack = new SlackAlert({
webhookUrl: process.env.FLIGHT_SLACK_WEBHOOK,
service: 'flight',
channels: {
'flight.error.all': '#flight-errors',
'flight.info.all': '#flight-info'
}
})
async function searchFlights(searchData) {
try {
await flightTelegram.info({
action: 'search_start',
message: 'Flight search initiated',
search_data: searchData
})
const results = await performFlightSearch(searchData)
await flightSlack.success({
action: 'search_complete',
message: `Found ${results.length} flights`,
search_id: searchData.search_id,
results_count: results.length
})
return results
} catch (error) {
// Send error to both Telegram and Slack
await Promise.allSettled([
flightTelegram.error({
error_code: 'SEARCH_FAILED',
message: error.message,
search_data: searchData
}),
flightSlack.error({
error_code: 'SEARCH_FAILED',
message: error.message,
search_data: searchData
})
])
throw error
}
}
```
## š Enhanced Multi-Channel Message Format
With version 2.0.0, all alert channels now include:
### Smart ID Extraction
Automatically extracts and displays IDs in copyable code blocks:
```
š **Search ID:**
```
d8e2048ba1974f568a807532752e452f
```
š **Trace ID:**
```
trace-12345-abcdef
```
š **Log ID:**
```
63b5c9faf56fc04fdf2509b3874cb6d4
```
```
### Rich Formatting with Emojis
- š§ **Environment**: `PRODUCTION`
- š **Type**: `third_party`
- ā **Error Code**: `BOOKING_FAILED`
- š **Message**: Error details in code blocks
- š¤ **User**: User information
- š¢ **Supplier**: Supplier details
- š **Request Data**: Formatted JSON with proper indentation
- š **Stack Trace**: Limited to first 2 lines for readability
### Example Enhanced Message Output
```
š§ Environment: `PRODUCTION`
š Type: `third_party`
ā Error Code: `BOOKING_FAILED`
š Message:
```
Failed to book hotel room
```
š Search ID:
```
d8e2048ba1974f568a807532752e452f
```
š Request Data:
```json
{
"adults": 2,
"children": 0,
"departure_date": "2025-09-29",
"search_id": "d8e2048ba1974f568a807532752e452f"
}
```
š Stack Trace:
```
BookingError: Room not available
at bookRoom (/app/services/booking.js:145:12)
```
```
## š§ Troubleshooting & Best Practices
### ā Common Issues
#### "message text is empty" Error (Telegram)
**Cause:** StrictMode filtering resulted in empty data or missing required fields.
**Solution:**
```javascript
// ā Wrong: No matching fields in specific configuration
const specific = [
{ key: 'user_name', title: 'User' } // But data has 'user', not 'user_name'
]
const data = { user: 'john', method: 'POST' }
// ā
Correct: Match field keys exactly
const specific = [
{ key: 'user', title: 'š¤ User' }, // Matches data.user
{ key: 'method', title: 'šØ Method' } // Matches data.method
]
```
#### No Data Filtering with StrictMode
**Cause:** `strictMode` is enabled but no `specific` configuration provided.
**Solution:**
```javascript
// ā Wrong: strictMode=true but no specific config
const alert = new TelegramAlert({
botToken: 'token',
chatId: 'chat',
strictMode: true // Will return original data if no specific config
})
// ā
Correct: Provide specific configuration
const alert = new TelegramAlert({
botToken: 'token',
chatId: 'chat',
strictMode: true,
specific: [
{ key: 'user', title: 'š¤ User' },
{ key: 'error_code', title: 'š“ Error' }
]
})
```
### šÆ Best Practices
#### 1. **Field Naming Consistency**
```javascript
// ā
Use consistent field names across your application
const standardFields = [
{ key: 'user', title: 'š¤ User' },
{ key: 'method', title: 'šØ Method' },
{ key: 'status', title: 'š Status' },
{ key: 'error_code', title: 'š“ Error Code' },
{ key: 'curl', title: 'š» cURL Command' }
]
```
#### 2. **Sensitive Data Protection**
```javascript
// ā
Use strictMode for production channels
const productionAlert = new MultiChannelAlert({
channels: [
{
type: 'telegram',
config: {
botToken: process.env.TELEGRAM_TOKEN,
chatId: process.env.TELEGRAM_CHAT_ID,
strictMode: true // Filter sensitive data
}
}
],
specific: [
{ key: 'user', title: 'š¤ User' },
{ key: 'error_code', title: 'š“ Error' }
// Sensitive fields like passwords, tokens are automatically excluded
]
})
```
#### 3. **Platform-Specific Formatting**
```javascript
// ā
Let the library handle platform-specific formatting
const data = {
user: 'john_doe',
metadata: { session_id: '123', ip: '192.168.1.1' }, // Object - auto JSON formatted
curl: 'curl -X POST https://api.com', // Auto code block
error_stack: 'Error\n at function()\n at ...' // Auto truncated
}
// Library automatically formats:
// - Objects as JSON code blocks
// - Curl commands as bash code blocks
// - Stack traces with truncation
// - Titles with emojis
```
#### 4. **Configuration Inheritance**
```javascript
// ā
Use global config with channel overrides
const multiAlert = new MultiChannelAlert({
channels: [
{
type: 'telegram',
config: {
botToken: 'token',
chatId: 'chat-id',
specific: [ // Override global specific for this channel
{ key: 'user', title: 'š¤ User' },
{ key: 'error_code', title: 'š“ Error' }
]
}
},
{
type: 'slack',
config: {
webhookUrl: 'webhook-url'
// Uses global specific configuration
}
}
],
specific: [ // Global configuration
{ key: 'user', title: 'š¤ User' },
{ key: 'method', title: 'šØ Method' },
{ key: 'status', title: 'š Status' }
],
strictMode: true
})
```
## API Reference
### Multi-Channel Alerts
#### `new MultiChannelAlert(config)`
Create a multi-channel alert instance.
**Config Parameters:**
- `channels` (Array, required): Array of channel configurations
- `service` (string, optional): Default service name ('hotel', 'flight', 'tour', 'transfer')
- `environment` (string, optional): Default environment ('DEV', 'STAGING', 'PRODUCTION')
- `failSilently` (boolean, optional): Don't throw errors if some channels fail (default: true)
**Channel Configuration:**
```javascript
{
type: 'telegram', // Channel type (required)
config: { // Channel-specific config (required)
// Channel-specific parameters
botToken: 'token',
chatId: 'chat-id',
service: 'hotel', // Inherits from main config if not provided
environment: 'PROD' // Inherits from main config if not provided
}
}
```
#### Logger Methods
```javascript
// Error alert (red/urgent styling)
await multiAlert.error(data)
// Info alert (blue/informational styling)
await multiAlert.info(data)
// Warning alert (yellow/caution styling)
await multiAlert.warn(data)
// Success alert (green/positive styling)
await multiAlert.success(data)
```
#### Management Methods
```javascript
// Get channel information
multiAlert.getChannels() // Array of channel info
multiAlert.getChannelCount() // Number of channels
multiAlert.hasChannel('telegram') // Check if type exists
// Dynamic management
multiAlert.addChannel(config) // Add new channel
multiAlert.removeChannel('slack') // Remove by type
```
#### Response Format
```javascript
{
success: true, // True if at least 1 channel succeeded
results: [ // Array of individual results
{
type: 'telegram',
success: true,
result: { /* telegram response */ }
},
{
type: 'slack',
success: false,
error: 'Webhook URL invalid'
}
],
errors: [ // Array of failed channels
{
type: 'slack',
success: false,
error: 'Webhook URL invalid'
}
],
summary: { // Summary statistics
total: 2,
successful: 1,
failed: 1
}
}
```
### Individual Alert Classes
#### `new TelegramAlert(config)`
**Required Config:**
- `botToken` (string): Telegram bot token from @BotFather
- `chatId` (string): Chat/channel ID where messages will be sent
**Optional Config:**
- `service` (string): Service name for routing ('hotel', 'flight', etc.)
- `environment` (string): Environment context ('DEV', 'STAGING', 'PRODUCTION')
- `messageThreadIds` (Object): Thread routing configuration
- `beauty` (boolean): Enable rich formatting (default: true)
- `timeout` (number): Request timeout in ms (default: 10000)
#### `new SlackAlert(config)`
**Required Config:**
- `webhookUrl` (string): Slack webhook URL from app configuration
**Optional Config:**
- `channel` (string): Override channel from webhook
- `service` (string): Service name for routing
- `environment` (string): Environment context
- `channels` (Object): Channel routing configuration
- `beauty` (boolean): Enable rich formatting (default: true)
#### `new MattermostAlert(config)`
**Required Config:**
- `url` (string): Mattermost server URL
- `token` (string): Personal access token
- `chatId` (string): Channel ID or name
**Optional Config:**
- `service` (string): Service name for routing
- `environment` (string): Environment context
#### `new EmailAlert(config)`
**Required Config:**
- `host` (string): SMTP server host
- `port` (number): SMTP server port
- `auth.user` (string): SMTP username
- `auth.pass` (string): SMTP password/app password
- `from` (string): From email address
- `to` (string|Array): Recipient email address(es)
**Optional Config:**
- `secure` (boolean): Use SSL/TLS (default: false for port 587)
- `service` (string): Service name for subject line
- `environment` (string): Environment context
### Third-Party Logging
#### `saveProviderLog(options)`
Save HTTP request/response logs to organized files.
**Parameters:**
- `action` (string): Action name (e.g., 'search', 'book')
- `req` (Object|string): Request data (JSON object or XML string)
- `res` (Object|string): Response data (JSON object or XML string)
- `code` (string): Provider/supplier code (e.g., 'EXPEDIA')
- `name` (string): Operation name (e.g., 'search-hotels')
#### `saveProviderCurl(options)`
Save curl command to file for debugging.
**Parameters:**
- `name` (string): Operation name
- `request` (Object): Request object with method, url, headers, body
- `code` (string): Provider/supplier code
- `action` (string, optional): Action name
### TelegramClient Class
#### `new TelegramClient(config)`
Create a Telegram client instance.
**Config Parameters:**
- `botToken` (string, required): Telegram bot token
- `chatId` (string, required): Chat ID to send messages to
- `product` (string, optional): Default product type ('hotel', 'flight', 'tour', 'transfer')
- `environment` (string, optional): Default environment ('DEV', 'STAGING', 'PROD')
- `messageThreadIds` (Object, optional): Thread routing configuration
- `disableNotification` (boolean, optional): Disable notifications (default: false)
- `timeout` (number, optional): Request timeout in ms (default: 5000)
#### `telegram.sendMessage(options)`
Send message using error-handler.js style options parameter.
**Options Parameters:**
- `error_message` (string, requir