通知

Discord / 飞书 Webhook 的接线与触发点

最近更新 2026 年 10 月 19 日


provider必要配置
discordDISCORD_WEBHOOK_URL,须以 https://discord.com/api/webhooks/ 开头
feishuFEISHU_WEBHOOK_URL,须以 https://open.feishu.cn/open-apis/bot/v2/hook/ 开头

URL 前缀由 Zod schema 强制校验。provider 必须与已配置的 webhook 一致——选了 feishu 却只配 DISCORD_WEBHOOK_URL 时,服务状态判定为 incomplete。

ts
notification: { enabled: true, provider: 'discord' },

触发点

事件位置
联系表单提交src/contact/notifications.ts
支付成功(订阅续费 / 一次性付款)src/routes/api/webhooks/stripe.ts

notify() 拿不到事件名到渠道的映射——调用方自己知道该走哪个 provider。这是有意的: 事件语义(「有人付款」)与投递渠道(Discord 还是飞书)是两个独立的关注点, 用一张事件→渠道表把它们绑死反而会让新增渠道时改一堆无关代码。

错误处理

notify() 在 provider 未注册、投递失败或抛错时都只写 console.warn 并返回 false——通知失败不阻塞业务流程。

两处 provider 细节值得注意:

  • 飞书可能返回 HTTP 200 但业务失败。签名 / 关键词 / IP 白名单校验不通过时,

响应体是 { code: 19021, msg: … },HTTP 状态仍是 200。只看 res.ok 会把失败 当成功、notify() 也不会告警。因此飞书 provider 会解析 code,非 0 即失败。

  • 请求带 5 秒超时(AbortSignal.timeout)。挂住的 webhook 会拖住整个请求,

而通知对业务并不关键,不应让一条通知把一次查询搞超时。

Discord 的错误体现在 HTTP 状态上(429 限流、401 失效、400 格式错误),因此不需要 解析响应体。

Discord embed 有长度上限:标题截断至 200 字符、正文 1000 字符。截断在调用方 做(contact 模块),provider 层只负责投递。

参考资料

下一步