Skip to content
Junjie edited this page May 6, 2024 · 1 revision

Welcome to the feishu-bot-webhook-action wiki!

边学边做,法力无边

飞书提供的官方github机器人是需要在企业版中,个人版本飞书下如何使用?利用飞书自定义机器人的webhook来实现一下对github仓库的信息变更的监控,同时利用github action的能力,省下对服务器的需求。

自定义机器人

开放平台有官方文档自定义机器人使用指南

这里参考的操作为:

  1. 建立一个群组,在设置-> 群机器人 -> 添加机器人 -> 自定义机器人
  2. 点击自定义机器人,设置中获取它的 webhook地址

有了这个地址就可以在其他平台post消息到这个地址,实现发送任意信息。

curl -X POST -H "Content-Type: application/json" \
    -d '{"msg_type":"text","content":{"text":"request example"}}' \
    https://open.feishu.cn/open-apis/bot/v2/hook/****

到这里就可以在飞书群中观察到消息了。

Github action

github action 是可以实现CI/CD等各种workflow的,这里利用action来实现监控所有的事件。 比如监控 push, star 事件可以用以下的写法:

on:
  push:
    branch: [ "main" ]
  watch:
    types: [started]

这里是所有的 github 事件 github webhook events

Github toolkit

由于是希望实现将github的event都发送到飞书中,直接在仓库的设置的webhook中填入机器人的地址是没法看到群内消息出现我们希望的内容的,因为github的消息格式不符合飞书的定义。所以我们需要做一下转换。

为了更加灵活,我们可以定制一个action,而不是直接在workflow的yaml文件中用curl命令来实现消息发送。

创建一个action 参考这里,用js方案来实现比较合适。 工程参考 https://github.com/actions/typescript-action

定义元数据

name: 'Github Events To Feishu Bot Action'
description: 'send message to feishu bot'
author: 'junka'
branding:
  icon: 'cloud-snow'
  color: 'blue'
inputs:
  webhook:
    description: 'feishu bot webhook url'
    required: true
  signkey:
    description: 'set when you enable signature verify'
    required: false
outputs:
  code:
    description: 'code from response'
runs:
  using: 'node20'
  main: 'dist/index.js'

其中input是我们需要配置的信息,通常是配置在secrects中,安装包依赖

npm install @actions/core
npm install @actions/github

我选择使用typescript,(前端门外汉,js/ts随便选了个)。在index.ts 中实现主要逻辑

import * as https from "https";
import * as core from "@actions/core";
import { context } from "@actions/github";

function PostToFeishu(id: string, content: string) {
    var options = {
        hostname: "open.feishu.cn",
        port: 443,
        path: `/open-apis/bot/v2/hook/${id}`,
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
    };
    var req = https.request(options, (res) => {
        res.on("data", (d) => {
            process.stdout.write(d);
        });
    });
    req.on("error", (e) => {
        console.error(e);
    });
    req.write(content);
    req.end();
}

function PostGithubEvent() {
    const webhook = core.getInput("webhook")
        ? core.getInput("webhook")
        : "https://open.feishu.cn/open-apis/bot/v2/hook/xxxx";//替换成你的机器人地址

    const payload = context.payload || {}
    console.log(payload)

    const msg = `{"msg_type":"text","content":{"text":"msg lai le "}}'
    PostToFeishu(webhookId, msg);
}

PostGithubEvent();

然后package.json 中配置编译和运行测试命令

"scripts": {
    "build": "tsc && ncc build dist/index.js",
    "run": "node ./dist/index.js",

ncc指令的意思和其他依赖参看创建一个action 然后在命令行中运行就可以看到消息了。

npm run build
npm run run

消息签名

到这里的消息是没有签名的,建议开启,在机器人设置界面,可以开启后拷贝出来密钥。 在 https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot 这里有java/go/python的例子。 摸索了一下,用ts代码实现签名

import * as crypto from "crypto";
function sign_with_timestamp(timestamp: number, key: string): string {
    const toencstr = `${timestamp}\n${key}`;
    const signature = crypto.createHmac("SHA256", toencstr).digest("base64");
    return signature;
}
...
function PostGithubEvent() {
...
    const signKey = core.getInput("signkey") ? core.getInput("signkey") : "Xbxxxxxxxx";// 替换成你的key
    const tm = Math.floor(Date.now() / 1000);//注意时间戳单位s
    const sign = sign_with_timestamp(tm, signKey);
    const msg = `{
        "timestamp": "${tm}",
        "sign": "${sign}",
        "msg_type": "text",
        "content":{"text":"msg lai le "}}`
    PostToFeishu(webhookId, msg);
}

必须添加timestamp,sign两个字段,到这里就完成签名校验部分

卡片消息

到这里为止实际上我并没有发送真实有价值的event数据,因为本地不太好调试,我们可以在组织完成消息后再测试。

飞书提供了卡片消息,实现更美观的消息, 使用飞书卡片搭建工具来构建一个消息模板,比如下面的格式

image.png

其中有些字段是绑定的变量,发布卡片后可以用卡片id来包裹到我们的消息中去

    const color = "blue";
    const msg = `{
        "timestamp": "${tm}",
        "sign": "${sign}",
        "msg_type": "interactive",
        "card": {
            "type": "template",
            "data": {
                "template_id": "AAqkaaaaaypMLb",
                "template_version_name": "1.0.6",
                "template_variable": {
                    "repo": "${repo}",
                    "eventType": "${eventType}",
                    "themeColor": "${color}",
                    "auser": "${actor}",
                    "avatar": "${avatar}",
                    "status": "${status}",
                    "etitle": "${etitle}",
                    "detailurl": "${detailurl}"
                }
            }
        }
    }`
    PostToFeishu(webhookId, msg);

template_variable 的key就是我们飞书卡片配置好的变量,value就是我们可以从github包中获取的。 比如对push事件

import { context } from "@actions/github";

etitle = `Commits: ["${context.payload["head_commit"]["id"]}"]("${context.payload["compare"]}")`;
status = context.payload["created"] == true ? "created": (context.payload["forced"] == true? "force updated" : "");
detailurl = context.payload["compare"];

获取了commit号和compare页面信息,填入到对应的字段,后续点击查看详情就能跳转

image.png

之后提交代码,release的时可以同步发布到marketplace就可以在其他项目中使用。

估计其他机器人也差不多?由于自定义机器人不支持图片上传,虽然文档写支持,但其实需要一个带token的机器人应用才可以,所以没有实现avatar上传展示。后续考虑开发一个机器人应用而非使用自定义机器人。

代码实现参看,未完工 https://github.com/junka/feishu-bot-webhook-action

回顾小结

  • 学习了github action的使用
  • 学习了飞书机器人的配置,webhook的含义,签名的代码实现
  • 学习了ts开发github action的流程,发布过程
  • 学习了github toolkit开发包的使用
  • 学习了下飞书机器人卡片消息的配置使用
Clone this wiki locally