本篇文章聊聊轻量的网络钩子(WebHook)工具:歪脖虎克。

写在前面

这是一篇迟到很久的文章,在 21 年和 22 年的时候,我分享过两篇关于轻量的计划任务工具 Cronicle 的文章:《轻量的定时任务工具 Cronicle:前篇》、《使用 Docker 和 Traefik 搭建轻量美观的计划任务工具》。随后在文章的评论区和聊天群里,以及一些关联的开源项目 issue 区里,我总是能够收到关于最佳使用实践的问题。

开源项目 WebHook 的新版本:歪脖虎克

我一般都会推荐将 Cron 工具和 WebHook 工具结合使用,说到具体的 WebHook 的时候,我当时一般都推荐“GitHub 上我 fork 的那个开源软件”:soulteary/webhook

自 2020 年,项目就陷入停滞状态

四年前,在我 fork 的软件仓库几乎不在更新后,原始项目的技术债务堆积越来越严重。

最近正好在折腾异步流程比较多的模型应用,正好把这个项目也顺带做一个翻新(开源分叉)。关于项目具体分叉的原因,我会在文末再做详细展开。

好啦,让我们开始折腾。

前置知识

鉴于文章的读者可能是新用户,那么让我们从前置知识的展开开始吧。

什么是 WebHook

WebHook 是一种常见的网络软件功能,通常用于连接异步的处理过程。直接介绍 WebHook 是什么会比较无趣,我们不妨参考 zapier 的 “WebHook 和 API 的异同” 来更直观的了解下 WebHook 的工作模式。

WebHook 和 API 的异同

相比较我们熟悉的 API 调用,尽管他们都能够将数据从一个地方发送到另外一个地方,完成数据的交互,但是通常 WebHook 都是异步、单向的,不强制要求即刻返回数据结果。

通常情况下,支持 WebHook 的功能的软件中能够定义一些自定义回调函数(Custom Callback Function),来让其他的外部程序在合适的时候调用这些函数,让一些被预先定义好的程序被调用,特别适合一些场景:

  • 比如,常见的耗时比较久的程序执行完毕,推送信息到用户,可以回调一个通知接口。
  • 比如,常见的多次跳转的支付过程,当用户扫码授权后,银行接口会回调我们购买商品的平台,告诉平台用户付款了。

歪脖虎克(soulteary/WebHook)是什么

歪脖虎克(WebHook)是一个用 Go 语言编写的轻量可配置的实用工具,它允许你轻松、快速的创建 HTTP 服务(钩子)。

“歪脖虎克”

你可以使用它来执行配置好的命令。并且还能够将 HTTP 请求中的数据(如请求头内容、请求体以及请求参数)灵活的传递给你配置好的命令、程序。当然,它也允许根据具体的条件规则来便触发钩子。

举个例子,如果你使用的是 GitHub 或 Gitea,可以使用歪脖虎克设置一个钩子,在每次你推送更改到项目的某个分支时,这个钩子会在你运行服务的设备上运行一个“更新程序部署内容”的脚本。

如果你使用飞书、钉钉、企业微信或者 Slack,你也可以设置一个“传出 Webhook 集成”或“斜杠命令”,来在你的服务器上运行各种命令。我们可以通过聊天工具的“传入 Webhook 集成”功能处理接口的响应内容,直接向你或你的 IM 会话或频道报告执行结果。

歪脖虎克(WebHook)的项目目标非常简单,只做它应该做的事情

  • 接收请求
  • 解析请求头、请求体和请求参数
  • 检查钩子指定的运行规则是否得到满足
  • 最后,通过命令行参数或环境变量将指定的参数传递给指定的命令

至于具体的命令,从处理数据、存储数据到用远程命令打开空调、关闭电脑,一些都由你做主,你可以实现任何你想要的事情,它只负责在合适的时间点,接受执行指令。

准备工作

开始实战之前,需要先获取程序文件。

下载“歪脖虎克”程序

目前下载程序有两个方法,下载在 GitHub 上通过自动化构建工具构建完毕的内容。

GitHub 程序发布页面

或者,使用 Docker 来快速下载程序。

# 下载最新的版本
docker pull soulteary/webhook:latest
# 下载指定版本的镜像
docker pull soulteary/webhook:3.4.5
# 下载包含了常用工具的镜像
docker pull soulteary/webhook:extend-3.4.5

当我们完成程序下载后,就可以开始使用啦。

查看软件文档(可选)

你可以在开源代码仓库的 docs 目录找到软件的中文使用文档,里面记录了:钩子的定义方法、具体的配置参数、如何解析和对请求参数进行调用命令参数绑定、各种常见的调用示例方法。

接下来,我们以一个简单的日常场景进行配置说明:使用 WebHook 和聊天工具进行联动,推送通知信息。

设置飞书的 WebHook 功能

其实我们日常使用的各种聊天工具,基本都支持使用 WebHook 方式进行交互。

嘿,你好呀👋

我这里以飞书为例,来实现一个能够从外部调用飞书 WebHook 发送消息的机器人。

和其他的聊天软件类似,在飞书里创建机器人的最简方案是先创建一个“聊天群组”。

创建一个飞书群

创建完毕后,戳开右上角的下拉菜单,找到“设置”选项。

找到设置菜单

进入设置选项后,能够看到群组的详细设置。

找到“机器人”菜单

点击“机器人”设置,能够看到一个新的弹出框,包含了许多不同类型的机器人。

添加一个“WebHook”机器人

这里,我们选择自定义机器人(WebHook)。

在机器人配置界面

在机器人配置界面中进行详细的自定义配置,比如可以设置个好看的头像,或者起个有趣的名字。

获得 WebHook 调用接口

点击确定,我们就能够得到 WebHook 程序调用所需要的接口地址啦。保存好地址,我们一会要用到。

获得 WebHook 调用接口

点击完成,机器人就被添加到群组中啦。

添加一个“WebHook”机器人

到这里为止,飞书的配置就完啦。

我们可以使用下面的命令,来验证接口是否能够被正确调用。

curl -X POST -H "Content-Type: application/json" --data '{"msg_type":"text","content":{"text":"嘿,你好呀👋"}}' \
    https://open.feishu.cn/open-apis/bot/v2/hook/6dca9854-381a-4bb9-a87b-33a222833e04

调用完毕,会出现类似下面的返回结果:

{"StatusCode":0,"StatusMessage":"success","code":0,"data":{},"msg":"success"}

在飞书的界面中,我们能够看到新出现的消息内容。

嘿,你好呀👋

接下来,我们来把“歪脖虎克”和飞书连接在一起,让程序内容能够动态化。

实战开始

在本文中,歪脖虎克可以将上面我们调用飞书 WebHook 接口的命令,赋予动态化调用的能力,并且提供更简单的调用接口,甚至提供额外的“接口验证”、关联的自动化处理信息能力。比如,当程序被调用的时候,能够根据具体的输入信息,结合上下文相关的资料,调用大模型接口,发送一段有趣的故事到群里。

考虑到复现的方便,我使用 Docker 环境的 “歪脖虎克”,直接使用二进制文件类似,只需要调整命令,在次不做赘述。

编写调用程序

我们先实现一个简单的程序,能够接收来自环境变量中的参数 $TEXT,并将参数中的内容传递到上面的飞书调用命令中(soulteary/webhook/example/lark/send-lark-message.sh):

#!/bin/sh

if [ -n "$TEXT" ]; then
    curl -X POST -H "Content-Type: application/json" --data {\"msg_type\":\"text\",\"content\":{\"text\":\"$TEXT\"}} \
        https://open.feishu.cn/open-apis/bot/v2/hook/6dca9854-381a-4bb9-a87b-33a222833e04
    echo "Send message successfully".
else
  echo "TEXT is empty"
fi

将上面的程序保存为 send-lark-message.sh (替换为你自己的飞书 API 地址)。然后我们对这个脚本赋予可执行权限:

chmod +x send-lark-message.sh

这里,我们也可以将上面的脚本程序替换成某些具备特定功能的程序,比如进行数据处理的程序,查询或者导出数据库文件的程序,调用大模型的能力等等。

编写配置文件

接下来,我们来编写一个让“歪脖虎克”来调用这个脚本的程序(soulteary/webhook/example/lark/hook-lark.yaml):

- id: lark
  execute-command: ./send-lark-message.sh
  command-working-directory: /app
  include-command-output-in-response: true
  include-command-out-in-response-on-error: true
  pass-environment-to-command:
    - source: url
      name: text
      envname: TEXT

我们将上面的内容保存为 hook-lark.yaml,“歪脖虎克” 的基本配置就准备完毕啦。

在上面的程序配置中,我们做了两件事,分别是:

  • 创建一个用于触发远程接口(飞书API)的脚本程序 send-lark-message.sh,能够在用户设置环境变量 TEXT 的时候,将环境变量内容传递到飞书发送消息参数中。
  • 创建了一个 id` 是 `lark 的 WebHook 配置,当 WebHook 被调用时,会自动调用位于 /app 目录的 send-lark-message.sh 脚本程序,将 URL 请求参数中的 text 内容转换为环境变量,传递给脚本程序,然后将脚本程序的执行结果展示给调用的用户。

编写容器配置文件或直接调用程序

为了稳定复现,我们再编写一个 docker-compose.yml 配置文件:

version: '2'

services:

  webhook:
    image: soulteary/webhook:extend-3.4.5
    ports:
      - 9000:9000
    environment:
      HOST: "0.0.0.0"
      PORT: 9000
      VERBOSE: true
      HOOKS: "/app/hook-lark.yaml"
    volumes:
      - ./hook-lark.yaml:/app/hook-lark.yaml
      - ./send-lark-message.sh:/app/send-lark-message.sh

保存这个配置文件和上面的两个配置到相同的目录后,我们执行 docker compose up,程序将运行在本机的 9000 端口。

如果你想直接运行程序,可以使用下面的命令:

./webhook --hooks ./example/lark/hook-lark.yaml --verbose 

一切顺利,你将看到类似下面的日志内容:

lark-webhook-1  | [webhook] 2024/04/06 07:52:11 version [3.4.5] starting
lark-webhook-1  | [webhook] 2024/04/06 07:52:11 setting up os signal watcher
lark-webhook-1  | [webhook] 2024/04/06 07:52:11 attempting to load hooks from /app/hook-lark.yaml
lark-webhook-1  | [webhook] 2024/04/06 07:52:11 os signal watcher ready
lark-webhook-1  | [webhook] 2024/04/06 07:52:11 found 1 hook(s) in file
lark-webhook-1  | [webhook] 2024/04/06 07:52:11 	loaded: lark
lark-webhook-1  | [webhook] 2024/04/06 07:52:11 serving hooks on http://0.0.0.0:9000/hooks/{id}

我们打开浏览器,访问 http://0.0.0.0:9000/hooks/lark?text=hey,将会很快得到下面的内容(命令行执行返回):

浏览器访问结果示意

当我们再次打开飞书的界面,能够看到消息已经发送到飞书中啦。当然,这个工具并非只能连接一个 API ,你可以通过它将一堆服务都关联起来,进行一些自动化操作,或者做一些需要定时、被动触发自动化处理的工作。

飞书中出现的“歪脖虎克”发送的消息

还记得之前文章提到的实现“计划任务”的 Cronicle 小工具吗?你可以通过类似这样的方法,将计划任务中的具体实现,都使用“歪脖虎克”来实现,而将任务调度和编排交给 Cronicle,这样的话,你可以分别维护不同的程序,甚至在合适的情况下,灵活的切换程序为其他的组件。

其他:为什么要进行开源分叉

在项目的文档中,我提到了主要有两个原因:

第一个是,原作者维护的 webhook 程序版本,是从比较陈旧的 Go 程序版本慢慢升级上来的。

其中,包含了许多不再被需要的内容,以及非常多的安全问题亟待修正。

第二个是,我在几年前曾经提交过一个改进版本的 PR,但是因为种种原因被作者忽略,与其继续使用明知道不可靠的程序,不如将它变的可靠。

这样,除了更容易从社区合并未被原始仓库作者合并的社区功能外,还可以快速对有安全风险的依赖作更新。除此之外,我希望这个程序接下来能够中文更加友好,包括文档。

开源项目贡献

当然,开源软件世界里,talk is cheap, code is everything.,目前看来,这个硬分叉项目应该是及格的吧 :D

最后

好啦,这篇文章就先写到这里,后面相关的文章中,我会试着分享一些更有趣的具体(偷懒)实践。

下篇文章再见。

–EOF