@panguard-ai/panguard-chat 0.1.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.
- package/dist/agent/chat-agent.d.ts +69 -0
- package/dist/agent/chat-agent.d.ts.map +1 -0
- package/dist/agent/chat-agent.js +477 -0
- package/dist/agent/chat-agent.js.map +1 -0
- package/dist/agent/formatter.d.ts +59 -0
- package/dist/agent/formatter.d.ts.map +1 -0
- package/dist/agent/formatter.js +399 -0
- package/dist/agent/formatter.js.map +1 -0
- package/dist/agent/index.d.ts +10 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +10 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/prompts.d.ts +27 -0
- package/dist/agent/prompts.d.ts.map +1 -0
- package/dist/agent/prompts.js +164 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/channels/email.d.ts +47 -0
- package/dist/channels/email.d.ts.map +1 -0
- package/dist/channels/email.js +250 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/index.d.ts +11 -0
- package/dist/channels/index.d.ts.map +1 -0
- package/dist/channels/index.js +11 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/line.d.ts.map +1 -0
- package/dist/channels/slack.d.ts +67 -0
- package/dist/channels/slack.d.ts.map +1 -0
- package/dist/channels/slack.js +269 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/telegram.d.ts +69 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +244 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/webhook.d.ts +70 -0
- package/dist/channels/webhook.d.ts.map +1 -0
- package/dist/channels/webhook.js +224 -0
- package/dist/channels/webhook.js.map +1 -0
- package/dist/cli/index.d.ts +22 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +337 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/onboarding/index.d.ts +9 -0
- package/dist/onboarding/index.d.ts.map +1 -0
- package/dist/onboarding/index.js +8 -0
- package/dist/onboarding/index.js.map +1 -0
- package/dist/onboarding/setup-flow.d.ts +65 -0
- package/dist/onboarding/setup-flow.d.ts.map +1 -0
- package/dist/onboarding/setup-flow.js +362 -0
- package/dist/onboarding/setup-flow.js.map +1 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/webhook-server.d.ts +46 -0
- package/dist/server/webhook-server.d.ts.map +1 -0
- package/dist/server/webhook-server.js +150 -0
- package/dist/server/webhook-server.js.map +1 -0
- package/dist/skills/index.d.ts +47 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +313 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/templates/alert-templates.d.ts +40 -0
- package/dist/templates/alert-templates.d.ts.map +1 -0
- package/dist/templates/alert-templates.js +159 -0
- package/dist/templates/alert-templates.js.map +1 -0
- package/dist/templates/index.d.ts +9 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +8 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types.d.ts +246 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack Channel
|
|
3
|
+
* Slack 管道
|
|
4
|
+
*
|
|
5
|
+
* Bidirectional Slack messaging channel using Slack Web API.
|
|
6
|
+
* Supports interactive messages with buttons and blocks.
|
|
7
|
+
* 使用 Slack Web API 的雙向通訊管道。
|
|
8
|
+
* 支援帶按鈕和 blocks 的互動訊息。
|
|
9
|
+
*
|
|
10
|
+
* Recommended for Pro/Business plans.
|
|
11
|
+
* 推薦 Pro/Business 方案使用。
|
|
12
|
+
*
|
|
13
|
+
* @module @panguard-ai/panguard-chat/channels/slack
|
|
14
|
+
*/
|
|
15
|
+
import { createLogger } from '@panguard-ai/core';
|
|
16
|
+
import { formatAlert } from '../agent/formatter.js';
|
|
17
|
+
const logger = createLogger('panguard-chat:channel:slack');
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Slack Message Helpers
|
|
20
|
+
// Slack 訊息輔助函式
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
/** Map severity to Slack attachment color */
|
|
23
|
+
function severityToColor(severity) {
|
|
24
|
+
switch (severity) {
|
|
25
|
+
case 'critical':
|
|
26
|
+
return '#FF0000';
|
|
27
|
+
case 'warning':
|
|
28
|
+
return '#FFA500';
|
|
29
|
+
case 'info':
|
|
30
|
+
default:
|
|
31
|
+
return '#2196F3';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Build Slack Block Kit blocks from a formatted message */
|
|
35
|
+
function buildBlocks(message) {
|
|
36
|
+
const blocks = [
|
|
37
|
+
{
|
|
38
|
+
type: 'section',
|
|
39
|
+
text: {
|
|
40
|
+
type: 'mrkdwn',
|
|
41
|
+
text: message.text,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
if (message.quickReplies && message.quickReplies.length > 0) {
|
|
46
|
+
blocks.push({
|
|
47
|
+
type: 'actions',
|
|
48
|
+
elements: message.quickReplies.map((qr) => ({
|
|
49
|
+
type: 'button',
|
|
50
|
+
text: {
|
|
51
|
+
type: 'plain_text',
|
|
52
|
+
text: qr.label,
|
|
53
|
+
},
|
|
54
|
+
action_id: qr.action,
|
|
55
|
+
value: qr.action,
|
|
56
|
+
})),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return blocks;
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Slack Channel Implementation
|
|
63
|
+
// Slack 管道實作
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
/**
|
|
66
|
+
* Slack bidirectional messaging channel
|
|
67
|
+
* Slack 雙向通訊管道
|
|
68
|
+
*/
|
|
69
|
+
export class SlackChannel {
|
|
70
|
+
channelType = 'slack';
|
|
71
|
+
config;
|
|
72
|
+
replyHandler = null;
|
|
73
|
+
constructor(config) {
|
|
74
|
+
this.config = config;
|
|
75
|
+
logger.info('Slack channel initialized / Slack 管道已初始化');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Send a formatted message via Slack Web API
|
|
79
|
+
* 透過 Slack Web API 發送格式化訊息
|
|
80
|
+
*/
|
|
81
|
+
async sendMessage(userId, message) {
|
|
82
|
+
try {
|
|
83
|
+
const channel = userId || this.config.defaultChannel;
|
|
84
|
+
const blocks = buildBlocks(message);
|
|
85
|
+
const payload = JSON.stringify({
|
|
86
|
+
channel,
|
|
87
|
+
text: message.text.slice(0, 150), // fallback text
|
|
88
|
+
blocks,
|
|
89
|
+
attachments: [
|
|
90
|
+
{
|
|
91
|
+
color: severityToColor(message.severity),
|
|
92
|
+
fallback: message.text.slice(0, 150),
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
const response = await this.httpPost('https://slack.com/api/chat.postMessage', payload);
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
throw new Error(response.error ?? 'Slack API error');
|
|
99
|
+
}
|
|
100
|
+
logger.info(`Slack message sent to ${channel} / Slack 訊息已發送`);
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
channel: 'slack',
|
|
104
|
+
messageId: response.ts,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
109
|
+
logger.error(`Slack send failed: ${msg} / Slack 發送失敗: ${msg}`);
|
|
110
|
+
return { success: false, channel: 'slack', error: msg };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Send a threat alert via Slack
|
|
115
|
+
* 透過 Slack 發送威脅告警
|
|
116
|
+
*/
|
|
117
|
+
async sendAlert(userId, alert) {
|
|
118
|
+
const message = formatAlert(alert, 'developer', 'zh-TW');
|
|
119
|
+
return this.sendMessage(userId, message);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Send a file via Slack
|
|
123
|
+
* 透過 Slack 發送檔案
|
|
124
|
+
*/
|
|
125
|
+
async sendFile(userId, file, filename) {
|
|
126
|
+
try {
|
|
127
|
+
const channel = userId || this.config.defaultChannel;
|
|
128
|
+
// Use Slack files.upload API (v2)
|
|
129
|
+
const boundary = `----FormBoundary${Date.now()}`;
|
|
130
|
+
const parts = [];
|
|
131
|
+
// channels field
|
|
132
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="channels"\r\n\r\n${channel}\r\n`));
|
|
133
|
+
// filename field
|
|
134
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="filename"\r\n\r\n${filename}\r\n`));
|
|
135
|
+
// file field
|
|
136
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${filename}"\r\nContent-Type: application/octet-stream\r\n\r\n`));
|
|
137
|
+
parts.push(file);
|
|
138
|
+
parts.push(Buffer.from(`\r\n--${boundary}--\r\n`));
|
|
139
|
+
const body = Buffer.concat(parts);
|
|
140
|
+
await this.httpPostRaw('https://slack.com/api/files.upload', body, `multipart/form-data; boundary=${boundary}`);
|
|
141
|
+
logger.info(`Slack file sent: ${filename} / Slack 檔案已發送: ${filename}`);
|
|
142
|
+
return { success: true, channel: 'slack' };
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
146
|
+
logger.error(`Slack file send failed: ${msg} / 檔案發送失敗: ${msg}`);
|
|
147
|
+
return { success: false, channel: 'slack', error: msg };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Register a reply handler
|
|
152
|
+
* 註冊回覆處理器
|
|
153
|
+
*/
|
|
154
|
+
onReply(handler) {
|
|
155
|
+
this.replyHandler = handler;
|
|
156
|
+
logger.info('Slack reply handler registered / Slack 回覆處理器已註冊');
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Process a Slack event (message or interaction)
|
|
160
|
+
* 處理 Slack 事件(訊息或互動)
|
|
161
|
+
*/
|
|
162
|
+
async processEvent(event) {
|
|
163
|
+
if (!this.replyHandler) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
// Handle text messages
|
|
167
|
+
if (event.type === 'message' && event.text && event.user) {
|
|
168
|
+
return this.replyHandler(event.channel ?? event.user, event.text);
|
|
169
|
+
}
|
|
170
|
+
// Handle interactive button presses
|
|
171
|
+
if (event.type === 'block_actions' && event.actions && event.actions.length > 0) {
|
|
172
|
+
const action = event.actions[0];
|
|
173
|
+
return this.replyHandler(event.channel ?? event.user ?? '', action.value);
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Verify Slack request signature
|
|
179
|
+
* 驗證 Slack 請求簽名
|
|
180
|
+
*/
|
|
181
|
+
async verifySignature(signature, timestamp, body) {
|
|
182
|
+
try {
|
|
183
|
+
const crypto = await import('node:crypto');
|
|
184
|
+
const sigBasestring = `v0:${timestamp}:${body}`;
|
|
185
|
+
const mySignature = `v0=${crypto
|
|
186
|
+
.createHmac('sha256', this.config.signingSecret)
|
|
187
|
+
.update(sigBasestring)
|
|
188
|
+
.digest('hex')}`;
|
|
189
|
+
return mySignature === signature;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// -------------------------------------------------------------------------
|
|
196
|
+
// HTTP Helpers
|
|
197
|
+
// -------------------------------------------------------------------------
|
|
198
|
+
async httpPost(url, body) {
|
|
199
|
+
const { hostname, pathname } = new URL(url);
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
import('node:https')
|
|
202
|
+
.then(({ request }) => {
|
|
203
|
+
const req = request({
|
|
204
|
+
hostname,
|
|
205
|
+
path: pathname,
|
|
206
|
+
method: 'POST',
|
|
207
|
+
headers: {
|
|
208
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
209
|
+
Authorization: `Bearer ${this.config.botToken}`,
|
|
210
|
+
'Content-Length': Buffer.byteLength(body),
|
|
211
|
+
},
|
|
212
|
+
}, (res) => {
|
|
213
|
+
let data = '';
|
|
214
|
+
res.on('data', (chunk) => {
|
|
215
|
+
data += chunk.toString();
|
|
216
|
+
});
|
|
217
|
+
res.on('end', () => {
|
|
218
|
+
try {
|
|
219
|
+
resolve(JSON.parse(data));
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
reject(new Error(`Invalid Slack response: ${data}`));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
req.on('error', reject);
|
|
227
|
+
req.write(body);
|
|
228
|
+
req.end();
|
|
229
|
+
})
|
|
230
|
+
.catch(reject);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async httpPostRaw(url, body, contentType) {
|
|
234
|
+
const { hostname, pathname } = new URL(url);
|
|
235
|
+
return new Promise((resolve, reject) => {
|
|
236
|
+
import('node:https')
|
|
237
|
+
.then(({ request }) => {
|
|
238
|
+
const req = request({
|
|
239
|
+
hostname,
|
|
240
|
+
path: pathname,
|
|
241
|
+
method: 'POST',
|
|
242
|
+
headers: {
|
|
243
|
+
'Content-Type': contentType,
|
|
244
|
+
Authorization: `Bearer ${this.config.botToken}`,
|
|
245
|
+
'Content-Length': body.length,
|
|
246
|
+
},
|
|
247
|
+
}, (res) => {
|
|
248
|
+
let data = '';
|
|
249
|
+
res.on('data', (chunk) => {
|
|
250
|
+
data += chunk.toString();
|
|
251
|
+
});
|
|
252
|
+
res.on('end', () => {
|
|
253
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
254
|
+
resolve();
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
reject(new Error(`Slack API error ${res.statusCode}: ${data}`));
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
req.on('error', reject);
|
|
262
|
+
req.write(body);
|
|
263
|
+
req.end();
|
|
264
|
+
})
|
|
265
|
+
.catch(reject);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=slack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/channels/slack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Bot Channel
|
|
3
|
+
* Telegram Bot 管道
|
|
4
|
+
*
|
|
5
|
+
* Bidirectional Telegram messaging channel.
|
|
6
|
+
* Uses Telegram Bot API for interactive conversations.
|
|
7
|
+
* 使用 Telegram Bot API 的雙向通訊管道。
|
|
8
|
+
*
|
|
9
|
+
* Note: For Business/compliance plans, Telegram is not recommended
|
|
10
|
+
* because Telegram defaults to unencrypted chat and holds keys server-side,
|
|
11
|
+
* which may not comply with ISO 27001 / local regulations.
|
|
12
|
+
* 注意:企業/合規方案不建議使用 Telegram,因為 Telegram
|
|
13
|
+
* 預設不加密、伺服器端持有金鑰,可能不符合 ISO 27001 / 資通安全管理法。
|
|
14
|
+
*
|
|
15
|
+
* @module @panguard-ai/panguard-chat/channels/telegram
|
|
16
|
+
*/
|
|
17
|
+
import type { MessagingChannel, ChannelResult, FormattedMessage, ThreatAlert, ReplyHandler, TelegramChannelConfig } from '../types.js';
|
|
18
|
+
/**
|
|
19
|
+
* Telegram Bot bidirectional messaging channel
|
|
20
|
+
* Telegram Bot 雙向通訊管道
|
|
21
|
+
*/
|
|
22
|
+
export declare class TelegramChannel implements MessagingChannel {
|
|
23
|
+
readonly channelType: "telegram";
|
|
24
|
+
private readonly config;
|
|
25
|
+
private replyHandler;
|
|
26
|
+
constructor(config: TelegramChannelConfig);
|
|
27
|
+
/**
|
|
28
|
+
* Send a formatted message via Telegram Bot API
|
|
29
|
+
* 透過 Telegram Bot API 發送格式化訊息
|
|
30
|
+
*/
|
|
31
|
+
sendMessage(userId: string, message: FormattedMessage): Promise<ChannelResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Send a threat alert via Telegram
|
|
34
|
+
* 透過 Telegram 發送威脅告警
|
|
35
|
+
*/
|
|
36
|
+
sendAlert(userId: string, alert: ThreatAlert): Promise<ChannelResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Send a file via Telegram
|
|
39
|
+
* 透過 Telegram 發送檔案
|
|
40
|
+
*/
|
|
41
|
+
sendFile(userId: string, file: Buffer, filename: string): Promise<ChannelResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Register a reply handler
|
|
44
|
+
* 註冊回覆處理器
|
|
45
|
+
*/
|
|
46
|
+
onReply(handler: ReplyHandler): void;
|
|
47
|
+
/**
|
|
48
|
+
* Process an incoming Telegram update
|
|
49
|
+
* 處理收到的 Telegram 更新
|
|
50
|
+
*/
|
|
51
|
+
processUpdate(update: {
|
|
52
|
+
message?: {
|
|
53
|
+
chat: {
|
|
54
|
+
id: number;
|
|
55
|
+
};
|
|
56
|
+
text?: string;
|
|
57
|
+
};
|
|
58
|
+
callback_query?: {
|
|
59
|
+
from: {
|
|
60
|
+
id: number;
|
|
61
|
+
};
|
|
62
|
+
data: string;
|
|
63
|
+
id: string;
|
|
64
|
+
};
|
|
65
|
+
}): Promise<string | null>;
|
|
66
|
+
private httpPost;
|
|
67
|
+
private httpPostRaw;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=telegram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../src/channels/telegram.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,qBAAqB,EAEtB,MAAM,aAAa,CAAC;AA2CrB;;;GAGG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,QAAQ,CAAC,WAAW,EAAG,UAAU,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,YAAY,CAA6B;gBAErC,MAAM,EAAE,qBAAqB;IAKzC;;;OAGG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;IAiCpF;;;OAGG;IACG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC;IAK3E;;;OAGG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAqCtF;;;OAGG;IACH,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAKpC;;;OAGG;IACG,aAAa,CAAC,MAAM,EAAE;QAC1B,OAAO,CAAC,EAAE;YAAE,IAAI,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAClD,cAAc,CAAC,EAAE;YAAE,IAAI,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAkCZ,QAAQ;YA8CR,WAAW;CAsC1B"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Bot Channel
|
|
3
|
+
* Telegram Bot 管道
|
|
4
|
+
*
|
|
5
|
+
* Bidirectional Telegram messaging channel.
|
|
6
|
+
* Uses Telegram Bot API for interactive conversations.
|
|
7
|
+
* 使用 Telegram Bot API 的雙向通訊管道。
|
|
8
|
+
*
|
|
9
|
+
* Note: For Business/compliance plans, Telegram is not recommended
|
|
10
|
+
* because Telegram defaults to unencrypted chat and holds keys server-side,
|
|
11
|
+
* which may not comply with ISO 27001 / local regulations.
|
|
12
|
+
* 注意:企業/合規方案不建議使用 Telegram,因為 Telegram
|
|
13
|
+
* 預設不加密、伺服器端持有金鑰,可能不符合 ISO 27001 / 資通安全管理法。
|
|
14
|
+
*
|
|
15
|
+
* @module @panguard-ai/panguard-chat/channels/telegram
|
|
16
|
+
*/
|
|
17
|
+
import { createLogger } from '@panguard-ai/core';
|
|
18
|
+
import { formatAlert } from '../agent/formatter.js';
|
|
19
|
+
const logger = createLogger('panguard-chat:channel:telegram');
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Telegram Message Helpers
|
|
22
|
+
// Telegram 訊息輔助函式
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/** Map severity to status icon (text-based, no emoji per project rules) */
|
|
25
|
+
function severityPrefix(severity) {
|
|
26
|
+
switch (severity) {
|
|
27
|
+
case 'critical':
|
|
28
|
+
return '[!!]';
|
|
29
|
+
case 'warning':
|
|
30
|
+
return '[!]';
|
|
31
|
+
case 'info':
|
|
32
|
+
default:
|
|
33
|
+
return '[i]';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Build Telegram inline keyboard from quick replies */
|
|
37
|
+
function buildInlineKeyboard(quickReplies) {
|
|
38
|
+
if (!quickReplies || quickReplies.length === 0)
|
|
39
|
+
return undefined;
|
|
40
|
+
return {
|
|
41
|
+
inline_keyboard: [
|
|
42
|
+
quickReplies.map((qr) => ({
|
|
43
|
+
text: qr.label,
|
|
44
|
+
callback_data: qr.action,
|
|
45
|
+
})),
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Telegram Channel Implementation
|
|
51
|
+
// Telegram 管道實作
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/**
|
|
54
|
+
* Telegram Bot bidirectional messaging channel
|
|
55
|
+
* Telegram Bot 雙向通訊管道
|
|
56
|
+
*/
|
|
57
|
+
export class TelegramChannel {
|
|
58
|
+
channelType = 'telegram';
|
|
59
|
+
config;
|
|
60
|
+
replyHandler = null;
|
|
61
|
+
constructor(config) {
|
|
62
|
+
this.config = config;
|
|
63
|
+
logger.info('Telegram channel initialized / Telegram 管道已初始化');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Send a formatted message via Telegram Bot API
|
|
67
|
+
* 透過 Telegram Bot API 發送格式化訊息
|
|
68
|
+
*/
|
|
69
|
+
async sendMessage(userId, message) {
|
|
70
|
+
try {
|
|
71
|
+
const chatId = userId || this.config.chatId;
|
|
72
|
+
const prefix = severityPrefix(message.severity);
|
|
73
|
+
const fullText = `${prefix} ${message.text}`;
|
|
74
|
+
const payload = {
|
|
75
|
+
chat_id: chatId,
|
|
76
|
+
text: fullText,
|
|
77
|
+
parse_mode: 'HTML',
|
|
78
|
+
};
|
|
79
|
+
const keyboard = buildInlineKeyboard(message.quickReplies);
|
|
80
|
+
if (keyboard) {
|
|
81
|
+
payload['reply_markup'] = keyboard;
|
|
82
|
+
}
|
|
83
|
+
const response = await this.httpPost('sendMessage', JSON.stringify(payload));
|
|
84
|
+
logger.info(`Telegram message sent to ${chatId} / Telegram 訊息已發送`);
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
channel: 'telegram',
|
|
88
|
+
messageId: String(response.result?.message_id ?? ''),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
93
|
+
logger.error(`Telegram send failed: ${msg} / Telegram 發送失敗: ${msg}`);
|
|
94
|
+
return { success: false, channel: 'telegram', error: msg };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Send a threat alert via Telegram
|
|
99
|
+
* 透過 Telegram 發送威脅告警
|
|
100
|
+
*/
|
|
101
|
+
async sendAlert(userId, alert) {
|
|
102
|
+
const message = formatAlert(alert, 'developer', 'zh-TW');
|
|
103
|
+
return this.sendMessage(userId, message);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Send a file via Telegram
|
|
107
|
+
* 透過 Telegram 發送檔案
|
|
108
|
+
*/
|
|
109
|
+
async sendFile(userId, file, filename) {
|
|
110
|
+
try {
|
|
111
|
+
const chatId = userId || this.config.chatId;
|
|
112
|
+
// Use multipart/form-data for file upload
|
|
113
|
+
const boundary = `----FormBoundary${Date.now()}`;
|
|
114
|
+
const parts = [];
|
|
115
|
+
// chat_id field
|
|
116
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="chat_id"\r\n\r\n${chatId}\r\n`));
|
|
117
|
+
// document field
|
|
118
|
+
parts.push(Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="document"; filename="${filename}"\r\nContent-Type: application/octet-stream\r\n\r\n`));
|
|
119
|
+
parts.push(file);
|
|
120
|
+
parts.push(Buffer.from(`\r\n--${boundary}--\r\n`));
|
|
121
|
+
const body = Buffer.concat(parts);
|
|
122
|
+
await this.httpPostRaw('sendDocument', body, `multipart/form-data; boundary=${boundary}`);
|
|
123
|
+
logger.info(`Telegram file sent: ${filename} / Telegram 檔案已發送: ${filename}`);
|
|
124
|
+
return { success: true, channel: 'telegram' };
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
128
|
+
logger.error(`Telegram file send failed: ${msg} / 檔案發送失敗: ${msg}`);
|
|
129
|
+
return { success: false, channel: 'telegram', error: msg };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Register a reply handler
|
|
134
|
+
* 註冊回覆處理器
|
|
135
|
+
*/
|
|
136
|
+
onReply(handler) {
|
|
137
|
+
this.replyHandler = handler;
|
|
138
|
+
logger.info('Telegram reply handler registered / Telegram 回覆處理器已註冊');
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Process an incoming Telegram update
|
|
142
|
+
* 處理收到的 Telegram 更新
|
|
143
|
+
*/
|
|
144
|
+
async processUpdate(update) {
|
|
145
|
+
if (!this.replyHandler) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
// Handle text messages
|
|
149
|
+
if (update.message?.text) {
|
|
150
|
+
const chatId = String(update.message.chat.id);
|
|
151
|
+
return this.replyHandler(chatId, update.message.text);
|
|
152
|
+
}
|
|
153
|
+
// Handle callback queries (inline keyboard button presses)
|
|
154
|
+
if (update.callback_query) {
|
|
155
|
+
const userId = String(update.callback_query.from.id);
|
|
156
|
+
const data = update.callback_query.data;
|
|
157
|
+
// Answer the callback query
|
|
158
|
+
await this.httpPost('answerCallbackQuery', JSON.stringify({
|
|
159
|
+
callback_query_id: update.callback_query.id,
|
|
160
|
+
}));
|
|
161
|
+
return this.replyHandler(userId, data);
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
// -------------------------------------------------------------------------
|
|
166
|
+
// HTTP Helpers
|
|
167
|
+
// -------------------------------------------------------------------------
|
|
168
|
+
async httpPost(method, body) {
|
|
169
|
+
const url = `https://api.telegram.org/bot${this.config.botToken}/${method}`;
|
|
170
|
+
const { hostname, pathname } = new URL(url);
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
import('node:https')
|
|
173
|
+
.then(({ request }) => {
|
|
174
|
+
const req = request({
|
|
175
|
+
hostname,
|
|
176
|
+
path: pathname,
|
|
177
|
+
method: 'POST',
|
|
178
|
+
headers: {
|
|
179
|
+
'Content-Type': 'application/json',
|
|
180
|
+
'Content-Length': Buffer.byteLength(body),
|
|
181
|
+
},
|
|
182
|
+
}, (res) => {
|
|
183
|
+
let data = '';
|
|
184
|
+
res.on('data', (chunk) => {
|
|
185
|
+
data += chunk.toString();
|
|
186
|
+
});
|
|
187
|
+
res.on('end', () => {
|
|
188
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
189
|
+
try {
|
|
190
|
+
resolve(JSON.parse(data));
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
resolve({});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
reject(new Error(`Telegram API error ${res.statusCode}: ${data}`));
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
req.on('error', reject);
|
|
202
|
+
req.write(body);
|
|
203
|
+
req.end();
|
|
204
|
+
})
|
|
205
|
+
.catch(reject);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async httpPostRaw(method, body, contentType) {
|
|
209
|
+
const url = `https://api.telegram.org/bot${this.config.botToken}/${method}`;
|
|
210
|
+
const { hostname, pathname } = new URL(url);
|
|
211
|
+
return new Promise((resolve, reject) => {
|
|
212
|
+
import('node:https')
|
|
213
|
+
.then(({ request }) => {
|
|
214
|
+
const req = request({
|
|
215
|
+
hostname,
|
|
216
|
+
path: pathname,
|
|
217
|
+
method: 'POST',
|
|
218
|
+
headers: {
|
|
219
|
+
'Content-Type': contentType,
|
|
220
|
+
'Content-Length': body.length,
|
|
221
|
+
},
|
|
222
|
+
}, (res) => {
|
|
223
|
+
let data = '';
|
|
224
|
+
res.on('data', (chunk) => {
|
|
225
|
+
data += chunk.toString();
|
|
226
|
+
});
|
|
227
|
+
res.on('end', () => {
|
|
228
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
229
|
+
resolve();
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
reject(new Error(`Telegram API error ${res.statusCode}: ${data}`));
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
req.on('error', reject);
|
|
237
|
+
req.write(body);
|
|
238
|
+
req.end();
|
|
239
|
+
})
|
|
240
|
+
.catch(reject);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=telegram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/channels/telegram.ts"],"names":[],"mappings":"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"}
|