feat(#3): 飞书消息发送 - Webhook签名校验+项目通知/里程碑提醒/风险预警/决策请求
This commit is contained in:
102
src/server/feishu.ts
Normal file
102
src/server/feishu.ts
Normal file
@@ -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<string, unknown> = {
|
||||
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<void> {
|
||||
await sendFeishuMessage({
|
||||
text: `🚀 新项目已创建\n\n项目:${projectName}\n目标:${goal}\n\n请及时查看并确认项目章程。`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送里程碑提醒
|
||||
*/
|
||||
export async function notifyMilestoneReminder(milestoneName: string, targetDate: string): Promise<void> {
|
||||
await sendFeishuMessage({
|
||||
text: `⏰ 里程碑提醒\n\n里程碑「${milestoneName}」即将到期\n目标日期:${targetDate}\n\n请确认进度是否正常。`,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送风险预警
|
||||
*/
|
||||
export async function notifyRiskAlert(riskDesc: string, priority: number): Promise<void> {
|
||||
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<void> {
|
||||
const optionList = options.map((o, i) => ` ${i + 1}. ${o}`).join('\n');
|
||||
await sendFeishuMessage({
|
||||
text: `🔑 需要您的决策\n\n${title}\n\n选项:\n${optionList}\n\n请回复选项编号。`,
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user