mioku-plugin-admin 2.3.5 → 3.0.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 +463 -473
- package/commands/notice.ts +3 -3
- package/commands/personal.ts +234 -276
- package/commands/verify.ts +325 -308
- package/config.ts +29 -17
- package/index.ts +5 -7
- package/notify/index.ts +110 -140
- package/notify/welcome.ts +52 -44
- package/package.json +4 -283
- package/skills/group.ts +87 -143
- package/skills/message-image.ts +8 -9
- package/skills/personal.ts +72 -98
- package/verify/chiral.ts +4 -4
- package/verify/index.ts +59 -45
- package/verify/number.ts +4 -4
- package/verify/reaction.ts +62 -14
- package/verify/state.ts +1 -1
- package/verify/types.ts +18 -8
package/config.ts
CHANGED
|
@@ -101,25 +101,34 @@ export function parseDuration(text: string): number {
|
|
|
101
101
|
return 0;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
import type { Bot } from "mioku";
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
interface SegmentLike {
|
|
108
|
+
type: string;
|
|
109
|
+
data?: Record<string, unknown>;
|
|
110
|
+
}
|
|
111
|
+
|
|
104
112
|
// 从消息中提取图片URL
|
|
105
|
-
export function extractImageUrl(message:
|
|
113
|
+
export function extractImageUrl(message: readonly SegmentLike[] | null | undefined): string | undefined {
|
|
106
114
|
if (!Array.isArray(message)) return undefined;
|
|
107
115
|
for (const seg of message) {
|
|
108
116
|
if (seg.type === "image") {
|
|
109
|
-
const
|
|
110
|
-
|
|
117
|
+
const data = seg.data ?? {};
|
|
118
|
+
const url = String(data.url ?? data.file ?? "");
|
|
119
|
+
return url || undefined;
|
|
111
120
|
}
|
|
112
121
|
}
|
|
113
122
|
return undefined;
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
export function extractImageUrls(message:
|
|
125
|
+
export function extractImageUrls(message: readonly SegmentLike[] | null | undefined): string[] {
|
|
117
126
|
if (!Array.isArray(message)) return [];
|
|
118
127
|
const urls: string[] = [];
|
|
119
128
|
for (const seg of message) {
|
|
120
129
|
if (seg.type === "image") {
|
|
121
|
-
const
|
|
122
|
-
const url = String(
|
|
130
|
+
const data = seg.data ?? {};
|
|
131
|
+
const url = String(data.url ?? data.file ?? "").trim();
|
|
123
132
|
if (url) urls.push(url);
|
|
124
133
|
}
|
|
125
134
|
}
|
|
@@ -127,34 +136,37 @@ export function extractImageUrls(message: RecvElement[]): string[] {
|
|
|
127
136
|
}
|
|
128
137
|
|
|
129
138
|
// 从消息中提取被@的人的QQ号
|
|
130
|
-
export function getAtUserId(message:
|
|
139
|
+
export function getAtUserId(message: readonly SegmentLike[] | null | undefined): number | undefined {
|
|
131
140
|
if (!Array.isArray(message)) return undefined;
|
|
132
141
|
const atSeg = message.find(
|
|
133
|
-
(seg)
|
|
142
|
+
(seg) =>
|
|
143
|
+
seg.type === "at" &&
|
|
144
|
+
String(seg.data?.qq ?? seg.data?.target) !== "all",
|
|
134
145
|
);
|
|
135
146
|
if (!atSeg) return undefined;
|
|
136
|
-
const qq = Number(atSeg.qq);
|
|
147
|
+
const qq = Number(atSeg.data?.qq ?? atSeg.data?.target);
|
|
137
148
|
return Number.isFinite(qq) ? qq : undefined;
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
// 获取群成员头像URL
|
|
141
|
-
export function getAvatarUrl(userId: number): string {
|
|
142
|
-
return `https://q1.qlogo.cn/g?b=qq&nk=${userId}&s=640`;
|
|
152
|
+
export function getAvatarUrl(userId: number | string): string {
|
|
153
|
+
return `https://q1.qlogo.cn/g?b=qq&nk=${String(userId)}&s=640`;
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
// 获取群头像URL
|
|
146
|
-
export function getGroupAvatarUrl(groupId: number): string {
|
|
147
|
-
|
|
157
|
+
export function getGroupAvatarUrl(groupId: number | string): string {
|
|
158
|
+
const g = String(groupId);
|
|
159
|
+
return `https://p.qlogo.cn/gh/${g}/${g}/640/`;
|
|
148
160
|
}
|
|
149
161
|
|
|
150
162
|
// 获取Bot群成员角色
|
|
151
163
|
export async function getMemberRole(
|
|
152
|
-
bot:
|
|
153
|
-
groupId: number,
|
|
154
|
-
userId: number,
|
|
164
|
+
bot: Bot,
|
|
165
|
+
groupId: string | number,
|
|
166
|
+
userId: string | number,
|
|
155
167
|
): Promise<string> {
|
|
156
168
|
try {
|
|
157
|
-
const info = await bot.
|
|
169
|
+
const info = await bot.getMemberInfo(groupId, userId);
|
|
158
170
|
return info?.role || "member";
|
|
159
171
|
} catch {
|
|
160
172
|
return "member";
|
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { definePlugin, type
|
|
1
|
+
import { definePlugin, type MiokuContext } from "mioku";
|
|
2
2
|
import {
|
|
3
3
|
setPluginRuntimeState,
|
|
4
4
|
resetPluginRuntimeState,
|
|
@@ -21,16 +21,14 @@ import { registerWelcomeHandler } from "./notify/welcome";
|
|
|
21
21
|
import { createVerifyController } from "./verify";
|
|
22
22
|
|
|
23
23
|
interface RuntimeState {
|
|
24
|
-
ctx?:
|
|
24
|
+
ctx?: MiokuContext;
|
|
25
25
|
config?: AdminConfig;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export default definePlugin({
|
|
29
29
|
name: "admin",
|
|
30
|
-
version: "1.0.0",
|
|
31
|
-
description: "管理插件,提供事件通知与群管/个人管理指令",
|
|
32
30
|
|
|
33
|
-
async setup(ctx:
|
|
31
|
+
async setup(ctx: MiokuContext) {
|
|
34
32
|
const configService = getService(ctx, Services.Config);
|
|
35
33
|
const aiService = getService(ctx, Services.AI);
|
|
36
34
|
|
|
@@ -91,8 +89,8 @@ export default definePlugin({
|
|
|
91
89
|
ctx,
|
|
92
90
|
aiService,
|
|
93
91
|
getConfig,
|
|
94
|
-
(info) => verifyController.handleMemberJoin(info),
|
|
95
|
-
(info) => verifyController.trySendCustomWelcome(info),
|
|
92
|
+
(info, bot) => verifyController.handleMemberJoin(info, bot),
|
|
93
|
+
(info, bot) => verifyController.trySendCustomWelcome(info, bot),
|
|
96
94
|
);
|
|
97
95
|
|
|
98
96
|
// 注册入群验证指令
|
package/notify/index.ts
CHANGED
|
@@ -1,30 +1,18 @@
|
|
|
1
|
-
import { botConfig, type MiokiContext } from "mioki";
|
|
2
1
|
import type {
|
|
3
|
-
FriendRequestEvent,
|
|
4
|
-
GroupBanNoticeEvent,
|
|
5
|
-
GroupDecreaseNoticeEvent,
|
|
6
|
-
GroupInviteRequestEvent,
|
|
7
2
|
MessageEvent,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
RecvImageElement,
|
|
15
|
-
RecvJsonElement,
|
|
16
|
-
RecvRecordElement,
|
|
17
|
-
RecvReplyElement,
|
|
18
|
-
RecvTextElement,
|
|
19
|
-
RecvVideoElement,
|
|
20
|
-
} from "napcat-sdk";
|
|
3
|
+
MessageSegment,
|
|
4
|
+
MiokuContext,
|
|
5
|
+
NoticeEvent,
|
|
6
|
+
RequestEvent,
|
|
7
|
+
} from "mioku";
|
|
8
|
+
import {botConfig} from "mioku";
|
|
21
9
|
import type { AdminConfig } from "../config";
|
|
22
10
|
import { formatDuration, getAvatarUrl, getGroupAvatarUrl } from "../config";
|
|
23
11
|
|
|
24
12
|
interface NotifyPayload {
|
|
25
13
|
avatarUrl?: string;
|
|
26
14
|
lines: string[];
|
|
27
|
-
rawSegments?:
|
|
15
|
+
rawSegments?: MessageSegment[];
|
|
28
16
|
}
|
|
29
17
|
|
|
30
18
|
interface PendingFriendRequest {
|
|
@@ -32,6 +20,8 @@ interface PendingFriendRequest {
|
|
|
32
20
|
userId: number;
|
|
33
21
|
flag: string;
|
|
34
22
|
createdAt: number;
|
|
23
|
+
approve: () => Promise<void>;
|
|
24
|
+
reject: (reason?: string) => Promise<void>;
|
|
35
25
|
}
|
|
36
26
|
|
|
37
27
|
interface PendingGroupInvite {
|
|
@@ -41,6 +31,8 @@ interface PendingGroupInvite {
|
|
|
41
31
|
flag: string;
|
|
42
32
|
subType: string;
|
|
43
33
|
createdAt: number;
|
|
34
|
+
approve: () => Promise<void>;
|
|
35
|
+
reject: (reason?: string) => Promise<void>;
|
|
44
36
|
}
|
|
45
37
|
|
|
46
38
|
const PENDING_TTL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -66,7 +58,7 @@ function getNotifyOwners(config: AdminConfig): number[] {
|
|
|
66
58
|
|
|
67
59
|
/** 注册所有事件通知处理器 */
|
|
68
60
|
export function registerNotificationHandlers(
|
|
69
|
-
ctx:
|
|
61
|
+
ctx: MiokuContext,
|
|
70
62
|
getConfig: () => AdminConfig,
|
|
71
63
|
) {
|
|
72
64
|
const pendingFriendRequests: PendingFriendRequest[] = [];
|
|
@@ -121,8 +113,8 @@ export function registerNotificationHandlers(
|
|
|
121
113
|
return true;
|
|
122
114
|
}
|
|
123
115
|
|
|
124
|
-
function buildNotifyMessage(payload: NotifyPayload):
|
|
125
|
-
const message:
|
|
116
|
+
function buildNotifyMessage(payload: NotifyPayload): MessageSegment[] {
|
|
117
|
+
const message: MessageSegment[] = [];
|
|
126
118
|
if (payload.avatarUrl) {
|
|
127
119
|
message.push(ctx.segment.image(payload.avatarUrl));
|
|
128
120
|
}
|
|
@@ -139,82 +131,60 @@ export function registerNotificationHandlers(
|
|
|
139
131
|
return message.length > 0 ? message : [ctx.segment.text("")];
|
|
140
132
|
}
|
|
141
133
|
|
|
142
|
-
function normalizeIncomingSegments(segments:
|
|
134
|
+
function normalizeIncomingSegments(segments: readonly MessageSegment[]): MessageSegment[] {
|
|
143
135
|
if (!Array.isArray(segments)) return [];
|
|
144
136
|
return segments
|
|
145
137
|
.map((seg) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if (type === "text") {
|
|
152
|
-
const text = String((seg as RecvTextElement).text || "");
|
|
153
|
-
return text ? ctx.segment.text(text) : null;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (type === "image" || type === "record" || type === "video") {
|
|
157
|
-
const mediaSeg = seg as
|
|
158
|
-
| RecvImageElement
|
|
159
|
-
| RecvRecordElement
|
|
160
|
-
| RecvVideoElement;
|
|
161
|
-
const source = String(mediaSeg.url || mediaSeg.file || "").trim();
|
|
162
|
-
if (!source) return null;
|
|
163
|
-
if (type === "image") {
|
|
164
|
-
return ctx.segment.image(source);
|
|
138
|
+
const data = seg.data as Record<string, unknown>;
|
|
139
|
+
switch (seg.type) {
|
|
140
|
+
case "text": {
|
|
141
|
+
const text = String(data.text ?? "").trim();
|
|
142
|
+
return text ? ctx.segment.text(text) : null;
|
|
165
143
|
}
|
|
166
|
-
|
|
167
|
-
|
|
144
|
+
case "image": {
|
|
145
|
+
const source = String(data.url ?? data.file ?? "").trim();
|
|
146
|
+
return source ? ctx.segment.image(source) : null;
|
|
168
147
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const id = (seg as RecvReplyElement).id;
|
|
193
|
-
if (id == null) return null;
|
|
194
|
-
return ctx.segment.reply(String(id));
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (type === "forward") {
|
|
198
|
-
const id = (seg as RecvForwardElement).id;
|
|
199
|
-
if (id == null) return null;
|
|
200
|
-
return (ctx.segment as any).forward?.(String(id)) ?? null;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (type === "json") {
|
|
204
|
-
const data = (seg as RecvJsonElement).data;
|
|
205
|
-
if (type === "json" && ctx.segment.json) {
|
|
206
|
-
return ctx.segment.json(data);
|
|
148
|
+
case "record": {
|
|
149
|
+
const source = String(data.file ?? data.url ?? "").trim();
|
|
150
|
+
return source ? ctx.segment.raw("record", { file: source }) : null;
|
|
151
|
+
}
|
|
152
|
+
case "video": {
|
|
153
|
+
const source = String(data.file ?? data.url ?? "").trim();
|
|
154
|
+
return source ? ctx.segment.raw("video", { file: source }) : null;
|
|
155
|
+
}
|
|
156
|
+
case "file": {
|
|
157
|
+
const source = String(data.file ?? data.url ?? "").trim();
|
|
158
|
+
return source ? ctx.segment.raw("file", { file: source }) : null;
|
|
159
|
+
}
|
|
160
|
+
case "at": {
|
|
161
|
+
const target = String(data.qq ?? data.target ?? "");
|
|
162
|
+
return target ? ctx.segment.at(target) : null;
|
|
163
|
+
}
|
|
164
|
+
case "face": {
|
|
165
|
+
const id = data.id;
|
|
166
|
+
return id == null ? null : ctx.segment.raw("face", { id: String(id) });
|
|
167
|
+
}
|
|
168
|
+
case "reply": {
|
|
169
|
+
const id = data.message_id ?? data.id;
|
|
170
|
+
return id == null ? null : ctx.segment.reply(String(id));
|
|
207
171
|
}
|
|
208
|
-
|
|
172
|
+
case "forward": {
|
|
173
|
+
const id = data.id;
|
|
174
|
+
return id == null ? null : ctx.segment.raw("forward", { id: String(id) });
|
|
175
|
+
}
|
|
176
|
+
case "json": {
|
|
177
|
+
return ctx.segment.raw("json", data);
|
|
178
|
+
}
|
|
179
|
+
default:
|
|
180
|
+
return seg;
|
|
209
181
|
}
|
|
210
|
-
|
|
211
|
-
return null;
|
|
212
182
|
})
|
|
213
|
-
.filter(
|
|
183
|
+
.filter((seg): seg is MessageSegment => seg !== null);
|
|
214
184
|
}
|
|
215
185
|
|
|
216
|
-
function isOwnerPrivateMessage(event:
|
|
217
|
-
if (ctx.
|
|
186
|
+
function isOwnerPrivateMessage(event: MessageEvent): boolean {
|
|
187
|
+
if (ctx.isMaster?.(event)) {
|
|
218
188
|
return true;
|
|
219
189
|
}
|
|
220
190
|
const userId = Number(event.user_id || 0);
|
|
@@ -224,19 +194,18 @@ export function registerNotificationHandlers(
|
|
|
224
194
|
return owners().includes(userId);
|
|
225
195
|
}
|
|
226
196
|
|
|
227
|
-
async function sendNotify(
|
|
228
|
-
const bot = ctx.pickBot(selfId);
|
|
197
|
+
async function sendNotify(bot: import("mioku").Bot | undefined, payload: NotifyPayload) {
|
|
229
198
|
if (!bot) return;
|
|
230
199
|
for (const ownerId of owners()) {
|
|
231
200
|
try {
|
|
232
|
-
await bot.
|
|
201
|
+
await bot.sendMessage({ type: "private", user_id: ownerId}, buildNotifyMessage(payload));
|
|
233
202
|
} catch (err) {
|
|
234
203
|
ctx.logger.error(`admin notify owner ${ownerId} failed: ${err}`);
|
|
235
204
|
}
|
|
236
205
|
}
|
|
237
206
|
}
|
|
238
207
|
|
|
239
|
-
function pushPendingFriendRequest(event:
|
|
208
|
+
function pushPendingFriendRequest(event: RequestEvent) {
|
|
240
209
|
const selfId = Number(event.self_id || 0);
|
|
241
210
|
const userId = Number(event.user_id || 0);
|
|
242
211
|
const flag = String(event.flag || "").trim();
|
|
@@ -246,11 +215,13 @@ export function registerNotificationHandlers(
|
|
|
246
215
|
userId,
|
|
247
216
|
flag,
|
|
248
217
|
createdAt: Date.now(),
|
|
218
|
+
approve: () => event.approve(),
|
|
219
|
+
reject: (reason?: string) => event.reject(reason),
|
|
249
220
|
});
|
|
250
221
|
prunePendingRequests();
|
|
251
222
|
}
|
|
252
223
|
|
|
253
|
-
function pushPendingGroupInvite(event:
|
|
224
|
+
function pushPendingGroupInvite(event: RequestEvent) {
|
|
254
225
|
const selfId = Number(event.self_id || 0);
|
|
255
226
|
const groupId = Number(event.group_id || 0);
|
|
256
227
|
const userId = Number(event.user_id || 0);
|
|
@@ -264,6 +235,8 @@ export function registerNotificationHandlers(
|
|
|
264
235
|
flag,
|
|
265
236
|
subType,
|
|
266
237
|
createdAt: Date.now(),
|
|
238
|
+
approve: () => event.approve(),
|
|
239
|
+
reject: (reason?: string) => event.reject(reason),
|
|
267
240
|
});
|
|
268
241
|
prunePendingRequests();
|
|
269
242
|
}
|
|
@@ -299,12 +272,12 @@ export function registerNotificationHandlers(
|
|
|
299
272
|
return undefined;
|
|
300
273
|
}
|
|
301
274
|
|
|
302
|
-
function extractTextFromSegments(segments:
|
|
275
|
+
function extractTextFromSegments(segments: readonly MessageSegment[]): string {
|
|
303
276
|
if (!Array.isArray(segments)) return "";
|
|
304
277
|
return segments
|
|
305
278
|
.map((seg) => {
|
|
306
279
|
if (seg.type !== "text") return "";
|
|
307
|
-
return String((seg as
|
|
280
|
+
return String((seg.data as Record<string, unknown>).text ?? "");
|
|
308
281
|
})
|
|
309
282
|
.join("")
|
|
310
283
|
.trim();
|
|
@@ -313,7 +286,7 @@ export function registerNotificationHandlers(
|
|
|
313
286
|
async function resolveQuotedText(event: MessageEvent): Promise<string> {
|
|
314
287
|
if (!event.quote_id) return "";
|
|
315
288
|
try {
|
|
316
|
-
const quoted = await event.
|
|
289
|
+
const quoted = await event.bot.getMessage(event.quote_id);
|
|
317
290
|
return extractTextFromSegments(quoted?.message || []);
|
|
318
291
|
} catch {
|
|
319
292
|
return "";
|
|
@@ -369,13 +342,10 @@ export function registerNotificationHandlers(
|
|
|
369
342
|
return null;
|
|
370
343
|
}
|
|
371
344
|
|
|
372
|
-
function extractReplyPayloadSegments(event: MessageEvent):
|
|
373
|
-
return normalizeIncomingSegments(event?.message || []).filter(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
return type !== "reply";
|
|
377
|
-
},
|
|
378
|
-
);
|
|
345
|
+
function extractReplyPayloadSegments(event: MessageEvent): MessageSegment[] {
|
|
346
|
+
return normalizeIncomingSegments(event?.message || []).filter((seg) => {
|
|
347
|
+
return seg.type !== "reply";
|
|
348
|
+
});
|
|
379
349
|
}
|
|
380
350
|
|
|
381
351
|
function isApproveText(text: string): boolean {
|
|
@@ -389,7 +359,7 @@ export function registerNotificationHandlers(
|
|
|
389
359
|
}
|
|
390
360
|
|
|
391
361
|
async function notifyGroupInvite(
|
|
392
|
-
event:
|
|
362
|
+
event: RequestEvent,
|
|
393
363
|
): Promise<void> {
|
|
394
364
|
if (!getConfig().notifyGroupInvite) return;
|
|
395
365
|
|
|
@@ -405,7 +375,7 @@ export function registerNotificationHandlers(
|
|
|
405
375
|
pushPendingGroupInvite(event);
|
|
406
376
|
|
|
407
377
|
const comment = event.comment || "无";
|
|
408
|
-
await sendNotify(
|
|
378
|
+
await sendNotify(event.bot, {
|
|
409
379
|
avatarUrl: getGroupAvatarUrl(groupId),
|
|
410
380
|
lines: [
|
|
411
381
|
"[群邀请]",
|
|
@@ -417,26 +387,27 @@ export function registerNotificationHandlers(
|
|
|
417
387
|
});
|
|
418
388
|
}
|
|
419
389
|
|
|
420
|
-
async function notifyGroupBan(event:
|
|
390
|
+
async function notifyGroupBan(event: NoticeEvent): Promise<void> {
|
|
421
391
|
const selfId = Number(event.self_id || 0);
|
|
422
392
|
const groupId = Number(event.group_id || 0);
|
|
423
393
|
const userId = Number(event.user_id || 0);
|
|
424
|
-
const
|
|
394
|
+
const rawBan = event.raw as { action_type?: string; duration?: number } | undefined;
|
|
395
|
+
const duration = Number(rawBan?.duration || 0);
|
|
425
396
|
if (!selfId || !groupId) return;
|
|
426
397
|
if (userId !== selfId) return;
|
|
427
398
|
|
|
428
399
|
const operatorId = Number(event.operator_id || 0);
|
|
429
|
-
const isUnban =
|
|
400
|
+
const isUnban = rawBan?.action_type === "lift_ban";
|
|
430
401
|
if (isUnban) {
|
|
431
402
|
if (!getConfig().notifyGroupUnban) return;
|
|
432
403
|
} else if (!getConfig().notifyGroupBan) {
|
|
433
404
|
return;
|
|
434
405
|
}
|
|
435
406
|
|
|
436
|
-
const eventKey = `group-ban:${selfId}:${groupId}:${operatorId}:${duration}:${
|
|
407
|
+
const eventKey = `group-ban:${selfId}:${groupId}:${operatorId}:${duration}:${rawBan?.action_type}:${Number(event.time || 0)}`;
|
|
437
408
|
if (!markEventOnce(eventKey)) return;
|
|
438
409
|
|
|
439
|
-
await sendNotify(
|
|
410
|
+
await sendNotify(event.bot, {
|
|
440
411
|
avatarUrl: getGroupAvatarUrl(groupId),
|
|
441
412
|
lines: [
|
|
442
413
|
isUnban ? "[Bot被解除禁言]" : "[Bot被禁言]",
|
|
@@ -449,14 +420,15 @@ export function registerNotificationHandlers(
|
|
|
449
420
|
}
|
|
450
421
|
|
|
451
422
|
async function notifyGroupKick(
|
|
452
|
-
event:
|
|
423
|
+
event: NoticeEvent,
|
|
453
424
|
): Promise<void> {
|
|
454
425
|
if (!getConfig().notifyGroupKick) return;
|
|
455
426
|
|
|
456
427
|
const selfId = Number(event.self_id || 0);
|
|
457
428
|
const groupId = Number(event.group_id || 0);
|
|
458
429
|
const userId = Number(event.user_id || 0);
|
|
459
|
-
const
|
|
430
|
+
const rawKick = event.raw as { action_type?: string } | undefined;
|
|
431
|
+
const leaveType = String(rawKick?.action_type || "").trim();
|
|
460
432
|
if (!selfId || !groupId) return;
|
|
461
433
|
if (userId !== selfId) return;
|
|
462
434
|
if (leaveType !== "kick" && leaveType !== "kick_me") return;
|
|
@@ -465,7 +437,7 @@ export function registerNotificationHandlers(
|
|
|
465
437
|
const eventKey = `group-kick:${selfId}:${groupId}:${operatorId}:${leaveType}:${Number(event.time || 0)}`;
|
|
466
438
|
if (!markEventOnce(eventKey)) return;
|
|
467
439
|
|
|
468
|
-
await sendNotify(
|
|
440
|
+
await sendNotify(event.bot, {
|
|
469
441
|
avatarUrl:
|
|
470
442
|
operatorId > 0 ? getAvatarUrl(operatorId) : getGroupAvatarUrl(groupId),
|
|
471
443
|
lines: ["[Bot被踢]", `群号:${groupId}`, `操作者QQ:${operatorId}`],
|
|
@@ -473,16 +445,16 @@ export function registerNotificationHandlers(
|
|
|
473
445
|
}
|
|
474
446
|
|
|
475
447
|
// 好友私聊消息通知
|
|
476
|
-
ctx.handle("message.private", async (event
|
|
448
|
+
ctx.handle("message.private", async (event) => {
|
|
477
449
|
if (!getConfig().notifyFriendMsg) return;
|
|
478
450
|
if (event.user_id === event.self_id) return;
|
|
479
451
|
if (isOwnerPrivateMessage(event)) return;
|
|
480
452
|
|
|
481
|
-
const userId = event.user_id;
|
|
482
|
-
const nickname = event.sender?.nickname ||
|
|
453
|
+
const userId = String(event.user_id ?? "");
|
|
454
|
+
const nickname = event.sender?.nickname || userId;
|
|
483
455
|
const rawSegments = normalizeIncomingSegments(event.message || []);
|
|
484
456
|
|
|
485
|
-
await sendNotify(event.
|
|
457
|
+
await sendNotify(event.bot, {
|
|
486
458
|
avatarUrl: getAvatarUrl(userId),
|
|
487
459
|
lines: [
|
|
488
460
|
"[好友消息]",
|
|
@@ -496,14 +468,14 @@ export function registerNotificationHandlers(
|
|
|
496
468
|
});
|
|
497
469
|
|
|
498
470
|
// 好友申请通知
|
|
499
|
-
ctx.handle("request.friend", async (event
|
|
471
|
+
ctx.handle("request.friend", async (event) => {
|
|
500
472
|
pushPendingFriendRequest(event);
|
|
501
473
|
if (!getConfig().notifyFriendRequest) return;
|
|
502
474
|
|
|
503
|
-
const userId = event.user_id;
|
|
475
|
+
const userId = String(event.user_id ?? "");
|
|
504
476
|
const comment = event.comment || "无";
|
|
505
477
|
|
|
506
|
-
await sendNotify(event.
|
|
478
|
+
await sendNotify(event.bot, {
|
|
507
479
|
avatarUrl: getAvatarUrl(userId),
|
|
508
480
|
lines: [
|
|
509
481
|
"[好友申请]",
|
|
@@ -515,27 +487,27 @@ export function registerNotificationHandlers(
|
|
|
515
487
|
});
|
|
516
488
|
|
|
517
489
|
// 群邀请通知
|
|
518
|
-
ctx.handle("request.group.invite", async (event
|
|
490
|
+
ctx.handle("request.group.invite", async (event) => {
|
|
519
491
|
await notifyGroupInvite(event);
|
|
520
492
|
});
|
|
521
493
|
|
|
522
494
|
// Bot被禁言通知
|
|
523
|
-
ctx.handle("notice.group.ban", async (event
|
|
495
|
+
ctx.handle("notice.group.ban", async (event) => {
|
|
524
496
|
await notifyGroupBan(event);
|
|
525
497
|
});
|
|
526
498
|
|
|
527
499
|
// Bot被踢通知
|
|
528
500
|
ctx.handle(
|
|
529
501
|
"notice.group.decrease",
|
|
530
|
-
async (event
|
|
502
|
+
async (event) => {
|
|
531
503
|
await notifyGroupKick(event);
|
|
532
504
|
},
|
|
533
505
|
);
|
|
534
506
|
|
|
535
507
|
// 引用回复处理
|
|
536
|
-
ctx.handle("message", async (event
|
|
508
|
+
ctx.handle("message", async (event) => {
|
|
537
509
|
if (event.message_type !== "private") return;
|
|
538
|
-
if (!ctx.
|
|
510
|
+
if (!ctx.isMaster?.(event)) return;
|
|
539
511
|
|
|
540
512
|
const quotedText = await resolveQuotedText(event);
|
|
541
513
|
if (!quotedText) {
|
|
@@ -549,7 +521,7 @@ export function registerNotificationHandlers(
|
|
|
549
521
|
const text = (ctx.text(event) || "").trim();
|
|
550
522
|
|
|
551
523
|
const selfId = Number(event.self_id || 0);
|
|
552
|
-
const bot =
|
|
524
|
+
const bot = event.bot;
|
|
553
525
|
if (!bot) {
|
|
554
526
|
await event.reply("Bot不可用", true);
|
|
555
527
|
return;
|
|
@@ -562,7 +534,7 @@ export function registerNotificationHandlers(
|
|
|
562
534
|
return;
|
|
563
535
|
}
|
|
564
536
|
try {
|
|
565
|
-
await bot.
|
|
537
|
+
await bot.sendMessage({ type: "private", user_id: target.userId}, payload);
|
|
566
538
|
await event.reply("done");
|
|
567
539
|
} catch (err) {
|
|
568
540
|
ctx.logger.error(
|
|
@@ -585,10 +557,11 @@ export function registerNotificationHandlers(
|
|
|
585
557
|
}
|
|
586
558
|
|
|
587
559
|
try {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
560
|
+
if (isApproveText(text)) {
|
|
561
|
+
await pending.approve();
|
|
562
|
+
} else {
|
|
563
|
+
await pending.reject();
|
|
564
|
+
}
|
|
592
565
|
await event.reply("done");
|
|
593
566
|
} catch (err) {
|
|
594
567
|
ctx.logger.error(
|
|
@@ -605,10 +578,7 @@ export function registerNotificationHandlers(
|
|
|
605
578
|
return;
|
|
606
579
|
}
|
|
607
580
|
try {
|
|
608
|
-
await bot.
|
|
609
|
-
group_id: target.groupId,
|
|
610
|
-
is_dismiss: false,
|
|
611
|
-
});
|
|
581
|
+
await bot.leaveGroup(String(target.groupId), false);
|
|
612
582
|
await event.reply("done");
|
|
613
583
|
} catch (err) {
|
|
614
584
|
ctx.logger.error(
|
|
@@ -636,11 +606,11 @@ export function registerNotificationHandlers(
|
|
|
636
606
|
}
|
|
637
607
|
|
|
638
608
|
try {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
609
|
+
if (isApproveText(text)) {
|
|
610
|
+
await pending.approve();
|
|
611
|
+
} else {
|
|
612
|
+
await pending.reject();
|
|
613
|
+
}
|
|
644
614
|
await event.reply("done");
|
|
645
615
|
} catch (err) {
|
|
646
616
|
ctx.logger.error(
|