From fc8bef5aae522d75d408d8c3aa74543269da5398 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Tue, 11 Mar 2025 00:29:18 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E9=80=9A=E7=9F=A5=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=92=89=E9=92=89=E7=BE=A4=E8=81=8A=E6=9C=BA=E5=99=A8=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugin-notification/dingtalk/index.ts | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts diff --git a/packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts b/packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts new file mode 100644 index 00000000..e1981a68 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-notification/dingtalk/index.ts @@ -0,0 +1,143 @@ +import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from "@certd/pipeline"; + +@IsNotification({ + name: 'dingtalk', + title: '钉钉通知', + desc: '钉钉群聊通知', + needPlus: true, +}) +// https://open.dingtalk.com/document/orgapp/the-creation-and-installation-of-the-application-robot-in-the?spm=ding_open_doc.document.0.0.242d1563cDgZz3 +export class DingTalkNotification extends BaseNotification { + @NotificationInput({ + title: 'webhook地址', + component: { + placeholder: 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxxxx', + }, + helper: '钉钉APP->群聊->设置->机器人->添加机器人->自定义->[创建机器人->复制webhook地址](https://open.dingtalk.com/document/robots/custom-robot-access)', + required: true, + }) + webhook = ''; + + @NotificationInput({ + title: '加签密钥', + component: { + placeholder: 'SECxxxxxxxxxxxxxxxxxxxxx', + }, + helper: '必须选择一种安全设置,请选择加密密钥', + required: false, + }) + secret = ''; + + + @NotificationInput({ + title: '@用户ID', + component: { + placeholder: '非必填,填写完一个按回车', + name: 'a-select', + vModel: 'value', + mode: 'tags', + multiple: true, + open: false, + }, + helper: '填写要@的用户ID', + required: false, + }) + atUserIds:string[]; + + @NotificationInput({ + title: '@用户手机号', + component: { + placeholder: '非必填,填写一个按回车', + name: 'a-select', + vModel: 'value', + mode: 'tags', + multiple: true, + open: false, + }, + helper: '填写要@的用户的手机号', + required: false, + }) + atMobiles:string[]; + + + @NotificationInput({ + title: '@all', + component: { + placeholder: '非必填', + name: 'a-switch', + vModel:"checked" + }, + helper: '是否@所有人', + required: false, + }) + isAtAll:boolean; + + + async sign(){ + const timestamp = Date.now(); + const secret = this.secret; + const stringToSign = `${timestamp}\n${secret}`; + /** + * string_to_sign = '{}\n{}'.format(timestamp, secret) + * string_to_sign_enc = string_to_sign.encode('utf-8') + * hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() + * sign = urllib.parse.quote_plus(base64.b64encode(hmac_code)) + */ + const crypto = await import('crypto'); + const hmac = crypto.createHmac('sha256', secret); + hmac.update(stringToSign); + const sign = encodeURIComponent(Buffer.from(hmac.digest()).toString('base64')); + + return { + timestamp, + sign, + }; + + } + + async send(body: NotificationBody) { + if (!this.webhook) { + throw new Error('webhook地址不能为空'); + } + /** + * + * "msgtype": "text", + * "text": { + * "content": "hello world" + * } + * } + */ + + let webhook = this.webhook; + if(this.secret){ + const signRet = await this.sign(); + webhook = `${webhook}×tamp=${signRet.timestamp}&sign=${signRet.sign}`; + } + + const at :any= {} + if(this.atUserIds && this.atUserIds.length>0){ + at.atUserIds = this.atUserIds; + } + if(this.atMobiles && this.atMobiles.length>0){ + at.atMobiles = this.atMobiles; + } + if(this.isAtAll){ + at.isAtAll = true; + } + + const res = await this.http.request({ + url: webhook, + method: 'POST', + data: { + at: at, + text: { + content: `· ${body.title}\n· ${body.content}\n· 查看详情: ${body.url}`, + }, + msgtype:"text" + }, + }); + if(res.errcode>100){ + throw new Error(`发送失败:${res.errmsg}`); + } + } +}