@astro-minimax/notify 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/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # @astro-minimax/notify
2
+
3
+ 多渠道通知包,为 astro-minimax 博客系统提供灵活的通知功能。
4
+
5
+ ## 特性
6
+
7
+ - **多渠道支持**:Telegram、Email (Resend)、Webhook
8
+ - **多事件类型**:评论通知、AI 对话监控
9
+ - **丰富信息展示**:Token 用量、阶段耗时、引用文章等
10
+ - **隐私保护**:Session ID 自动匿名化
11
+ - **代理支持**:支持 HTTP/HTTPS 代理
12
+ - **容错设计**:失败不影响主业务流程
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ pnpm add @astro-minimax/notify
18
+ ```
19
+
20
+ ## 环境变量配置
21
+
22
+ ### Telegram Provider
23
+
24
+ ```bash
25
+ NOTIFY_TELEGRAM_BOT_TOKEN=your-bot-token
26
+ NOTIFY_TELEGRAM_CHAT_ID=your-chat-id
27
+ ```
28
+
29
+ 获取方式:
30
+
31
+ 1. 在 Telegram 搜索 `@BotFather`,发送 `/newbot` 创建 Bot,获取 Token
32
+ 2. 在 Telegram 搜索 `@userinfobot`,发送 `/start` 获取你的 Chat ID
33
+
34
+ ### Email Provider (Resend)
35
+
36
+ ```bash
37
+ NOTIFY_RESEND_API_KEY=re_xxx
38
+ NOTIFY_RESEND_FROM=noreply@yourdomain.com
39
+ NOTIFY_RESEND_TO=you@example.com
40
+ ```
41
+
42
+ 获取方式:
43
+
44
+ 1. 注册 [Resend](https://resend.com) 账号
45
+ 2. 在 Dashboard 创建 API Key
46
+ 3. 验证发件域名
47
+
48
+ ### Webhook Provider
49
+
50
+ ```bash
51
+ NOTIFY_WEBHOOK_URL=https://your-webhook.com/notify
52
+ ```
53
+
54
+ ## 使用方式
55
+
56
+ ### 基础用法
57
+
58
+ ```typescript
59
+ import { createNotifier } from '@astro-minimax/notify';
60
+
61
+ const notifier = createNotifier({
62
+ telegram: {
63
+ botToken: process.env.NOTIFY_TELEGRAM_BOT_TOKEN,
64
+ chatId: process.env.NOTIFY_TELEGRAM_CHAT_ID,
65
+ },
66
+ email: {
67
+ provider: 'resend',
68
+ apiKey: process.env.NOTIFY_RESEND_API_KEY,
69
+ from: process.env.NOTIFY_RESEND_FROM,
70
+ to: process.env.NOTIFY_RESEND_TO,
71
+ },
72
+ webhook: {
73
+ url: process.env.NOTIFY_WEBHOOK_URL,
74
+ },
75
+ });
76
+
77
+ // 发送评论通知
78
+ await notifier.comment({
79
+ author: '张三',
80
+ content: '文章写得真好!',
81
+ postTitle: '如何使用 Astro',
82
+ postUrl: 'https://example.com/posts/how-to-use-astro',
83
+ });
84
+
85
+ // 发送 AI 对话通知
86
+ await notifier.aiChat({
87
+ sessionId: 'abc123',
88
+ roundNumber: 1,
89
+ userMessage: '你好',
90
+ aiResponse: '你好!有什么可以帮助你的?',
91
+ model: {
92
+ name: 'gpt-4',
93
+ provider: 'openai',
94
+ },
95
+ usage: {
96
+ total: 100,
97
+ input: 50,
98
+ output: 50,
99
+ },
100
+ timing: {
101
+ total: 1500,
102
+ generation: 1400,
103
+ },
104
+ });
105
+ ```
106
+
107
+ ### 自定义模板
108
+
109
+ ```typescript
110
+ const notifier = createNotifier({
111
+ telegram: { botToken, chatId },
112
+ templates: {
113
+ comment: {
114
+ telegram: (event) => ({
115
+ text: `📬 新评论\n\n${event.author}: ${event.content}`,
116
+ parse_mode: 'HTML',
117
+ }),
118
+ },
119
+ },
120
+ });
121
+ ```
122
+
123
+ ## API
124
+
125
+ ### `createNotifier(config: NotifyConfig): Notifier`
126
+
127
+ 创建通知器实例。
128
+
129
+ #### NotifyConfig
130
+
131
+ ```typescript
132
+ interface NotifyConfig {
133
+ telegram?: {
134
+ botToken: string;
135
+ chatId: string;
136
+ };
137
+ webhook?: {
138
+ url: string;
139
+ method?: 'POST';
140
+ headers?: Record<string, string>;
141
+ };
142
+ email?: {
143
+ provider: 'resend';
144
+ apiKey: string;
145
+ from: string;
146
+ to: string;
147
+ };
148
+ templates?: Partial<EventTemplates>;
149
+ logger?: Logger;
150
+ }
151
+ ```
152
+
153
+ #### Notifier
154
+
155
+ ```typescript
156
+ interface Notifier {
157
+ comment(event: Omit<CommentEvent, 'type'>): Promise<NotifyResult>;
158
+ aiChat(event: Omit<AiChatEvent, 'type'>): Promise<NotifyResult>;
159
+ send(event: NotifyEvent): Promise<NotifyResult>;
160
+ }
161
+ ```
162
+
163
+ ### 事件类型
164
+
165
+ #### CommentEvent
166
+
167
+ ```typescript
168
+ interface CommentEvent {
169
+ type: 'comment';
170
+ author: string;
171
+ content: string;
172
+ postTitle: string;
173
+ postUrl: string;
174
+ }
175
+ ```
176
+
177
+ #### AiChatEvent
178
+
179
+ ```typescript
180
+ interface AiChatEvent {
181
+ type: 'ai-chat';
182
+ sessionId: string;
183
+ roundNumber: number;
184
+ userMessage: string;
185
+ aiResponse?: string;
186
+ referencedArticles?: Array<{ title: string; url?: string }>;
187
+ model?: {
188
+ name: string;
189
+ provider?: string;
190
+ apiHost?: string;
191
+ };
192
+ usage?: {
193
+ total: number;
194
+ input: number;
195
+ output: number;
196
+ };
197
+ timing?: {
198
+ total: number;
199
+ keywordExtraction?: number;
200
+ search?: number;
201
+ generation?: number;
202
+ };
203
+ }
204
+ ```
205
+
206
+ ## 通知效果
207
+
208
+ ### 评论通知
209
+
210
+ ```
211
+ 💬 新评论
212
+
213
+ 📖 文章:如何使用 Astro
214
+ 👤 评论者:张三
215
+
216
+ 「文章写得真好!」
217
+
218
+ 🔗 查看评论
219
+ ```
220
+
221
+ ### AI 对话通知
222
+
223
+ ```
224
+ 🗣 博客 AI 对话
225
+
226
+ 👤 a1b2***f6 · 🕐 03-17 12:30 · 第 3 轮
227
+
228
+ ❓ 读者:
229
+ 「你好,测试 AI 对话通知功能」
230
+
231
+ 💬 AI:
232
+ 你好!AI 对话通知功能测试成功...
233
+
234
+ 📎 引用文章:
235
+ · 京都马拉松
236
+ · 东京旅行
237
+
238
+ ⚙️ 模型配置:
239
+ · API Host: api.openai.com
240
+ · 主对话模型: gpt-4
241
+
242
+ 🧮 Token 用量:
243
+ · 本次请求合计: 总 1,089 / 入 500 / 出 589
244
+
245
+ ⏱️ 阶段耗时:
246
+ · 总耗时: 15.50s
247
+ · 文本生成: 15.49s
248
+ ```
249
+
250
+ ## License
251
+
252
+ MIT
@@ -0,0 +1,5 @@
1
+ export { createNotifier } from './notify.js';
2
+ export type { NotifyConfig, NotifyEvent, CommentEvent, AiChatEvent, ArticleRef, ModelInfo, TokenUsage, PhaseTiming, NotifyResult, SendResult, Notifier, EventTemplates, Logger, TelegramConfig, WebhookConfig, EmailConfig, TelegramTemplate, WebhookPayload, EmailTemplate, Channel, EventType, } from './types.js';
3
+ export { defaultTemplates } from './templates/index.js';
4
+ export { createTelegramProvider, createWebhookProvider, createEmailProvider, } from './providers/index.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,WAAW,EACX,UAAU,EACV,SAAS,EACT,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,cAAc,EACd,MAAM,EACN,cAAc,EACd,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,OAAO,EACP,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createNotifier } from './notify.js';
2
+ export { defaultTemplates } from './templates/index.js';
3
+ export { createTelegramProvider, createWebhookProvider, createEmailProvider, } from './providers/index.js';
@@ -0,0 +1,3 @@
1
+ import type { NotifyConfig, Notifier } from './types.js';
2
+ export declare function createNotifier(config: NotifyConfig): Notifier;
3
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EAMZ,QAAQ,EAIT,MAAM,YAAY,CAAC;AAkDpB,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,QAAQ,CA4H7D"}
package/dist/notify.js ADDED
@@ -0,0 +1,130 @@
1
+ import { createTelegramProvider, createWebhookProvider, createEmailProvider, } from './providers/index.js';
2
+ import { defaultTemplates } from './templates/index.js';
3
+ class DefaultLogger {
4
+ info(message, data) {
5
+ console.log(`[notify] ${message}`, data ?? '');
6
+ }
7
+ error(message, error, data) {
8
+ console.error(`[notify] ${message}`, error?.message ?? '', data ?? '');
9
+ }
10
+ warn(message, data) {
11
+ console.warn(`[notify] ${message}`, data ?? '');
12
+ }
13
+ }
14
+ function mergeTemplates(custom) {
15
+ if (!custom)
16
+ return defaultTemplates;
17
+ return {
18
+ comment: {
19
+ telegram: custom.comment?.telegram ?? defaultTemplates.comment.telegram,
20
+ webhook: custom.comment?.webhook ?? defaultTemplates.comment.webhook,
21
+ email: custom.comment?.email ?? defaultTemplates.comment.email,
22
+ },
23
+ 'ai-chat': {
24
+ telegram: custom['ai-chat']?.telegram ?? defaultTemplates['ai-chat'].telegram,
25
+ webhook: custom['ai-chat']?.webhook ?? defaultTemplates['ai-chat'].webhook,
26
+ email: custom['ai-chat']?.email ?? defaultTemplates['ai-chat'].email,
27
+ },
28
+ };
29
+ }
30
+ export function createNotifier(config) {
31
+ const logger = config.logger ?? new DefaultLogger();
32
+ const templates = mergeTemplates(config.templates);
33
+ const providers = {};
34
+ if (config.telegram) {
35
+ providers.telegram = createTelegramProvider(config.telegram, logger);
36
+ }
37
+ if (config.webhook) {
38
+ providers.webhook = createWebhookProvider(config.webhook, logger);
39
+ }
40
+ if (config.email) {
41
+ providers.email = createEmailProvider(config.email, logger);
42
+ }
43
+ const hasProviders = Object.keys(providers).length > 0;
44
+ if (!hasProviders) {
45
+ logger.warn('No notification providers configured');
46
+ }
47
+ async function sendToAll(event) {
48
+ if (!hasProviders) {
49
+ return {
50
+ event: event.type,
51
+ results: [],
52
+ success: false,
53
+ };
54
+ }
55
+ const results = [];
56
+ const eventType = event.type;
57
+ const eventTemplates = templates[eventType];
58
+ const sendPromises = [];
59
+ if (providers.telegram) {
60
+ sendPromises.push(providers.telegram.send(eventTemplates.telegram(event))
61
+ .then(result => {
62
+ logger.info('Telegram notification result', { success: result.success, duration: result.duration });
63
+ return { channel: 'telegram', result };
64
+ })
65
+ .catch(error => ({
66
+ channel: 'telegram',
67
+ result: { channel: 'telegram', success: false, error: error?.message ?? 'Unknown error' },
68
+ })));
69
+ }
70
+ if (providers.webhook) {
71
+ sendPromises.push(providers.webhook.send(eventTemplates.webhook(event))
72
+ .then(result => {
73
+ logger.info('Webhook notification result', { success: result.success, duration: result.duration });
74
+ return { channel: 'webhook', result };
75
+ })
76
+ .catch(error => ({
77
+ channel: 'webhook',
78
+ result: { channel: 'webhook', success: false, error: error?.message ?? 'Unknown error' },
79
+ })));
80
+ }
81
+ if (providers.email) {
82
+ sendPromises.push(providers.email.send(eventTemplates.email(event))
83
+ .then(result => {
84
+ logger.info('Email notification result', { success: result.success, duration: result.duration });
85
+ return { channel: 'email', result };
86
+ })
87
+ .catch(error => ({
88
+ channel: 'email',
89
+ result: { channel: 'email', success: false, error: error?.message ?? 'Unknown error' },
90
+ })));
91
+ }
92
+ const settledResults = await Promise.allSettled(sendPromises);
93
+ for (const settled of settledResults) {
94
+ if (settled.status === 'fulfilled') {
95
+ results.push(settled.value.result);
96
+ }
97
+ }
98
+ const success = results.some(r => r.success);
99
+ return {
100
+ event: eventType,
101
+ results,
102
+ success,
103
+ };
104
+ }
105
+ return {
106
+ async comment(event) {
107
+ const fullEvent = {
108
+ ...event,
109
+ type: 'comment',
110
+ timestamp: new Date(),
111
+ };
112
+ return sendToAll(fullEvent);
113
+ },
114
+ async aiChat(event) {
115
+ const fullEvent = {
116
+ ...event,
117
+ type: 'ai-chat',
118
+ timestamp: new Date(),
119
+ };
120
+ return sendToAll(fullEvent);
121
+ },
122
+ async send(event) {
123
+ const fullEvent = {
124
+ ...event,
125
+ timestamp: event.timestamp ?? new Date(),
126
+ };
127
+ return sendToAll(fullEvent);
128
+ },
129
+ };
130
+ }
@@ -0,0 +1,6 @@
1
+ import type { EmailConfig, EmailTemplate, SendResult, Logger } from '../types.js';
2
+ export interface EmailProvider {
3
+ send(template: EmailTemplate): Promise<SendResult>;
4
+ }
5
+ export declare function createEmailProvider(config: EmailConfig, logger?: Logger): EmailProvider;
6
+ //# sourceMappingURL=email.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/providers/email.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAElF,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACpD;AAoBD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,EACnB,MAAM,CAAC,EAAE,MAAM,GACd,aAAa,CA8Ef"}
@@ -0,0 +1,84 @@
1
+ let proxyConfigured = false;
2
+ async function configureProxy() {
3
+ if (proxyConfigured)
4
+ return;
5
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
6
+ if (proxyUrl) {
7
+ try {
8
+ const { setGlobalDispatcher, ProxyAgent } = await import('undici');
9
+ setGlobalDispatcher(new ProxyAgent(proxyUrl));
10
+ proxyConfigured = true;
11
+ }
12
+ catch {
13
+ // ProxyAgent not available, continue without proxy
14
+ }
15
+ }
16
+ }
17
+ export function createEmailProvider(config, logger) {
18
+ const { provider, apiKey, from, to } = config;
19
+ return {
20
+ async send(template) {
21
+ const start = Date.now();
22
+ if (provider !== 'resend') {
23
+ return {
24
+ channel: 'email',
25
+ success: false,
26
+ error: `Unsupported email provider: ${provider}`,
27
+ duration: 0,
28
+ };
29
+ }
30
+ try {
31
+ await configureProxy();
32
+ const response = await fetch('https://api.resend.com/emails', {
33
+ method: 'POST',
34
+ headers: {
35
+ 'Authorization': `Bearer ${apiKey}`,
36
+ 'Content-Type': 'application/json',
37
+ },
38
+ body: JSON.stringify({
39
+ from,
40
+ to,
41
+ subject: template.subject,
42
+ html: template.html,
43
+ text: template.text,
44
+ }),
45
+ });
46
+ const duration = Date.now() - start;
47
+ if (!response.ok) {
48
+ const errorData = await response.json();
49
+ const errorMsg = `Resend API error: ${response.status} - ${errorData.message || 'Unknown error'}`;
50
+ logger?.error('Email send failed', new Error(errorMsg), {
51
+ to,
52
+ status: response.status,
53
+ });
54
+ return {
55
+ channel: 'email',
56
+ success: false,
57
+ error: errorMsg,
58
+ duration,
59
+ };
60
+ }
61
+ logger?.info('Email notification sent', { to, duration });
62
+ return {
63
+ channel: 'email',
64
+ success: true,
65
+ duration,
66
+ };
67
+ }
68
+ catch (error) {
69
+ const duration = Date.now() - start;
70
+ const errorMsg = error instanceof Error ? error.message : String(error);
71
+ logger?.error('Email send failed', error instanceof Error ? error : undefined, {
72
+ to,
73
+ error: errorMsg,
74
+ });
75
+ return {
76
+ channel: 'email',
77
+ success: false,
78
+ error: errorMsg,
79
+ duration,
80
+ };
81
+ }
82
+ },
83
+ };
84
+ }
@@ -0,0 +1,4 @@
1
+ export { createTelegramProvider, type TelegramProvider } from './telegram.js';
2
+ export { createWebhookProvider, type WebhookProvider } from './webhook.js';
3
+ export { createEmailProvider, type EmailProvider } from './email.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,qBAAqB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createTelegramProvider } from './telegram.js';
2
+ export { createWebhookProvider } from './webhook.js';
3
+ export { createEmailProvider } from './email.js';
@@ -0,0 +1,6 @@
1
+ import type { TelegramConfig, TelegramTemplate, SendResult, Logger } from '../types.js';
2
+ export interface TelegramProvider {
3
+ send(template: TelegramTemplate): Promise<SendResult>;
4
+ }
5
+ export declare function createTelegramProvider(config: TelegramConfig, logger?: Logger): TelegramProvider;
6
+ //# sourceMappingURL=telegram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../src/providers/telegram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAExF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACvD;AAoBD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,MAAM,GACd,gBAAgB,CAmElB"}
@@ -0,0 +1,73 @@
1
+ let proxyConfigured = false;
2
+ async function configureProxy() {
3
+ if (proxyConfigured)
4
+ return;
5
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
6
+ if (proxyUrl) {
7
+ try {
8
+ const { setGlobalDispatcher, ProxyAgent } = await import('undici');
9
+ setGlobalDispatcher(new ProxyAgent(proxyUrl));
10
+ proxyConfigured = true;
11
+ }
12
+ catch {
13
+ // ProxyAgent not available, continue without proxy
14
+ }
15
+ }
16
+ }
17
+ export function createTelegramProvider(config, logger) {
18
+ const { botToken, chatId } = config;
19
+ return {
20
+ async send(template) {
21
+ const start = Date.now();
22
+ try {
23
+ await configureProxy();
24
+ const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
25
+ const response = await fetch(url, {
26
+ method: 'POST',
27
+ headers: { 'Content-Type': 'application/json' },
28
+ body: JSON.stringify({
29
+ chat_id: chatId,
30
+ text: template.text,
31
+ parse_mode: template.parse_mode ?? 'HTML',
32
+ disable_web_page_preview: false,
33
+ }),
34
+ });
35
+ const duration = Date.now() - start;
36
+ if (!response.ok) {
37
+ const errorText = await response.text();
38
+ const errorMsg = `Telegram API error: ${response.status} - ${errorText}`;
39
+ logger?.error('Telegram send failed', new Error(errorMsg), {
40
+ chatId,
41
+ status: response.status,
42
+ });
43
+ return {
44
+ channel: 'telegram',
45
+ success: false,
46
+ error: errorMsg,
47
+ duration,
48
+ };
49
+ }
50
+ logger?.info('Telegram notification sent', { chatId, duration });
51
+ return {
52
+ channel: 'telegram',
53
+ success: true,
54
+ duration,
55
+ };
56
+ }
57
+ catch (error) {
58
+ const duration = Date.now() - start;
59
+ const errorMsg = error instanceof Error ? error.message : String(error);
60
+ logger?.error('Telegram send failed', error instanceof Error ? error : undefined, {
61
+ chatId,
62
+ error: errorMsg,
63
+ });
64
+ return {
65
+ channel: 'telegram',
66
+ success: false,
67
+ error: errorMsg,
68
+ duration,
69
+ };
70
+ }
71
+ },
72
+ };
73
+ }
@@ -0,0 +1,6 @@
1
+ import type { WebhookConfig, WebhookPayload, SendResult, Logger } from '../types.js';
2
+ export interface WebhookProvider {
3
+ send(payload: WebhookPayload): Promise<SendResult>;
4
+ }
5
+ export declare function createWebhookProvider(config: WebhookConfig, logger?: Logger): WebhookProvider;
6
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/providers/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErF,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACpD;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,MAAM,CAAC,EAAE,MAAM,GACd,eAAe,CA6DjB"}
@@ -0,0 +1,53 @@
1
+ export function createWebhookProvider(config, logger) {
2
+ const { url, method = 'POST', headers = {} } = config;
3
+ return {
4
+ async send(payload) {
5
+ const start = Date.now();
6
+ try {
7
+ const response = await fetch(url, {
8
+ method,
9
+ headers: {
10
+ 'Content-Type': 'application/json',
11
+ ...headers,
12
+ },
13
+ body: JSON.stringify(payload),
14
+ });
15
+ const duration = Date.now() - start;
16
+ if (!response.ok) {
17
+ const errorText = await response.text();
18
+ const errorMsg = `Webhook error: ${response.status} - ${errorText}`;
19
+ logger?.error('Webhook send failed', new Error(errorMsg), {
20
+ url,
21
+ status: response.status,
22
+ });
23
+ return {
24
+ channel: 'webhook',
25
+ success: false,
26
+ error: errorMsg,
27
+ duration,
28
+ };
29
+ }
30
+ logger?.info('Webhook notification sent', { url, duration });
31
+ return {
32
+ channel: 'webhook',
33
+ success: true,
34
+ duration,
35
+ };
36
+ }
37
+ catch (error) {
38
+ const duration = Date.now() - start;
39
+ const errorMsg = error instanceof Error ? error.message : String(error);
40
+ logger?.error('Webhook send failed', error instanceof Error ? error : undefined, {
41
+ url,
42
+ error: errorMsg,
43
+ });
44
+ return {
45
+ channel: 'webhook',
46
+ success: false,
47
+ error: errorMsg,
48
+ duration,
49
+ };
50
+ }
51
+ },
52
+ };
53
+ }
@@ -0,0 +1,5 @@
1
+ import type { AiChatEvent, TelegramTemplate, WebhookPayload, EmailTemplate } from '../types.js';
2
+ export declare function telegramTemplate(event: AiChatEvent): TelegramTemplate;
3
+ export declare function webhookPayload(event: AiChatEvent): WebhookPayload;
4
+ export declare function emailTemplate(event: AiChatEvent): EmailTemplate;
5
+ //# sourceMappingURL=ai-chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-chat.d.ts","sourceRoot":"","sources":["../../src/templates/ai-chat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAYhG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,gBAAgB,CA4ErE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,CAiBjE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,aAAa,CA8G/D"}
@@ -0,0 +1,212 @@
1
+ function anonymizeSessionId(sessionId) {
2
+ if (!sessionId || sessionId.length <= 6)
3
+ return sessionId;
4
+ return `${sessionId.slice(0, 4)}***${sessionId.slice(-2)}`;
5
+ }
6
+ function formatTime(ms) {
7
+ if (ms < 1000)
8
+ return `${ms}ms`;
9
+ return `${(ms / 1000).toFixed(2)}s`;
10
+ }
11
+ export function telegramTemplate(event) {
12
+ const userMsg = event.userMessage.length > 150
13
+ ? event.userMessage.slice(0, 150) + '...'
14
+ : event.userMessage;
15
+ const sessionIdDisplay = anonymizeSessionId(event.sessionId);
16
+ const timestamp = event.timestamp ? formatDate(event.timestamp) : formatDate(new Date());
17
+ const lines = [
18
+ `🗣 <b>博客 AI 对话</b>`,
19
+ ``,
20
+ `👤 ${sessionIdDisplay} · 🕐 ${timestamp} · 第 ${event.roundNumber} 轮`,
21
+ ``,
22
+ `❓ <b>读者:</b>`,
23
+ `<i>「${escapeHtml(userMsg)}」</i>`,
24
+ ];
25
+ if (event.aiResponse) {
26
+ const aiMsg = event.aiResponse.length > 200
27
+ ? event.aiResponse.slice(0, 200) + '...'
28
+ : event.aiResponse;
29
+ lines.push(``, `💬 <b>AI:</b>`, escapeHtml(aiMsg));
30
+ }
31
+ if (event.referencedArticles?.length) {
32
+ lines.push(``, `📎 <b>引用文章:</b>`);
33
+ event.referencedArticles.slice(0, 5).forEach(article => {
34
+ if (article.url) {
35
+ lines.push(` · <a href="${article.url}">${escapeHtml(article.title)}</a>`);
36
+ }
37
+ else {
38
+ lines.push(` · ${escapeHtml(article.title)}`);
39
+ }
40
+ });
41
+ }
42
+ if (event.model) {
43
+ lines.push(``, `⚙️ <b>模型配置:</b>`);
44
+ if (event.model.apiHost) {
45
+ lines.push(` · API Host: ${escapeHtml(event.model.apiHost)}`);
46
+ }
47
+ lines.push(` · 主对话模型: ${escapeHtml(event.model.name)}`);
48
+ if (event.model.provider) {
49
+ lines.push(` · Provider: ${escapeHtml(event.model.provider)}`);
50
+ }
51
+ }
52
+ if (event.usage) {
53
+ lines.push(``, `🧮 <b>Token 用量:</b>`);
54
+ lines.push(` · 本次请求合计: 总 ${event.usage.total} / 入 ${event.usage.input} / 出 ${event.usage.output}`);
55
+ }
56
+ if (event.timing) {
57
+ lines.push(``, `⏱️ <b>阶段耗时:</b>`);
58
+ lines.push(` · 总耗时: ${formatTime(event.timing.total)}`);
59
+ if (event.timing.keywordExtraction) {
60
+ lines.push(` · 关键词提取: ${formatTime(event.timing.keywordExtraction)}`);
61
+ }
62
+ if (event.timing.search) {
63
+ lines.push(` · 检索执行: ${formatTime(event.timing.search)}`);
64
+ }
65
+ if (event.timing.evidenceAnalysis) {
66
+ lines.push(` · 证据分析: ${formatTime(event.timing.evidenceAnalysis)}`);
67
+ }
68
+ if (event.timing.generation) {
69
+ lines.push(` · 文本生成: ${formatTime(event.timing.generation)}`);
70
+ }
71
+ }
72
+ if (event.siteUrl) {
73
+ lines.push(``, `🔗 <a href="${event.siteUrl}">访问网站</a>`);
74
+ }
75
+ return {
76
+ text: lines.join('\n'),
77
+ parse_mode: 'HTML',
78
+ };
79
+ }
80
+ export function webhookPayload(event) {
81
+ return {
82
+ event: 'ai-chat',
83
+ timestamp: event.timestamp?.toISOString() ?? new Date().toISOString(),
84
+ data: {
85
+ sessionId: event.sessionId,
86
+ sessionIdAnonymized: anonymizeSessionId(event.sessionId),
87
+ roundNumber: event.roundNumber,
88
+ userMessage: event.userMessage,
89
+ aiResponse: event.aiResponse,
90
+ referencedArticles: event.referencedArticles,
91
+ model: event.model,
92
+ usage: event.usage,
93
+ timing: event.timing,
94
+ siteUrl: event.siteUrl,
95
+ },
96
+ };
97
+ }
98
+ export function emailTemplate(event) {
99
+ const userMsg = event.userMessage.length > 300
100
+ ? event.userMessage.slice(0, 300) + '...'
101
+ : event.userMessage;
102
+ const sessionIdDisplay = anonymizeSessionId(event.sessionId);
103
+ const timestamp = event.timestamp ? formatDate(event.timestamp) : formatDate(new Date());
104
+ const articlesHtml = event.referencedArticles?.length ? `
105
+ <div class="section">
106
+ <div class="section-title">📎 引用文章</div>
107
+ <div class="content">
108
+ ${event.referencedArticles.slice(0, 5).map(a => a.url ? `<a href="${a.url}">${escapeHtml(a.title)}</a>` : escapeHtml(a.title)).join('<br>')}
109
+ </div>
110
+ </div>
111
+ ` : '';
112
+ const modelHtml = event.model ? `
113
+ <div class="section">
114
+ <div class="section-title">⚙️ 模型配置</div>
115
+ <div class="content meta-grid">
116
+ ${event.model.apiHost ? `<span>API Host: ${escapeHtml(event.model.apiHost)}</span>` : ''}
117
+ <span>模型: ${escapeHtml(event.model.name)}</span>
118
+ ${event.model.provider ? `<span>Provider: ${escapeHtml(event.model.provider)}</span>` : ''}
119
+ </div>
120
+ </div>
121
+ ` : '';
122
+ const usageHtml = event.usage ? `
123
+ <div class="section">
124
+ <div class="section-title">🧮 Token 用量</div>
125
+ <div class="content meta-grid">
126
+ <span>总计: ${event.usage.total}</span>
127
+ <span>输入: ${event.usage.input}</span>
128
+ <span>输出: ${event.usage.output}</span>
129
+ </div>
130
+ </div>
131
+ ` : '';
132
+ const timingHtml = event.timing ? `
133
+ <div class="section">
134
+ <div class="section-title">⏱️ 阶段耗时</div>
135
+ <div class="content meta-grid">
136
+ <span>总耗时: ${formatTime(event.timing.total)}</span>
137
+ ${event.timing.keywordExtraction ? `<span>关键词提取: ${formatTime(event.timing.keywordExtraction)}</span>` : ''}
138
+ ${event.timing.search ? `<span>检索: ${formatTime(event.timing.search)}</span>` : ''}
139
+ ${event.timing.generation ? `<span>生成: ${formatTime(event.timing.generation)}</span>` : ''}
140
+ </div>
141
+ </div>
142
+ ` : '';
143
+ return {
144
+ subject: `🗣 博客 AI 对话 - 第 ${event.roundNumber} 轮`,
145
+ html: `
146
+ <!DOCTYPE html>
147
+ <html>
148
+ <head>
149
+ <meta charset="utf-8">
150
+ <style>
151
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
152
+ .header { border-bottom: 1px solid #eee; padding-bottom: 16px; margin-bottom: 16px; }
153
+ .title { font-size: 18px; font-weight: 600; margin: 0 0 8px 0; }
154
+ .meta-header { font-size: 13px; color: #666; }
155
+ .section { margin: 16px 0; }
156
+ .section-title { font-weight: 600; color: #555; font-size: 14px; margin-bottom: 8px; }
157
+ .content { background: #f9f9f9; padding: 16px; border-radius: 8px; }
158
+ .user-msg { border-left: 3px solid #007bff; padding-left: 12px; }
159
+ .ai-msg { border-left: 3px solid #28a745; padding-left: 12px; }
160
+ .meta-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; font-size: 13px; }
161
+ .meta-grid span { padding: 4px 8px; background: #eee; border-radius: 4px; }
162
+ .link { display: inline-block; background: #007bff; color: #fff; padding: 10px 20px; text-decoration: none; border-radius: 6px; margin-top: 16px; }
163
+ </style>
164
+ </head>
165
+ <body>
166
+ <div class="header">
167
+ <h1 class="title">🗣 博客 AI 对话</h1>
168
+ <p class="meta-header">👤 ${sessionIdDisplay} · 🕐 ${timestamp} · 第 ${event.roundNumber} 轮</p>
169
+ </div>
170
+ <div class="section">
171
+ <div class="section-title">❓ 读者提问</div>
172
+ <div class="content user-msg">${escapeHtml(userMsg)}</div>
173
+ </div>
174
+ ${event.aiResponse ? `
175
+ <div class="section">
176
+ <div class="section-title">💬 AI 回复</div>
177
+ <div class="content ai-msg">${escapeHtml(event.aiResponse.slice(0, 400))}${event.aiResponse.length > 400 ? '...' : ''}</div>
178
+ </div>
179
+ ` : ''}
180
+ ${articlesHtml}
181
+ ${modelHtml}
182
+ ${usageHtml}
183
+ ${timingHtml}
184
+ ${event.siteUrl ? `<a href="${event.siteUrl}" class="link">访问网站</a>` : ''}
185
+ </body>
186
+ </html>`,
187
+ text: `博客 AI 对话
188
+
189
+ 👤 ${sessionIdDisplay} · 🕐 ${timestamp} · 第 ${event.roundNumber} 轮
190
+
191
+ ❓ 读者提问:
192
+ ${userMsg}
193
+
194
+ ${event.aiResponse ? `💬 AI 回复:\n${event.aiResponse.slice(0, 400)}\n` : ''}
195
+ ${event.referencedArticles?.length ? `📎 引用文章:\n${event.referencedArticles.map(a => ` · ${a.title}`).join('\n')}\n` : ''}
196
+ ${event.model ? `⚙️ 模型: ${event.model.name}${event.model.provider ? ` @ ${event.model.provider}` : ''}\n` : ''}
197
+ ${event.usage ? `🧮 Token: 总 ${event.usage.total} / 入 ${event.usage.input} / 出 ${event.usage.output}\n` : ''}
198
+ ${event.timing ? `⏱️ 耗时: ${formatTime(event.timing.total)}\n` : ''}`,
199
+ };
200
+ }
201
+ function escapeHtml(text) {
202
+ return text
203
+ .replace(/&/g, '&amp;')
204
+ .replace(/</g, '&lt;')
205
+ .replace(/>/g, '&gt;')
206
+ .replace(/"/g, '&quot;')
207
+ .replace(/'/g, '&#039;');
208
+ }
209
+ function formatDate(date) {
210
+ const pad = (n) => n.toString().padStart(2, '0');
211
+ return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
212
+ }
@@ -0,0 +1,5 @@
1
+ import type { CommentEvent, TelegramTemplate, WebhookPayload, EmailTemplate } from '../types.js';
2
+ export declare function telegramTemplate(event: CommentEvent): TelegramTemplate;
3
+ export declare function webhookPayload(event: CommentEvent): WebhookPayload;
4
+ export declare function emailTemplate(event: CommentEvent): EmailTemplate;
5
+ //# sourceMappingURL=comment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment.d.ts","sourceRoot":"","sources":["../../src/templates/comment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,gBAAgB,CAgBtE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,cAAc,CAWlE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CA+ChE"}
@@ -0,0 +1,83 @@
1
+ export function telegramTemplate(event) {
2
+ const content = event.content.length > 200
3
+ ? event.content.slice(0, 200) + '...'
4
+ : event.content;
5
+ return {
6
+ text: `💬 <b>新评论</b>
7
+
8
+ 📖 文章:${escapeHtml(event.postTitle)}
9
+ 👤 评论者:${escapeHtml(event.author)}
10
+
11
+ <i>「${escapeHtml(content)}」</i>
12
+
13
+ 🔗 <a href="${event.postUrl}">查看评论</a>`,
14
+ parse_mode: 'HTML',
15
+ };
16
+ }
17
+ export function webhookPayload(event) {
18
+ return {
19
+ event: 'comment',
20
+ timestamp: new Date().toISOString(),
21
+ data: {
22
+ author: event.author,
23
+ content: event.content,
24
+ postTitle: event.postTitle,
25
+ postUrl: event.postUrl,
26
+ },
27
+ };
28
+ }
29
+ export function emailTemplate(event) {
30
+ const content = event.content.length > 500
31
+ ? event.content.slice(0, 500) + '...'
32
+ : event.content;
33
+ return {
34
+ subject: `💬 新评论 - ${event.postTitle}`,
35
+ html: `
36
+ <!DOCTYPE html>
37
+ <html>
38
+ <head>
39
+ <meta charset="utf-8">
40
+ <style>
41
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; }
42
+ .header { border-bottom: 1px solid #eee; padding-bottom: 16px; margin-bottom: 16px; }
43
+ .title { font-size: 18px; font-weight: 600; margin: 0 0 8px 0; }
44
+ .meta { font-size: 14px; color: #666; }
45
+ .content { background: #f9f9f9; padding: 16px; border-radius: 8px; margin: 16px 0; }
46
+ .quote { font-style: italic; color: #555; }
47
+ .link { display: inline-block; background: #007bff; color: #fff; padding: 10px 20px; text-decoration: none; border-radius: 6px; margin-top: 16px; }
48
+ .footer { margin-top: 24px; font-size: 12px; color: #999; }
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <div class="header">
53
+ <h1 class="title">💬 新评论</h1>
54
+ <p class="meta">文章:<a href="${event.postUrl}">${escapeHtml(event.postTitle)}</a></p>
55
+ </div>
56
+ <p><strong>评论者:</strong>${escapeHtml(event.author)}</p>
57
+ <div class="content">
58
+ <p class="quote">"${escapeHtml(content)}"</p>
59
+ </div>
60
+ <a href="${event.postUrl}" class="link">查看评论</a>
61
+ <div class="footer">
62
+ <p>此邮件由您的博客通知系统发送</p>
63
+ </div>
64
+ </body>
65
+ </html>`,
66
+ text: `新评论
67
+
68
+ 文章:${event.postTitle}
69
+ 评论者:${event.author}
70
+
71
+ "${content}"
72
+
73
+ 查看评论:${event.postUrl}`,
74
+ };
75
+ }
76
+ function escapeHtml(text) {
77
+ return text
78
+ .replace(/&/g, '&amp;')
79
+ .replace(/</g, '&lt;')
80
+ .replace(/>/g, '&gt;')
81
+ .replace(/"/g, '&quot;')
82
+ .replace(/'/g, '&#039;');
83
+ }
@@ -0,0 +1,3 @@
1
+ import type { EventTemplates } from '../types.js';
2
+ export declare const defaultTemplates: EventTemplates;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/templates/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,eAAO,MAAM,gBAAgB,EAAE,cAW9B,CAAC"}
@@ -0,0 +1,14 @@
1
+ import * as commentTemplates from './comment.js';
2
+ import * as aiChatTemplates from './ai-chat.js';
3
+ export const defaultTemplates = {
4
+ comment: {
5
+ telegram: commentTemplates.telegramTemplate,
6
+ webhook: commentTemplates.webhookPayload,
7
+ email: commentTemplates.emailTemplate,
8
+ },
9
+ 'ai-chat': {
10
+ telegram: aiChatTemplates.telegramTemplate,
11
+ webhook: aiChatTemplates.webhookPayload,
12
+ email: aiChatTemplates.emailTemplate,
13
+ },
14
+ };
@@ -0,0 +1,117 @@
1
+ export interface TelegramConfig {
2
+ botToken: string;
3
+ chatId: string;
4
+ }
5
+ export interface WebhookConfig {
6
+ url: string;
7
+ method?: 'POST';
8
+ headers?: Record<string, string>;
9
+ }
10
+ export interface EmailConfig {
11
+ provider: 'resend';
12
+ apiKey: string;
13
+ from: string;
14
+ to: string;
15
+ }
16
+ export interface NotifyConfig {
17
+ telegram?: TelegramConfig;
18
+ webhook?: WebhookConfig;
19
+ email?: EmailConfig;
20
+ templates?: Partial<EventTemplates>;
21
+ logger?: Logger;
22
+ }
23
+ export type EventType = 'comment' | 'ai-chat';
24
+ export interface BaseEvent {
25
+ type: EventType;
26
+ timestamp?: Date;
27
+ }
28
+ export interface CommentEvent extends BaseEvent {
29
+ type: 'comment';
30
+ author: string;
31
+ content: string;
32
+ postTitle: string;
33
+ postUrl: string;
34
+ }
35
+ export interface ArticleRef {
36
+ title: string;
37
+ url?: string;
38
+ }
39
+ export interface ModelInfo {
40
+ name: string;
41
+ provider?: string;
42
+ apiHost?: string;
43
+ }
44
+ export interface TokenUsage {
45
+ total: number;
46
+ input: number;
47
+ output: number;
48
+ }
49
+ export interface PhaseTiming {
50
+ total: number;
51
+ keywordExtraction?: number;
52
+ search?: number;
53
+ evidenceAnalysis?: number;
54
+ generation?: number;
55
+ }
56
+ export interface AiChatEvent extends BaseEvent {
57
+ type: 'ai-chat';
58
+ sessionId: string;
59
+ roundNumber: number;
60
+ userMessage: string;
61
+ aiResponse?: string;
62
+ referencedArticles?: ArticleRef[];
63
+ model?: ModelInfo;
64
+ usage?: TokenUsage;
65
+ timing?: PhaseTiming;
66
+ siteUrl?: string;
67
+ }
68
+ export type NotifyEvent = CommentEvent | AiChatEvent;
69
+ export interface TelegramTemplate {
70
+ text: string;
71
+ parse_mode?: 'HTML' | 'MarkdownV2';
72
+ }
73
+ export interface WebhookPayload {
74
+ event: EventType;
75
+ timestamp: string;
76
+ data: Record<string, unknown>;
77
+ }
78
+ export interface EmailTemplate {
79
+ subject: string;
80
+ html: string;
81
+ text?: string;
82
+ }
83
+ export interface EventTemplates {
84
+ comment: {
85
+ telegram: (event: CommentEvent) => TelegramTemplate;
86
+ webhook: (event: CommentEvent) => WebhookPayload;
87
+ email: (event: CommentEvent) => EmailTemplate;
88
+ };
89
+ 'ai-chat': {
90
+ telegram: (event: AiChatEvent) => TelegramTemplate;
91
+ webhook: (event: AiChatEvent) => WebhookPayload;
92
+ email: (event: AiChatEvent) => EmailTemplate;
93
+ };
94
+ }
95
+ export type Channel = 'telegram' | 'webhook' | 'email';
96
+ export interface SendResult {
97
+ channel: Channel;
98
+ success: boolean;
99
+ error?: string;
100
+ duration?: number;
101
+ }
102
+ export interface NotifyResult {
103
+ event: EventType;
104
+ results: SendResult[];
105
+ success: boolean;
106
+ }
107
+ export interface Logger {
108
+ info(message: string, data?: Record<string, unknown>): void;
109
+ error(message: string, error?: Error, data?: Record<string, unknown>): void;
110
+ warn(message: string, data?: Record<string, unknown>): void;
111
+ }
112
+ export interface Notifier {
113
+ comment(event: Omit<CommentEvent, 'type'>): Promise<NotifyResult>;
114
+ aiChat(event: Omit<AiChatEvent, 'type'>): Promise<NotifyResult>;
115
+ send(event: NotifyEvent): Promise<NotifyResult>;
116
+ }
117
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ // ============================================================================
2
+ // Provider Configuration Types
3
+ // ============================================================================
4
+ export {};
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@astro-minimax/notify",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Multi-channel notification package for astro-minimax blogs",
6
+ "author": "Souloss",
7
+ "license": "MIT",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist/"
19
+ ],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.build.json",
23
+ "build:watch": "tsc -p tsconfig.build.json --watch",
24
+ "clean": "rm -rf dist",
25
+ "prepublishOnly": "pnpm run clean && pnpm run build",
26
+ "publish": "npm publish --access public"
27
+ },
28
+ "dependencies": {
29
+ "grammy": "^1.41.1",
30
+ "undici": "^6.0.0"
31
+ },
32
+ "peerDependencies": {
33
+ "resend": "^3.0.0"
34
+ },
35
+ "peerDependenciesMeta": {
36
+ "resend": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "devDependencies": {
41
+ "@cloudflare/workers-types": "^4.20260313.1",
42
+ "@types/node": "^22.0.0",
43
+ "typescript": "^5.9.3"
44
+ },
45
+ "engines": {
46
+ "node": ">=22.12.0"
47
+ }
48
+ }