@kirigaya/openclaw-onebot 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +174 -7
- package/dist/channel.js +2 -2
- package/dist/cli-commands.d.ts +4 -0
- package/dist/cli-commands.js +119 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +43 -0
- package/dist/connection.d.ts +63 -6
- package/dist/connection.js +263 -23
- package/dist/handlers/group-increase.d.ts +2 -1
- package/dist/handlers/group-increase.js +78 -12
- package/dist/handlers/process-inbound.d.ts +18 -0
- package/dist/handlers/process-inbound.js +420 -25
- package/dist/index.js +5 -1
- package/dist/load-script.d.ts +5 -1
- package/dist/load-script.js +7 -3
- package/dist/markdown-to-html.d.ts +10 -0
- package/dist/markdown-to-html.js +111 -0
- package/dist/markdown.d.ts +7 -0
- package/dist/markdown.js +43 -0
- package/dist/message.d.ts +6 -0
- package/dist/message.js +45 -5
- package/dist/og-image.d.ts +7 -0
- package/dist/og-image.js +51 -0
- package/dist/reply-context.d.ts +19 -0
- package/dist/reply-context.js +54 -0
- package/dist/send-debug-log.d.ts +27 -0
- package/dist/send-debug-log.js +28 -0
- package/dist/send.d.ts +16 -2
- package/dist/send.js +65 -8
- package/dist/setup.js +58 -5
- package/dist/tools.d.ts +3 -1
- package/dist/tools.js +60 -7
- package/openclaw.plugin.json +59 -4
- package/package.json +37 -12
- package/skills/onebot-ops/SKILL.md +14 -3
- package/skills/onebot-ops/agent-tools.md +116 -0
- package/skills/onebot-ops/config.md +71 -55
- package/skills/onebot-ops/receive.md +88 -12
- package/skills/onebot-ops/send.md +56 -39
- package/themes/dust.css +1096 -0
- package/dist/gateway-proxy.d.ts +0 -8
- package/dist/gateway-proxy.js +0 -36
package/dist/setup.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OneBot TUI 配置向导
|
|
3
3
|
* openclaw onebot setup
|
|
4
4
|
*/
|
|
5
|
-
import { cancel as clackCancel, isCancel, note as clackNote, outro as clackOutro, select as clackSelect, text as clackText, } from "@clack/prompts";
|
|
5
|
+
import { cancel as clackCancel, confirm as clackConfirm, isCancel, note as clackNote, outro as clackOutro, select as clackSelect, text as clackText, } from "@clack/prompts";
|
|
6
6
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
7
7
|
import { homedir } from "os";
|
|
8
8
|
import { join } from "path";
|
|
@@ -36,11 +36,44 @@ export async function runOneBotSetup() {
|
|
|
36
36
|
message: "Access Token(可选,留空回车跳过)",
|
|
37
37
|
initialValue: process.env.ONEBOT_WS_ACCESS_TOKEN || "",
|
|
38
38
|
}));
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const renderMarkdownToPlain = guardCancel(await clackConfirm({
|
|
40
|
+
message: "是否将机器人回复中的 Markdown 渲染为纯文本再发送?(去除 **、# 等标记,推荐开启)",
|
|
41
|
+
initialValue: true,
|
|
42
|
+
}));
|
|
43
|
+
const longMessageMode = guardCancel(await clackSelect({
|
|
44
|
+
message: "长消息处理模式(单次回复超过阈值时):",
|
|
45
|
+
options: [
|
|
46
|
+
{ value: "normal", label: "正常发送(分段发送)" },
|
|
47
|
+
{ value: "og_image", label: "生成图片发送(需安装 node-html-to-image)" },
|
|
48
|
+
{ value: "forward", label: "合并转发发送(发给自己后打包转发)" },
|
|
49
|
+
],
|
|
50
|
+
initialValue: "normal",
|
|
51
|
+
}));
|
|
52
|
+
let ogImageRenderTheme = "default";
|
|
53
|
+
let ogImageRenderThemePath;
|
|
54
|
+
if (longMessageMode === "og_image") {
|
|
55
|
+
const themeChoice = guardCancel(await clackSelect({
|
|
56
|
+
message: "长消息生成图片时的渲染样式:",
|
|
57
|
+
options: [
|
|
58
|
+
{ value: "default", label: "default(无额外样式,默认白底黑字)" },
|
|
59
|
+
{ value: "dust", label: "dust(内置暖色旧纸质感)" },
|
|
60
|
+
{ value: "custom", label: "custom(自定义 CSS 文件路径)" },
|
|
61
|
+
],
|
|
62
|
+
initialValue: "default",
|
|
63
|
+
}));
|
|
64
|
+
ogImageRenderTheme = themeChoice;
|
|
65
|
+
if (themeChoice === "custom") {
|
|
66
|
+
const customPath = guardCancel(await clackText({
|
|
67
|
+
message: "CSS 文件绝对路径",
|
|
68
|
+
initialValue: "",
|
|
69
|
+
}));
|
|
70
|
+
ogImageRenderThemePath = (customPath || "").trim() || undefined;
|
|
71
|
+
}
|
|
43
72
|
}
|
|
73
|
+
const longMessageThreshold = guardCancel(await clackText({
|
|
74
|
+
message: "长消息阈值(字符数,超过则启用上述模式)",
|
|
75
|
+
initialValue: "300",
|
|
76
|
+
}));
|
|
44
77
|
let existing = {};
|
|
45
78
|
if (existsSync(CONFIG_PATH)) {
|
|
46
79
|
try {
|
|
@@ -48,7 +81,22 @@ export async function runOneBotSetup() {
|
|
|
48
81
|
}
|
|
49
82
|
catch { }
|
|
50
83
|
}
|
|
84
|
+
const prevOnebot = (existing.channels || {}).onebot;
|
|
85
|
+
const whitelistInitial = Array.isArray(prevOnebot?.whitelistUserIds)
|
|
86
|
+
? prevOnebot.whitelistUserIds.join(", ")
|
|
87
|
+
: "";
|
|
88
|
+
const whitelistInput = guardCancel(await clackText({
|
|
89
|
+
message: "白名单 QQ 号(逗号分隔,留空则所有人可回复)",
|
|
90
|
+
initialValue: whitelistInitial,
|
|
91
|
+
}));
|
|
92
|
+
const port = parseInt(String(portStr).trim(), 10);
|
|
93
|
+
if (!Number.isFinite(port)) {
|
|
94
|
+
console.error("端口必须为数字");
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
51
97
|
const channels = existing.channels || {};
|
|
98
|
+
const thresholdNum = parseInt(String(longMessageThreshold).trim(), 10);
|
|
99
|
+
const whitelistIds = (String(whitelistInput).trim().split(/[,\s]+/).map((s) => s.trim()).filter(Boolean).map((s) => (/^\d+$/.test(s) ? Number(s) : null)).filter((n) => n != null));
|
|
52
100
|
channels.onebot = {
|
|
53
101
|
...(channels.onebot || {}),
|
|
54
102
|
type,
|
|
@@ -57,6 +105,11 @@ export async function runOneBotSetup() {
|
|
|
57
105
|
...(accessToken?.trim() ? { accessToken: String(accessToken).trim() } : {}),
|
|
58
106
|
enabled: true,
|
|
59
107
|
requireMention: true,
|
|
108
|
+
renderMarkdownToPlain,
|
|
109
|
+
longMessageMode,
|
|
110
|
+
...(longMessageMode === "og_image" ? { ogImageRenderTheme, ...(ogImageRenderThemePath != null ? { ogImageRenderThemePath } : {}) } : {}),
|
|
111
|
+
longMessageThreshold: Number.isFinite(thresholdNum) ? thresholdNum : 300,
|
|
112
|
+
...(whitelistIds.length > 0 ? { whitelistUserIds: whitelistIds } : {}),
|
|
60
113
|
};
|
|
61
114
|
const next = { ...existing, channels };
|
|
62
115
|
writeFileSync(CONFIG_PATH, JSON.stringify(next, null, 2), "utf-8");
|
package/dist/tools.d.ts
CHANGED
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
* Agent 工具注册
|
|
3
3
|
* 供 OpenClaw cron 等场景下,AI 调用 OneBot 能力
|
|
4
4
|
*/
|
|
5
|
-
import { sendPrivateMsg, sendGroupMsg, sendGroupImage, sendPrivateImage, getGroupMsgHistory, getGroupInfo, getStrangerInfo, getGroupMemberInfo, getAvatarUrl } from "./connection.js";
|
|
5
|
+
import { sendPrivateMsg, sendGroupMsg, sendGroupImage, sendPrivateImage, getGroupMsgHistory, getGroupMsgHistoryInRange, getGroupInfo, getStrangerInfo, getGroupMemberInfo, searchGroupMemberByName, getAvatarUrl } from "./connection.js";
|
|
6
6
|
export interface OneBotClient {
|
|
7
7
|
sendGroupMsg: typeof sendGroupMsg;
|
|
8
8
|
sendGroupImage: typeof sendGroupImage;
|
|
9
9
|
sendPrivateMsg: typeof sendPrivateMsg;
|
|
10
10
|
sendPrivateImage: typeof sendPrivateImage;
|
|
11
11
|
getGroupMsgHistory: typeof getGroupMsgHistory;
|
|
12
|
+
getGroupMsgHistoryInRange: typeof getGroupMsgHistoryInRange;
|
|
12
13
|
getGroupInfo: typeof getGroupInfo;
|
|
13
14
|
getStrangerInfo: typeof getStrangerInfo;
|
|
14
15
|
getGroupMemberInfo: typeof getGroupMemberInfo;
|
|
16
|
+
searchGroupMemberByName: typeof searchGroupMemberByName;
|
|
15
17
|
getAvatarUrl: typeof getAvatarUrl;
|
|
16
18
|
}
|
|
17
19
|
export declare const onebotClient: OneBotClient;
|
package/dist/tools.js
CHANGED
|
@@ -4,16 +4,20 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import WebSocket from "ws";
|
|
6
6
|
import { loadScript } from "./load-script.js";
|
|
7
|
-
import { getWs, sendPrivateMsg, sendGroupMsg, sendGroupImage, sendPrivateImage, uploadGroupFile, uploadPrivateFile, getGroupMsgHistory, getGroupInfo, getStrangerInfo, getGroupMemberInfo, getAvatarUrl, } from "./connection.js";
|
|
7
|
+
import { getWs, sendPrivateMsg, sendGroupMsg, sendGroupImage, sendPrivateImage, uploadGroupFile, uploadPrivateFile, getGroupMsgHistory, getGroupMsgHistoryInRange, getGroupInfo, getStrangerInfo, getGroupMemberInfo, searchGroupMemberByName, getAvatarUrl, } from "./connection.js";
|
|
8
|
+
import { getRenderMarkdownToPlain } from "./config.js";
|
|
9
|
+
import { markdownToPlain } from "./markdown.js";
|
|
8
10
|
export const onebotClient = {
|
|
9
11
|
sendGroupMsg,
|
|
10
12
|
sendGroupImage,
|
|
11
13
|
sendPrivateMsg,
|
|
12
14
|
sendPrivateImage,
|
|
13
15
|
getGroupMsgHistory,
|
|
16
|
+
getGroupMsgHistoryInRange,
|
|
14
17
|
getGroupInfo,
|
|
15
18
|
getStrangerInfo,
|
|
16
19
|
getGroupMemberInfo,
|
|
20
|
+
searchGroupMemberByName,
|
|
17
21
|
getAvatarUrl,
|
|
18
22
|
};
|
|
19
23
|
export function registerTools(api) {
|
|
@@ -35,14 +39,16 @@ export function registerTools(api) {
|
|
|
35
39
|
if (!w || w.readyState !== WebSocket.OPEN) {
|
|
36
40
|
return { content: [{ type: "text", text: "OneBot 未连接" }] };
|
|
37
41
|
}
|
|
42
|
+
const cfg = api?.config;
|
|
43
|
+
const textToSend = getRenderMarkdownToPlain(cfg) ? markdownToPlain(params.text) : params.text;
|
|
38
44
|
const t = params.target.replace(/^onebot:/i, "");
|
|
39
45
|
try {
|
|
40
46
|
if (t.startsWith("group:")) {
|
|
41
|
-
await sendGroupMsg(parseInt(t.slice(6), 10),
|
|
47
|
+
await sendGroupMsg(parseInt(t.slice(6), 10), textToSend);
|
|
42
48
|
}
|
|
43
49
|
else {
|
|
44
50
|
const id = parseInt(t.replace(/^user:/, ""), 10);
|
|
45
|
-
await sendPrivateMsg(id,
|
|
51
|
+
await sendPrivateMsg(id, textToSend);
|
|
46
52
|
}
|
|
47
53
|
return { content: [{ type: "text", text: "发送成功" }] };
|
|
48
54
|
}
|
|
@@ -116,14 +122,16 @@ export function registerTools(api) {
|
|
|
116
122
|
});
|
|
117
123
|
api.registerTool({
|
|
118
124
|
name: "onebot_get_group_msg_history",
|
|
119
|
-
description: "
|
|
125
|
+
description: "获取群聊历史消息。可指定 hours 获取最近 N 小时内消息(分页拉取),不指定则返回单页(始终从旧到新)。需 Lagrange.Core",
|
|
120
126
|
parameters: {
|
|
121
127
|
type: "object",
|
|
122
128
|
properties: {
|
|
123
129
|
group_id: { type: "number", description: "群号" },
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
hours: { type: "number", description: "可选。指定则获取从现在到过去 N 小时内的消息(如 24 即过去 24 小时)" },
|
|
131
|
+
count: { type: "number", description: "单页条数(未指定 hours 时生效),默认 50" },
|
|
132
|
+
message_seq: { type: "number", description: "可选,起始消息序号(分页用,未指定 hours 时生效)" },
|
|
133
|
+
message_id: { type: "number", description: "可选,起始消息 ID(未指定 hours 时生效)" },
|
|
134
|
+
limit: { type: "number", description: "指定 hours 时最多返回条数,默认 3000" },
|
|
127
135
|
},
|
|
128
136
|
required: ["group_id"],
|
|
129
137
|
},
|
|
@@ -133,10 +141,26 @@ export function registerTools(api) {
|
|
|
133
141
|
return { content: [{ type: "text", text: "OneBot 未连接" }] };
|
|
134
142
|
}
|
|
135
143
|
try {
|
|
144
|
+
const hoursNum = params.hours != null ? Number(params.hours) : undefined;
|
|
145
|
+
if (typeof hoursNum === "number" && Number.isFinite(hoursNum) && hoursNum > 0) {
|
|
146
|
+
const startTime = Math.floor(Date.now() / 1000) - hoursNum * 3600;
|
|
147
|
+
const msgs = await getGroupMsgHistoryInRange(params.group_id, {
|
|
148
|
+
startTime,
|
|
149
|
+
limit: params.limit ?? 3000,
|
|
150
|
+
chunkSize: 100,
|
|
151
|
+
});
|
|
152
|
+
const summary = msgs.map((m) => {
|
|
153
|
+
const text = typeof m.message === "string" ? m.message : JSON.stringify(m.message);
|
|
154
|
+
const nick = m.sender?.nickname ?? m.sender?.user_id ?? "?";
|
|
155
|
+
return `[${new Date(m.time * 1000).toISOString()}] ${nick}: ${text.slice(0, 200)}`;
|
|
156
|
+
});
|
|
157
|
+
return { content: [{ type: "text", text: summary.join("\n") || "无历史消息", metadata: { count: msgs.length } }] };
|
|
158
|
+
}
|
|
136
159
|
const msgs = await getGroupMsgHistory(params.group_id, {
|
|
137
160
|
count: params.count ?? 50,
|
|
138
161
|
message_seq: params.message_seq,
|
|
139
162
|
message_id: params.message_id,
|
|
163
|
+
reverse_order: true,
|
|
140
164
|
});
|
|
141
165
|
const summary = msgs.map((m) => {
|
|
142
166
|
const text = typeof m.message === "string" ? m.message : JSON.stringify(m.message);
|
|
@@ -150,6 +174,35 @@ export function registerTools(api) {
|
|
|
150
174
|
}
|
|
151
175
|
},
|
|
152
176
|
});
|
|
177
|
+
api.registerTool({
|
|
178
|
+
name: "onebot_search_group_member",
|
|
179
|
+
description: "按名字模糊匹配群成员,返回匹配到的 QQ 号与展示名(群名片优先)。用于根据昵称/群名片查 QQ 号",
|
|
180
|
+
parameters: {
|
|
181
|
+
type: "object",
|
|
182
|
+
properties: {
|
|
183
|
+
group_id: { type: "number", description: "群号" },
|
|
184
|
+
name: { type: "string", description: "要搜索的名字(群名片或昵称,支持模糊匹配)" },
|
|
185
|
+
},
|
|
186
|
+
required: ["group_id", "name"],
|
|
187
|
+
},
|
|
188
|
+
async execute(_id, params) {
|
|
189
|
+
const w = getWs();
|
|
190
|
+
if (!w || w.readyState !== WebSocket.OPEN) {
|
|
191
|
+
return { content: [{ type: "text", text: "OneBot 未连接" }] };
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const list = await searchGroupMemberByName(params.group_id, params.name);
|
|
195
|
+
if (!list.length) {
|
|
196
|
+
return { content: [{ type: "text", text: `未找到匹配「${params.name}」的群成员` }] };
|
|
197
|
+
}
|
|
198
|
+
const lines = list.map((m) => `QQ: ${m.user_id} 展示名: ${m.displayName}`);
|
|
199
|
+
return { content: [{ type: "text", text: lines.join("\n"), metadata: { count: list.length, matches: list } }] };
|
|
200
|
+
}
|
|
201
|
+
catch (e) {
|
|
202
|
+
return { content: [{ type: "text", text: `搜索失败: ${e?.message}` }] };
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
});
|
|
153
206
|
api.registerTool({
|
|
154
207
|
name: "onebot_run_script",
|
|
155
208
|
description: "执行用户配置的 JS/TS 脚本(.mjs/.ts/.mts),脚本可调用 OneBot API(获取群历史、发图等)。用于定时任务中实现自定义逻辑(如 OG 图片生成、日报汇总)",
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-onebot",
|
|
3
3
|
"name": "OneBot Channel",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.1",
|
|
5
5
|
"description": "OneBot v11 protocol channel (QQ/Lagrange.Core via WebSocket)",
|
|
6
6
|
"author": "Lagrange.Onebot",
|
|
7
7
|
"channels": ["onebot"],
|
|
8
|
+
"commands": ["onebot"],
|
|
8
9
|
"configSchema": {
|
|
9
10
|
"type": "object",
|
|
10
11
|
"additionalProperties": false,
|
|
@@ -23,6 +24,26 @@
|
|
|
23
24
|
"default": true,
|
|
24
25
|
"description": "群聊是否必须 @ 机器人才回复"
|
|
25
26
|
},
|
|
27
|
+
"whitelistUserIds": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": { "type": "number" },
|
|
30
|
+
"description": "白名单 QQ 号,非空时仅白名单内用户可触发 AI;为空则所有人可回复"
|
|
31
|
+
},
|
|
32
|
+
"renderMarkdownToPlain": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"default": true,
|
|
35
|
+
"description": "是否将机器人回复中的 Markdown 渲染为纯文本再发送(去除 **、# 等标记)"
|
|
36
|
+
},
|
|
37
|
+
"normalModeFlushIntervalMs": {
|
|
38
|
+
"type": "number",
|
|
39
|
+
"default": 1200,
|
|
40
|
+
"description": "normal 模式下聚合发送的等待窗口(毫秒)。块流式开启后,插件会在该时间窗口内合并回复再发送。"
|
|
41
|
+
},
|
|
42
|
+
"normalModeFlushChars": {
|
|
43
|
+
"type": "number",
|
|
44
|
+
"default": 160,
|
|
45
|
+
"description": "normal 模式下聚合发送的字符阈值。达到该长度会提前 flush,而不是继续等待时间窗口结束。"
|
|
46
|
+
},
|
|
26
47
|
"groupIncrease": {
|
|
27
48
|
"type": "object",
|
|
28
49
|
"properties": {
|
|
@@ -31,9 +52,13 @@
|
|
|
31
52
|
"type": "string",
|
|
32
53
|
"description": "欢迎语模板。占位符:{name} 新成员昵称,{userId} QQ号,{groupName} 群名,{groupId} 群号,{avatarUrl} 头像链接"
|
|
33
54
|
},
|
|
34
|
-
"
|
|
55
|
+
"command": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "在 cwd 下用系统 shell 执行的命令。调用方自动追加 --userId、--username、--groupId 参数。命令可自行发送(如调用 openclaw message send),或向 stdout 输出 JSON 行供 handler 发送"
|
|
58
|
+
},
|
|
59
|
+
"cwd": {
|
|
35
60
|
"type": "string",
|
|
36
|
-
"description": "
|
|
61
|
+
"description": "命令执行的工作目录(绝对路径)"
|
|
37
62
|
}
|
|
38
63
|
}
|
|
39
64
|
},
|
|
@@ -42,6 +67,31 @@
|
|
|
42
67
|
"default": 60,
|
|
43
68
|
"description": "表情 ID(Lagrange/QQ NT set_msg_emoji_like),收到消息时添加,回复完成后取消"
|
|
44
69
|
},
|
|
70
|
+
"onReplySessionEnd": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "回复会话结束时的钩子脚本路径(.mjs/.ts/.mts)。脚本接收 ctx: { replySessionId, sessionId, to, chunks, userMessage },可对同一问题下的多次发送做统一处理(如日志、合并、上报等)"
|
|
73
|
+
},
|
|
74
|
+
"longMessageMode": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"enum": ["normal", "og_image", "forward"],
|
|
77
|
+
"default": "normal",
|
|
78
|
+
"description": "长消息处理模式:normal 正常发送;og_image 生成图片(需 node-html-to-image);forward 合并转发"
|
|
79
|
+
},
|
|
80
|
+
"longMessageThreshold": {
|
|
81
|
+
"type": "number",
|
|
82
|
+
"default": 300,
|
|
83
|
+
"description": "长消息阈值(字符数),超过则启用 longMessageMode"
|
|
84
|
+
},
|
|
85
|
+
"ogImageRenderTheme": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"enum": ["default", "dust", "custom"],
|
|
88
|
+
"default": "default",
|
|
89
|
+
"description": "长消息 og_image 时的渲染主题:default 无额外样式,dust 内置 dust 样式,custom 时需填写 ogImageRenderThemePath"
|
|
90
|
+
},
|
|
91
|
+
"ogImageRenderThemePath": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"description": "当 ogImageRenderTheme 为 custom 时,填写 CSS 文件绝对路径"
|
|
94
|
+
},
|
|
45
95
|
"cronJobs": {
|
|
46
96
|
"type": "array",
|
|
47
97
|
"description": "内置定时任务(无 AI 介入,直接执行脚本并推送到群聊)",
|
|
@@ -67,6 +117,11 @@
|
|
|
67
117
|
"accessToken": { "label": "Access Token", "sensitive": true },
|
|
68
118
|
"host": { "label": "主机地址", "placeholder": "127.0.0.1" },
|
|
69
119
|
"port": { "label": "端口", "placeholder": "8080" },
|
|
70
|
-
"requireMention": { "label": "群聊需 @ 回复" }
|
|
120
|
+
"requireMention": { "label": "群聊需 @ 回复" },
|
|
121
|
+
"renderMarkdownToPlain": { "label": "Markdown 转纯文本" },
|
|
122
|
+
"normalModeFlushIntervalMs": { "label": "normal 聚合窗口(ms)" },
|
|
123
|
+
"normalModeFlushChars": { "label": "normal 提前发送阈值" },
|
|
124
|
+
"ogImageRenderTheme": { "label": "OG 图片渲染主题(default/dust/custom)" },
|
|
125
|
+
"ogImageRenderThemePath": { "label": "自定义 CSS 路径(仅当主题为 custom 时)" }
|
|
71
126
|
}
|
|
72
127
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kirigaya/openclaw-onebot",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "OneBot v11 protocol channel plugin for OpenClaw (QQ/Lagrange.Core/go-cqhttp)",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"publishConfig": {
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
7
9
|
"author": "LSTM-Kirigaya",
|
|
8
10
|
"repository": {
|
|
9
11
|
"type": "git",
|
|
@@ -26,13 +28,19 @@
|
|
|
26
28
|
"exports": "./dist/index.js",
|
|
27
29
|
"files": [
|
|
28
30
|
"dist",
|
|
31
|
+
"themes",
|
|
29
32
|
"skills",
|
|
30
33
|
"openclaw.plugin.json",
|
|
31
34
|
"README.md"
|
|
32
35
|
],
|
|
33
36
|
"openclaw": {
|
|
34
|
-
"extensions": [
|
|
35
|
-
|
|
37
|
+
"extensions": [
|
|
38
|
+
"./dist/index.js"
|
|
39
|
+
],
|
|
40
|
+
"skills": [
|
|
41
|
+
"skills/onebot-ops",
|
|
42
|
+
"skills/onebot-scripts"
|
|
43
|
+
],
|
|
36
44
|
"channel": {
|
|
37
45
|
"id": "onebot",
|
|
38
46
|
"label": "OneBot",
|
|
@@ -40,33 +48,50 @@
|
|
|
40
48
|
"docsPath": "/channels/onebot",
|
|
41
49
|
"blurb": "OneBot v11 protocol via WebSocket (go-cqhttp, Lagrange.Core, etc.)",
|
|
42
50
|
"order": 85,
|
|
43
|
-
"aliases": [
|
|
51
|
+
"aliases": [
|
|
52
|
+
"qq",
|
|
53
|
+
"lagrange",
|
|
54
|
+
"cqhttp"
|
|
55
|
+
]
|
|
44
56
|
}
|
|
45
57
|
},
|
|
46
58
|
"scripts": {
|
|
59
|
+
"openclaw": "openclaw",
|
|
47
60
|
"build": "tsc",
|
|
48
61
|
"test:connect": "npx tsx scripts/test-connect.ts",
|
|
62
|
+
"test:group-welcome": "npx tsx scripts/test-group-welcome.ts",
|
|
63
|
+
"test:group-increase-handler": "npx tsx scripts/test-group-increase-handler.ts",
|
|
64
|
+
"test:render-og-image": "npx tsx test/render-og-image.ts",
|
|
49
65
|
"prepublishOnly": "npm run build",
|
|
50
66
|
"pub": "npm run build && npm publish"
|
|
51
67
|
},
|
|
52
68
|
"dependencies": {
|
|
53
|
-
"ws": "^8.17.0",
|
|
54
69
|
"@clack/prompts": "^1.0.0",
|
|
70
|
+
"cron": "^4.4.0",
|
|
71
|
+
"fuse.js": "^7.1.0",
|
|
72
|
+
"highlight.js": "^11.10.0",
|
|
73
|
+
"marked": "^15.0.0",
|
|
55
74
|
"tsx": "^4.0.0",
|
|
56
|
-
"
|
|
75
|
+
"ws": "^8.17.0"
|
|
76
|
+
},
|
|
77
|
+
"optionalDependencies": {
|
|
78
|
+
"node-html-to-image": "^3.0.0"
|
|
57
79
|
},
|
|
58
80
|
"peerDependencies": {
|
|
59
|
-
"openclaw": "*",
|
|
60
81
|
"clawdbot": "*"
|
|
61
82
|
},
|
|
62
83
|
"peerDependenciesMeta": {
|
|
63
|
-
"clawdbot": {
|
|
64
|
-
|
|
84
|
+
"clawdbot": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
"openclaw": {
|
|
88
|
+
"optional": true
|
|
89
|
+
}
|
|
65
90
|
},
|
|
66
91
|
"devDependencies": {
|
|
67
|
-
"typescript": "^5.4.0",
|
|
68
92
|
"@types/node": "^22.0.0",
|
|
69
|
-
"@types/ws": "^8.5.10"
|
|
93
|
+
"@types/ws": "^8.5.10",
|
|
94
|
+
"typescript": "^5.4.0"
|
|
70
95
|
},
|
|
71
96
|
"engines": {
|
|
72
97
|
"node": ">=22"
|
|
@@ -46,16 +46,27 @@ openclaw plugins install ./openclaw-onebot
|
|
|
46
46
|
| **图片消息** | message 为 `[{ type: "image", data: { file } }]` |
|
|
47
47
|
| **delete_msg** | 撤回消息 |
|
|
48
48
|
| **get_msg** | 获取单条消息 |
|
|
49
|
-
| **get_group_msg_history** | 获取群历史(Lagrange.Core
|
|
49
|
+
| **get_group_msg_history** | 获取群历史(Lagrange.Core 扩展),支持 reverse_order 分页 |
|
|
50
50
|
| **upload_group_file** | 上传群文件 |
|
|
51
51
|
| **upload_private_file** | 上传私聊文件 |
|
|
52
52
|
| **set_msg_emoji_like** | 表情回应(Lagrange/QQ NT 扩展) |
|
|
53
53
|
|
|
54
|
-
##
|
|
54
|
+
## Agent 工具与 CLI
|
|
55
|
+
|
|
56
|
+
插件提供群历史、按名字搜 QQ 等能力,既注册为 **Agent 工具**(供 Cron/脚本/AI 调用),也提供等价的 **CLI 命令**,便于 AI 与人工按文档调用。
|
|
57
|
+
|
|
58
|
+
**详细说明与所有命令用法见:[agent-tools.md](agent-tools.md)**
|
|
59
|
+
|
|
60
|
+
### 常用 CLI 一览
|
|
55
61
|
|
|
56
62
|
| 命令 | 说明 |
|
|
57
63
|
|------|------|
|
|
58
64
|
| `openclaw onebot setup` | 交互式配置 OneBot 连接 |
|
|
59
|
-
| `openclaw
|
|
65
|
+
| `openclaw onebot get-group-msg-history --group-id <群号> [--hours N]` | 获取群历史(单页或最近 N 小时内,从旧到新) |
|
|
66
|
+
| `openclaw onebot search-group-member --group-id <群号> --name <名字>` | 按名字模糊搜群友 QQ 号 |
|
|
67
|
+
| `openclaw onebot upload-file --target group:<群号> --file <路径> --name <文件名>` | 上传文件到群/私聊 |
|
|
68
|
+
| `openclaw message send --channel onebot --target group:xxx --message "hi"` | 发送文本/图片 |
|
|
60
69
|
| `openclaw gateway status` | 查看 Gateway 状态 |
|
|
61
70
|
| `openclaw logs --follow` | 查看日志 |
|
|
71
|
+
|
|
72
|
+
AI 或脚本需要「获取群历史、查群友 QQ」时,应使用上述 CLI 或查阅 [agent-tools.md](agent-tools.md) 中的完整参数说明。
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# OneBot Agent 工具与 CLI
|
|
2
|
+
|
|
3
|
+
插件通过 WebSocket 与 OneBot(Lagrange.Core / go-cqhttp)通信,提供 **Agent 工具**(供 Cron/脚本/AI 调用)和等价的 **CLI 命令**(供人工或工作流直接调用)。
|
|
4
|
+
AI 或脚本应优先使用 **CLI**,便于复现、调试和文档化。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 前置条件
|
|
9
|
+
|
|
10
|
+
- **Gateway 已启动**:`openclaw gateway`(或 `openclaw gateway run`)
|
|
11
|
+
- OneBot 已运行并连接;若为正向 WS,CLI 会按配置自动建连
|
|
12
|
+
- 配置:`openclaw.json` 的 `channels.onebot` 或环境变量 `ONEBOT_WS_*`
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. 获取群历史消息
|
|
17
|
+
|
|
18
|
+
**Agent 工具**:`onebot_get_group_msg_history`
|
|
19
|
+
**CLI**:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
openclaw onebot get-group-msg-history --group-id <群号> [--hours <N>] [--count 50] [--message-seq <序号>]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- 指定 **`--hours N`**:获取**从现在到过去 N 小时**内的消息(内部按时间范围分页拉取),例如 `--hours 24` 即过去 24 小时。
|
|
26
|
+
- 不指定 `--hours`:按单页返回,始终**从旧到新**;分页时用上一批最早一条的 `message_seq` 作为 `--message-seq`。
|
|
27
|
+
|
|
28
|
+
| 参数 | 说明 |
|
|
29
|
+
|------|------|
|
|
30
|
+
| `--group-id` | 群号(必填) |
|
|
31
|
+
| `--hours` | 获取最近 N 小时内的消息(可选;指定后按时间范围拉取) |
|
|
32
|
+
| `--count` | 条数,默认 50(未指定 --hours 时生效) |
|
|
33
|
+
| `--message-seq` | 起始消息序号(可选,分页用,未指定 --hours 时生效) |
|
|
34
|
+
|
|
35
|
+
示例:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
openclaw onebot get-group-msg-history --group-id 123456789
|
|
39
|
+
openclaw onebot get-group-msg-history --group-id 123456789 --hours 24
|
|
40
|
+
openclaw onebot get-group-msg-history --group-id 123456789 --hours 1 --limit 500
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 2. 按名字模糊搜索群成员(查 QQ 号)
|
|
46
|
+
|
|
47
|
+
**Agent 工具**:`onebot_search_group_member`
|
|
48
|
+
**CLI**:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
openclaw onebot search-group-member --group-id <群号> --name <名字>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
| 参数 | 说明 |
|
|
55
|
+
|------|------|
|
|
56
|
+
| `--group-id` | 群号(必填) |
|
|
57
|
+
| `--name` | 要搜的名字(群名片或昵称,模糊匹配) |
|
|
58
|
+
|
|
59
|
+
输出:匹配到的 QQ 与展示名。
|
|
60
|
+
|
|
61
|
+
示例:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
openclaw onebot search-group-member --group-id 123456789 --name 小明
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. 发送文本
|
|
70
|
+
|
|
71
|
+
**Agent 工具**:`onebot_send_text`
|
|
72
|
+
**CLI**(推荐使用主命令):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
openclaw message send --channel onebot --target group:<群号> --message "内容"
|
|
76
|
+
openclaw message send --channel onebot --target user:<QQ号> --message "内容"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 4. 发送图片
|
|
82
|
+
|
|
83
|
+
**Agent 工具**:`onebot_send_image`
|
|
84
|
+
**CLI**:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
openclaw message send --channel onebot --target group:<群号> --media "file:///path/to.png"
|
|
88
|
+
openclaw message send --channel onebot --target user:<QQ号> --media "https://example.com/pic.jpg"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 5. 上传文件到群/私聊
|
|
94
|
+
|
|
95
|
+
**Agent 工具**:`onebot_upload_file`
|
|
96
|
+
**CLI**:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
openclaw onebot upload-file --target group:<群号> --file <本地绝对路径> --name <显示文件名>
|
|
100
|
+
openclaw onebot upload-file --target user:<QQ号> --file <本地绝对路径> --name <显示文件名>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 6. 执行脚本(Cron 等)
|
|
106
|
+
|
|
107
|
+
**Agent 工具**:`onebot_run_script`
|
|
108
|
+
**CLI**:无直接一对一命令,可由 Cron 或工作流调用脚本,脚本内使用上述 CLI 或 `onebotClient` API。
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 使用建议
|
|
113
|
+
|
|
114
|
+
- **AI / 自动化**:优先使用上述 **CLI 命令**,便于在 Skill 中写明「如何调用」、可复现。
|
|
115
|
+
- **Cron / 内置任务**:在 `openclaw.json` 的 `cronJobs` 中配置 `script`,脚本内通过 `onebotClient` 或子进程调用 CLI。
|
|
116
|
+
- **临时查询**:直接运行 `openclaw onebot search-group-member ...` 等。
|