Files
snowluma/README.md

174 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# snowluma
对接 [SnowLuma](https://snowluma.github.io/)(OneBot v11 协议)的 Go 软件包,零第三方依赖(仅 gorilla/websocket)。
本包与 [gitea.doicat.com/bot/napcat](https://gitea.doicat.com/bot/napcat) 的公开 API **保持一致**:方法名、消息段构造函数、事件结构、Client/Context 用法完全相同。现有的 napcat 机器人项目,只需改一行导入即可切到 SnowLuma。
```go
bot := snowluma.New().SetToken("xxx").SetForwardWS("ws://127.0.0.1:3001")
bot.OnGroupMessage(func(c *snowluma.Context) {
if c.HasAtMe() {
c.ReplyText("hi")
}
})
bot.Run(ctx)
```
## 从 napcat 迁移(drop-in)
napcat 用法:
```go
import "gitea.doicat.com/bot/napcat"
// ... napcat.New() / napcat.Text(...) / *napcat.Context ...
```
改成:
```go
import napcat "gitea.doicat.com/bot/snowluma"
// ... 其余代码一字不改,napcat.New() / napcat.Text(...) / *napcat.Context 全部照常编译
```
导入别名 `napcat` 覆盖了包的声明名,因此所有 `napcat.XXX` 引用保持不变。差异只在协议/能力覆盖上(见下文「与 napcat 的差异」)。
## 特性
- **一个对象搞定**:`New()` 创建与 SnowLuma 交互的客户端对象,配置、事件、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 兼容 + SnowLuma 扩展(闪传、Ark 卡片、群待办、群相册、QQ 空间、系统表情、流式接口等);未收录的扩展可用 `Call` 裸调
- **echo 路由**:WS 上的 action 请求/响应按 echo 精确配对,支持并发调用
- **连接级路由**:事件处理函数里的 API 调用自动走产生该事件的连接,多 bot 互不串线
- 内置文本命令路由、请求 Approve/Reject、心跳记录、panic 隔离
## 目录结构
```text
snowluma/
├── doc.go 包文档(pkg.go.dev 首页)
├── client.go Client:配置方法、连接汇聚、事件分发、Handler 注册
├── context.go Context:处理函数上下文(Reply/Approve/快速操作)
├── api.go API:OneBot 11 / go-cqhttp / NapCat 兼容的类型化方法
├── api_ext.go API:SnowLuma 扩展动作(闪传/Ark/待办/相册/空间/表情/流式)
├── 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 Example)
└── snowluma_test.go 单元测试(CQ 码回环、事件解码、消息段解码)
```
## 安装
```text
go get gitea.doicat.com/bot/snowluma
```
使用指定版本:
```text
go get gitea.doicat.com/bot/snowluma@v1.0.0
```
**私有仓库使用者**:如果仓库设为私有,除了给 Git 配置 Gitea 凭据外,还需设置:
```text
set GOPRIVATE=gitea.doicat.com
```
## 快速开始
### 1. SnowLuma 侧配置
在 SnowLuma 的 OneBot 网络配置里按需启用一项(或多项):
| 配置方法 | SnowLuma 侧配置 |
| --- | --- |
| `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/` |
若配置了 token,`SetToken` 填同一个值即可,框架会在 WS 握手(query + Authorization 头)和 HTTP 调用(Authorization 头)中自动携带;HTTP 上报场景若配了 secret,`SetSecret` 会自动校验 `X-Signature`(HMAC-SHA1)。
### 2. 完整示例
见 [example_test.go](./example_test.go) 的 `Example` 函数。
## 配置方法一览
| 方法 | 说明 |
| --- | --- |
| `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` 之后修改配置。
## 事件处理
```go
bot.OnEvent(fn) // 所有事件
bot.OnMessage(fn) // 私聊 + 群聊消息(含 message_sent)
bot.OnPrivateMessage(fn) // 私聊消息
bot.OnGroupMessage(fn) // 群聊消息
bot.OnNotice(fn) // 全部通知
bot.OnNoticeType(t, fn) // 指定 notice_type
bot.OnRequest(fn) // 好友/群请求
bot.OnMeta(fn) // 心跳/生命周期
bot.OnCommand(name, fn) // 文本命令,args 为空白切分的参数
```
同一会话的事件**串行分发**(保序);handler 里的 panic 会被隔离并记录日志。
## 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 |
## 消息段
```go
snowluma.Text("hello")
snowluma.At(12345) // snowluma.AtAll()
snowluma.Image("C:/a.jpg") // 支持本地路径 / http(s) URL / base64://
snowluma.Reply(678).With("text", "引用")
snowluma.CustomNode(10001, "某人", []snowluma.Segment{snowluma.Text("...")})
```
## 测试
```text
go test ./...
```
## 与 napcat 的差异
两包面向不同运行时(SnowLuma vs NapCatQQ),但都是 OneBot v11 + NapCat 兼容协议。协议层完全一致,方法名一一对应;能力覆盖上的差异体现在:
- **SnowLuma 独有**:闪传(flash)、Ark 卡片分享、群待办、群分类标签、群相册、QQ 空间、系统表情、流式文件上传/下载等扩展动作;本包在 `api_ext.go` 中提供类型化封装。
- **napcat 有而 SnowLuma 可能未实现/改名**:个别 NapCat 私有动作(如 `ArkShareGroup` 这类大小写混合名)在 SnowLuma 中改用了标准名(`send_group_ark_share`),本包对这些动作做了别名适配,保证同名方法仍可用。
未收录到类型化方法里的动作,一律可通过 `Client.Call` / `Context.Call`(或 `CallAPI[T]` 泛型)裸调,因此能力差异不会卡住迁移。