使用 Worker 实现发版通知

目前的站点发版是由开发人员自己操作,运营人员通常需要等待开发人员完成发版后才知道进行了发版。

这就导致了如果开发人员在发版后如果忘记了通知运营人员,运营人员就无法知道网站进行了发版。为了解决这个问题,我们使用了 webhook 来实现发版通知。

当我们进行网站发版的时候,会触发一个 webhook, 会收集信息调用机器人发送到工作群中。

目前,我们工作使用的通讯工具是企业微信,所以我们使用企业微信机器人来实现发版通知。

首先需要在企业微信中创建一个机器人,然后获取到机器人的 webhook 地址。

然后我们使用 worker 来实现发版通知。

因为公司使用的是 CloudFlare 作为部署平台,所以我们使用 CloudFlareWorker 来实现发版通知。

具体操作可以查看 CloudFlare Worker 文档

可以通过 开始使用 - 命令行 这一节来进行简单的入门。

使用 npm create cloudflare@latest 命令创建一个 Worker 项目,然后使用 npx wrangler dev 运行项目。

src/index.jssrc/index.ts 中开始编写我们的代码。

先定义调用机器人的方法:

/**
 * 发送信息
 * @param url 机器人回调地址
 * @param content 通知信息内容
 * @returns 回调响应体
 */
const sendMessage = (url: string, content: string) => {
  return fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      msgtype: 'markdown',
      markdown: {
        content,
      },
    }),
  })
}

因为使用的是 TS 编写的代码,所以我们需要定义一下 Env 接口:

export interface Env {
  // 机器人密钥
  BOT_KEY: string
  // 主分支名称
  MAIN_BRANCH: string
  // github token
  GITHUB_TOKEN: string
  // 人名映射, 因为 github 用户名和真实人名不一致
  USER_NAME_MAP: Record<string, string>
}

接着我们导出一个 Worker 对象:

export default {
  async fetch(request, env ctx): Promise<Response> {
    // 我们的通知逻辑都将在这里编写
  }
}

我们的仓库都是存储在 github 上,因此我们要使用到 github 提供的 webhook, 可参阅 官方文档

目前,只需要用到 push 事件,因此我们只需要关注 push 事件的请求体即可。可查阅 push说明文档

因为需要 push 事件传递给 webhook 的数据中不存在是否有 PR 信息。因此我们需要调用到 github 提供的 api,这又需要用到 github 提供的 token。我们通过访问 网页 进行创建。记得为令牌赋予 Pull requestsContents 权限(只读即可)。

如果是公开仓库,那么就不需要使用到 token 了。

具体思路如下:

  • 首先判断是否是发布分支
  • 然后判断是否是 push 事件
  • 再判断是否由 PR 而来
  • 如果是 PR 而来,那么需要获取 PR 的信息
  • 接着,通过提取数据,获取到相关信息
  • 最后,调用机器人发送消息

具体代码如下:

if (gitEvent === 'push' && data.ref === `refs/heads/${env.MAIN_BRANCH}`) {
  // 推送到主分支
  const commits = data.commits

  const lastCommit = commits[commits.length - 1]
  const message = lastCommit?.message ?? '未知提交'
  const num = message.match(/^Merge pull request #(\d+) from .+/)?.[1]

  console.clear()
  console.log(data)

  const name = data.repository.name
  const description = data.repository.description
  // const repo_url = data.repository.html_url
  const push_at = new Date(data.repository.updated_at).toLocaleString('zh-CN', {
    timeZone: 'Asia/Shanghai',
    /** UTC+8 */ hour12: false,
  })
  const pusher = data.sender.id || data.pusher.name

  await new Promise((resolve, reject) => {
    if (num) {
      const queryPullRequestDescriptionUrl = data.repository.pulls_url.replace('{/number}', `/${num}`)
      fetch(queryPullRequestDescriptionUrl, {
        method: 'GET',
        headers: {
          Authorization: `Bearer ${env.GITHUB_TOKEN}`,
          Accept: 'application/vnd.github.v3+json',
          'X-GitHub-Api-Version': '2022-11-28',
          'User-Agent': 'CloudFlare Worker',
        },
      })
        .then((response) => response.json())
        .then(async (data) => {
          const {
            title,
            body,
            user: { id: creater },
            merged_by: { id: operator },
          } = data as Record<string, any>
          resolve(
            [
              '# 发版通知',
              `> 项目仓库:${name}`,
              `> 项目说明:${description}`,
              `> 发版标题:${title}`,
              `> 创建者:${env.USER_NAME_MAP[`${creater}`] || 'ID' + creater}`,
              `> 操作者:${env.USER_NAME_MAP[`${operator}`] || 'ID' + operator}`,
              `> 发版时间: ${push_at}`,
              '',
              ...body.split('\r\n'),
            ].join('\n'),
          )
        })
    } else {
      resolve(
        [
          '# 发版通知',
          `> 项目仓库:${name}`,
          `> 项目说明:${description}`,
          `> 创建者:${env.USER_NAME_MAP[`${pusher}`] || pusher}`,
          `> 操作者:${env.USER_NAME_MAP[`${pusher}`] || pusher}`,
          `> 发版时间: ${push_at}`,
          '',
          ...message.split('\r\n'),
        ].join('\n'),
      )
    }
  })
    .then((content) => {
      console.log({ content })
      if (typeof content === 'string') {
        return sendMessage(baseUrl, content)
      }
    })
    .then((response) => response?.json())
    .then((data) => {
      console.log({ data })
    })
}

使用了 Markdown 格式的信息,这样格式会更好看一些。

如果需要进行测试的话,可以查看 官方给出的教程

在完成以上代码编写和测试之后,我们可以通过 npx wrangler deploy 命令来将代码部署到 CloudFlare 上。

这样,我们就可以通过 webhook 来实现发版通知了。


使用 Worker 实现发版通知
http://localhost:8080/archives/untitled-post
作者
inksha
发布于
2025年02月22日
许可协议