@hyl_aa/openclaw-napcat 0.1.0
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 -0
- package/README.md +129 -0
- package/README_EN.md +129 -0
- package/index.ts +17 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +49 -0
- package/src/accounts.ts +101 -0
- package/src/api.ts +134 -0
- package/src/channel.ts +366 -0
- package/src/config-schema.ts +72 -0
- package/src/monitor.ts +565 -0
- package/src/probe.ts +37 -0
- package/src/runtime.ts +6 -0
- package/src/send.ts +95 -0
- package/src/tools.ts +635 -0
- package/src/types.ts +104 -0
package/src/tools.ts
ADDED
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
import type { ChannelAgentTool } from "openclaw/plugin-sdk";
|
|
2
|
+
import { callOneBotApi } from "./api.js";
|
|
3
|
+
import { resolveNapCatAccount } from "./accounts.js";
|
|
4
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Helpers — plain JSON Schema objects (no typebox to avoid jiti dual-instance)
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
function resolveHttpApi(cfg: OpenClawConfig): { httpApi: string; accessToken?: string } {
|
|
11
|
+
const account = resolveNapCatAccount({ cfg });
|
|
12
|
+
return { httpApi: account.httpApi, accessToken: account.accessToken };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function numberProp(description: string, extra?: Record<string, unknown>) {
|
|
16
|
+
return { type: "number" as const, description, ...extra };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function optionalNumberProp(description: string, extra?: Record<string, unknown>) {
|
|
20
|
+
return { type: "number" as const, description, ...extra };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function stringProp(description: string) {
|
|
24
|
+
return { type: "string" as const, description };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function booleanProp(description: string) {
|
|
28
|
+
return { type: "boolean" as const, description };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function objectSchema(
|
|
32
|
+
properties: Record<string, unknown>,
|
|
33
|
+
required: string[],
|
|
34
|
+
) {
|
|
35
|
+
return { type: "object" as const, properties, required };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Tools
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
/** Give a QQ user a "like" (thumbs-up). */
|
|
43
|
+
export function createQQLikeTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
44
|
+
return {
|
|
45
|
+
label: "QQ Like User",
|
|
46
|
+
name: "qq_like_user",
|
|
47
|
+
ownerOnly: false,
|
|
48
|
+
description:
|
|
49
|
+
"Give a QQ user a thumbs-up (like/praise). Provide the target QQ number and how many times to like (1-10). When a user @mentions someone, extract the QQ number from @QQNumber in the message.",
|
|
50
|
+
parameters: objectSchema(
|
|
51
|
+
{
|
|
52
|
+
user_id: numberProp("Target QQ number"),
|
|
53
|
+
times: optionalNumberProp("Number of likes, 1-10, default 10", { minimum: 1, maximum: 10 }),
|
|
54
|
+
},
|
|
55
|
+
["user_id"],
|
|
56
|
+
),
|
|
57
|
+
execute: async (_toolCallId, args) => {
|
|
58
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
59
|
+
const { user_id, times = 10 } = args as { user_id: number; times?: number };
|
|
60
|
+
await callOneBotApi(httpApi, "send_like", { user_id, times }, { accessToken });
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: "text" as const, text: `Successfully liked user ${user_id} ${times} time(s).` }],
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Get a QQ user's profile info (stranger info). */
|
|
69
|
+
export function createQQGetUserInfoTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
70
|
+
return {
|
|
71
|
+
label: "QQ Get User Info",
|
|
72
|
+
name: "qq_get_user_info",
|
|
73
|
+
ownerOnly: false,
|
|
74
|
+
description:
|
|
75
|
+
"Get a QQ user's profile information including nickname, age, sex, signature, level, etc. Useful for analyzing a person's profile. When a user @mentions someone, extract the QQ number from @QQNumber in the message.",
|
|
76
|
+
parameters: objectSchema(
|
|
77
|
+
{ user_id: numberProp("Target QQ number") },
|
|
78
|
+
["user_id"],
|
|
79
|
+
),
|
|
80
|
+
execute: async (_toolCallId, args) => {
|
|
81
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
82
|
+
const { user_id } = args as { user_id: number };
|
|
83
|
+
const resp = await callOneBotApi<Record<string, unknown>>(
|
|
84
|
+
httpApi,
|
|
85
|
+
"get_stranger_info",
|
|
86
|
+
{ user_id, no_cache: true },
|
|
87
|
+
{ accessToken },
|
|
88
|
+
);
|
|
89
|
+
const info = resp.data;
|
|
90
|
+
const lines = [
|
|
91
|
+
`QQ: ${info.user_id}`,
|
|
92
|
+
`Nickname: ${info.nickname ?? "unknown"}`,
|
|
93
|
+
info.sex ? `Sex: ${info.sex}` : null,
|
|
94
|
+
info.age ? `Age: ${info.age}` : null,
|
|
95
|
+
info.sign ? `Signature: ${info.sign}` : null,
|
|
96
|
+
info.level ? `Level: ${info.level}` : null,
|
|
97
|
+
info.login_days ? `Login days: ${info.login_days}` : null,
|
|
98
|
+
info.qid ? `QID: ${info.qid}` : null,
|
|
99
|
+
]
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.join("\n");
|
|
102
|
+
return { content: [{ type: "text" as const, text: lines }] };
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Get group info. */
|
|
108
|
+
export function createQQGetGroupInfoTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
109
|
+
return {
|
|
110
|
+
label: "QQ Get Group Info",
|
|
111
|
+
name: "qq_get_group_info",
|
|
112
|
+
ownerOnly: false,
|
|
113
|
+
description: "Get QQ group information including name, member count, etc.",
|
|
114
|
+
parameters: objectSchema(
|
|
115
|
+
{ group_id: numberProp("Target group number") },
|
|
116
|
+
["group_id"],
|
|
117
|
+
),
|
|
118
|
+
execute: async (_toolCallId, args) => {
|
|
119
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
120
|
+
const { group_id } = args as { group_id: number };
|
|
121
|
+
const resp = await callOneBotApi<Record<string, unknown>>(
|
|
122
|
+
httpApi,
|
|
123
|
+
"get_group_info",
|
|
124
|
+
{ group_id, no_cache: true },
|
|
125
|
+
{ accessToken },
|
|
126
|
+
);
|
|
127
|
+
const g = resp.data;
|
|
128
|
+
const lines = [
|
|
129
|
+
`Group: ${g.group_id}`,
|
|
130
|
+
`Name: ${g.group_name ?? "unknown"}`,
|
|
131
|
+
g.member_count ? `Members: ${g.member_count}` : null,
|
|
132
|
+
g.max_member_count ? `Max members: ${g.max_member_count}` : null,
|
|
133
|
+
]
|
|
134
|
+
.filter(Boolean)
|
|
135
|
+
.join("\n");
|
|
136
|
+
return { content: [{ type: "text" as const, text: lines }] };
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Get group member info. */
|
|
142
|
+
export function createQQGetGroupMemberInfoTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
143
|
+
return {
|
|
144
|
+
label: "QQ Get Group Member Info",
|
|
145
|
+
name: "qq_get_group_member_info",
|
|
146
|
+
ownerOnly: false,
|
|
147
|
+
description:
|
|
148
|
+
"Get a specific member's info within a QQ group, including card name, role, join time, last active time, title, etc. When a user @mentions someone, extract the QQ number from @QQNumber in the message.",
|
|
149
|
+
parameters: objectSchema(
|
|
150
|
+
{
|
|
151
|
+
group_id: numberProp("Group number"),
|
|
152
|
+
user_id: numberProp("Target QQ number"),
|
|
153
|
+
},
|
|
154
|
+
["group_id", "user_id"],
|
|
155
|
+
),
|
|
156
|
+
execute: async (_toolCallId, args) => {
|
|
157
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
158
|
+
const { group_id, user_id } = args as { group_id: number; user_id: number };
|
|
159
|
+
const resp = await callOneBotApi<Record<string, unknown>>(
|
|
160
|
+
httpApi,
|
|
161
|
+
"get_group_member_info",
|
|
162
|
+
{ group_id, user_id, no_cache: true },
|
|
163
|
+
{ accessToken },
|
|
164
|
+
);
|
|
165
|
+
const m = resp.data;
|
|
166
|
+
const lines = [
|
|
167
|
+
`QQ: ${m.user_id}`,
|
|
168
|
+
`Nickname: ${m.nickname ?? "unknown"}`,
|
|
169
|
+
m.card ? `Card: ${m.card}` : null,
|
|
170
|
+
m.role ? `Role: ${m.role}` : null,
|
|
171
|
+
m.title ? `Title: ${m.title}` : null,
|
|
172
|
+
m.join_time ? `Joined: ${new Date((m.join_time as number) * 1000).toISOString()}` : null,
|
|
173
|
+
m.last_sent_time
|
|
174
|
+
? `Last active: ${new Date((m.last_sent_time as number) * 1000).toISOString()}`
|
|
175
|
+
: null,
|
|
176
|
+
m.level ? `Level: ${m.level}` : null,
|
|
177
|
+
]
|
|
178
|
+
.filter(Boolean)
|
|
179
|
+
.join("\n");
|
|
180
|
+
return { content: [{ type: "text" as const, text: lines }] };
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Mute a group member. */
|
|
186
|
+
export function createQQMuteGroupMemberTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
187
|
+
return {
|
|
188
|
+
label: "QQ Mute Group Member",
|
|
189
|
+
name: "qq_mute_group_member",
|
|
190
|
+
ownerOnly: true,
|
|
191
|
+
description:
|
|
192
|
+
"Mute (ban) a member in a QQ group for a specified duration. Set duration to 0 to unmute. The user_id can come from an @QQNumber mention in the conversation.",
|
|
193
|
+
parameters: objectSchema(
|
|
194
|
+
{
|
|
195
|
+
group_id: numberProp("Group number"),
|
|
196
|
+
user_id: numberProp("Target QQ number to mute"),
|
|
197
|
+
duration: optionalNumberProp("Mute duration in seconds (0 = unmute, default 600)", { minimum: 0 }),
|
|
198
|
+
},
|
|
199
|
+
["group_id", "user_id"],
|
|
200
|
+
),
|
|
201
|
+
execute: async (_toolCallId, args) => {
|
|
202
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
203
|
+
const { group_id, user_id, duration = 600 } = args as {
|
|
204
|
+
group_id: number;
|
|
205
|
+
user_id: number;
|
|
206
|
+
duration?: number;
|
|
207
|
+
};
|
|
208
|
+
await callOneBotApi(
|
|
209
|
+
httpApi,
|
|
210
|
+
"set_group_ban",
|
|
211
|
+
{ group_id, user_id, duration },
|
|
212
|
+
{ accessToken },
|
|
213
|
+
);
|
|
214
|
+
const action = duration === 0 ? "unmuted" : `muted for ${duration}s`;
|
|
215
|
+
return {
|
|
216
|
+
content: [{ type: "text" as const, text: `User ${user_id} has been ${action} in group ${group_id}.` }],
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Kick a member from a group. */
|
|
223
|
+
export function createQQKickGroupMemberTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
224
|
+
return {
|
|
225
|
+
label: "QQ Kick Group Member",
|
|
226
|
+
name: "qq_kick_group_member",
|
|
227
|
+
ownerOnly: true,
|
|
228
|
+
description: "Remove (kick) a member from a QQ group. The user_id can come from an @QQNumber mention in the conversation.",
|
|
229
|
+
parameters: objectSchema(
|
|
230
|
+
{
|
|
231
|
+
group_id: numberProp("Group number"),
|
|
232
|
+
user_id: numberProp("Target QQ number to kick"),
|
|
233
|
+
reject_add_request: booleanProp("Whether to reject future join requests from this user"),
|
|
234
|
+
},
|
|
235
|
+
["group_id", "user_id"],
|
|
236
|
+
),
|
|
237
|
+
execute: async (_toolCallId, args) => {
|
|
238
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
239
|
+
const { group_id, user_id, reject_add_request = false } = args as {
|
|
240
|
+
group_id: number;
|
|
241
|
+
user_id: number;
|
|
242
|
+
reject_add_request?: boolean;
|
|
243
|
+
};
|
|
244
|
+
await callOneBotApi(
|
|
245
|
+
httpApi,
|
|
246
|
+
"set_group_kick",
|
|
247
|
+
{ group_id, user_id, reject_add_request },
|
|
248
|
+
{ accessToken },
|
|
249
|
+
);
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text" as const, text: `User ${user_id} has been kicked from group ${group_id}.` }],
|
|
252
|
+
};
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Send a poke/nudge to a user. */
|
|
258
|
+
export function createQQPokeTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
259
|
+
return {
|
|
260
|
+
label: "QQ Poke",
|
|
261
|
+
name: "qq_poke",
|
|
262
|
+
ownerOnly: false,
|
|
263
|
+
description: "Send a poke (nudge) to a QQ user in a group or private chat. The user_id can come from an @QQNumber mention in the conversation.",
|
|
264
|
+
parameters: objectSchema(
|
|
265
|
+
{
|
|
266
|
+
user_id: numberProp("Target QQ number to poke"),
|
|
267
|
+
group_id: optionalNumberProp("Group number (omit for private poke)"),
|
|
268
|
+
},
|
|
269
|
+
["user_id"],
|
|
270
|
+
),
|
|
271
|
+
execute: async (_toolCallId, args) => {
|
|
272
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
273
|
+
const { user_id, group_id } = args as { user_id: number; group_id?: number };
|
|
274
|
+
if (group_id) {
|
|
275
|
+
await callOneBotApi(httpApi, "group_poke", { group_id, user_id }, { accessToken });
|
|
276
|
+
} else {
|
|
277
|
+
await callOneBotApi(httpApi, "friend_poke", { user_id }, { accessToken });
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
content: [{ type: "text" as const, text: `Poked user ${user_id}${group_id ? ` in group ${group_id}` : ""}.` }],
|
|
281
|
+
};
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** Recall (delete) a sent message. */
|
|
287
|
+
export function createQQRecallMessageTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
288
|
+
return {
|
|
289
|
+
label: "QQ Recall Message",
|
|
290
|
+
name: "qq_recall_message",
|
|
291
|
+
ownerOnly: true,
|
|
292
|
+
description: "Recall (unsend/delete) a message by its message ID.",
|
|
293
|
+
parameters: objectSchema(
|
|
294
|
+
{ message_id: numberProp("Message ID to recall") },
|
|
295
|
+
["message_id"],
|
|
296
|
+
),
|
|
297
|
+
execute: async (_toolCallId, args) => {
|
|
298
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
299
|
+
const { message_id } = args as { message_id: number };
|
|
300
|
+
await callOneBotApi(httpApi, "delete_msg", { message_id }, { accessToken });
|
|
301
|
+
return {
|
|
302
|
+
content: [{ type: "text" as const, text: `Message ${message_id} has been recalled.` }],
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Set group member card (nickname in group). */
|
|
309
|
+
export function createQQSetGroupCardTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
310
|
+
return {
|
|
311
|
+
label: "QQ Set Group Card",
|
|
312
|
+
name: "qq_set_group_card",
|
|
313
|
+
ownerOnly: true,
|
|
314
|
+
description: "Set a member's card (display name) in a QQ group. The user_id can come from an @QQNumber mention in the conversation.",
|
|
315
|
+
parameters: objectSchema(
|
|
316
|
+
{
|
|
317
|
+
group_id: numberProp("Group number"),
|
|
318
|
+
user_id: numberProp("Target QQ number"),
|
|
319
|
+
card: stringProp("New card name (empty string to clear)"),
|
|
320
|
+
},
|
|
321
|
+
["group_id", "user_id", "card"],
|
|
322
|
+
),
|
|
323
|
+
execute: async (_toolCallId, args) => {
|
|
324
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
325
|
+
const { group_id, user_id, card } = args as { group_id: number; user_id: number; card: string };
|
|
326
|
+
await callOneBotApi(
|
|
327
|
+
httpApi,
|
|
328
|
+
"set_group_card",
|
|
329
|
+
{ group_id, user_id, card },
|
|
330
|
+
{ accessToken },
|
|
331
|
+
);
|
|
332
|
+
return {
|
|
333
|
+
content: [
|
|
334
|
+
{ type: "text" as const, text: `Set user ${user_id}'s card to "${card}" in group ${group_id}.` },
|
|
335
|
+
],
|
|
336
|
+
};
|
|
337
|
+
},
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Get bot's friend list. */
|
|
342
|
+
export function createQQGetFriendListTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
343
|
+
return {
|
|
344
|
+
label: "QQ Get Friend List",
|
|
345
|
+
name: "qq_get_friend_list",
|
|
346
|
+
ownerOnly: false,
|
|
347
|
+
description:
|
|
348
|
+
"Get the bot's full friend list. Returns each friend's QQ number, nickname, and remark. When a user mentions someone by @QQNumber, you can use this to find matching friends.",
|
|
349
|
+
parameters: objectSchema({}, []),
|
|
350
|
+
execute: async (_toolCallId, _args) => {
|
|
351
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
352
|
+
const resp = await callOneBotApi<Array<Record<string, unknown>>>(
|
|
353
|
+
httpApi,
|
|
354
|
+
"get_friend_list",
|
|
355
|
+
{},
|
|
356
|
+
{ accessToken },
|
|
357
|
+
);
|
|
358
|
+
const friends = resp.data;
|
|
359
|
+
const lines = friends.map(
|
|
360
|
+
(f) => `${f.user_id} | ${f.nickname ?? ""}${f.remark ? ` (${f.remark})` : ""}`,
|
|
361
|
+
);
|
|
362
|
+
return {
|
|
363
|
+
content: [{ type: "text" as const, text: `Friends (${friends.length}):\n${lines.join("\n")}` }],
|
|
364
|
+
};
|
|
365
|
+
},
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Get bot's group list. */
|
|
370
|
+
export function createQQGetGroupListTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
371
|
+
return {
|
|
372
|
+
label: "QQ Get Group List",
|
|
373
|
+
name: "qq_get_group_list",
|
|
374
|
+
ownerOnly: false,
|
|
375
|
+
description:
|
|
376
|
+
"Get the bot's full group list. Returns each group's ID, name, and member count.",
|
|
377
|
+
parameters: objectSchema({}, []),
|
|
378
|
+
execute: async (_toolCallId, _args) => {
|
|
379
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
380
|
+
const resp = await callOneBotApi<Array<Record<string, unknown>>>(
|
|
381
|
+
httpApi,
|
|
382
|
+
"get_group_list",
|
|
383
|
+
{},
|
|
384
|
+
{ accessToken },
|
|
385
|
+
);
|
|
386
|
+
const groups = resp.data;
|
|
387
|
+
const lines = groups.map(
|
|
388
|
+
(g) => `${g.group_id} | ${g.group_name ?? "unknown"} | ${g.member_count ?? "?"} members`,
|
|
389
|
+
);
|
|
390
|
+
return {
|
|
391
|
+
content: [{ type: "text" as const, text: `Groups (${groups.length}):\n${lines.join("\n")}` }],
|
|
392
|
+
};
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Get all members of a group. */
|
|
398
|
+
export function createQQGetGroupMemberListTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
399
|
+
return {
|
|
400
|
+
label: "QQ Get Group Member List",
|
|
401
|
+
name: "qq_get_group_member_list",
|
|
402
|
+
ownerOnly: false,
|
|
403
|
+
description:
|
|
404
|
+
"Get the full member list of a QQ group. Returns each member's QQ number, nickname, card, and role. Useful for resolving @QQNumber mentions to real names.",
|
|
405
|
+
parameters: objectSchema(
|
|
406
|
+
{ group_id: numberProp("Group number") },
|
|
407
|
+
["group_id"],
|
|
408
|
+
),
|
|
409
|
+
execute: async (_toolCallId, args) => {
|
|
410
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
411
|
+
const { group_id } = args as { group_id: number };
|
|
412
|
+
const resp = await callOneBotApi<Array<Record<string, unknown>>>(
|
|
413
|
+
httpApi,
|
|
414
|
+
"get_group_member_list",
|
|
415
|
+
{ group_id },
|
|
416
|
+
{ accessToken },
|
|
417
|
+
);
|
|
418
|
+
const members = resp.data;
|
|
419
|
+
const lines = members.map(
|
|
420
|
+
(m) =>
|
|
421
|
+
`${m.user_id} | ${m.card || m.nickname || "unknown"} | ${m.role ?? "member"}`,
|
|
422
|
+
);
|
|
423
|
+
return {
|
|
424
|
+
content: [
|
|
425
|
+
{ type: "text" as const, text: `Group ${group_id} members (${members.length}):\n${lines.join("\n")}` },
|
|
426
|
+
],
|
|
427
|
+
};
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** Set/unset group admin. */
|
|
433
|
+
export function createQQSetGroupAdminTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
434
|
+
return {
|
|
435
|
+
label: "QQ Set Group Admin",
|
|
436
|
+
name: "qq_set_group_admin",
|
|
437
|
+
ownerOnly: true,
|
|
438
|
+
description:
|
|
439
|
+
"Set or unset a member as group admin in a QQ group. The user_id can come from an @QQNumber mention in the conversation.",
|
|
440
|
+
parameters: objectSchema(
|
|
441
|
+
{
|
|
442
|
+
group_id: numberProp("Group number"),
|
|
443
|
+
user_id: numberProp("Target QQ number"),
|
|
444
|
+
enable: booleanProp("true = set as admin, false = remove admin"),
|
|
445
|
+
},
|
|
446
|
+
["group_id", "user_id", "enable"],
|
|
447
|
+
),
|
|
448
|
+
execute: async (_toolCallId, args) => {
|
|
449
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
450
|
+
const { group_id, user_id, enable } = args as {
|
|
451
|
+
group_id: number;
|
|
452
|
+
user_id: number;
|
|
453
|
+
enable: boolean;
|
|
454
|
+
};
|
|
455
|
+
await callOneBotApi(
|
|
456
|
+
httpApi,
|
|
457
|
+
"set_group_admin",
|
|
458
|
+
{ group_id, user_id, enable },
|
|
459
|
+
{ accessToken },
|
|
460
|
+
);
|
|
461
|
+
const action = enable ? "promoted to admin" : "removed from admin";
|
|
462
|
+
return {
|
|
463
|
+
content: [{ type: "text" as const, text: `User ${user_id} has been ${action} in group ${group_id}.` }],
|
|
464
|
+
};
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/** Change group name. */
|
|
470
|
+
export function createQQSetGroupNameTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
471
|
+
return {
|
|
472
|
+
label: "QQ Set Group Name",
|
|
473
|
+
name: "qq_set_group_name",
|
|
474
|
+
ownerOnly: true,
|
|
475
|
+
description: "Change the name of a QQ group.",
|
|
476
|
+
parameters: objectSchema(
|
|
477
|
+
{
|
|
478
|
+
group_id: numberProp("Group number"),
|
|
479
|
+
group_name: stringProp("New group name"),
|
|
480
|
+
},
|
|
481
|
+
["group_id", "group_name"],
|
|
482
|
+
),
|
|
483
|
+
execute: async (_toolCallId, args) => {
|
|
484
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
485
|
+
const { group_id, group_name } = args as { group_id: number; group_name: string };
|
|
486
|
+
await callOneBotApi(
|
|
487
|
+
httpApi,
|
|
488
|
+
"set_group_name",
|
|
489
|
+
{ group_id, group_name },
|
|
490
|
+
{ accessToken },
|
|
491
|
+
);
|
|
492
|
+
return {
|
|
493
|
+
content: [{ type: "text" as const, text: `Group ${group_id} name changed to "${group_name}".` }],
|
|
494
|
+
};
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Toggle whole-group mute. */
|
|
500
|
+
export function createQQSetGroupWholeBanTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
501
|
+
return {
|
|
502
|
+
label: "QQ Set Group Whole Ban",
|
|
503
|
+
name: "qq_set_group_whole_ban",
|
|
504
|
+
ownerOnly: true,
|
|
505
|
+
description: "Enable or disable whole-group mute (all members muted). Only admins/owners can speak when enabled.",
|
|
506
|
+
parameters: objectSchema(
|
|
507
|
+
{
|
|
508
|
+
group_id: numberProp("Group number"),
|
|
509
|
+
enable: booleanProp("true = mute all, false = unmute all"),
|
|
510
|
+
},
|
|
511
|
+
["group_id", "enable"],
|
|
512
|
+
),
|
|
513
|
+
execute: async (_toolCallId, args) => {
|
|
514
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
515
|
+
const { group_id, enable } = args as { group_id: number; enable: boolean };
|
|
516
|
+
await callOneBotApi(
|
|
517
|
+
httpApi,
|
|
518
|
+
"set_group_whole_ban",
|
|
519
|
+
{ group_id, enable },
|
|
520
|
+
{ accessToken },
|
|
521
|
+
);
|
|
522
|
+
const action = enable ? "enabled" : "disabled";
|
|
523
|
+
return {
|
|
524
|
+
content: [{ type: "text" as const, text: `Whole-group mute ${action} for group ${group_id}.` }],
|
|
525
|
+
};
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/** Send a group announcement/notice. */
|
|
531
|
+
export function createQQSendGroupNoticeTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
532
|
+
return {
|
|
533
|
+
label: "QQ Send Group Notice",
|
|
534
|
+
name: "qq_send_group_notice",
|
|
535
|
+
ownerOnly: true,
|
|
536
|
+
description: "Send a group announcement/notice in a QQ group. Requires admin or owner permission.",
|
|
537
|
+
parameters: objectSchema(
|
|
538
|
+
{
|
|
539
|
+
group_id: numberProp("Group number"),
|
|
540
|
+
content: stringProp("Announcement content text"),
|
|
541
|
+
},
|
|
542
|
+
["group_id", "content"],
|
|
543
|
+
),
|
|
544
|
+
execute: async (_toolCallId, args) => {
|
|
545
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
546
|
+
const { group_id, content } = args as { group_id: number; content: string };
|
|
547
|
+
await callOneBotApi(
|
|
548
|
+
httpApi,
|
|
549
|
+
"_send_group_notice",
|
|
550
|
+
{ group_id, content },
|
|
551
|
+
{ accessToken },
|
|
552
|
+
);
|
|
553
|
+
return {
|
|
554
|
+
content: [{ type: "text" as const, text: `Group notice sent to group ${group_id}.` }],
|
|
555
|
+
};
|
|
556
|
+
},
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/** Get group honor/achievements info. */
|
|
561
|
+
export function createQQGetGroupHonorInfoTool(cfg: OpenClawConfig): ChannelAgentTool {
|
|
562
|
+
return {
|
|
563
|
+
label: "QQ Get Group Honor Info",
|
|
564
|
+
name: "qq_get_group_honor_info",
|
|
565
|
+
ownerOnly: false,
|
|
566
|
+
description:
|
|
567
|
+
"Get group honor/achievement info (talkative, performer, legend, strong newbie, emotion). Use type='all' for everything.",
|
|
568
|
+
parameters: objectSchema(
|
|
569
|
+
{
|
|
570
|
+
group_id: numberProp("Group number"),
|
|
571
|
+
type: stringProp("Honor type: talkative, performer, legend, strong_newbie, emotion, or all"),
|
|
572
|
+
},
|
|
573
|
+
["group_id", "type"],
|
|
574
|
+
),
|
|
575
|
+
execute: async (_toolCallId, args) => {
|
|
576
|
+
const { httpApi, accessToken } = resolveHttpApi(cfg);
|
|
577
|
+
const { group_id, type } = args as { group_id: number; type: string };
|
|
578
|
+
const resp = await callOneBotApi<Record<string, unknown>>(
|
|
579
|
+
httpApi,
|
|
580
|
+
"get_group_honor_info",
|
|
581
|
+
{ group_id, type },
|
|
582
|
+
{ accessToken },
|
|
583
|
+
);
|
|
584
|
+
const data = resp.data;
|
|
585
|
+
const sections: string[] = [`Group ${group_id} Honor Info:`];
|
|
586
|
+
// Current talkative
|
|
587
|
+
if (data.current_talkative) {
|
|
588
|
+
const t = data.current_talkative as Record<string, unknown>;
|
|
589
|
+
sections.push(`Dragon King: ${t.nickname ?? t.user_id} (${t.day_count} days)`);
|
|
590
|
+
}
|
|
591
|
+
// List-type honors
|
|
592
|
+
for (const key of ["talkative_list", "performer_list", "legend_list", "strong_newbie_list", "emotion_list"]) {
|
|
593
|
+
const list = data[key] as Array<Record<string, unknown>> | undefined;
|
|
594
|
+
if (list && list.length > 0) {
|
|
595
|
+
const label = key.replace(/_list$/, "").replace(/_/g, " ");
|
|
596
|
+
sections.push(`\n${label}:`);
|
|
597
|
+
for (const entry of list.slice(0, 10)) {
|
|
598
|
+
sections.push(` ${entry.nickname ?? entry.user_id} — ${entry.description ?? ""}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
content: [{ type: "text" as const, text: sections.join("\n") }],
|
|
604
|
+
};
|
|
605
|
+
},
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// ---------------------------------------------------------------------------
|
|
610
|
+
// Aggregate
|
|
611
|
+
// ---------------------------------------------------------------------------
|
|
612
|
+
|
|
613
|
+
/** Build all NapCat agent tools. */
|
|
614
|
+
export function createNapCatAgentTools(cfg: OpenClawConfig): ChannelAgentTool[] {
|
|
615
|
+
return [
|
|
616
|
+
createQQLikeTool(cfg),
|
|
617
|
+
createQQGetUserInfoTool(cfg),
|
|
618
|
+
createQQGetGroupInfoTool(cfg),
|
|
619
|
+
createQQGetGroupMemberInfoTool(cfg),
|
|
620
|
+
createQQMuteGroupMemberTool(cfg),
|
|
621
|
+
createQQKickGroupMemberTool(cfg),
|
|
622
|
+
createQQPokeTool(cfg),
|
|
623
|
+
createQQRecallMessageTool(cfg),
|
|
624
|
+
createQQSetGroupCardTool(cfg),
|
|
625
|
+
// First-batch expansion
|
|
626
|
+
createQQGetFriendListTool(cfg),
|
|
627
|
+
createQQGetGroupListTool(cfg),
|
|
628
|
+
createQQGetGroupMemberListTool(cfg),
|
|
629
|
+
createQQSetGroupAdminTool(cfg),
|
|
630
|
+
createQQSetGroupNameTool(cfg),
|
|
631
|
+
createQQSetGroupWholeBanTool(cfg),
|
|
632
|
+
createQQSendGroupNoticeTool(cfg),
|
|
633
|
+
createQQGetGroupHonorInfoTool(cfg),
|
|
634
|
+
];
|
|
635
|
+
}
|