From 107a972647fc6a16248bdd6bc76e7f190a5447e2 Mon Sep 17 00:00:00 2001 From: xiaohei Date: Sat, 11 Apr 2026 18:37:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(#3):=20=E9=A3=9E=E4=B9=A6=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=8F=91=E9=80=81=20-=20Webhook=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C+=E9=A1=B9=E7=9B=AE=E9=80=9A=E7=9F=A5/?= =?UTF-8?q?=E9=87=8C=E7=A8=8B=E7=A2=91=E6=8F=90=E9=86=92/=E9=A3=8E?= =?UTF-8?q?=E9=99=A9=E9=A2=84=E8=AD=A6/=E5=86=B3=E7=AD=96=E8=AF=B7?= =?UTF-8?q?=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/feishu.ts | 102 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/server/feishu.ts diff --git a/src/server/feishu.ts b/src/server/feishu.ts new file mode 100644 index 0000000..d1e58d7 --- /dev/null +++ b/src/server/feishu.ts @@ -0,0 +1,102 @@ +/** + * 飞书消息发送模块 + * 通过飞书自定义机器人Webhook发送项目通知 + */ + +const FEISHU_WEBHOOK = 'https://open.feishu.cn/open-apis/bot/v2/hook/58321c74-5881-4f41-bcd4-85f4d7c5b3c1'; +const FEISHU_SECRET = 'UgCdzrcci4s9YS1GSAHt4e'; + +import { createHmac } from 'crypto'; + +/** + * 生成飞书签名 + */ +function generateSign(timestamp: number, secret: string): string { + const stringToSign = `${timestamp}\n${secret}`; + const hmac = createHmac('sha256', stringToSign); + return hmac.digest('base64'); +} + +export interface FeishuMessageOptions { + /** 消息内容 */ + text: string; + /** 是否使用签名校验 */ + sign?: boolean; +} + +/** + * 发送飞书文本消息 + */ +export async function sendFeishuMessage(options: FeishuMessageOptions): Promise<{ ok: boolean; code: number; msg: string }> { + const { text, sign = true } = options; + const timestamp = Math.floor(Date.now() / 1000); + + const body: Record = { + msg_type: 'text', + content: { text }, + }; + + if (sign) { + body.timestamp = String(timestamp); + body.sign = generateSign(timestamp, FEISHU_SECRET); + } + + try { + const response = await fetch(FEISHU_WEBHOOK, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + + const result = await response.json(); + return { + ok: result.code === 0 || result.StatusCode === 0, + code: result.code ?? result.StatusCode ?? -1, + msg: result.msg ?? result.StatusMessage ?? '', + }; + } catch (error) { + return { + ok: false, + code: -1, + msg: error instanceof Error ? error.message : 'Unknown error', + }; + } +} + +/** + * 发送项目创建通知 + */ +export async function notifyProjectCreated(projectName: string, goal: string): Promise { + await sendFeishuMessage({ + text: `🚀 新项目已创建\n\n项目:${projectName}\n目标:${goal}\n\n请及时查看并确认项目章程。`, + }); +} + +/** + * 发送里程碑提醒 + */ +export async function notifyMilestoneReminder(milestoneName: string, targetDate: string): Promise { + await sendFeishuMessage({ + text: `⏰ 里程碑提醒\n\n里程碑「${milestoneName}」即将到期\n目标日期:${targetDate}\n\n请确认进度是否正常。`, + }); +} + +/** + * 发送风险预警 + */ +export async function notifyRiskAlert(riskDesc: string, priority: number): Promise { + const level = priority >= 15 ? '🔴 高' : priority >= 10 ? '🟡 中' : '🟢 低'; + await sendFeishuMessage({ + text: `⚠️ 风险预警\n\n风险:${riskDesc}\n优先级:${level}(${priority}分)\n\n请评估并制定应对措施。`, + }); +} + +/** + * 发送决策请求 + */ +export async function notifyDecisionRequired(title: string, options: string[]): Promise { + const optionList = options.map((o, i) => ` ${i + 1}. ${o}`).join('\n'); + await sendFeishuMessage({ + text: `🔑 需要您的决策\n\n${title}\n\n选项:\n${optionList}\n\n请回复选项编号。`, + }); +}