@zhin.js/adapter-icqq 2.0.0 → 2.0.3
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/CHANGELOG.md +38 -0
- package/lib/adapter.d.ts +0 -9
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +1 -331
- package/lib/adapter.js.map +1 -1
- package/lib/bot.d.ts.map +1 -1
- package/lib/bot.js +2 -0
- package/lib/bot.js.map +1 -1
- package/lib/index.js +205 -1
- package/lib/index.js.map +1 -1
- package/package.json +9 -9
- package/skills/icqq/SKILL.md +49 -3
- package/src/adapter.ts +1 -355
- package/src/bot.ts +2 -0
- package/src/index.ts +209 -1
package/src/adapter.ts
CHANGED
|
@@ -5,12 +5,9 @@ import type { MemberInfo } from "@icqqjs/icqq";
|
|
|
5
5
|
import {
|
|
6
6
|
Adapter,
|
|
7
7
|
Plugin,
|
|
8
|
-
createGroupManagementTools,
|
|
9
|
-
type IGroupManagement,
|
|
10
|
-
type ToolPermissionLevel,
|
|
11
8
|
} from "zhin.js";
|
|
12
9
|
import { IcqqBot } from "./bot.js";
|
|
13
|
-
import type { IcqqBotConfig
|
|
10
|
+
import type { IcqqBotConfig } from "./types.js";
|
|
14
11
|
|
|
15
12
|
export class IcqqAdapter extends Adapter<IcqqBot> {
|
|
16
13
|
constructor(plugin: Plugin) {
|
|
@@ -91,358 +88,7 @@ export class IcqqAdapter extends Adapter<IcqqBot> {
|
|
|
91
88
|
// ── 生命周期 ───────────────────────────────────────────────────────
|
|
92
89
|
|
|
93
90
|
async start(): Promise<void> {
|
|
94
|
-
this.registerIcqqPlatformTools();
|
|
95
|
-
const groupTools = createGroupManagementTools(this as unknown as IGroupManagement, this.name);
|
|
96
|
-
groupTools.forEach((t) => this.addTool(t));
|
|
97
91
|
await super.start();
|
|
98
92
|
}
|
|
99
93
|
|
|
100
|
-
/**
|
|
101
|
-
* 注册 ICQQ 平台特有工具(头衔、戳一戳、群公告等)
|
|
102
|
-
*/
|
|
103
|
-
private registerIcqqPlatformTools(): void {
|
|
104
|
-
const CTX_BOT = {
|
|
105
|
-
type: "string" as const,
|
|
106
|
-
description: "执行操作的 Bot QQ号",
|
|
107
|
-
contextKey: "botId" as const,
|
|
108
|
-
};
|
|
109
|
-
const CTX_GROUP = {
|
|
110
|
-
type: "number" as const,
|
|
111
|
-
description: "目标群号",
|
|
112
|
-
contextKey: "sceneId" as const,
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// 设置头衔工具
|
|
116
|
-
this.addTool({
|
|
117
|
-
name: "icqq_set_title",
|
|
118
|
-
description:
|
|
119
|
-
"设置 QQ 群成员的专属头衔(显示在群昵称旁边的标签)。只有群主才能操作。可设置持续时间或永久。如果只有昵称没有 QQ号,请先调用 list_members 查询。",
|
|
120
|
-
tags: ["群管理", "成员管理", "头衔"],
|
|
121
|
-
keywords: ["头衔", "专属头衔", "设置头衔", "给头衔", "加头衔", "称号"],
|
|
122
|
-
parameters: {
|
|
123
|
-
type: "object",
|
|
124
|
-
properties: {
|
|
125
|
-
bot: CTX_BOT,
|
|
126
|
-
group_id: CTX_GROUP,
|
|
127
|
-
user_id: {
|
|
128
|
-
type: "number",
|
|
129
|
-
description: "目标成员 QQ号",
|
|
130
|
-
},
|
|
131
|
-
title: {
|
|
132
|
-
type: "string",
|
|
133
|
-
description: "头衔文字内容",
|
|
134
|
-
},
|
|
135
|
-
duration: {
|
|
136
|
-
type: "number",
|
|
137
|
-
description: "持续时间(秒),-1 表示永久,默认永久",
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
required: ["bot", "group_id", "user_id", "title"],
|
|
141
|
-
},
|
|
142
|
-
platforms: ["icqq"],
|
|
143
|
-
scopes: ["group"],
|
|
144
|
-
permissionLevel: "group_owner",
|
|
145
|
-
execute: async (args, context) => {
|
|
146
|
-
const { bot: botId, group_id, user_id, title, duration = -1 } = args;
|
|
147
|
-
const bot = this.bots.get(botId);
|
|
148
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
149
|
-
|
|
150
|
-
this.checkPermission(context, "group_owner");
|
|
151
|
-
|
|
152
|
-
const success = await bot.setTitle(group_id, user_id, title, duration);
|
|
153
|
-
return {
|
|
154
|
-
success,
|
|
155
|
-
message: success
|
|
156
|
-
? `已将 ${user_id} 的头衔设为 "${title}"`
|
|
157
|
-
: "设置失败",
|
|
158
|
-
};
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// 发送群公告工具
|
|
163
|
-
this.addTool({
|
|
164
|
-
name: "icqq_announce",
|
|
165
|
-
description: "发送 QQ 群公告(需要管理员权限)",
|
|
166
|
-
parameters: {
|
|
167
|
-
type: "object",
|
|
168
|
-
properties: {
|
|
169
|
-
bot: CTX_BOT,
|
|
170
|
-
group_id: CTX_GROUP,
|
|
171
|
-
content: {
|
|
172
|
-
type: "string",
|
|
173
|
-
description: "公告内容",
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
required: ["bot", "group_id", "content"],
|
|
177
|
-
},
|
|
178
|
-
platforms: ["icqq"],
|
|
179
|
-
scopes: ["group"],
|
|
180
|
-
permissionLevel: "group_admin",
|
|
181
|
-
execute: async (args, context) => {
|
|
182
|
-
const { bot: botId, group_id, content } = args;
|
|
183
|
-
const bot = this.bots.get(botId);
|
|
184
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
185
|
-
|
|
186
|
-
this.checkPermission(context, "group_admin");
|
|
187
|
-
|
|
188
|
-
const success = await bot.sendAnnounce(group_id, content);
|
|
189
|
-
return { success, message: success ? "群公告已发送" : "发送失败" };
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// 戳一戳工具
|
|
194
|
-
this.addTool({
|
|
195
|
-
name: "icqq_poke",
|
|
196
|
-
description:
|
|
197
|
-
'在 QQ 群中对某个成员执行"戳一戳"互动操作(类似拍一拍)。任何人都可以使用。注意:每次请求只戳一次,不要重复调用此工具。如果只有昵称没有 QQ号,请先调用 list_members 查询。',
|
|
198
|
-
tags: ["互动", "趣味", "戳一戳"],
|
|
199
|
-
keywords: ["戳", "戳一戳", "拍一拍", "拍", "碰一碰", "poke"],
|
|
200
|
-
parameters: {
|
|
201
|
-
type: "object",
|
|
202
|
-
properties: {
|
|
203
|
-
bot: CTX_BOT,
|
|
204
|
-
group_id: CTX_GROUP,
|
|
205
|
-
user_id: {
|
|
206
|
-
type: "number",
|
|
207
|
-
description: "要戳的目标成员 QQ号",
|
|
208
|
-
},
|
|
209
|
-
},
|
|
210
|
-
required: ["bot", "group_id", "user_id"],
|
|
211
|
-
},
|
|
212
|
-
platforms: ["icqq"],
|
|
213
|
-
scopes: ["group"],
|
|
214
|
-
permissionLevel: "user",
|
|
215
|
-
execute: async (args) => {
|
|
216
|
-
const { bot: botId, group_id, user_id } = args;
|
|
217
|
-
const bot = this.bots.get(botId);
|
|
218
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
219
|
-
|
|
220
|
-
const success = await bot.pokeMember(group_id, user_id);
|
|
221
|
-
return {
|
|
222
|
-
success,
|
|
223
|
-
message: success ? `已戳了戳 ${user_id}` : "戳一戳失败",
|
|
224
|
-
};
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// 获取被禁言列表工具
|
|
229
|
-
this.addTool({
|
|
230
|
-
name: "icqq_list_muted",
|
|
231
|
-
description:
|
|
232
|
-
"查询 QQ 群中当前被禁言的成员列表,返回被禁言成员的 QQ号和剩余禁言时间。此工具仅用于查询,不会执行禁言操作。如需禁言或解除禁言,请使用 icqq_mute_member 工具。",
|
|
233
|
-
tags: ["群查询", "禁言查询", "列表"],
|
|
234
|
-
keywords: ["禁言列表", "被禁言", "谁被禁言", "查看禁言", "禁言名单"],
|
|
235
|
-
parameters: {
|
|
236
|
-
type: "object",
|
|
237
|
-
properties: {
|
|
238
|
-
bot: CTX_BOT,
|
|
239
|
-
group_id: CTX_GROUP,
|
|
240
|
-
},
|
|
241
|
-
required: ["bot", "group_id"],
|
|
242
|
-
},
|
|
243
|
-
platforms: ["icqq"],
|
|
244
|
-
scopes: ["group"],
|
|
245
|
-
permissionLevel: "user",
|
|
246
|
-
execute: async (args) => {
|
|
247
|
-
const { bot: botId, group_id } = args;
|
|
248
|
-
const bot = this.bots.get(botId);
|
|
249
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
250
|
-
|
|
251
|
-
const mutedList = await bot.getMutedMembers(group_id);
|
|
252
|
-
return {
|
|
253
|
-
muted_members: mutedList.filter((m) => m !== null),
|
|
254
|
-
count: mutedList.filter((m) => m !== null).length,
|
|
255
|
-
};
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
this.addTool({
|
|
259
|
-
name: "icqq_send_user_like",
|
|
260
|
-
description: "给用户点赞(竖大拇指)。每人每天最多点赞 20 次,超出无效。如果只有昵称没有 QQ号,请先调用 list_members 查询。",
|
|
261
|
-
tags: ["互动", "趣味", "点赞"],
|
|
262
|
-
keywords: ["点赞", "赞我", "赞一下", "大拇指"],
|
|
263
|
-
parameters: {
|
|
264
|
-
type: "object",
|
|
265
|
-
properties: {
|
|
266
|
-
bot: CTX_BOT,
|
|
267
|
-
user_id: {
|
|
268
|
-
type: "number",
|
|
269
|
-
description: "要点赞的目标用户 QQ号(如果只有昵称,先用 list_members 查询获取)",
|
|
270
|
-
},
|
|
271
|
-
times: {
|
|
272
|
-
type: "number",
|
|
273
|
-
description: "点赞次数(1-20),默认 1 次,每人每天上限 20 次",
|
|
274
|
-
default: 1,
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
required: ["bot", "user_id"],
|
|
278
|
-
},
|
|
279
|
-
platforms: ["icqq"],
|
|
280
|
-
scopes: ["group", "private"],
|
|
281
|
-
permissionLevel: "user",
|
|
282
|
-
execute: async (args) => {
|
|
283
|
-
const { bot: botId, user_id, times = 1 } = args;
|
|
284
|
-
const bot = this.bots.get(botId);
|
|
285
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
286
|
-
const success = await bot.sendLike(user_id, Math.min(times, 20));
|
|
287
|
-
return {
|
|
288
|
-
success,
|
|
289
|
-
message: success ? `已发送用户点赞消息给 ${user_id}` : "发送失败",
|
|
290
|
-
};
|
|
291
|
-
},
|
|
292
|
-
});
|
|
293
|
-
// 设置匿名状态工具
|
|
294
|
-
this.addTool({
|
|
295
|
-
name: "icqq_set_anonymous",
|
|
296
|
-
description:
|
|
297
|
-
"开启或关闭 QQ 群的匿名聊天功能。开启后群成员可以匿名发言。需要 Bot 拥有管理员权限。",
|
|
298
|
-
tags: ["群管理", "群设置", "匿名"],
|
|
299
|
-
keywords: ["匿名", "匿名聊天", "开启匿名", "关闭匿名", "允许匿名"],
|
|
300
|
-
parameters: {
|
|
301
|
-
type: "object",
|
|
302
|
-
properties: {
|
|
303
|
-
bot: CTX_BOT,
|
|
304
|
-
group_id: CTX_GROUP,
|
|
305
|
-
enable: {
|
|
306
|
-
type: "boolean",
|
|
307
|
-
description: "true=开启匿名聊天,false=关闭匿名聊天,默认 true",
|
|
308
|
-
},
|
|
309
|
-
},
|
|
310
|
-
required: ["bot", "group_id"],
|
|
311
|
-
},
|
|
312
|
-
platforms: ["icqq"],
|
|
313
|
-
scopes: ["group"],
|
|
314
|
-
permissionLevel: "group_admin",
|
|
315
|
-
execute: async (args, context) => {
|
|
316
|
-
const { bot: botId, group_id, enable = true } = args;
|
|
317
|
-
const bot = this.bots.get(botId);
|
|
318
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
319
|
-
|
|
320
|
-
this.checkPermission(context, "group_admin");
|
|
321
|
-
|
|
322
|
-
const success = await bot.setAnonymous(group_id, enable);
|
|
323
|
-
return {
|
|
324
|
-
success,
|
|
325
|
-
message: success
|
|
326
|
-
? enable
|
|
327
|
-
? "已开启匿名聊天"
|
|
328
|
-
: "已关闭匿名聊天"
|
|
329
|
-
: "操作失败",
|
|
330
|
-
};
|
|
331
|
-
},
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
// 群文件列表
|
|
335
|
-
this.addTool({
|
|
336
|
-
name: "icqq_group_files",
|
|
337
|
-
description: "获取 QQ 群的群文件列表",
|
|
338
|
-
parameters: {
|
|
339
|
-
type: "object",
|
|
340
|
-
properties: {
|
|
341
|
-
bot: { type: "string", description: "Bot 名称" },
|
|
342
|
-
group_id: { type: "number", description: "群号" },
|
|
343
|
-
},
|
|
344
|
-
required: ["bot", "group_id"],
|
|
345
|
-
},
|
|
346
|
-
platforms: ["icqq"],
|
|
347
|
-
scopes: ["group"],
|
|
348
|
-
permissionLevel: "user",
|
|
349
|
-
execute: async (args) => {
|
|
350
|
-
const { bot: botId, group_id } = args;
|
|
351
|
-
const bot = this.bots.get(botId);
|
|
352
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
353
|
-
const files = await bot.getGroupFiles(group_id);
|
|
354
|
-
if (!files?.length) return { files: [], message: "群文件为空" };
|
|
355
|
-
return {
|
|
356
|
-
files: files.slice(0, 30).map((f: any) => ({
|
|
357
|
-
name: f.name,
|
|
358
|
-
size: f.size,
|
|
359
|
-
uploader: f.uploader_uin,
|
|
360
|
-
upload_time: f.upload_time,
|
|
361
|
-
})),
|
|
362
|
-
count: files.length,
|
|
363
|
-
};
|
|
364
|
-
},
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
// 好友列表
|
|
368
|
-
this.addTool({
|
|
369
|
-
name: "icqq_friend_list",
|
|
370
|
-
description: "获取 QQ 好友列表",
|
|
371
|
-
parameters: {
|
|
372
|
-
type: "object",
|
|
373
|
-
properties: {
|
|
374
|
-
bot: { type: "string", description: "Bot 名称" },
|
|
375
|
-
},
|
|
376
|
-
required: ["bot"],
|
|
377
|
-
},
|
|
378
|
-
platforms: ["icqq"],
|
|
379
|
-
scopes: ["group", "private"],
|
|
380
|
-
permissionLevel: "user",
|
|
381
|
-
execute: async (args) => {
|
|
382
|
-
const { bot: botId } = args;
|
|
383
|
-
const bot = this.bots.get(botId);
|
|
384
|
-
if (!bot) throw new Error(`Bot ${botId} 不存在`);
|
|
385
|
-
const fl = bot.fl;
|
|
386
|
-
const friends = Array.from(fl.values()).map((f: any) => ({
|
|
387
|
-
user_id: f.user_id,
|
|
388
|
-
nickname: f.nickname,
|
|
389
|
-
remark: f.remark,
|
|
390
|
-
}));
|
|
391
|
-
return { friends: friends.slice(0, 50), count: fl.size };
|
|
392
|
-
},
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
this.plugin.logger.debug("已注册 ICQQ 平台群管理工具");
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* 检查执行上下文中的权限
|
|
400
|
-
* 双重验证:优先从 ToolContext 获取权限级别,其次从消息发送者信息获取
|
|
401
|
-
*/
|
|
402
|
-
private checkPermission(context: any, required: ToolPermissionLevel): void {
|
|
403
|
-
const permissionLevels: Record<ToolPermissionLevel, number> = {
|
|
404
|
-
user: 0,
|
|
405
|
-
group_admin: 1,
|
|
406
|
-
group_owner: 2,
|
|
407
|
-
bot_admin: 3,
|
|
408
|
-
owner: 4,
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
const requiredLevel = permissionLevels[required] ?? 0;
|
|
412
|
-
if (requiredLevel === 0) return; // user 级别无需检查
|
|
413
|
-
|
|
414
|
-
// 1. 优先从 ToolContext.senderPermissionLevel 获取(AI Agent 路径注入)
|
|
415
|
-
if (context?.senderPermissionLevel) {
|
|
416
|
-
const ctxLevel =
|
|
417
|
-
permissionLevels[
|
|
418
|
-
context.senderPermissionLevel as ToolPermissionLevel
|
|
419
|
-
] ?? 0;
|
|
420
|
-
if (ctxLevel < requiredLevel) {
|
|
421
|
-
throw new Error(
|
|
422
|
-
`权限不足:需要 ${required} 权限,当前为 ${context.senderPermissionLevel}`,
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
return; // 检查通过
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// 2. 从消息的 $sender 获取权限(命令行/中间件路径)
|
|
429
|
-
const sender = context?.message?.$sender as IcqqSenderInfo | undefined;
|
|
430
|
-
if (!sender) {
|
|
431
|
-
// 无上下文且无发送者信息 → 拒绝高权限操作
|
|
432
|
-
throw new Error(
|
|
433
|
-
`权限不足:无法验证身份,拒绝执行需要 ${required} 权限的操作`,
|
|
434
|
-
);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
let senderLevel: ToolPermissionLevel = "user";
|
|
438
|
-
if (sender.isOwner || sender.role === "owner") {
|
|
439
|
-
senderLevel = "group_owner";
|
|
440
|
-
} else if (sender.isAdmin || sender.role === "admin") {
|
|
441
|
-
senderLevel = "group_admin";
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (permissionLevels[senderLevel] < requiredLevel) {
|
|
445
|
-
throw new Error(`权限不足:需要 ${required} 权限,当前为 ${senderLevel}`);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
94
|
}
|
package/src/bot.ts
CHANGED
|
@@ -165,6 +165,8 @@ export class IcqqBot
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
async $disconnect(): Promise<void> {
|
|
168
|
+
// Client 继承自 EventEmitter,清除 $connect() 注册的所有监听器
|
|
169
|
+
(this as unknown as import('node:events').EventEmitter).removeAllListeners();
|
|
168
170
|
await this.logout();
|
|
169
171
|
this.$connected = false;
|
|
170
172
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ICQQ 适配器入口:类型扩展、导出、注册
|
|
3
3
|
*/
|
|
4
4
|
import path from "path";
|
|
5
|
-
import { usePlugin, type Plugin } from "zhin.js";
|
|
5
|
+
import { usePlugin, type Plugin, type IGroupManagement, createGroupManagementTools, type ToolFeature } from "zhin.js";
|
|
6
6
|
import type { Router } from "@zhin.js/http";
|
|
7
7
|
import { MessageCommand } from "zhin.js";
|
|
8
8
|
import { IcqqAdapter } from "./adapter.js";
|
|
@@ -59,6 +59,214 @@ useContext("icqq", (icqq: IcqqAdapter) => {
|
|
|
59
59
|
);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
useContext("tool", "icqq", (toolService: ToolFeature, icqq: IcqqAdapter) => {
|
|
63
|
+
const groupTools = createGroupManagementTools(
|
|
64
|
+
icqq,
|
|
65
|
+
"icqq",
|
|
66
|
+
);
|
|
67
|
+
const disposers: (() => void)[] = groupTools.map(t => toolService.addTool(t, "icqq"));
|
|
68
|
+
|
|
69
|
+
// 设置头衔
|
|
70
|
+
disposers.push(toolService.addTool({
|
|
71
|
+
name: "icqq_set_title",
|
|
72
|
+
description: "设置 QQ 群成员的专属头衔",
|
|
73
|
+
parameters: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
bot: { type: "string", description: "Bot QQ号" },
|
|
77
|
+
group_id: { type: "number", description: "目标群号" },
|
|
78
|
+
user_id: { type: "number", description: "目标成员 QQ号" },
|
|
79
|
+
title: { type: "string", description: "头衔文字" },
|
|
80
|
+
duration: { type: "number", description: "持续时间(秒),-1永久" },
|
|
81
|
+
},
|
|
82
|
+
required: ["bot", "group_id", "user_id", "title"],
|
|
83
|
+
},
|
|
84
|
+
platforms: ["icqq"],
|
|
85
|
+
tags: ["icqq"],
|
|
86
|
+
execute: async (args: Record<string, any>) => {
|
|
87
|
+
const bot = icqq.bots.get(args.bot);
|
|
88
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
89
|
+
const success = await bot.setTitle(args.group_id, args.user_id, args.title, args.duration ?? -1);
|
|
90
|
+
return { success, message: success ? `已将 ${args.user_id} 的头衔设为 "${args.title}"` : "设置失败" };
|
|
91
|
+
},
|
|
92
|
+
}, "icqq"));
|
|
93
|
+
|
|
94
|
+
// 发送群公告
|
|
95
|
+
disposers.push(toolService.addTool({
|
|
96
|
+
name: "icqq_announce",
|
|
97
|
+
description: "发送 QQ 群公告(需要管理员权限)",
|
|
98
|
+
parameters: {
|
|
99
|
+
type: "object",
|
|
100
|
+
properties: {
|
|
101
|
+
bot: { type: "string", description: "Bot QQ号" },
|
|
102
|
+
group_id: { type: "number", description: "目标群号" },
|
|
103
|
+
content: { type: "string", description: "公告内容" },
|
|
104
|
+
},
|
|
105
|
+
required: ["bot", "group_id", "content"],
|
|
106
|
+
},
|
|
107
|
+
platforms: ["icqq"],
|
|
108
|
+
tags: ["icqq"],
|
|
109
|
+
execute: async (args: Record<string, any>) => {
|
|
110
|
+
const bot = icqq.bots.get(args.bot);
|
|
111
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
112
|
+
const success = await bot.sendAnnounce(args.group_id, args.content);
|
|
113
|
+
return { success, message: success ? "群公告已发送" : "发送失败" };
|
|
114
|
+
},
|
|
115
|
+
}, "icqq"));
|
|
116
|
+
|
|
117
|
+
// 戳一戳
|
|
118
|
+
disposers.push(toolService.addTool({
|
|
119
|
+
name: "icqq_poke",
|
|
120
|
+
description: "在 QQ 群中对某个成员执行戳一戳互动操作",
|
|
121
|
+
parameters: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
bot: { type: "string", description: "Bot QQ号" },
|
|
125
|
+
group_id: { type: "number", description: "目标群号" },
|
|
126
|
+
user_id: { type: "number", description: "要戳的目标成员 QQ号" },
|
|
127
|
+
},
|
|
128
|
+
required: ["bot", "group_id", "user_id"],
|
|
129
|
+
},
|
|
130
|
+
platforms: ["icqq"],
|
|
131
|
+
tags: ["icqq"],
|
|
132
|
+
execute: async (args: Record<string, any>) => {
|
|
133
|
+
const bot = icqq.bots.get(args.bot);
|
|
134
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
135
|
+
const success = await bot.pokeMember(args.group_id, args.user_id);
|
|
136
|
+
return { success, message: success ? `已戳了戳 ${args.user_id}` : "戳一戳失败" };
|
|
137
|
+
},
|
|
138
|
+
}, "icqq"));
|
|
139
|
+
|
|
140
|
+
// 获取被禁言列表
|
|
141
|
+
disposers.push(toolService.addTool({
|
|
142
|
+
name: "icqq_list_muted",
|
|
143
|
+
description: "查询 QQ 群中当前被禁言的成员列表",
|
|
144
|
+
parameters: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
bot: { type: "string", description: "Bot QQ号" },
|
|
148
|
+
group_id: { type: "number", description: "目标群号" },
|
|
149
|
+
},
|
|
150
|
+
required: ["bot", "group_id"],
|
|
151
|
+
},
|
|
152
|
+
platforms: ["icqq"],
|
|
153
|
+
tags: ["icqq"],
|
|
154
|
+
execute: async (args: Record<string, any>) => {
|
|
155
|
+
const bot = icqq.bots.get(args.bot);
|
|
156
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
157
|
+
const mutedList = await bot.getMutedMembers(args.group_id);
|
|
158
|
+
const filtered = mutedList.filter((m: any) => m !== null);
|
|
159
|
+
return { muted_members: filtered, count: filtered.length };
|
|
160
|
+
},
|
|
161
|
+
}, "icqq"));
|
|
162
|
+
|
|
163
|
+
// 给用户点赞
|
|
164
|
+
disposers.push(toolService.addTool({
|
|
165
|
+
name: "icqq_send_user_like",
|
|
166
|
+
description: "给用户点赞(竖大拇指),每人每天最多 20 次",
|
|
167
|
+
parameters: {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {
|
|
170
|
+
bot: { type: "string", description: "Bot QQ号" },
|
|
171
|
+
user_id: { type: "number", description: "要点赞的目标用户 QQ号" },
|
|
172
|
+
times: { type: "number", description: "点赞次数(1-20),默认 1" },
|
|
173
|
+
},
|
|
174
|
+
required: ["bot", "user_id"],
|
|
175
|
+
},
|
|
176
|
+
platforms: ["icqq"],
|
|
177
|
+
tags: ["icqq"],
|
|
178
|
+
execute: async (args: Record<string, any>) => {
|
|
179
|
+
const bot = icqq.bots.get(args.bot);
|
|
180
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
181
|
+
const success = await bot.sendLike(args.user_id, Math.min(args.times ?? 1, 20));
|
|
182
|
+
return { success, message: success ? `已给 ${args.user_id} 点赞` : "发送失败" };
|
|
183
|
+
},
|
|
184
|
+
}, "icqq"));
|
|
185
|
+
|
|
186
|
+
// 设置匿名聊天
|
|
187
|
+
disposers.push(toolService.addTool({
|
|
188
|
+
name: "icqq_set_anonymous",
|
|
189
|
+
description: "开启或关闭 QQ 群的匿名聊天功能",
|
|
190
|
+
parameters: {
|
|
191
|
+
type: "object",
|
|
192
|
+
properties: {
|
|
193
|
+
bot: { type: "string", description: "Bot QQ号" },
|
|
194
|
+
group_id: { type: "number", description: "目标群号" },
|
|
195
|
+
enable: { type: "boolean", description: "true=开启,false=关闭,默认 true" },
|
|
196
|
+
},
|
|
197
|
+
required: ["bot", "group_id"],
|
|
198
|
+
},
|
|
199
|
+
platforms: ["icqq"],
|
|
200
|
+
tags: ["icqq"],
|
|
201
|
+
execute: async (args: Record<string, any>) => {
|
|
202
|
+
const bot = icqq.bots.get(args.bot);
|
|
203
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
204
|
+
const enable = args.enable ?? true;
|
|
205
|
+
const success = await bot.setAnonymous(args.group_id, enable);
|
|
206
|
+
return { success, message: success ? (enable ? "已开启匿名聊天" : "已关闭匿名聊天") : "操作失败" };
|
|
207
|
+
},
|
|
208
|
+
}, "icqq"));
|
|
209
|
+
|
|
210
|
+
// 群文件列表
|
|
211
|
+
disposers.push(toolService.addTool({
|
|
212
|
+
name: "icqq_group_files",
|
|
213
|
+
description: "获取 QQ 群的群文件列表",
|
|
214
|
+
parameters: {
|
|
215
|
+
type: "object",
|
|
216
|
+
properties: {
|
|
217
|
+
bot: { type: "string", description: "Bot 名称" },
|
|
218
|
+
group_id: { type: "number", description: "群号" },
|
|
219
|
+
},
|
|
220
|
+
required: ["bot", "group_id"],
|
|
221
|
+
},
|
|
222
|
+
platforms: ["icqq"],
|
|
223
|
+
tags: ["icqq"],
|
|
224
|
+
execute: async (args: Record<string, any>) => {
|
|
225
|
+
const bot = icqq.bots.get(args.bot);
|
|
226
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
227
|
+
const files = await bot.getGroupFiles(args.group_id);
|
|
228
|
+
if (!files?.length) return { files: [], message: "群文件为空" };
|
|
229
|
+
return {
|
|
230
|
+
files: files.slice(0, 30).map((f: any) => ({
|
|
231
|
+
name: f.name,
|
|
232
|
+
size: f.size,
|
|
233
|
+
uploader: f.uploader_uin,
|
|
234
|
+
upload_time: f.upload_time,
|
|
235
|
+
})),
|
|
236
|
+
count: files.length,
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
}, "icqq"));
|
|
240
|
+
|
|
241
|
+
// 好友列表
|
|
242
|
+
disposers.push(toolService.addTool({
|
|
243
|
+
name: "icqq_friend_list",
|
|
244
|
+
description: "获取 QQ 好友列表",
|
|
245
|
+
parameters: {
|
|
246
|
+
type: "object",
|
|
247
|
+
properties: {
|
|
248
|
+
bot: { type: "string", description: "Bot 名称" },
|
|
249
|
+
},
|
|
250
|
+
required: ["bot"],
|
|
251
|
+
},
|
|
252
|
+
platforms: ["icqq"],
|
|
253
|
+
tags: ["icqq"],
|
|
254
|
+
execute: async (args: Record<string, any>) => {
|
|
255
|
+
const bot = icqq.bots.get(args.bot);
|
|
256
|
+
if (!bot) throw new Error(`Bot ${args.bot} 不存在`);
|
|
257
|
+
const fl = bot.fl;
|
|
258
|
+
const friends = Array.from(fl.values()).map((f: any) => ({
|
|
259
|
+
user_id: f.user_id,
|
|
260
|
+
nickname: f.nickname,
|
|
261
|
+
remark: f.remark,
|
|
262
|
+
}));
|
|
263
|
+
return { friends: friends.slice(0, 50), count: fl.size };
|
|
264
|
+
},
|
|
265
|
+
}, "icqq"));
|
|
266
|
+
|
|
267
|
+
return () => disposers.forEach(d => d());
|
|
268
|
+
});
|
|
269
|
+
|
|
62
270
|
useContext("web", (web: WebServer) => {
|
|
63
271
|
const dispose = web.addEntry(
|
|
64
272
|
path.resolve(import.meta.dirname, "../client/index.tsx"),
|