mioku-plugin-admin 2.1.0 → 2.2.1
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/commands/group.ts +36 -0
- package/package.json +11 -1
- package/skills/group.ts +242 -307
- package/skills/personal.ts +170 -320
package/commands/group.ts
CHANGED
|
@@ -41,6 +41,8 @@ export function registerGroupAdminCommands(ctx: MiokiContext) {
|
|
|
41
41
|
"/改群名片",
|
|
42
42
|
"/改群昵称",
|
|
43
43
|
"/改群头像",
|
|
44
|
+
"/撤回",
|
|
45
|
+
"撤回",
|
|
44
46
|
].some((prefix) => text.startsWith(prefix));
|
|
45
47
|
|
|
46
48
|
try {
|
|
@@ -479,6 +481,40 @@ export function registerGroupAdminCommands(ctx: MiokiContext) {
|
|
|
479
481
|
}
|
|
480
482
|
return;
|
|
481
483
|
}
|
|
484
|
+
|
|
485
|
+
// 撤回 — 引用一条消息后,bot 撤回该消息
|
|
486
|
+
if (text === "/撤回" || text === "撤回") {
|
|
487
|
+
if (!isGroup || !groupId) {
|
|
488
|
+
return event.reply("这个要在群里用哦~", true);
|
|
489
|
+
}
|
|
490
|
+
if (!(await ensureAdminPermission())) return;
|
|
491
|
+
|
|
492
|
+
const quotedId = event.quote_id;
|
|
493
|
+
if (!quotedId) {
|
|
494
|
+
await replyAdminErrorNotice({
|
|
495
|
+
ctx,
|
|
496
|
+
event,
|
|
497
|
+
instruction:
|
|
498
|
+
"用户希望撤回一条消息,但命令中没有引用具体消息,请提醒用户引用要撤回的消息后输入 /撤回",
|
|
499
|
+
fallbackMessage: "要撤回哪条消息呀~先引用一下~",
|
|
500
|
+
});
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
try {
|
|
505
|
+
await bot.api("delete_msg", { message_id: quotedId });
|
|
506
|
+
await replyDone();
|
|
507
|
+
} catch (err) {
|
|
508
|
+
await replyAdminErrorNotice({
|
|
509
|
+
ctx,
|
|
510
|
+
event,
|
|
511
|
+
instruction: `撤回消息执行失败:${String(err)}。失败原因通常是Bot没有该消息的撤回权限(需要Bot为群主或管理员,且消息发出不超过2分钟),管理员无法撤回管理员的消息或群主的消息。`,
|
|
512
|
+
fallbackMessage: `撤回失败啦~可能是没权限或消息超过2分钟了:${String(err)}`,
|
|
513
|
+
error: err,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
482
518
|
} catch (err) {
|
|
483
519
|
ctx.logger.error(`[admin group] 未捕获异常: ${String(err)}`);
|
|
484
520
|
if (!isGroupAdminCommand) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mioku-plugin-admin",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "管理插件,提供事件通知与群管/个人管理指令",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -53,6 +53,10 @@
|
|
|
53
53
|
"id": "/改群头像",
|
|
54
54
|
"match": "/^\\/改群头像/"
|
|
55
55
|
},
|
|
56
|
+
{
|
|
57
|
+
"id": "/撤回",
|
|
58
|
+
"match": "/^(\\/撤回|撤回)/"
|
|
59
|
+
},
|
|
56
60
|
{
|
|
57
61
|
"id": "/改头衔",
|
|
58
62
|
"match": "/^\\/改头衔/"
|
|
@@ -149,6 +153,12 @@
|
|
|
149
153
|
"desc": "修改群头像",
|
|
150
154
|
"role": "admin"
|
|
151
155
|
},
|
|
156
|
+
{
|
|
157
|
+
"cmd": "/撤回",
|
|
158
|
+
"desc": "撤回别人的消息",
|
|
159
|
+
"usage": "引用一条消息后输入 /撤回",
|
|
160
|
+
"role": "admin"
|
|
161
|
+
},
|
|
152
162
|
{
|
|
153
163
|
"cmd": "/改头衔",
|
|
154
164
|
"desc": "设置群成员专属头衔",
|
package/skills/group.ts
CHANGED
|
@@ -56,348 +56,283 @@ async function checkDangerousTargetPermission(
|
|
|
56
56
|
const groupAdminSkill: AISkill = {
|
|
57
57
|
name: "admin_group",
|
|
58
58
|
description:
|
|
59
|
-
"
|
|
59
|
+
"群管理统一入口。action 决定具体行为:管理成员(踢/禁言/解禁/设管理/设头衔)或管理群(全体禁言/改群名/改Bot群名片/改群头像/撤回消息)。",
|
|
60
60
|
permission: "admin",
|
|
61
61
|
tools: [
|
|
62
62
|
{
|
|
63
|
-
name: "
|
|
64
|
-
description:
|
|
63
|
+
name: "manage_member",
|
|
64
|
+
description:
|
|
65
|
+
"管理群成员:踢人(kick)、禁言(mute)、解除禁言(unmute)、设为管理员(set_admin)、取消管理员(unset_admin)、设置某成员头衔(set_title)、设置自己头衔(set_self_title)。",
|
|
65
66
|
parameters: {
|
|
66
67
|
type: "object",
|
|
67
68
|
properties: {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return { error: permissionError };
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
await bot.api("set_group_kick", {
|
|
90
|
-
group_id: groupId,
|
|
91
|
-
user_id: args.user_id,
|
|
92
|
-
});
|
|
93
|
-
return {
|
|
94
|
-
success: true,
|
|
95
|
-
message: `已将 ${args.user_id} 移出当前群`,
|
|
96
|
-
};
|
|
97
|
-
} catch (err) {
|
|
98
|
-
logAdminSkillError(runtimeCtx, "admin_group.kick_member", err);
|
|
99
|
-
return { error: `踢人失败: ${err}` };
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
name: "mute_member",
|
|
105
|
-
description: "禁言群成员,不指定时长时默认10分钟",
|
|
106
|
-
parameters: {
|
|
107
|
-
type: "object",
|
|
108
|
-
properties: {
|
|
109
|
-
user_id: { type: "number", description: "要禁言的成员QQ号" },
|
|
69
|
+
action: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "要执行的动作",
|
|
72
|
+
enum: [
|
|
73
|
+
"kick",
|
|
74
|
+
"mute",
|
|
75
|
+
"unmute",
|
|
76
|
+
"set_admin",
|
|
77
|
+
"unset_admin",
|
|
78
|
+
"set_title",
|
|
79
|
+
"set_self_title",
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
user_id: {
|
|
83
|
+
type: "number",
|
|
84
|
+
description:
|
|
85
|
+
"目标成员QQ号。set_self_title 不需要;其他动作必填。",
|
|
86
|
+
},
|
|
110
87
|
duration: {
|
|
111
88
|
type: "number",
|
|
112
|
-
description: "
|
|
89
|
+
description: "禁言时长(秒),仅 mute 生效;不提供或<=0时默认600秒",
|
|
113
90
|
},
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
118
|
-
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
119
|
-
if ("error" in runtime) return { error: runtime.error };
|
|
120
|
-
const { bot, groupId } = runtime;
|
|
121
|
-
const duration =
|
|
122
|
-
Number(args.duration) > 0 ? Number(args.duration) : 10 * 60;
|
|
123
|
-
const permissionError = await checkDangerousTargetPermission(
|
|
124
|
-
runtime,
|
|
125
|
-
args.user_id,
|
|
126
|
-
);
|
|
127
|
-
if (permissionError) {
|
|
128
|
-
logAdminSkillError(
|
|
129
|
-
runtimeCtx,
|
|
130
|
-
"admin_group.mute_member",
|
|
131
|
-
permissionError,
|
|
132
|
-
);
|
|
133
|
-
return { error: permissionError };
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
await bot.setGroupBan(groupId, args.user_id, duration);
|
|
137
|
-
return {
|
|
138
|
-
success: true,
|
|
139
|
-
message: `已禁言 ${args.user_id} ${duration}秒`,
|
|
140
|
-
};
|
|
141
|
-
} catch (err) {
|
|
142
|
-
logAdminSkillError(runtimeCtx, "admin_group.mute_member", err);
|
|
143
|
-
return { error: `禁言失败: ${err}` };
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
name: "unmute_member",
|
|
149
|
-
description: "解除群成员禁言",
|
|
150
|
-
parameters: {
|
|
151
|
-
type: "object",
|
|
152
|
-
properties: {
|
|
153
|
-
user_id: { type: "number", description: "要解禁的成员QQ号" },
|
|
154
|
-
},
|
|
155
|
-
required: ["user_id"],
|
|
156
|
-
},
|
|
157
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
158
|
-
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
159
|
-
if ("error" in runtime) return { error: runtime.error };
|
|
160
|
-
const { bot, groupId } = runtime;
|
|
161
|
-
try {
|
|
162
|
-
await bot.setGroupBan(groupId, args.user_id, 0);
|
|
163
|
-
return { success: true, message: `已解除 ${args.user_id} 的禁言` };
|
|
164
|
-
} catch (err) {
|
|
165
|
-
logAdminSkillError(runtimeCtx, "admin_group.unmute_member", err);
|
|
166
|
-
return { error: `解禁失败: ${err}` };
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
name: "set_group_admin",
|
|
172
|
-
description: "设置或取消群管理员",
|
|
173
|
-
parameters: {
|
|
174
|
-
type: "object",
|
|
175
|
-
properties: {
|
|
176
|
-
user_id: { type: "number", description: "要设为管理的QQ号" },
|
|
177
|
-
enable: {
|
|
178
|
-
type: "boolean",
|
|
179
|
-
description: "true设为管理,false取消管理",
|
|
91
|
+
title: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "头衔内容,仅 set_title / set_self_title 需要",
|
|
180
94
|
},
|
|
181
95
|
},
|
|
182
|
-
required: ["
|
|
96
|
+
required: ["action"],
|
|
183
97
|
},
|
|
184
98
|
handler: async (args: any, runtimeCtx?: any) => {
|
|
185
99
|
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
186
100
|
if ("error" in runtime) return { error: runtime.error };
|
|
187
|
-
const { bot, groupId } = runtime;
|
|
101
|
+
const { bot, selfId, groupId, event } = runtime;
|
|
102
|
+
const action = String(args?.action || "");
|
|
103
|
+
|
|
188
104
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
105
|
+
switch (action) {
|
|
106
|
+
case "kick": {
|
|
107
|
+
const userId = Number(args?.user_id);
|
|
108
|
+
if (!userId) return { error: "kick 需要提供 user_id" };
|
|
109
|
+
const permErr = await checkDangerousTargetPermission(
|
|
110
|
+
runtime,
|
|
111
|
+
userId,
|
|
112
|
+
);
|
|
113
|
+
if (permErr) return { error: permErr };
|
|
114
|
+
await bot.api("set_group_kick", {
|
|
115
|
+
group_id: groupId,
|
|
116
|
+
user_id: userId,
|
|
117
|
+
});
|
|
118
|
+
return { success: true, message: `已将 ${userId} 移出当前群` };
|
|
119
|
+
}
|
|
120
|
+
case "mute": {
|
|
121
|
+
const userId = Number(args?.user_id);
|
|
122
|
+
if (!userId) return { error: "mute 需要提供 user_id" };
|
|
123
|
+
const permErr = await checkDangerousTargetPermission(
|
|
124
|
+
runtime,
|
|
125
|
+
userId,
|
|
126
|
+
);
|
|
127
|
+
if (permErr) return { error: permErr };
|
|
128
|
+
const duration =
|
|
129
|
+
Number(args?.duration) > 0 ? Number(args.duration) : 10 * 60;
|
|
130
|
+
await bot.setGroupBan(groupId, userId, duration);
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
message: `已禁言 ${userId} ${duration}秒`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
case "unmute": {
|
|
137
|
+
const userId = Number(args?.user_id);
|
|
138
|
+
if (!userId) return { error: "unmute 需要提供 user_id" };
|
|
139
|
+
await bot.setGroupBan(groupId, userId, 0);
|
|
140
|
+
return { success: true, message: `已解除 ${userId} 的禁言` };
|
|
141
|
+
}
|
|
142
|
+
case "set_admin": {
|
|
143
|
+
const userId = Number(args?.user_id);
|
|
144
|
+
if (!userId) return { error: "set_admin 需要提供 user_id" };
|
|
145
|
+
await bot.api("set_group_admin", {
|
|
146
|
+
group_id: groupId,
|
|
147
|
+
user_id: userId,
|
|
148
|
+
enable: true,
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
success: true,
|
|
152
|
+
message: `已将 ${userId} 设为当前群的管理员`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
case "unset_admin": {
|
|
156
|
+
const userId = Number(args?.user_id);
|
|
157
|
+
if (!userId) return { error: "unset_admin 需要提供 user_id" };
|
|
158
|
+
await bot.api("set_group_admin", {
|
|
159
|
+
group_id: groupId,
|
|
160
|
+
user_id: userId,
|
|
161
|
+
enable: false,
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
message: `已取消 ${userId} 在当前群的管理员`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case "set_title": {
|
|
169
|
+
const userId = Number(args?.user_id);
|
|
170
|
+
const title = String(args?.title || "").trim();
|
|
171
|
+
if (!userId || !title) {
|
|
172
|
+
return { error: "set_title 需要提供 user_id 和 title" };
|
|
173
|
+
}
|
|
174
|
+
await (bot as any).setGroupSpecialTitle(groupId, userId, title);
|
|
175
|
+
return {
|
|
176
|
+
success: true,
|
|
177
|
+
message: `已将 ${userId} 的头衔设为 "${title}"`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
case "set_self_title": {
|
|
181
|
+
const userId = Number(event?.user_id);
|
|
182
|
+
if (!userId) return { error: "无法获取当前用户ID" };
|
|
183
|
+
const title = String(args?.title || "").trim();
|
|
184
|
+
if (!title) return { error: "set_self_title 需要提供 title" };
|
|
185
|
+
await (bot as any).setGroupSpecialTitle(groupId, userId, title);
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
message: `已将你的头衔设为 "${title}"`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
default:
|
|
192
|
+
return { error: `未知的 action: ${action}` };
|
|
193
|
+
}
|
|
200
194
|
} catch (err) {
|
|
201
|
-
logAdminSkillError(runtimeCtx,
|
|
202
|
-
return { error:
|
|
195
|
+
logAdminSkillError(runtimeCtx, `admin_group.manage_member.${action}`, err);
|
|
196
|
+
return { error: `执行 ${action} 失败: ${err}` };
|
|
203
197
|
}
|
|
204
198
|
},
|
|
205
199
|
},
|
|
206
200
|
{
|
|
207
|
-
name: "
|
|
208
|
-
description:
|
|
201
|
+
name: "manage_group",
|
|
202
|
+
description:
|
|
203
|
+
"管理群本身或批量撤回消息:开启/关闭全体禁言(set_whole_ban/unset_whole_ban)、改群名(set_group_name)、改Bot在群里的名片(set_self_card)、改群头像(set_group_avatar)、撤回一条或多条消息(recall_messages,需要 message_ids 数组)。",
|
|
209
204
|
parameters: {
|
|
210
205
|
type: "object",
|
|
211
206
|
properties: {
|
|
212
|
-
|
|
213
|
-
type: "
|
|
214
|
-
description: "
|
|
207
|
+
action: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "要执行的动作",
|
|
210
|
+
enum: [
|
|
211
|
+
"set_whole_ban",
|
|
212
|
+
"unset_whole_ban",
|
|
213
|
+
"set_group_name",
|
|
214
|
+
"set_self_card",
|
|
215
|
+
"set_group_avatar",
|
|
216
|
+
"recall_messages",
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
group_name: {
|
|
220
|
+
type: "string",
|
|
221
|
+
description: "新群名,仅 set_group_name 需要",
|
|
222
|
+
},
|
|
223
|
+
card: {
|
|
224
|
+
type: "string",
|
|
225
|
+
description: "新的Bot群名片,仅 set_self_card 需要",
|
|
215
226
|
},
|
|
216
|
-
},
|
|
217
|
-
required: ["enable"],
|
|
218
|
-
},
|
|
219
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
220
|
-
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
221
|
-
if ("error" in runtime) return { error: runtime.error };
|
|
222
|
-
const { bot, groupId } = runtime;
|
|
223
|
-
try {
|
|
224
|
-
await bot.api("set_group_whole_ban", {
|
|
225
|
-
group_id: groupId,
|
|
226
|
-
enable: args.enable,
|
|
227
|
-
});
|
|
228
|
-
return {
|
|
229
|
-
success: true,
|
|
230
|
-
message: args.enable
|
|
231
|
-
? "当前群已开启全体禁言"
|
|
232
|
-
: "当前群已关闭全体禁言",
|
|
233
|
-
};
|
|
234
|
-
} catch (err) {
|
|
235
|
-
logAdminSkillError(
|
|
236
|
-
runtimeCtx,
|
|
237
|
-
"admin_group.set_group_whole_ban",
|
|
238
|
-
err,
|
|
239
|
-
);
|
|
240
|
-
return { error: `全体禁言操作失败: ${err}` };
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
name: "set_group_name",
|
|
246
|
-
description: "修改群聊名称",
|
|
247
|
-
parameters: {
|
|
248
|
-
type: "object",
|
|
249
|
-
properties: {
|
|
250
|
-
group_name: { type: "string", description: "新群名" },
|
|
251
|
-
},
|
|
252
|
-
required: ["group_name"],
|
|
253
|
-
},
|
|
254
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
255
|
-
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
256
|
-
if ("error" in runtime) return { error: runtime.error };
|
|
257
|
-
const { bot, groupId } = runtime;
|
|
258
|
-
try {
|
|
259
|
-
await bot.api("set_group_name", {
|
|
260
|
-
group_id: groupId,
|
|
261
|
-
group_name: args.group_name,
|
|
262
|
-
});
|
|
263
|
-
return {
|
|
264
|
-
success: true,
|
|
265
|
-
message: `当前群名称已修改为 ${args.group_name}`,
|
|
266
|
-
};
|
|
267
|
-
} catch (err) {
|
|
268
|
-
logAdminSkillError(runtimeCtx, "admin_group.set_group_name", err);
|
|
269
|
-
return { error: `修改群名失败: ${err}` };
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
name: "set_group_card",
|
|
275
|
-
description: "修改Bot在当前群内的群名片",
|
|
276
|
-
parameters: {
|
|
277
|
-
type: "object",
|
|
278
|
-
properties: {
|
|
279
|
-
card: { type: "string", description: "新的群名片" },
|
|
280
|
-
},
|
|
281
|
-
required: ["card"],
|
|
282
|
-
},
|
|
283
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
284
|
-
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
285
|
-
if ("error" in runtime) return { error: runtime.error };
|
|
286
|
-
const { bot, selfId, groupId } = runtime;
|
|
287
|
-
const card = String(args?.card || "").trim();
|
|
288
|
-
if (!card) return { error: "card 不能为空" };
|
|
289
|
-
try {
|
|
290
|
-
await bot.setGroupCard(groupId, selfId, card);
|
|
291
|
-
return {
|
|
292
|
-
success: true,
|
|
293
|
-
message: `Bot在当前群的群名片已修改为 ${card}`,
|
|
294
|
-
};
|
|
295
|
-
} catch (err) {
|
|
296
|
-
logAdminSkillError(runtimeCtx, "admin_group.set_group_card", err);
|
|
297
|
-
return { error: `修改群名片失败: ${err}` };
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
{
|
|
302
|
-
name: "set_group_avatar",
|
|
303
|
-
description: "修改群头像",
|
|
304
|
-
parameters: {
|
|
305
|
-
type: "object",
|
|
306
|
-
properties: {
|
|
307
227
|
message_id: {
|
|
308
228
|
type: "number",
|
|
309
|
-
description:
|
|
229
|
+
description:
|
|
230
|
+
"包含图片的消息 message_id,仅 set_group_avatar 需要",
|
|
231
|
+
},
|
|
232
|
+
message_ids: {
|
|
233
|
+
type: "array",
|
|
234
|
+
items: { type: "number" },
|
|
235
|
+
description:
|
|
236
|
+
"要撤回的消息 message_id 数组,仅 recall_messages 需要。一次调用可批量撤回。",
|
|
310
237
|
},
|
|
311
238
|
},
|
|
312
|
-
required: ["
|
|
313
|
-
},
|
|
314
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
315
|
-
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
316
|
-
if ("error" in runtime) return { error: runtime.error };
|
|
317
|
-
const { bot, groupId } = runtime;
|
|
318
|
-
const messageId = Number(args?.message_id);
|
|
319
|
-
if (!Number.isFinite(messageId) || messageId <= 0) {
|
|
320
|
-
return { error: "请提供有效的 message_id" };
|
|
321
|
-
}
|
|
322
|
-
const imageUrl = await getImageUrlByMessageId(bot, messageId);
|
|
323
|
-
if (!imageUrl) {
|
|
324
|
-
return { error: "指定 message_id 中未找到图片" };
|
|
325
|
-
}
|
|
326
|
-
try {
|
|
327
|
-
await bot.api("set_group_portrait", {
|
|
328
|
-
group_id: groupId,
|
|
329
|
-
file: imageUrl,
|
|
330
|
-
});
|
|
331
|
-
return { success: true, message: "当前群头像已修改" };
|
|
332
|
-
} catch (err) {
|
|
333
|
-
logAdminSkillError(runtimeCtx, "admin_group.set_group_avatar", err);
|
|
334
|
-
return { error: `修改群头像失败: ${err}` };
|
|
335
|
-
}
|
|
336
|
-
},
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
name: "set_group_title",
|
|
340
|
-
description: "设置群成员专属头衔",
|
|
341
|
-
parameters: {
|
|
342
|
-
type: "object",
|
|
343
|
-
properties: {
|
|
344
|
-
user_id: { type: "number", description: "成员QQ号" },
|
|
345
|
-
title: { type: "string", description: "新头衔" },
|
|
346
|
-
},
|
|
347
|
-
required: ["user_id", "title"],
|
|
348
|
-
},
|
|
349
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
350
|
-
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
351
|
-
if ("error" in runtime) return { error: runtime.error };
|
|
352
|
-
const { bot, groupId } = runtime;
|
|
353
|
-
try {
|
|
354
|
-
await (bot as any).setGroupSpecialTitle(
|
|
355
|
-
groupId,
|
|
356
|
-
args.user_id,
|
|
357
|
-
args.title,
|
|
358
|
-
);
|
|
359
|
-
return {
|
|
360
|
-
success: true,
|
|
361
|
-
message: `已将 ${args.user_id} 的头衔设为 "${args.title}"`,
|
|
362
|
-
};
|
|
363
|
-
} catch (err) {
|
|
364
|
-
logAdminSkillError(runtimeCtx, "admin_group.set_group_title", err);
|
|
365
|
-
return { error: `设置头衔失败: ${err}` };
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
name: "set_self_group_title",
|
|
371
|
-
description: "给当前发起用户设置自己的群专属头衔,不能指定或修改别人",
|
|
372
|
-
parameters: {
|
|
373
|
-
type: "object",
|
|
374
|
-
properties: {
|
|
375
|
-
title: { type: "string", description: "新头衔" },
|
|
376
|
-
},
|
|
377
|
-
required: ["title"],
|
|
239
|
+
required: ["action"],
|
|
378
240
|
},
|
|
379
241
|
handler: async (args: any, runtimeCtx?: any) => {
|
|
380
242
|
const runtime = await resolveGroupRuntime(runtimeCtx);
|
|
381
243
|
if ("error" in runtime) return { error: runtime.error };
|
|
382
|
-
const { bot, groupId } = runtime;
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
if (!userId) return { error: "无法获取当前用户ID" };
|
|
386
|
-
const title = String(args?.title || "").trim();
|
|
387
|
-
if (!title) return { error: "title 不能为空" };
|
|
244
|
+
const { bot, selfId, groupId } = runtime;
|
|
245
|
+
const action = String(args?.action || "");
|
|
246
|
+
|
|
388
247
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
248
|
+
switch (action) {
|
|
249
|
+
case "set_whole_ban":
|
|
250
|
+
await bot.api("set_group_whole_ban", {
|
|
251
|
+
group_id: groupId,
|
|
252
|
+
enable: true,
|
|
253
|
+
});
|
|
254
|
+
return { success: true, message: "当前群已开启全体禁言" };
|
|
255
|
+
case "unset_whole_ban":
|
|
256
|
+
await bot.api("set_group_whole_ban", {
|
|
257
|
+
group_id: groupId,
|
|
258
|
+
enable: false,
|
|
259
|
+
});
|
|
260
|
+
return { success: true, message: "当前群已关闭全体禁言" };
|
|
261
|
+
case "set_group_name": {
|
|
262
|
+
const groupName = String(args?.group_name || "").trim();
|
|
263
|
+
if (!groupName) return { error: "set_group_name 需要提供 group_name" };
|
|
264
|
+
await bot.api("set_group_name", {
|
|
265
|
+
group_id: groupId,
|
|
266
|
+
group_name: groupName,
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
success: true,
|
|
270
|
+
message: `当前群名称已修改为 ${groupName}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
case "set_self_card": {
|
|
274
|
+
const card = String(args?.card || "").trim();
|
|
275
|
+
if (!card) return { error: "set_self_card 需要提供 card" };
|
|
276
|
+
await bot.setGroupCard(groupId, selfId, card);
|
|
277
|
+
return {
|
|
278
|
+
success: true,
|
|
279
|
+
message: `Bot在当前群的群名片已修改为 ${card}`,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
case "set_group_avatar": {
|
|
283
|
+
const messageId = Number(args?.message_id);
|
|
284
|
+
if (!Number.isFinite(messageId) || messageId <= 0) {
|
|
285
|
+
return { error: "set_group_avatar 需要提供有效的 message_id" };
|
|
286
|
+
}
|
|
287
|
+
const imageUrl = await getImageUrlByMessageId(bot, messageId);
|
|
288
|
+
if (!imageUrl) {
|
|
289
|
+
return { error: "指定 message_id 中未找到图片" };
|
|
290
|
+
}
|
|
291
|
+
await bot.api("set_group_portrait", {
|
|
292
|
+
group_id: groupId,
|
|
293
|
+
file: imageUrl,
|
|
294
|
+
});
|
|
295
|
+
return { success: true, message: "当前群头像已修改" };
|
|
296
|
+
}
|
|
297
|
+
case "recall_messages": {
|
|
298
|
+
const ids = Array.isArray(args?.message_ids)
|
|
299
|
+
? args.message_ids
|
|
300
|
+
.map((v: any) => Number(v))
|
|
301
|
+
.filter((n: number) => Number.isFinite(n) && n !== 0)
|
|
302
|
+
: [];
|
|
303
|
+
if (ids.length === 0) {
|
|
304
|
+
return { error: "recall_messages 需要提供至少一个 message_id" };
|
|
305
|
+
}
|
|
306
|
+
const results: Array<{
|
|
307
|
+
message_id: number;
|
|
308
|
+
success: boolean;
|
|
309
|
+
error?: string;
|
|
310
|
+
}> = [];
|
|
311
|
+
for (const id of ids) {
|
|
312
|
+
try {
|
|
313
|
+
await bot.api("delete_msg", { message_id: id });
|
|
314
|
+
results.push({ message_id: id, success: true });
|
|
315
|
+
} catch (err) {
|
|
316
|
+
results.push({
|
|
317
|
+
message_id: id,
|
|
318
|
+
success: false,
|
|
319
|
+
error: String(err),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const ok = results.filter((r) => r.success).length;
|
|
324
|
+
return {
|
|
325
|
+
success: ok === results.length,
|
|
326
|
+
message: `共尝试撤回 ${results.length} 条消息,成功 ${ok} 条,失败 ${results.length - ok} 条`,
|
|
327
|
+
results,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
default:
|
|
331
|
+
return { error: `未知的 action: ${action}` };
|
|
332
|
+
}
|
|
394
333
|
} catch (err) {
|
|
395
|
-
logAdminSkillError(
|
|
396
|
-
|
|
397
|
-
"admin_group.set_self_group_title",
|
|
398
|
-
err,
|
|
399
|
-
);
|
|
400
|
-
return { error: `设置头衔失败: ${err}` };
|
|
334
|
+
logAdminSkillError(runtimeCtx, `admin_group.manage_group.${action}`, err);
|
|
335
|
+
return { error: `执行 ${action} 失败: ${err}` };
|
|
401
336
|
}
|
|
402
337
|
},
|
|
403
338
|
},
|
package/skills/personal.ts
CHANGED
|
@@ -21,352 +21,202 @@ function logAdminSkillError(runtimeCtx: any, toolName: string, err: unknown) {
|
|
|
21
21
|
const personalSkill: AISkill = {
|
|
22
22
|
name: "admin_personal",
|
|
23
23
|
description:
|
|
24
|
-
"
|
|
24
|
+
"Bot个人账号与消息管理统一入口。action 决定行为:修改Bot资料(set_avatar/set_nickname/set_signature/set_gender)、发送消息(send_private/send_group)、获取列表(list_friends/list_groups)、管理关系(delete_friend/leave_group)。",
|
|
25
25
|
permission: "owner",
|
|
26
26
|
tools: [
|
|
27
27
|
{
|
|
28
|
-
name: "
|
|
29
|
-
description:
|
|
28
|
+
name: "manage_personal",
|
|
29
|
+
description:
|
|
30
|
+
"Bot个人账号管理:修改头像/昵称/签名/性别、给指定用户或群发消息、查看好友/群列表、删除好友、退群。所有功能通过 action 字段区分。",
|
|
30
31
|
parameters: {
|
|
31
32
|
type: "object",
|
|
32
33
|
properties: {
|
|
34
|
+
action: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "要执行的动作",
|
|
37
|
+
enum: [
|
|
38
|
+
"set_avatar",
|
|
39
|
+
"set_nickname",
|
|
40
|
+
"set_signature",
|
|
41
|
+
"set_gender",
|
|
42
|
+
"send_private",
|
|
43
|
+
"send_group",
|
|
44
|
+
"list_friends",
|
|
45
|
+
"list_groups",
|
|
46
|
+
"delete_friend",
|
|
47
|
+
"leave_group",
|
|
48
|
+
],
|
|
49
|
+
},
|
|
33
50
|
message_id: {
|
|
34
51
|
type: "number",
|
|
35
|
-
description: "包含图片的消息 message_id",
|
|
52
|
+
description: "包含图片的消息 message_id,仅 set_avatar 需要",
|
|
53
|
+
},
|
|
54
|
+
nickname: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "新昵称,仅 set_nickname 需要",
|
|
57
|
+
},
|
|
58
|
+
personal_note: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "新个性签名,仅 set_signature 需要",
|
|
36
61
|
},
|
|
37
|
-
},
|
|
38
|
-
required: ["message_id"],
|
|
39
|
-
},
|
|
40
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
41
|
-
const ctx = runtimeCtx?.ctx;
|
|
42
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
43
|
-
const selfId =
|
|
44
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
45
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
46
|
-
const bot = ctx.pickBot(selfId);
|
|
47
|
-
if (!bot) return { error: "Bot不可用" };
|
|
48
|
-
const messageId = Number(args?.message_id);
|
|
49
|
-
if (!Number.isFinite(messageId) || messageId <= 0) {
|
|
50
|
-
return { error: "请提供有效的 message_id" };
|
|
51
|
-
}
|
|
52
|
-
const imageUrl = await getImageUrlByMessageId(bot, messageId);
|
|
53
|
-
if (!imageUrl) {
|
|
54
|
-
return { error: "指定 message_id 中未找到图片" };
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
await bot.api("set_qq_avatar", { file: imageUrl });
|
|
58
|
-
return { success: true, message: "Bot头像已修改" };
|
|
59
|
-
} catch (err) {
|
|
60
|
-
logAdminSkillError(runtimeCtx, "admin_personal.set_bot_avatar", err);
|
|
61
|
-
return { error: `修改头像失败: ${err}` };
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: "set_bot_nickname",
|
|
67
|
-
description: "修改Bot昵称",
|
|
68
|
-
parameters: {
|
|
69
|
-
type: "object",
|
|
70
|
-
properties: { nickname: { type: "string", description: "新昵称" } },
|
|
71
|
-
required: ["nickname"],
|
|
72
|
-
},
|
|
73
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
74
|
-
const ctx = runtimeCtx?.ctx;
|
|
75
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
76
|
-
const selfId =
|
|
77
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
78
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
79
|
-
const bot = ctx.pickBot(selfId);
|
|
80
|
-
if (!bot) return { error: "Bot不可用" };
|
|
81
|
-
try {
|
|
82
|
-
await bot.api("set_qq_profile", {
|
|
83
|
-
nickname: String(args.nickname || "").trim(),
|
|
84
|
-
});
|
|
85
|
-
return {
|
|
86
|
-
success: true,
|
|
87
|
-
message: `Bot昵称已修改为: ${args.nickname}`,
|
|
88
|
-
};
|
|
89
|
-
} catch (err) {
|
|
90
|
-
logAdminSkillError(
|
|
91
|
-
runtimeCtx,
|
|
92
|
-
"admin_personal.set_bot_nickname",
|
|
93
|
-
err,
|
|
94
|
-
);
|
|
95
|
-
return { error: `修改昵称失败: ${err}` };
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
name: "set_bot_signature",
|
|
101
|
-
description: "修改Bot个性签名",
|
|
102
|
-
parameters: {
|
|
103
|
-
type: "object",
|
|
104
|
-
properties: {
|
|
105
|
-
personal_note: { type: "string", description: "新个性签名" },
|
|
106
|
-
},
|
|
107
|
-
required: ["personal_note"],
|
|
108
|
-
},
|
|
109
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
110
|
-
const ctx = runtimeCtx?.ctx;
|
|
111
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
112
|
-
const selfId =
|
|
113
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
114
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
115
|
-
const bot = ctx.pickBot(selfId);
|
|
116
|
-
if (!bot) return { error: "Bot不可用" };
|
|
117
|
-
const personalNote = String(args?.personal_note || "").trim();
|
|
118
|
-
if (!personalNote) return { error: "personal_note 不能为空" };
|
|
119
|
-
try {
|
|
120
|
-
await bot.api("set_qq_profile", { personal_note: personalNote });
|
|
121
|
-
return { success: true, message: "Bot个性签名已修改" };
|
|
122
|
-
} catch (err) {
|
|
123
|
-
logAdminSkillError(
|
|
124
|
-
runtimeCtx,
|
|
125
|
-
"admin_personal.set_bot_signature",
|
|
126
|
-
err,
|
|
127
|
-
);
|
|
128
|
-
return { error: `修改个性签名失败: ${err}` };
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
name: "set_bot_gender",
|
|
134
|
-
description: "修改Bot性别",
|
|
135
|
-
parameters: {
|
|
136
|
-
type: "object",
|
|
137
|
-
properties: {
|
|
138
62
|
gender: {
|
|
139
63
|
type: "string",
|
|
140
|
-
description:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
64
|
+
description:
|
|
65
|
+
"性别:男/女/无 或 male/female/unknown 或 1/2/0,仅 set_gender 需要",
|
|
66
|
+
},
|
|
67
|
+
user_id: {
|
|
68
|
+
type: "number",
|
|
69
|
+
description:
|
|
70
|
+
"目标QQ号。send_private / delete_friend 需要。",
|
|
71
|
+
},
|
|
72
|
+
group_id: {
|
|
73
|
+
type: "number",
|
|
74
|
+
description: "目标群号。send_group / leave_group 需要。",
|
|
75
|
+
},
|
|
76
|
+
content: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "消息内容。send_private / send_group 需要。",
|
|
152
79
|
},
|
|
153
80
|
},
|
|
154
|
-
required: ["
|
|
155
|
-
},
|
|
156
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
157
|
-
const ctx = runtimeCtx?.ctx;
|
|
158
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
159
|
-
const selfId =
|
|
160
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
161
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
162
|
-
const bot = ctx.pickBot(selfId);
|
|
163
|
-
if (!bot) return { error: "Bot不可用" };
|
|
164
|
-
try {
|
|
165
|
-
const sex = parseProfileSex(args.gender);
|
|
166
|
-
await bot.api("set_qq_profile", { sex });
|
|
167
|
-
const genderMap: Record<number, string> = {
|
|
168
|
-
0: "无",
|
|
169
|
-
1: "男",
|
|
170
|
-
2: "女",
|
|
171
|
-
};
|
|
172
|
-
return {
|
|
173
|
-
success: true,
|
|
174
|
-
message: `Bot性别已修改为: ${genderMap[sex]}`,
|
|
175
|
-
};
|
|
176
|
-
} catch (err) {
|
|
177
|
-
logAdminSkillError(runtimeCtx, "admin_personal.set_bot_gender", err);
|
|
178
|
-
return { error: `修改性别失败: ${err}` };
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
name: "send_private_message",
|
|
184
|
-
description: "给指定QQ号发送私聊消息",
|
|
185
|
-
parameters: {
|
|
186
|
-
type: "object",
|
|
187
|
-
properties: {
|
|
188
|
-
user_id: { type: "number", description: "目标QQ号" },
|
|
189
|
-
content: { type: "string", description: "消息内容" },
|
|
190
|
-
},
|
|
191
|
-
required: ["user_id", "content"],
|
|
192
|
-
},
|
|
193
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
194
|
-
const ctx = runtimeCtx?.ctx;
|
|
195
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
196
|
-
const selfId =
|
|
197
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
198
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
199
|
-
const bot = ctx.pickBot(selfId);
|
|
200
|
-
if (!bot) return { error: "Bot不可用" };
|
|
201
|
-
try {
|
|
202
|
-
await bot.sendPrivateMsg(args.user_id, [
|
|
203
|
-
ctx.segment.text(args.content),
|
|
204
|
-
]);
|
|
205
|
-
return { success: true, message: `已发送私聊消息给 ${args.user_id}` };
|
|
206
|
-
} catch (err) {
|
|
207
|
-
logAdminSkillError(
|
|
208
|
-
runtimeCtx,
|
|
209
|
-
"admin_personal.send_private_message",
|
|
210
|
-
err,
|
|
211
|
-
);
|
|
212
|
-
return { error: `发送私聊失败: ${err}` };
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: "send_group_message",
|
|
218
|
-
description: "给指定群发送消息",
|
|
219
|
-
parameters: {
|
|
220
|
-
type: "object",
|
|
221
|
-
properties: {
|
|
222
|
-
group_id: { type: "number", description: "目标群号" },
|
|
223
|
-
content: { type: "string", description: "消息内容" },
|
|
224
|
-
},
|
|
225
|
-
required: ["group_id", "content"],
|
|
81
|
+
required: ["action"],
|
|
226
82
|
},
|
|
227
83
|
handler: async (args: any, runtimeCtx?: any) => {
|
|
228
84
|
const ctx = runtimeCtx?.ctx;
|
|
229
85
|
if (!ctx) return { error: "无法获取上下文" };
|
|
230
|
-
const
|
|
231
|
-
|
|
86
|
+
const event = runtimeCtx?.event || runtimeCtx?.rawEvent;
|
|
87
|
+
const selfId = event?.self_id;
|
|
232
88
|
if (!selfId) return { error: "无法获取Bot ID" };
|
|
233
89
|
const bot = ctx.pickBot(selfId);
|
|
234
90
|
if (!bot) return { error: "Bot不可用" };
|
|
91
|
+
const action = String(args?.action || "");
|
|
92
|
+
|
|
235
93
|
try {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
94
|
+
switch (action) {
|
|
95
|
+
case "set_avatar": {
|
|
96
|
+
const messageId = Number(args?.message_id);
|
|
97
|
+
if (!Number.isFinite(messageId) || messageId <= 0) {
|
|
98
|
+
return { error: "set_avatar 需要提供有效的 message_id" };
|
|
99
|
+
}
|
|
100
|
+
const imageUrl = await getImageUrlByMessageId(bot, messageId);
|
|
101
|
+
if (!imageUrl) {
|
|
102
|
+
return { error: "指定 message_id 中未找到图片" };
|
|
103
|
+
}
|
|
104
|
+
await bot.api("set_qq_avatar", { file: imageUrl });
|
|
105
|
+
return { success: true, message: "Bot头像已修改" };
|
|
106
|
+
}
|
|
107
|
+
case "set_nickname": {
|
|
108
|
+
const nickname = String(args?.nickname || "").trim();
|
|
109
|
+
if (!nickname) {
|
|
110
|
+
return { error: "set_nickname 需要提供 nickname" };
|
|
111
|
+
}
|
|
112
|
+
await bot.api("set_qq_profile", { nickname });
|
|
113
|
+
return {
|
|
114
|
+
success: true,
|
|
115
|
+
message: `Bot昵称已修改为: ${nickname}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
case "set_signature": {
|
|
119
|
+
const personalNote = String(args?.personal_note || "").trim();
|
|
120
|
+
if (!personalNote) {
|
|
121
|
+
return { error: "set_signature 需要提供 personal_note" };
|
|
122
|
+
}
|
|
123
|
+
await bot.api("set_qq_profile", { personal_note: personalNote });
|
|
124
|
+
return { success: true, message: "Bot个性签名已修改" };
|
|
125
|
+
}
|
|
126
|
+
case "set_gender": {
|
|
127
|
+
const sex = parseProfileSex(args?.gender);
|
|
128
|
+
await bot.api("set_qq_profile", { sex });
|
|
129
|
+
const genderMap: Record<number, string> = {
|
|
130
|
+
0: "无",
|
|
131
|
+
1: "男",
|
|
132
|
+
2: "女",
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
message: `Bot性别已修改为: ${genderMap[sex]}`,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
case "send_private": {
|
|
140
|
+
const userId = Number(args?.user_id);
|
|
141
|
+
const content = String(args?.content || "");
|
|
142
|
+
if (!userId || !content) {
|
|
143
|
+
return { error: "send_private 需要提供 user_id 和 content" };
|
|
144
|
+
}
|
|
145
|
+
await bot.sendPrivateMsg(userId, [ctx.segment.text(content)]);
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
message: `已发送私聊消息给 ${userId}`,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
case "send_group": {
|
|
152
|
+
const groupId = Number(args?.group_id);
|
|
153
|
+
const content = String(args?.content || "");
|
|
154
|
+
if (!groupId || !content) {
|
|
155
|
+
return { error: "send_group 需要提供 group_id 和 content" };
|
|
156
|
+
}
|
|
157
|
+
await bot.sendGroupMsg(groupId, [ctx.segment.text(content)]);
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
message: `已发送群消息到 ${groupId}`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
case "list_friends": {
|
|
164
|
+
if (event?.message_type === "group") {
|
|
165
|
+
return { error: "在私聊使用试试看吧~" };
|
|
166
|
+
}
|
|
167
|
+
const friendList: any[] = await bot.api("get_friend_list");
|
|
168
|
+
if (!Array.isArray(friendList)) return { friends: [] };
|
|
169
|
+
return {
|
|
170
|
+
friends: friendList.map((f) => ({
|
|
171
|
+
user_id: f.user_id,
|
|
172
|
+
nickname: f.nickname,
|
|
173
|
+
remark: f.remark,
|
|
174
|
+
})),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
case "list_groups": {
|
|
178
|
+
if (event?.message_type === "group") {
|
|
179
|
+
return { error: "在私聊使用试试看吧~" };
|
|
180
|
+
}
|
|
181
|
+
const groupList: any[] = await bot.api("get_group_list");
|
|
182
|
+
if (!Array.isArray(groupList)) return { groups: [] };
|
|
183
|
+
return {
|
|
184
|
+
groups: groupList.map((g) => ({
|
|
185
|
+
group_id: g.group_id,
|
|
186
|
+
group_name: g.group_name,
|
|
187
|
+
member_count: g.member_count,
|
|
188
|
+
})),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
case "delete_friend": {
|
|
192
|
+
const userId = Number(args?.user_id);
|
|
193
|
+
if (!userId) {
|
|
194
|
+
return { error: "delete_friend 需要提供 user_id" };
|
|
195
|
+
}
|
|
196
|
+
await bot.api("delete_friend", { user_id: userId });
|
|
197
|
+
return { success: true, message: `已删除好友 ${userId}` };
|
|
198
|
+
}
|
|
199
|
+
case "leave_group": {
|
|
200
|
+
const groupId = Number(args?.group_id);
|
|
201
|
+
if (!groupId) {
|
|
202
|
+
return { error: "leave_group 需要提供 group_id" };
|
|
203
|
+
}
|
|
204
|
+
await bot.api("set_group_leave", {
|
|
205
|
+
group_id: groupId,
|
|
206
|
+
is_dismiss: false,
|
|
207
|
+
});
|
|
208
|
+
return { success: true, message: `已退出群 ${groupId}` };
|
|
209
|
+
}
|
|
210
|
+
default:
|
|
211
|
+
return { error: `未知的 action: ${action}` };
|
|
212
|
+
}
|
|
240
213
|
} catch (err) {
|
|
241
214
|
logAdminSkillError(
|
|
242
215
|
runtimeCtx,
|
|
243
|
-
|
|
216
|
+
`admin_personal.manage_personal.${action}`,
|
|
244
217
|
err,
|
|
245
218
|
);
|
|
246
|
-
return { error:
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
},
|
|
250
|
-
{
|
|
251
|
-
name: "get_friend_list",
|
|
252
|
-
description:
|
|
253
|
-
"获取Bot的全部好友列表,对应/全部好友命令,仅适合在私聊场景调用",
|
|
254
|
-
parameters: { type: "object", properties: {}, required: [] },
|
|
255
|
-
handler: async (_args: any, runtimeCtx?: any) => {
|
|
256
|
-
const ctx = runtimeCtx?.ctx;
|
|
257
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
258
|
-
const event = runtimeCtx?.event || runtimeCtx?.rawEvent;
|
|
259
|
-
if (event?.message_type === "group") {
|
|
260
|
-
return { error: "在私聊使用试试看吧~" };
|
|
261
|
-
}
|
|
262
|
-
const selfId =
|
|
263
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
264
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
265
|
-
const bot = ctx.pickBot(selfId);
|
|
266
|
-
if (!bot) return { error: "Bot不可用" };
|
|
267
|
-
try {
|
|
268
|
-
const friendList: any[] = await bot.api("get_friend_list");
|
|
269
|
-
if (!Array.isArray(friendList)) return { friends: [] };
|
|
270
|
-
return {
|
|
271
|
-
friends: friendList.map((f) => ({
|
|
272
|
-
user_id: f.user_id,
|
|
273
|
-
nickname: f.nickname,
|
|
274
|
-
remark: f.remark,
|
|
275
|
-
})),
|
|
276
|
-
};
|
|
277
|
-
} catch (err) {
|
|
278
|
-
logAdminSkillError(runtimeCtx, "admin_personal.get_friend_list", err);
|
|
279
|
-
return { error: `获取好友列表失败: ${err}` };
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
name: "get_group_list",
|
|
285
|
-
description:
|
|
286
|
-
"获取Bot的全部群聊列表,对应/全部群聊命令,仅适合在私聊场景调用",
|
|
287
|
-
parameters: { type: "object", properties: {}, required: [] },
|
|
288
|
-
handler: async (_args: any, runtimeCtx?: any) => {
|
|
289
|
-
const ctx = runtimeCtx?.ctx;
|
|
290
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
291
|
-
const event = runtimeCtx?.event || runtimeCtx?.rawEvent;
|
|
292
|
-
if (event?.message_type === "group") {
|
|
293
|
-
return { error: "在私聊使用试试看吧~" };
|
|
294
|
-
}
|
|
295
|
-
const selfId =
|
|
296
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
297
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
298
|
-
const bot = ctx.pickBot(selfId);
|
|
299
|
-
if (!bot) return { error: "Bot不可用" };
|
|
300
|
-
try {
|
|
301
|
-
const groupList: any[] = await bot.api("get_group_list");
|
|
302
|
-
if (!Array.isArray(groupList)) return { groups: [] };
|
|
303
|
-
return {
|
|
304
|
-
groups: groupList.map((g) => ({
|
|
305
|
-
group_id: g.group_id,
|
|
306
|
-
group_name: g.group_name,
|
|
307
|
-
member_count: g.member_count,
|
|
308
|
-
})),
|
|
309
|
-
};
|
|
310
|
-
} catch (err) {
|
|
311
|
-
logAdminSkillError(runtimeCtx, "admin_personal.get_group_list", err);
|
|
312
|
-
return { error: `获取群聊列表失败: ${err}` };
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
name: "delete_friend",
|
|
318
|
-
description: "删除好友",
|
|
319
|
-
parameters: {
|
|
320
|
-
type: "object",
|
|
321
|
-
properties: {
|
|
322
|
-
user_id: { type: "number", description: "要删除的好友QQ号" },
|
|
323
|
-
},
|
|
324
|
-
required: ["user_id"],
|
|
325
|
-
},
|
|
326
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
327
|
-
const ctx = runtimeCtx?.ctx;
|
|
328
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
329
|
-
const selfId =
|
|
330
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
331
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
332
|
-
const bot = ctx.pickBot(selfId);
|
|
333
|
-
if (!bot) return { error: "Bot不可用" };
|
|
334
|
-
try {
|
|
335
|
-
await bot.api("delete_friend", { user_id: args.user_id });
|
|
336
|
-
return { success: true, message: `已删除好友 ${args.user_id}` };
|
|
337
|
-
} catch (err) {
|
|
338
|
-
logAdminSkillError(runtimeCtx, "admin_personal.delete_friend", err);
|
|
339
|
-
return { error: `删除好友失败: ${err}` };
|
|
340
|
-
}
|
|
341
|
-
},
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
name: "leave_group",
|
|
345
|
-
description: "退出群聊",
|
|
346
|
-
parameters: {
|
|
347
|
-
type: "object",
|
|
348
|
-
properties: {
|
|
349
|
-
group_id: { type: "number", description: "要退出的群号" },
|
|
350
|
-
},
|
|
351
|
-
required: ["group_id"],
|
|
352
|
-
},
|
|
353
|
-
handler: async (args: any, runtimeCtx?: any) => {
|
|
354
|
-
const ctx = runtimeCtx?.ctx;
|
|
355
|
-
if (!ctx) return { error: "无法获取上下文" };
|
|
356
|
-
const selfId =
|
|
357
|
-
runtimeCtx?.event?.self_id || runtimeCtx?.rawEvent?.self_id;
|
|
358
|
-
if (!selfId) return { error: "无法获取Bot ID" };
|
|
359
|
-
const bot = ctx.pickBot(selfId);
|
|
360
|
-
if (!bot) return { error: "Bot不可用" };
|
|
361
|
-
try {
|
|
362
|
-
await bot.api("set_group_leave", {
|
|
363
|
-
group_id: args.group_id,
|
|
364
|
-
is_dismiss: false,
|
|
365
|
-
});
|
|
366
|
-
return { success: true, message: `已退出群 ${args.group_id}` };
|
|
367
|
-
} catch (err) {
|
|
368
|
-
logAdminSkillError(runtimeCtx, "admin_personal.leave_group", err);
|
|
369
|
-
return { error: `退群失败: ${err}` };
|
|
219
|
+
return { error: `执行 ${action} 失败: ${err}` };
|
|
370
220
|
}
|
|
371
221
|
},
|
|
372
222
|
},
|