Files
napcat/README.md

8.4 KiB
Raw Permalink Blame History

napcat

对接 NapCatQQ(OneBot 11 协议)的 Go 软件包,零第三方依赖(仅 gorilla/websocket)。

bot := napcat.New().SetToken("xxx").SetForwardWS("ws://127.0.0.1:3001")
bot.OnGroupMessage(func(c *napcat.Context) {
    if c.HasAtMe() {
        c.ReplyText("hi")
    }
})
bot.Run(ctx)

特性

  • 一个对象搞定:New() 创建与 NapCat 交互的客户端对象,配置、事件、API 全在对象上
  • 四种通信方式,链式配置、可任意叠加:
    • 正向 WebSocket(自动重连 + 指数退避)
    • 反向 WebSocket(支持多 bot 实例同时连入)
    • HTTP API
    • HTTP POST 上报(自动支持 OneBot 快速操作)
  • 事件模型:message / message_sent / notice / request / meta_event 全量结构化, notice 未知字段不丢失(收进 Extra)
  • 消息段:全类型构造函数;收到的 CQ 码字符串与段数组统一解码
  • API 覆盖:OneBot 11 标准 + go-cqhttp 兼容 + NapCat 扩展共 80+ 个类型化方法, 未收录的扩展可用 Call 裸调
  • echo 路由:WS 上的 action 请求/响应按 echo 精确配对,支持并发调用
  • 连接级路由:事件处理函数里的 API 调用自动走产生该事件的连接,多 bot 互不串线
  • 内置文本命令路由、请求 Approve/Reject、心跳记录、panic 隔离

目录结构

napcat/
├── doc.go           包文档(pkg.go.dev 首页)
├── client.go        Client:配置方法、连接汇聚、事件分发、Handler 注册
├── context.go       Context:处理函数上下文(Reply/Approve/快速操作)
├── api.go           API:80+ 类型化 API 方法
├── caller.go        Caller 接口 + WS echo 路由 + CallAPI 泛型
├── connect_ws.go    正向/反向 WebSocket 连接器 + Session
├── connect_http.go  HTTP 上报(快速操作)+ HTTP API 连接器
├── event.go         事件结构(message/notice/request/meta)
├── segment.go       消息段构造与解码
├── cqcode.go        CQ 码解析/编码
├── types.go         响应信封与实体类型
├── example_test.go  用法示例(godoc Examples)
└── *_test.go        单元测试(内置 mock NapCat)

安装

go get gitea.doicat.com/bot/napcat

使用指定版本:

go get gitea.doicat.com/bot/napcat@v1.0.0

私有仓库使用者:如果仓库设为私有,除了给 Git 配置 Gitea 凭据外,还需设置:

set GOPRIVATE=gitea.doicat.com

这样 go 命令才会走 Git 凭据拉取而不是公共校验和数据库。

快速开始

1. NapCat 侧配置

在 NapCat WebUI 的「网络配置」里按需启用一项(或多项):

配置方法 NapCat 侧配置
SetForwardWS("ws://127.0.0.1:3001") 启用「WebSocket 服务器」,端口 3001
SetReverseWS(":8080") 启用「WebSocket 客户端」,URL 填 ws://<本机IP>:8080/
SetHTTPAPI("http://127.0.0.1:3000") 启用「HTTP 服务器」,端口 3000
SetHTTPReport(":8081") 启用「HTTP POST 上报」,地址填 http://<本机IP>:8081/

若配置了 access_token,SetToken 填同一个值即可,框架会在 WS 握手 (query + Authorization 头)和 HTTP 调用(Authorization 头)中自动携带; HTTP 上报场景若配了 secret,SetSecret 会自动校验 X-Signature(HMAC-SHA1)。

2. 完整示例

package main

import (
	"context"
	"os"
	"os/signal"

	"gitea.doicat.com/bot/napcat"
)

func main() {
	bot := napcat.New().
		SetToken("your-token").
		SetForwardWS("ws://127.0.0.1:3001")
	// 可叠加其他通信方式:
	// bot.SetReverseWS(":8080")
	// bot.SetHTTPAPI("http://127.0.0.1:3000")
	// bot.SetHTTPReport(":8081")

	// 文本命令
	bot.OnCommand("/ping", func(c *napcat.Context, args []string) {
		c.ReplyText("pong!")
	})

	// 群里 @机器人 就复读
	bot.OnGroupMessage(func(c *napcat.Context) {
		if c.HasAtMe() && !c.IsSelfMessage() {
			c.ReplyText("收到: " + c.PlainText())
		}
	})

	// 进群欢迎
	bot.OnNoticeType("group_increase", func(c *napcat.Context) {
		c.SendGroupMsg(nil, c.Event.Notice.GroupID, napcat.Text("欢迎新人~"))
	})

	// 被戳就戳回去
	bot.OnNoticeType("notify", func(c *napcat.Context) {
		n := c.Event.Notice
		if n.SubType == "poke" && n.TargetID == c.SelfID() {
			c.SendPoke(nil, n.GroupID, n.UserID)
		}
	})

	// 好友/群请求自动同意
	bot.OnRequest(func(c *napcat.Context) {
		c.Approve()
	})

	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
	defer stop()
	bot.Run(ctx)
}

配置方法一览

方法 说明
SetToken(t) OneBot access_token,所有连接自动携带
SetSecret(s) HTTP 上报 X-Signature 校验密钥
SetLogger(l) 自定义 *slog.Logger
SetForwardWS(urls...) 正向 WS 目标,可多个,重复调用替换
SetReverseWS(addrs...) 反向 WS 监听地址,可多个,重复调用替换
SetHTTPAPI(baseURL) HTTP API 地址
SetHTTPReport(addrs...) HTTP 上报监听地址,可多个,重复调用替换
Use(conn...) 注册自定义 Connector(高级扩展点)

以上方法均返回 *Client,支持链式调用。请勿在 Run 之后修改配置。

事件处理

bot.OnEvent(fn)          // 所有事件
bot.OnMessage(fn)        // 私聊 + 群聊消息(含 message_sent)
bot.OnPrivateMessage(fn) // 私聊消息
bot.OnGroupMessage(fn)   // 群聊消息
bot.OnNotice(fn)         // 全部通知
bot.OnNoticeType(t, fn)  // 指定 notice_type,如 "group_increase" "notify" "group_recall"
bot.OnRequest(fn)        // 好友/群请求
bot.OnMeta(fn)           // 心跳/生命周期
bot.OnCommand(name, fn)  // 文本命令,args 为空白切分的参数

同一会话的事件串行分发(保序);handler 里的 panic 会被隔离并记录日志。 事件队列上限 256 条,溢出时丢弃并告警。

Context 常用方法

方法 说明
Reply(msg...) / ReplyText(s) 回复当前消息(群聊/私聊自动判断)
Recall() 撤回当前消息
Approve() / Reject(reason) 同意/拒绝当前的请求事件
HasAtMe() / PlainText() / Segments() 消息内容判断
IsGroup() / IsPrivate() / IsSelfMessage() 事件类型判断
SendGroupMsg(nil, gid, ...) 等 直接调用任意 API(ctx 传 nil 用默认 30s 超时)
Call(nil, action, params, &out) 裸调未收录的扩展 API

HTTP 上报模式下,Reply / Approve / Reject 会随 HTTP 响应以 OneBot「快速操作」返回,不产生额外请求。

消息段

napcat.Text("hello")
napcat.At(12345)          // napcat.AtAll()
napcat.Image("C:/a.jpg")  // 支持本地路径 / http(s) URL / base64://
napcat.Reply(678).With("text", "引用")   // With 追加任意字段
napcat.CustomNode(10001, "某人", []napcat.Segment{napcat.Text("...")})

收到的消息统一用 c.Segments() 获取,CQ 码字符串会被自动解析; napcat.ParseCQ / napcat.EncodeCQ 可单独使用。

API 调用

全部 API 挂在 Client(默认通道)与 Context(事件来源通道)上, 命名与 OneBot action 一一对应(驼峰化),例如:

login, _ := bot.GetLoginInfo(nil)                    // get_login_info
mid, err := bot.SendGroupMsg(nil, 123, napcat.Text("hi"))
bot.SetGroupBan(nil, 123, 456, 600)                  // set_group_ban
info, _ := bot.GetGroupMemberInfo(nil, 123, 456, false)
bot.UploadGroupFile(nil, 123, `C:\file.zip`, "file.zip", "")
hist, _ := bot.GetGroupMsgHistory(nil, 123, 0, 20, false)

返回未结构化的 NapCat 扩展 API(如 GetProfileLike)时得到 jsonRaw(map), 需要更细的结构时用 Call 裸调自行解码。

测试

go test ./...

测试内置 mock NapCat(正向 WS、反向 WS、HTTP 上报签名校验、HTTP API), 覆盖 echo 往返、错误响应、快速操作、命令分发、CQ 码转义回环等场景。

注意事项

  • message_sent 是 bot 自己发出的消息事件,同样进入 message 处理链, 用 c.IsSelfMessage() 区分,避免 bot 复读自己。
  • 多个 bot 实例同时连入反向 WS 时,Client.Call(默认通道)只作用于 最近连入的会话;事件处理内请一律用 Context 的方法,自动路由正确。
  • 正向 WS 断开后自动重连(1s 起步、30s 封顶指数退避)。