Fred's Blog

四通八達

为 UptimeFlare 设置飞书通知

Posted on # 教程向
On this page

在这之前,我使用 Uptime Kuma 作为我的服务监控工具,并通过飞书推送状态信息。但一个搞笑的事实是:我只有一台服务器,服务要么在这台服务器上,要么在第三方平台上。如果在我唯一的服务器上再跑一个状态监控,显然多此一举。因此我切换到了基于 CloudFlare Workers 的监控程序 UptimeFlare。不过与 Uptime Kuma 不同,UptimeFlare 仅支持手动配置 Webhook,并且在 Wiki 中没有提供适用于飞书的 Webhook 配置方法。经过一番探索之后,我找到了适合我的配置写法,并在这里分享出来。

写在前面

本文适用于已经创建好飞书 Webhook 机器人的读者。

另外,UptimeFlare 原生支持通过 workerConfig.notification.webhook 配置项把状态通知推送到任意 Webhook,其中 $MSG 占位符会被替换为可读的状态文本。最简单的飞书配置是这样的:

notification: {
  webhook: {
    url: 'https://open.feishu.cn/open-apis/bot/v2/hook/xxx',
    method: 'POST',
    payloadType: 'x-www-form-urlencoded',
    payload: {
      msg_type: "text",
      content: { text: "$MSG" }
    },
  },
  timeZone: 'Asia/Shanghai',
  gracePeriod: 5,
}

这个方案虽然可行,但通过这段配置所发出的消息类型是「纯文本消息」。在飞书开发文档中,给出了 5 种支持发送的消息类型,可以丰富机器人消息的内容。为了使发出的信息更加直观,使用户能够一眼看出是宕机还是恢复,我选择了「飞书卡片」消息样式进行更丰富的自定义。

卡片消息

飞书自定义机器人发送卡片消息时,请求体格式为:

{
  "msg_type": "interactive",
  "card": {
    "header": {
      "title": { "tag": "plain_text", "content": "标题" },
      "template": "green"
    },
    "elements": [
      {
        "tag": "div",
        "text": { "tag": "lark_md", "content": "**Markdown** 内容" }
      },
      { "tag": "hr" },
      {
        "tag": "note",
        "elements": [{ "tag": "plain_text", "content": "底部说明" }]
      }
    ]
  }
}

在配置文件中,我们可以使用 workerConfig.callbacks.onStatusChange 来自定义更多细节。这里直接给出 uptime.config.ts 相关配置写法:

const FEISHU_WEBHOOK_URL = 'https://open.feishu.cn/open-apis/bot/v2/hook/xxxxxx'  // 你的自定义机器人  Webhook 地址

const workerConfig: WorkerConfig = {
  monitors: [ /* 你的监控列表 */ ],

  callbacks: {
    onStatusChange: async (
      env, monitor, isUp,
      timeIncidentStart, timeNow, reason
    ) => {
      const statusText = isUp ? '服务已恢复' : '服务宕机'
      const template = isUp ? 'green' : 'red'
      const reasonText = reason || '未获取到详细信息'

      const formattedTime = new Date(timeNow * 1000).toLocaleString('zh-CN', {
        timeZone: 'Asia/Shanghai',
        year: 'numeric', month: '2-digit', day: '2-digit',
        hour: '2-digit', minute: '2-digit', second: '2-digit',
      })

      const cardPayload = {
        msg_type: 'interactive',
        card: {
          header: {
            title: { tag: 'plain_text', content: `${statusText}:${monitor.name}` },
            template,
          },
          elements: [
            {
              tag: 'div',
              text: {
                tag: 'lark_md',
                content: `**监控对象**:${monitor.target}\n**详细信息**:${reasonText}`,
              },
            },
            { tag: 'hr' },
            {
              tag: 'note',
              elements: [{ tag: 'plain_text', content: `${formattedTime}` }],
            },
          ],
        },
      }

      try {
        const resp = await fetch(FEISHU_WEBHOOK_URL, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(cardPayload),
        })
        if (!resp.ok) {
          console.error(`通知发送失败: HTTP ${resp.status}`, await resp.text())
        }
      } catch (err) {
        console.error('请求 Webhook 异常:', err)
      }
    },
    onIncident: async () => {},
  },
}

需要注意的是,当你设置好 callbacks.onStatusChange 后,请删除 notification.webhook 配置,否则你将会收到重复通知。

配置完成后,你可以临时破坏一个监控项(如把 target 改成一个不存在的域名)并部署,等待一个检测周期,应收到由机器人发出的卡片信息,含有当前这个监控项的状态信息。

参考资料

Comment