墨问 OpenAPI 的 Go SDK,覆盖公开文档中的笔记、密钥重置和文件上传接口。需要 Go 1.24 或更新版本,无第三方运行时依赖。
go get github.com/lib-x/mowen-go@v0.1.0从墨问获取 API Key,通过环境变量传给应用。
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/lib-x/mowen-go"
)
func main() {
client, err := mowen.NewClient(os.Getenv("MOWEN_API_KEY"))
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
note, err := client.Notes.Create(ctx, mowen.NoteCreateRequest{
Body: mowen.Document(
mowen.Paragraph(mowen.Text("从 Go 写入墨问", mowen.Bold())),
),
Settings: &mowen.NoteCreateSettings{
AutoPublish: false,
Tags: []string{"Go"},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(note.NoteID)
}Settings 可省略;显式传入时,AutoPublish: false 会保留在请求中。标签最多 10 个,每个最多 30 个字符。创建、编辑的正文根节点必须是 doc。
| 方法 | 用途 |
|---|---|
client.Notes.Create(ctx, request) |
创建笔记,返回笔记 ID |
client.Notes.Edit(ctx, request) |
编辑通过 API 创建的笔记 |
client.Notes.Set(ctx, request) |
设置公开、私密或规则公开 |
client.Auth.ResetKey(ctx) |
重置 API Key,返回新密钥 |
client.Files.Prepare(ctx, request) |
获取本地上传授权表单 |
client.Files.Upload(ctx, request) |
向授权端点投递本地文件 |
client.Files.UploadViaURL(ctx, request) |
由墨问抓取 URL 并上传文件 |
每个方法都接受 context.Context。公开索引中还有查询相关的数据模型,但没有对应接口路径,因此本版本没有据此推测查询接口。
_, err := client.Notes.Edit(ctx, mowen.NoteEditRequest{
NoteID: noteID,
Body: mowen.Document(mowen.Paragraph(mowen.Text("修改后的正文"))),
})
err = client.Notes.Set(ctx, mowen.NoteSetRequest{
NoteID: noteID,
Section: mowen.NoteSectionPrivacy,
Settings: mowen.NoteSettings{
Privacy: &mowen.NotePrivacySet{Type: mowen.PrivacyPrivate},
},
})规则公开使用 PrivacyRule 和 NotePrivacyRule。ExpireAt 是秒级 Unix 时间戳字符串,"0" 表示永久;NoShare: false 表示允许分享。上游规定,规则公开但未传规则时等同于完全公开。
提供 Document、Paragraph、Text、Bold、Highlight、InlineCode、Link、Quote、Heading、CodeBlock、Image、Audio、PDF 和 NoteReference。
body := mowen.Document(
mowen.Heading("2", mowen.Text("记录")),
mowen.Paragraph(mowen.Text("一个链接", mowen.Link("https://mowen.cn"))),
mowen.Image(imageFileID, "图片说明", "center"),
mowen.Audio(audioFileID, "00:00 开始"),
mowen.CodeBlock("fmt.Println(1)", "go"),
)也可以直接构造 NoteAtom,其 Attrs 为 map[string]string。音频节点使用官方示例中的 audio-uuid,图片和 PDF 使用 uuid。节点包含切片和映射,调用期间不要并发修改请求数据。
URL 上传由墨问服务器完成下载:
reply, err := client.Files.UploadViaURL(ctx, mowen.UploadViaURLRequest{
FileType: mowen.FileTypeImage,
URL: "https://example.com/photo.png",
FileName: "photo.png",
})
// 成功后,reply.File.FileID 可用于图片节点。本地上传分两步。先获取授权,再把服务返回的上传端点和表单传给 Upload:
prepared, err := client.Files.Prepare(ctx, mowen.UploadPrepareRequest{
FileType: mowen.FileTypeImage,
FileName: "photo.png",
})
if err != nil {
return err
}
// 从授权结果中取得 endpoint,form 只包含需要提交的表单字段。
// OpenAPI 仅声明 prepared.Form 为字符串映射,未定义端点字段布局;
// 应按实际返回结果提取,不要将端点元数据混入表单。
_ = prepared
f, err := os.Open("photo.png")
if err != nil {
return err
}
defer f.Close()
info, err := f.Stat()
if err != nil {
return err
}
result, err := client.Files.Upload(ctx, mowen.UploadRequest{
Endpoint: endpoint,
Form: form,
FileName: "photo.png",
Content: f,
Size: info.Size(),
})
// result 是 json.RawMessage,保留对象存储返回的完整 JSON。Upload 只接受可信授权结果中的 HTTPS 端点,以已知长度流式投递,文件字段在最后,不发送墨问 API Key 或 Cookie,不跟随重定向。调用者负责关闭文件;Size 必须与要上传的字节数一致,SDK 最多读取该长度。自定义 io.Reader 若可能阻塞,调用者需提供中断它的机制。官方投递示例没有定义成功响应的字段,SDK 不假设它与 URL 上传响应相同。
上游文档给出的大小限制如下,实际校验由服务端执行:
| 类型 | 本地上传 | URL 上传 |
|---|---|---|
| 图片 | 小于 50 MB | 小于 30 MB |
| 音频 | 小于 200 MB | 小于 100 MB |
| 小于 100 MB | 小于 50 MB |
默认地址为 https://open.mowen.cn,每次调用默认超时 30 秒。大文件可用 WithTimeout 延长,也可通过 WithHTTPClient 配置代理或 http.RoundTripper 中间件。WithBaseURL 用于测试或代理环境。
client, err := mowen.NewClient(apiKey,
mowen.WithTimeout(2*time.Minute),
mowen.WithHTTPClient(&http.Client{Transport: transport}),
)SDK 复制传入的 HTTP 客户端,禁用重定向及 Cookie Jar;自定义 Transport 由调用者负责,不能向上传端点注入凭据或自动重放写入。配置完成后可并发使用 Client;不要重新赋值其服务字段。
var apiErr *mowen.APIError
if errors.As(err, &apiErr) {
switch apiErr.Reason {
case mowen.ReasonRateLimit:
// 可读取 RetryAfter,结合业务状态决定后续操作。
case mowen.ReasonQuota:
// 当日配额不足。
case mowen.ReasonPermission:
// 当前账户无权执行此操作。
}
}
if errors.Is(err, context.DeadlineExceeded) {
// 服务端可能已执行写入,先确认状态。
}APIError 保留 HTTP 状态、code、reason、message、metadata、meta、details、原始响应体及请求 ID。Error() 不输出响应正文;这些诊断字段可能含私密数据,不宜直接写入日志。非 JSON 的错误响应也可通过 APIError 读取。单次响应上限为 8 MiB,超过时返回 ErrResponseTooLarge。
传输错误的 Error() 使用固定描述,原始原因可通过 errors.Is、errors.As 或 errors.Unwrap 检查;原始原因可能含签名 URL,不宜直接记录。HTTP 200 响应若缺少新密钥、笔记 ID、文件 ID 或上传授权表单等必要结果,SDK 也会返回错误。
所有接口默认不自动重试。上游没有公开幂等键协议,超时可能发生在写入成功之后;自动重试创建、上传或密钥重置会产生重复操作或失去有效凭据。各 API 文档标注每个用户、每个接口每秒 1 次,调用者还需结合自己的多实例部署协调限频和日配额。
ResetKey 成功后旧密钥立即失效。请保存返回的 APIKey 并创建新 Client;现有 Client 的密钥不会被修改。SDK 不会在初始化时重置密钥。
接口依据 墨问文档索引,通过 Firecrawl 抓取并核对于 2026-09-08。接口映射、来源差异及设计取舍见 设计文档。这是一份第三方 SDK。
go test -race -cover ./...
go vet ./...
go build ./...测试使用本地 HTTP/TLS 服务,覆盖请求契约、富文本、文件投递及故障处理。测试不会调用真实账号的写入接口;未使用真实墨问凭据做端到端联调。可编译示例见 example_test.go。
许可证:MIT。