mirror of
https://github.com/whyour/qinglong.git
synced 2025-11-28 05:05:37 +08:00
Add signature verification support for Feishu bot notifications (#2856)
* Initial plan * Add signature verification support for Feishu bot notifications Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> * Add clarifying comments about Feishu signature algorithm Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> * Add i18n translations for larkSecret configuration field Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
parent
3b0f55caf4
commit
02a05f06bd
|
|
@ -142,6 +142,7 @@ export class WebhookNotification extends NotificationBaseInfo {
|
||||||
|
|
||||||
export class LarkNotification extends NotificationBaseInfo {
|
export class LarkNotification extends NotificationBaseInfo {
|
||||||
public larkKey = '';
|
public larkKey = '';
|
||||||
|
public larkSecret = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NtfyNotification extends NotificationBaseInfo {
|
export class NtfyNotification extends NotificationBaseInfo {
|
||||||
|
|
|
||||||
|
|
@ -550,19 +550,33 @@ export default class NotificationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async lark() {
|
private async lark() {
|
||||||
let { larkKey } = this.params;
|
let { larkKey, larkSecret } = this.params;
|
||||||
|
|
||||||
if (!larkKey.startsWith('http')) {
|
if (!larkKey.startsWith('http')) {
|
||||||
larkKey = `https://open.feishu.cn/open-apis/bot/v2/hook/${larkKey}`;
|
larkKey = `https://open.feishu.cn/open-apis/bot/v2/hook/${larkKey}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body: Record<string, any> = {
|
||||||
|
msg_type: 'text',
|
||||||
|
content: { text: `${this.title}\n\n${this.content}` },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add signature if secret is provided
|
||||||
|
// Note: Feishu's signature algorithm uses timestamp+"\n"+secret as the HMAC key
|
||||||
|
// and signs an empty message, which differs from typical HMAC usage
|
||||||
|
if (larkSecret) {
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000).toString();
|
||||||
|
const stringToSign = `${timestamp}\n${larkSecret}`;
|
||||||
|
const hmac = crypto.createHmac('sha256', stringToSign);
|
||||||
|
const sign = hmac.digest('base64');
|
||||||
|
body.timestamp = timestamp;
|
||||||
|
body.sign = sign;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await httpClient.post(larkKey, {
|
const res = await httpClient.post(larkKey, {
|
||||||
...this.gotOption,
|
...this.gotOption,
|
||||||
json: {
|
json: body,
|
||||||
msg_type: 'text',
|
|
||||||
content: { text: `${this.title}\n\n${this.content}` },
|
|
||||||
},
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
});
|
});
|
||||||
if (res.StatusCode === 0 || res.code === 0) {
|
if (res.StatusCode === 0 || res.code === 0) {
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ const push_config = {
|
||||||
DD_BOT_TOKEN: '', // 钉钉机器人的 DD_BOT_TOKEN
|
DD_BOT_TOKEN: '', // 钉钉机器人的 DD_BOT_TOKEN
|
||||||
|
|
||||||
FSKEY: '', // 飞书机器人的 FSKEY
|
FSKEY: '', // 飞书机器人的 FSKEY
|
||||||
|
FSSECRET: '', // 飞书机器人的 FSSECRET,对应安全设置里的签名校验密钥
|
||||||
|
|
||||||
// 推送到个人QQ:http://127.0.0.1/send_private_msg
|
// 推送到个人QQ:http://127.0.0.1/send_private_msg
|
||||||
// 群:http://127.0.0.1/send_group_msg
|
// 群:http://127.0.0.1/send_group_msg
|
||||||
|
|
@ -989,11 +990,26 @@ function aibotkNotify(text, desp) {
|
||||||
|
|
||||||
function fsBotNotify(text, desp) {
|
function fsBotNotify(text, desp) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const { FSKEY } = push_config;
|
const { FSKEY, FSSECRET } = push_config;
|
||||||
if (FSKEY) {
|
if (FSKEY) {
|
||||||
|
const body = { msg_type: 'text', content: { text: `${text}\n\n${desp}` } };
|
||||||
|
|
||||||
|
// Add signature if secret is provided
|
||||||
|
// Note: Feishu's signature algorithm uses timestamp+"\n"+secret as the HMAC key
|
||||||
|
// and signs an empty message, which differs from typical HMAC usage
|
||||||
|
if (FSSECRET) {
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000).toString();
|
||||||
|
const stringToSign = `${timestamp}\n${FSSECRET}`;
|
||||||
|
const hmac = crypto.createHmac('sha256', stringToSign);
|
||||||
|
const sign = hmac.digest('base64');
|
||||||
|
body.timestamp = timestamp;
|
||||||
|
body.sign = sign;
|
||||||
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
url: `https://open.feishu.cn/open-apis/bot/v2/hook/${FSKEY}`,
|
url: `https://open.feishu.cn/open-apis/bot/v2/hook/${FSKEY}`,
|
||||||
json: { msg_type: 'text', content: { text: `${text}\n\n${desp}` } },
|
json: body,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ push_config = {
|
||||||
'DD_BOT_TOKEN': '', # 钉钉机器人的 DD_BOT_TOKEN
|
'DD_BOT_TOKEN': '', # 钉钉机器人的 DD_BOT_TOKEN
|
||||||
|
|
||||||
'FSKEY': '', # 飞书机器人的 FSKEY
|
'FSKEY': '', # 飞书机器人的 FSKEY
|
||||||
|
'FSSECRET': '', # 飞书机器人的 FSSECRET,对应安全设置里的签名校验密钥
|
||||||
|
|
||||||
'GOBOT_URL': '', # go-cqhttp
|
'GOBOT_URL': '', # go-cqhttp
|
||||||
# 推送到个人QQ:http://127.0.0.1/send_private_msg
|
# 推送到个人QQ:http://127.0.0.1/send_private_msg
|
||||||
|
|
@ -233,6 +234,20 @@ def feishu_bot(title: str, content: str) -> None:
|
||||||
|
|
||||||
url = f'https://open.feishu.cn/open-apis/bot/v2/hook/{push_config.get("FSKEY")}'
|
url = f'https://open.feishu.cn/open-apis/bot/v2/hook/{push_config.get("FSKEY")}'
|
||||||
data = {"msg_type": "text", "content": {"text": f"{title}\n\n{content}"}}
|
data = {"msg_type": "text", "content": {"text": f"{title}\n\n{content}"}}
|
||||||
|
|
||||||
|
# Add signature if secret is provided
|
||||||
|
# Note: Feishu's signature algorithm uses timestamp+"\n"+secret as the HMAC key
|
||||||
|
# and signs an empty message, which differs from typical HMAC usage
|
||||||
|
if push_config.get("FSSECRET"):
|
||||||
|
timestamp = str(int(time.time()))
|
||||||
|
string_to_sign = f'{timestamp}\n{push_config.get("FSSECRET")}'
|
||||||
|
hmac_code = hmac.new(
|
||||||
|
string_to_sign.encode("utf-8"), digestmod=hashlib.sha256
|
||||||
|
).digest()
|
||||||
|
sign = base64.b64encode(hmac_code).decode("utf-8")
|
||||||
|
data["timestamp"] = timestamp
|
||||||
|
data["sign"] = sign
|
||||||
|
|
||||||
response = requests.post(url, data=json.dumps(data)).json()
|
response = requests.post(url, data=json.dumps(data)).json()
|
||||||
|
|
||||||
if response.get("StatusCode") == 0 or response.get("code") == 0:
|
if response.get("StatusCode") == 0 or response.get("code") == 0:
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,7 @@
|
||||||
"消息接收人": "message recipient",
|
"消息接收人": "message recipient",
|
||||||
"调用版本;专业版填写pro,个人版填写personal,为空默认使用专业版": "Version, you can specify 'pro' for the Professional version and 'personal' for the Personal version. If left blank, it will default to the Professional version.",
|
"调用版本;专业版填写pro,个人版填写personal,为空默认使用专业版": "Version, you can specify 'pro' for the Professional version and 'personal' for the Personal version. If left blank, it will default to the Professional version.",
|
||||||
"飞书群组机器人:https://www.feishu.cn/hc/zh-CN/articles/360024984973": "Feishu group bot: https://www.feishu.cn/hc/zh-CN/articles/360024984973",
|
"飞书群组机器人:https://www.feishu.cn/hc/zh-CN/articles/360024984973": "Feishu group bot: https://www.feishu.cn/hc/zh-CN/articles/360024984973",
|
||||||
|
"飞书群组机器人加签密钥,安全设置中开启签名校验后获得": "Feishu group bot signature secret, obtained after enabling signature verification in security settings",
|
||||||
"邮箱服务名称,比如126、163、Gmail、QQ等,支持列表https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json": "Email service name, e.g., 126, 163, Gmail, QQ, etc. Supported list: https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json",
|
"邮箱服务名称,比如126、163、Gmail、QQ等,支持列表https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json": "Email service name, e.g., 126, 163, Gmail, QQ, etc. Supported list: https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json",
|
||||||
"邮箱地址": "Email Address",
|
"邮箱地址": "Email Address",
|
||||||
"SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定": "The SMTP login password may also be a special passphrase, depending on the specific email service provider's instructions",
|
"SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定": "The SMTP login password may also be a special passphrase, depending on the specific email service provider's instructions",
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,7 @@
|
||||||
"消息接收人": "消息接收人",
|
"消息接收人": "消息接收人",
|
||||||
"调用版本;专业版填写pro,个人版填写personal,为空默认使用专业版": "调用版本;专业版填写pro,个人版填写personal,为空默认使用专业版",
|
"调用版本;专业版填写pro,个人版填写personal,为空默认使用专业版": "调用版本;专业版填写pro,个人版填写personal,为空默认使用专业版",
|
||||||
"飞书群组机器人:https://www.feishu.cn/hc/zh-CN/articles/360024984973": "飞书群组机器人:https://www.feishu.cn/hc/zh-CN/articles/360024984973",
|
"飞书群组机器人:https://www.feishu.cn/hc/zh-CN/articles/360024984973": "飞书群组机器人:https://www.feishu.cn/hc/zh-CN/articles/360024984973",
|
||||||
|
"飞书群组机器人加签密钥,安全设置中开启签名校验后获得": "飞书群组机器人加签密钥,安全设置中开启签名校验后获得",
|
||||||
"邮箱服务名称,比如126、163、Gmail、QQ等,支持列表https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json": "邮箱服务名称,比如126、163、Gmail、QQ等,支持列表https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json",
|
"邮箱服务名称,比如126、163、Gmail、QQ等,支持列表https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json": "邮箱服务名称,比如126、163、Gmail、QQ等,支持列表https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json",
|
||||||
"邮箱地址": "邮箱地址",
|
"邮箱地址": "邮箱地址",
|
||||||
"SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定": "SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定",
|
"SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定": "SMTP 登录密码,也可能为特殊口令,视具体邮件服务商说明而定",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,12 @@ export default {
|
||||||
),
|
),
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'larkSecret',
|
||||||
|
tip: intl.get(
|
||||||
|
'飞书群组机器人加签密钥,安全设置中开启签名校验后获得',
|
||||||
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
email: [
|
email: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user