@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.
Files changed (77) hide show
  1. package/dist/agent/chat-agent.d.ts +69 -0
  2. package/dist/agent/chat-agent.d.ts.map +1 -0
  3. package/dist/agent/chat-agent.js +477 -0
  4. package/dist/agent/chat-agent.js.map +1 -0
  5. package/dist/agent/formatter.d.ts +59 -0
  6. package/dist/agent/formatter.d.ts.map +1 -0
  7. package/dist/agent/formatter.js +399 -0
  8. package/dist/agent/formatter.js.map +1 -0
  9. package/dist/agent/index.d.ts +10 -0
  10. package/dist/agent/index.d.ts.map +1 -0
  11. package/dist/agent/index.js +10 -0
  12. package/dist/agent/index.js.map +1 -0
  13. package/dist/agent/prompts.d.ts +27 -0
  14. package/dist/agent/prompts.d.ts.map +1 -0
  15. package/dist/agent/prompts.js +164 -0
  16. package/dist/agent/prompts.js.map +1 -0
  17. package/dist/channels/email.d.ts +47 -0
  18. package/dist/channels/email.d.ts.map +1 -0
  19. package/dist/channels/email.js +250 -0
  20. package/dist/channels/email.js.map +1 -0
  21. package/dist/channels/index.d.ts +11 -0
  22. package/dist/channels/index.d.ts.map +1 -0
  23. package/dist/channels/index.js +11 -0
  24. package/dist/channels/index.js.map +1 -0
  25. package/dist/channels/line.d.ts.map +1 -0
  26. package/dist/channels/slack.d.ts +67 -0
  27. package/dist/channels/slack.d.ts.map +1 -0
  28. package/dist/channels/slack.js +269 -0
  29. package/dist/channels/slack.js.map +1 -0
  30. package/dist/channels/telegram.d.ts +69 -0
  31. package/dist/channels/telegram.d.ts.map +1 -0
  32. package/dist/channels/telegram.js +244 -0
  33. package/dist/channels/telegram.js.map +1 -0
  34. package/dist/channels/webhook.d.ts +70 -0
  35. package/dist/channels/webhook.d.ts.map +1 -0
  36. package/dist/channels/webhook.js +224 -0
  37. package/dist/channels/webhook.js.map +1 -0
  38. package/dist/cli/index.d.ts +22 -0
  39. package/dist/cli/index.d.ts.map +1 -0
  40. package/dist/cli/index.js +337 -0
  41. package/dist/cli/index.js.map +1 -0
  42. package/dist/index.d.ts +29 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +41 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/onboarding/index.d.ts +9 -0
  47. package/dist/onboarding/index.d.ts.map +1 -0
  48. package/dist/onboarding/index.js +8 -0
  49. package/dist/onboarding/index.js.map +1 -0
  50. package/dist/onboarding/setup-flow.d.ts +65 -0
  51. package/dist/onboarding/setup-flow.d.ts.map +1 -0
  52. package/dist/onboarding/setup-flow.js +362 -0
  53. package/dist/onboarding/setup-flow.js.map +1 -0
  54. package/dist/server/index.d.ts.map +1 -0
  55. package/dist/server/index.js +2 -0
  56. package/dist/server/index.js.map +1 -0
  57. package/dist/server/webhook-server.d.ts +46 -0
  58. package/dist/server/webhook-server.d.ts.map +1 -0
  59. package/dist/server/webhook-server.js +150 -0
  60. package/dist/server/webhook-server.js.map +1 -0
  61. package/dist/skills/index.d.ts +47 -0
  62. package/dist/skills/index.d.ts.map +1 -0
  63. package/dist/skills/index.js +313 -0
  64. package/dist/skills/index.js.map +1 -0
  65. package/dist/templates/alert-templates.d.ts +40 -0
  66. package/dist/templates/alert-templates.d.ts.map +1 -0
  67. package/dist/templates/alert-templates.js +159 -0
  68. package/dist/templates/alert-templates.js.map +1 -0
  69. package/dist/templates/index.d.ts +9 -0
  70. package/dist/templates/index.d.ts.map +1 -0
  71. package/dist/templates/index.js +8 -0
  72. package/dist/templates/index.js.map +1 -0
  73. package/dist/types.d.ts +246 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/types.js +8 -0
  76. package/dist/types.js.map +1 -0
  77. 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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"}