perf: 通知支持vocechat、bark、telegram、discord、slack

This commit is contained in:
xiaojunnuo
2024-11-26 15:13:57 +08:00
parent cbccd9e3d0
commit 642f57ff6d
16 changed files with 315 additions and 19 deletions
@@ -39,7 +39,7 @@ export class AnPushNotification extends BaseNotification {
},
data: {
title: body.title,
content: body.content,
content: body.content + '[查看详情](' + body.url + ')',
channel: this.channel,
},
};
@@ -0,0 +1,52 @@
/**
* curl -X "POST" "https://api.day.app/your_key" \
* -H 'Content-Type: application/json; charset=utf-8' \
* -d $'{
* "body": "Test Bark Server",
* "title": "Test Title",
* "badge": 1,
* "category": "myNotificationCategory",
* "sound": "minuet.caf",
* "icon": "https://day.app/assets/images/avatar.jpg",
* "group": "test",
* "url": "https://mritd.com"
* }'
*/
import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
@IsNotification({
name: 'bark',
title: 'Bark 通知',
desc: 'Bark 推送通知插件',
})
export class BarkNotification extends BaseNotification {
@NotificationInput({
title: '服务地址',
component: {
placeholder: 'https://api.day.app/your_key',
},
required: true,
helper: '你的bark服务地址+key',
})
webhook = '';
async send(body: NotificationBody) {
if (!this.webhook) {
throw new Error('服务器地址不能为空');
}
const payload = {
body: body.content, // 使用传入的内容或默认内容
title: body.title, // 使用传入的标题或默认标题
};
await this.http.request({
url: `${this.webhook}`,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8',
},
data: payload,
});
}
}
@@ -0,0 +1,53 @@
import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
@IsNotification({
name: 'discord',
title: 'Discord 通知',
desc: 'Discord 机器人通知',
})
export class DiscordNotification extends BaseNotification {
@NotificationInput({
title: 'Webhook URL',
component: {
placeholder: 'https://discord.com/api/webhooks/xxxxx/xxxx',
},
helper: '[Discord Webhook 说明](https://discord.com/developers/docs/resources/webhook#execute-webhook)',
required: true,
})
webhook = '';
@NotificationInput({
title: '提醒指定成员',
component: {
name: 'a-select',
vModel: 'value',
mode: 'tags',
open: false,
},
required: false,
helper: '填写成员的Id,或者角色Id&id),或者everyone',
})
mentionedList!: string[];
async send(body: NotificationBody) {
if (!this.webhook) {
throw new Error('Webhook URL 不能为空');
}
// 创建 Discord 消息体
let content = `${body.title}\n${body.content}\n[查看详情](${body.url})`;
if (this.mentionedList && this.mentionedList.length > 0) {
content += `\n${this.mentionedList.map(item => `<@${item}> `).join('')}`;
}
const json = {
content: content,
};
await this.http.request({
url: this.webhook,
method: 'POST',
data: json,
});
}
}
@@ -23,7 +23,7 @@ export class EmailNotification extends BaseNotification {
await this.ctx.emailService.send({
userId: body.userId,
subject: body.title,
content: body.content,
content: body.content + '\n[查看详情](' + body.url + ')',
receivers: this.receivers,
});
}
@@ -4,3 +4,7 @@ export * from './iyuu/index.js';
export * from './webhook/index.js';
export * from './serverchan/index.js';
export * from './anpush/index.js';
export * from './telegram/index.js';
export * from './discord/index.js';
export * from './slack/index.js';
export * from './bark/index.js';
@@ -25,7 +25,7 @@ export class IyuuNotification extends BaseNotification {
method: 'POST',
data: {
text: body.title,
desp: body.content,
desp: body.content + '[查看详情](' + body.url + ')',
},
});
@@ -52,7 +52,7 @@ export class ServerChanNotification extends BaseNotification {
method: 'POST',
data: {
text: body.title,
desp: body.content,
desp: body.content + '[查看详情](' + body.url + ')',
},
});
}
@@ -0,0 +1,32 @@
import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
@IsNotification({
name: 'slack',
title: 'Slack通知',
desc: 'Slack消息推送通知',
})
export class SlackNotification extends BaseNotification {
@NotificationInput({
title: 'webhook地址',
component: {
placeholder: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX',
},
helper: '[APPS](https://api.slack.com/apps/)->进入APP->incoming-webhooks->Add New Webhook to Workspace',
required: true,
})
webhook = '';
async send(body: NotificationBody) {
if (!this.webhook) {
throw new Error('token不能为空');
}
await this.http.request({
url: this.webhook,
method: 'POST',
data: {
text: `${body.title}\n${body.content}\n[查看详情](${body.url})`,
},
});
}
}
@@ -0,0 +1,51 @@
import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
@IsNotification({
name: 'telegram',
title: 'Telegram通知',
desc: 'Telegram Bot推送通知',
})
export class TelegramNotification extends BaseNotification {
@NotificationInput({
title: 'Bot Token',
component: {
placeholder: '123456789:ABCdefGhijklmnopqrstUVWXyz',
},
helper: '[token获取](https://core.telegram.org/bots/features#botfather)',
required: true,
})
botToken = '';
@NotificationInput({
title: '聊天ID',
component: {
placeholder: '聊天ID,例如 -123456789 或 @channelusername',
},
helper: '聊天的唯一标识符或用户名',
required: true,
})
chatId = '';
async send(body: NotificationBody) {
if (!this.botToken || !this.chatId) {
throw new Error('Bot Token 和聊天ID不能为空');
}
// 构建消息内容
const messageContent = `*${body.title}*\n\n${body.content}\n[查看详情](${body.url})`;
// Telegram API URL
const url = `https://api.telegram.org/bot${this.botToken}/sendMessage`;
// 发送 HTTP 请求
await this.http.request({
url: url,
method: 'POST',
data: {
chat_id: this.chatId,
text: messageContent,
parse_mode: 'MarkdownV2', // 或使用 'HTML' 取决于需要的格式
},
});
}
}
@@ -0,0 +1,73 @@
import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
@IsNotification({
name: 'vocechat',
title: 'VoceChat通知',
desc: 'https://voce.chat',
})
export class VoceChatNotification extends BaseNotification {
@NotificationInput({
title: '服务地址',
component: {
placeholder: 'https://replace.your.domain',
},
required: true,
})
endpoint = '';
@NotificationInput({
title: 'apiKey',
component: {
placeholder: '',
},
helper: '[获取APIKEY](https://doc.voce.chat/bot/bot-and-webhook)',
required: true,
})
apiKey = '';
@NotificationInput({
title: '目标类型',
component: {
name: 'a-select',
options: [
{ value: 'user', label: '用户' },
{ value: 'channel', label: '频道' },
],
},
required: true,
helper: '发送消息的目标类型',
})
targetType = '';
@NotificationInput({
title: '目标ID',
component: {
placeholder: '发送消息的目标ID',
},
required: true,
helper: '目标ID可以是用户ID或频道ID',
})
targetId = '';
async send(body: NotificationBody) {
if (!this.apiKey) {
throw new Error('API Key不能为空');
}
if (!this.targetId) {
throw new Error('目标ID不能为空');
}
const url = this.targetType === 'user' ? '/api/bot/send_to_user/' : '/api/bot/send_to_group/';
await this.http.request({
url: url + this.targetId, // 这是示例API URL,请根据实际API文档调整
baseURL: this.endpoint,
method: 'POST',
headers: {
'x-api-key': this.apiKey,
'Content-Type': 'text/markdown',
},
data: `# ${body.title}\n\n${body.content}\n[查看详情](${body.url})`,
});
}
}
@@ -44,11 +44,22 @@ export class WebhookNotification extends BaseNotification {
})
contentType = '';
@NotificationInput({
title: 'Headers',
component: {
name: 'a-textarea',
rows: 3,
},
helper: '一行一个,格式为key:value',
required: true,
})
headers = '';
@NotificationInput({
title: '消息body模版',
value: `{
title:"{title}",
content:"{content}"
"text":"{title}",
"desp":"{content}\\n[查看详情]({url})"
}`,
component: {
name: 'a-textarea',
@@ -57,7 +68,7 @@ export class WebhookNotification extends BaseNotification {
col: {
span: 24,
},
helper: `根据实际的webhook接口,构建一个json对象作为参数,支持{title}{content}两个变量,变量用{}包裹,字符串需要双引号`,
helper: `根据实际的webhook接口,构建一个json对象作为参数,支持变量:{title}{content}、{url},变量用{}包裹,字符串需要双引号`,
required: true,
})
template = '';
@@ -67,15 +78,21 @@ export class WebhookNotification extends BaseNotification {
throw new Error('模版不能为空');
}
const bodyStr = this.template.replaceAll('{title}', body.title).replaceAll('{content}', body.content);
const bodyStr = this.template.replaceAll('{title}', body.title).replaceAll('{content}', body.content).replaceAll('{url}', body.url);
const data = JSON.parse(bodyStr);
const headers: any = {};
this.headers.split('\n').forEach(item => {
const [key, value] = item.trim().split(':');
headers[key] = value;
});
await this.http.request({
url: this.webhook,
method: this.method,
headers: {
'Content-Type': `${this.contentType}; charset=UTF-8`,
...headers,
},
data: data,
});