koishi-plugin-group-control 1.1.0-alpha.0 → 1.1.0-alpha.2
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/lib/index.js +2091 -0
- package/package.json +26 -26
package/lib/index.js
ADDED
|
@@ -0,0 +1,2091 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name9 in all)
|
|
8
|
+
__defProp(target, name9, { get: all[name9], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
Config: () => Config,
|
|
24
|
+
apply: () => apply8,
|
|
25
|
+
inject: () => inject,
|
|
26
|
+
name: () => name8
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(src_exports);
|
|
29
|
+
|
|
30
|
+
// src/database.ts
|
|
31
|
+
var database_exports = {};
|
|
32
|
+
__export(database_exports, {
|
|
33
|
+
BLACKLIST_PLATFORM: () => BLACKLIST_PLATFORM,
|
|
34
|
+
addPendingFriendRequest: () => addPendingFriendRequest,
|
|
35
|
+
addPendingInvite: () => addPendingInvite,
|
|
36
|
+
addToSmallGroupWhitelist: () => addToSmallGroupWhitelist,
|
|
37
|
+
apply: () => apply,
|
|
38
|
+
blacklistKicked: () => blacklistKicked,
|
|
39
|
+
clearApprovedGuild: () => clearApprovedGuild,
|
|
40
|
+
clearBlacklistedGuilds: () => clearBlacklistedGuilds,
|
|
41
|
+
clearExpiredPendingFriendRequests: () => clearExpiredPendingFriendRequests,
|
|
42
|
+
clearExpiredPendingInvites: () => clearExpiredPendingInvites,
|
|
43
|
+
clearExpiredSelfLeft: () => clearExpiredSelfLeft,
|
|
44
|
+
clearSelfLeft: () => clearSelfLeft,
|
|
45
|
+
consumeSelfLeft: () => consumeSelfLeft,
|
|
46
|
+
createBlacklistedGuild: () => createBlacklistedGuild,
|
|
47
|
+
getAllBlacklistedGuilds: () => getAllBlacklistedGuilds,
|
|
48
|
+
getAllPendingFriendRequests: () => getAllPendingFriendRequests,
|
|
49
|
+
getAllPendingInvites: () => getAllPendingInvites,
|
|
50
|
+
getAllSmallGroupWhitelist: () => getAllSmallGroupWhitelist,
|
|
51
|
+
getBlacklistedGuild: () => getBlacklistedGuild,
|
|
52
|
+
getCommandFrequencyRecord: () => getCommandFrequencyRecord,
|
|
53
|
+
getGroupBotStatus: () => getGroupBotStatus,
|
|
54
|
+
getPendingFriendRequest: () => getPendingFriendRequest,
|
|
55
|
+
getPendingInvite: () => getPendingInvite,
|
|
56
|
+
isApprovedGuild: () => isApprovedGuild,
|
|
57
|
+
isInSmallGroupWhitelist: () => isInSmallGroupWhitelist,
|
|
58
|
+
markApprovedGuild: () => markApprovedGuild,
|
|
59
|
+
markSelfLeft: () => markSelfLeft,
|
|
60
|
+
name: () => name,
|
|
61
|
+
removeBlacklistedGuild: () => removeBlacklistedGuild,
|
|
62
|
+
removeFromSmallGroupWhitelist: () => removeFromSmallGroupWhitelist,
|
|
63
|
+
removePendingFriendRequest: () => removePendingFriendRequest,
|
|
64
|
+
removePendingInvite: () => removePendingInvite,
|
|
65
|
+
setGroupBotStatus: () => setGroupBotStatus,
|
|
66
|
+
updateCommandFrequencyRecord: () => updateCommandFrequencyRecord
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// src/utils.ts
|
|
70
|
+
function isBlacklistEnabled(config) {
|
|
71
|
+
if (!config.enableBlacklist) return "黑名单功能未启用。";
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
__name(isBlacklistEnabled, "isBlacklistEnabled");
|
|
75
|
+
function parseGuildId(input) {
|
|
76
|
+
if (!input) return null;
|
|
77
|
+
const match = input.trim().match(/^(?:[^:]+:)?(\d+)$/);
|
|
78
|
+
return match ? match[1] : null;
|
|
79
|
+
}
|
|
80
|
+
__name(parseGuildId, "parseGuildId");
|
|
81
|
+
function toOneBotNumber(input) {
|
|
82
|
+
const id = parseGuildId(input);
|
|
83
|
+
if (!id) return null;
|
|
84
|
+
const value = Number(id);
|
|
85
|
+
return Number.isSafeInteger(value) ? value : null;
|
|
86
|
+
}
|
|
87
|
+
__name(toOneBotNumber, "toOneBotNumber");
|
|
88
|
+
function getAdminCommandOptions(config) {
|
|
89
|
+
return config.permission.mode === "koishi" ? { authority: config.permission.koishiAuthority } : {};
|
|
90
|
+
}
|
|
91
|
+
__name(getAdminCommandOptions, "getAdminCommandOptions");
|
|
92
|
+
function formatDate(timestamp) {
|
|
93
|
+
return new Date(timestamp * 1e3).toLocaleString();
|
|
94
|
+
}
|
|
95
|
+
__name(formatDate, "formatDate");
|
|
96
|
+
async function notifyAdmins(bot, config, message) {
|
|
97
|
+
if (config.admin.notificationGroupId) {
|
|
98
|
+
try {
|
|
99
|
+
await bot.sendMessage(config.admin.notificationGroupId, message);
|
|
100
|
+
return;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`发送通知到通知群 ${config.admin.notificationGroupId} 失败:`, error);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (config.admin.adminQQs?.length > 0) {
|
|
106
|
+
for (const adminQQ of config.admin.adminQQs) {
|
|
107
|
+
try {
|
|
108
|
+
await bot.sendPrivateMessage(adminQQ, message);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error(`发送通知给管理员 ${adminQQ} 失败:`, error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
__name(notifyAdmins, "notifyAdmins");
|
|
116
|
+
function isGlobalAdmin(session, config) {
|
|
117
|
+
if (config.permission.mode === "koishi") {
|
|
118
|
+
const user = session.user;
|
|
119
|
+
return typeof user?.authority === "number" && user.authority >= config.permission.koishiAuthority;
|
|
120
|
+
}
|
|
121
|
+
return config.admin.adminQQs?.includes(session.userId) ?? false;
|
|
122
|
+
}
|
|
123
|
+
__name(isGlobalAdmin, "isGlobalAdmin");
|
|
124
|
+
var GUILD_ADMIN_CACHE_TTL = 30 * 1e3;
|
|
125
|
+
var GUILD_ADMIN_NEGATIVE_CACHE_TTL = 5 * 1e3;
|
|
126
|
+
var GUILD_ADMIN_CACHE_MAX = 1e3;
|
|
127
|
+
var SimpleLRUCache = class {
|
|
128
|
+
constructor(ttlMs, maxSize = 1e3) {
|
|
129
|
+
this.ttlMs = ttlMs;
|
|
130
|
+
this.maxSize = maxSize;
|
|
131
|
+
}
|
|
132
|
+
static {
|
|
133
|
+
__name(this, "SimpleLRUCache");
|
|
134
|
+
}
|
|
135
|
+
entries = /* @__PURE__ */ new Map();
|
|
136
|
+
get(key) {
|
|
137
|
+
const entry = this.entries.get(key);
|
|
138
|
+
if (!entry) return void 0;
|
|
139
|
+
if (entry.expiresAt <= Date.now()) {
|
|
140
|
+
this.entries.delete(key);
|
|
141
|
+
return void 0;
|
|
142
|
+
}
|
|
143
|
+
this.entries.delete(key);
|
|
144
|
+
this.entries.set(key, entry);
|
|
145
|
+
return entry.value;
|
|
146
|
+
}
|
|
147
|
+
set(key, value, ttlMs = this.ttlMs) {
|
|
148
|
+
this.entries.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
149
|
+
this.prune();
|
|
150
|
+
}
|
|
151
|
+
delete(key) {
|
|
152
|
+
this.entries.delete(key);
|
|
153
|
+
}
|
|
154
|
+
clear() {
|
|
155
|
+
this.entries.clear();
|
|
156
|
+
}
|
|
157
|
+
prune(now = Date.now()) {
|
|
158
|
+
for (const [key, entry] of this.entries) {
|
|
159
|
+
if (entry.expiresAt <= now) this.entries.delete(key);
|
|
160
|
+
}
|
|
161
|
+
while (this.entries.size > this.maxSize) {
|
|
162
|
+
const oldestKey = this.entries.keys().next().value;
|
|
163
|
+
if (!oldestKey) break;
|
|
164
|
+
this.entries.delete(oldestKey);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
var guildAdminCache = new SimpleLRUCache(GUILD_ADMIN_CACHE_TTL, GUILD_ADMIN_CACHE_MAX);
|
|
169
|
+
function guildAdminCacheKey(session) {
|
|
170
|
+
if (!session.guildId || !session.userId) return null;
|
|
171
|
+
return `${session.platform}:${session.guildId}:${session.userId}`;
|
|
172
|
+
}
|
|
173
|
+
__name(guildAdminCacheKey, "guildAdminCacheKey");
|
|
174
|
+
function getCachedGuildAdmin(session) {
|
|
175
|
+
const key = guildAdminCacheKey(session);
|
|
176
|
+
if (!key) return void 0;
|
|
177
|
+
return guildAdminCache.get(key);
|
|
178
|
+
}
|
|
179
|
+
__name(getCachedGuildAdmin, "getCachedGuildAdmin");
|
|
180
|
+
function setCachedGuildAdmin(session, value) {
|
|
181
|
+
const key = guildAdminCacheKey(session);
|
|
182
|
+
if (!key) return;
|
|
183
|
+
const ttl = value ? GUILD_ADMIN_CACHE_TTL : GUILD_ADMIN_NEGATIVE_CACHE_TTL;
|
|
184
|
+
guildAdminCache.set(key, value, ttl);
|
|
185
|
+
}
|
|
186
|
+
__name(setCachedGuildAdmin, "setCachedGuildAdmin");
|
|
187
|
+
function clearGuildAdminCache() {
|
|
188
|
+
guildAdminCache.clear();
|
|
189
|
+
}
|
|
190
|
+
__name(clearGuildAdminCache, "clearGuildAdminCache");
|
|
191
|
+
function hasAdminRole(member) {
|
|
192
|
+
if (!member) return false;
|
|
193
|
+
const data = member.data ?? member.member ?? member.sender ?? member;
|
|
194
|
+
const role = data.role ?? data.memberRole ?? data.permissions;
|
|
195
|
+
if (role === "admin" || role === "owner" || role === "administrator") return true;
|
|
196
|
+
const roles = data.roles ?? data.roleIds;
|
|
197
|
+
if (Array.isArray(roles)) {
|
|
198
|
+
return roles.some((role2) => role2 === "admin" || role2 === "owner" || role2 === "administrator");
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
__name(hasAdminRole, "hasAdminRole");
|
|
203
|
+
async function getOneBotGroupMemberInfo(session) {
|
|
204
|
+
const guildId = Number(parseGuildId(session.guildId) ?? session.guildId);
|
|
205
|
+
const userId = Number(parseGuildId(session.userId) ?? session.userId);
|
|
206
|
+
if (!Number.isFinite(guildId) || !Number.isFinite(userId)) return null;
|
|
207
|
+
return await session.bot.internal?.getGroupMemberInfo?.(guildId, userId, true);
|
|
208
|
+
}
|
|
209
|
+
__name(getOneBotGroupMemberInfo, "getOneBotGroupMemberInfo");
|
|
210
|
+
async function isGuildAdmin(session) {
|
|
211
|
+
const event = session.event;
|
|
212
|
+
const raw = event?._data ?? session.original ?? session.onebot;
|
|
213
|
+
if (hasAdminRole(event?.member) || hasAdminRole(raw?.sender)) {
|
|
214
|
+
setCachedGuildAdmin(session, true);
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
const cached = getCachedGuildAdmin(session);
|
|
218
|
+
if (cached !== void 0) return cached;
|
|
219
|
+
let result = false;
|
|
220
|
+
try {
|
|
221
|
+
const member = await session.bot.getGuildMember(session.guildId, session.userId);
|
|
222
|
+
if (hasAdminRole(member)) result = true;
|
|
223
|
+
} catch {
|
|
224
|
+
}
|
|
225
|
+
if (!result) {
|
|
226
|
+
try {
|
|
227
|
+
const info = await getOneBotGroupMemberInfo(session);
|
|
228
|
+
if (hasAdminRole(info)) result = true;
|
|
229
|
+
} catch {
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
setCachedGuildAdmin(session, result);
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
__name(isGuildAdmin, "isGuildAdmin");
|
|
236
|
+
async function hasGuildPermission(session, config) {
|
|
237
|
+
if (config.permission.mode === "koishi") {
|
|
238
|
+
const user = session.user;
|
|
239
|
+
return typeof user?.authority === "number" && user.authority >= config.permission.koishiAuthority;
|
|
240
|
+
}
|
|
241
|
+
if (isGlobalAdmin(session, config)) return true;
|
|
242
|
+
return await isGuildAdmin(session);
|
|
243
|
+
}
|
|
244
|
+
__name(hasGuildPermission, "hasGuildPermission");
|
|
245
|
+
function hasGlobalPermission(session, config) {
|
|
246
|
+
return isGlobalAdmin(session, config);
|
|
247
|
+
}
|
|
248
|
+
__name(hasGlobalPermission, "hasGlobalPermission");
|
|
249
|
+
var ADMIN_COMMANDS = /* @__PURE__ */ new Set([
|
|
250
|
+
"bot-on",
|
|
251
|
+
"bot-off",
|
|
252
|
+
"quit",
|
|
253
|
+
"gc",
|
|
254
|
+
"gc.banlist",
|
|
255
|
+
"gc.unban",
|
|
256
|
+
"gc.ban",
|
|
257
|
+
"gc.clearban",
|
|
258
|
+
"gc.approve",
|
|
259
|
+
"gc.reject",
|
|
260
|
+
"gc.pending",
|
|
261
|
+
"gc.sg-add",
|
|
262
|
+
"gc.sg-rm",
|
|
263
|
+
"gc.sg-list",
|
|
264
|
+
"gc.friends",
|
|
265
|
+
"gc.delfriend",
|
|
266
|
+
"gc.groups",
|
|
267
|
+
"gc.leave",
|
|
268
|
+
"gc.friend-pending",
|
|
269
|
+
"gc.friend-approve",
|
|
270
|
+
"gc.friend-reject",
|
|
271
|
+
"gc.fp",
|
|
272
|
+
"gc.fa",
|
|
273
|
+
"gc.fr"
|
|
274
|
+
]);
|
|
275
|
+
|
|
276
|
+
// src/database.ts
|
|
277
|
+
var name = "group-control-database";
|
|
278
|
+
function apply(ctx) {
|
|
279
|
+
ctx.on("dispose", () => {
|
|
280
|
+
groupBotStatusCache.clear();
|
|
281
|
+
});
|
|
282
|
+
ctx.model.extend("blacklisted_guild", {
|
|
283
|
+
platform: "string",
|
|
284
|
+
guildId: "string",
|
|
285
|
+
timestamp: "integer",
|
|
286
|
+
reason: "string"
|
|
287
|
+
}, { primary: ["platform", "guildId"] });
|
|
288
|
+
ctx.model.extend("command_frequency_record", {
|
|
289
|
+
platform: "string",
|
|
290
|
+
guildId: "string",
|
|
291
|
+
commandCount: "integer",
|
|
292
|
+
lastCommandTime: "integer",
|
|
293
|
+
warningSent: "boolean",
|
|
294
|
+
blockExpiryTime: "integer",
|
|
295
|
+
firstWarningTime: "integer",
|
|
296
|
+
blockCount: "integer",
|
|
297
|
+
lastBlockNotifyTime: "integer"
|
|
298
|
+
}, { primary: ["platform", "guildId"] });
|
|
299
|
+
ctx.model.extend("group_bot_status", {
|
|
300
|
+
platform: "string",
|
|
301
|
+
guildId: "string",
|
|
302
|
+
botEnabled: "boolean"
|
|
303
|
+
}, { primary: ["platform", "guildId"] });
|
|
304
|
+
ctx.model.extend("small_group_whitelist", {
|
|
305
|
+
platform: "string",
|
|
306
|
+
guildId: "string"
|
|
307
|
+
}, { primary: ["platform", "guildId"] });
|
|
308
|
+
ctx.model.extend("self_left_guild", {
|
|
309
|
+
platform: "string",
|
|
310
|
+
guildId: "string",
|
|
311
|
+
selfId: "string",
|
|
312
|
+
timestamp: "integer"
|
|
313
|
+
}, { primary: ["platform", "guildId"] });
|
|
314
|
+
ctx.model.extend("approved_guild", {
|
|
315
|
+
platform: "string",
|
|
316
|
+
guildId: "string",
|
|
317
|
+
selfId: "string",
|
|
318
|
+
timestamp: "integer"
|
|
319
|
+
}, { primary: ["platform", "guildId"] });
|
|
320
|
+
ctx.model.extend("pending_invite", {
|
|
321
|
+
platform: "string",
|
|
322
|
+
groupId: "string",
|
|
323
|
+
selfId: "string",
|
|
324
|
+
userId: "string",
|
|
325
|
+
userName: "string",
|
|
326
|
+
groupName: "string",
|
|
327
|
+
time: "integer",
|
|
328
|
+
flag: "string"
|
|
329
|
+
}, { primary: ["platform", "groupId"] });
|
|
330
|
+
ctx.model.extend("pending_friend_request", {
|
|
331
|
+
platform: "string",
|
|
332
|
+
selfId: "string",
|
|
333
|
+
userId: "string",
|
|
334
|
+
nickname: "string",
|
|
335
|
+
comment: "string",
|
|
336
|
+
flag: "string",
|
|
337
|
+
time: "integer"
|
|
338
|
+
}, { primary: ["platform", "userId"] });
|
|
339
|
+
}
|
|
340
|
+
__name(apply, "apply");
|
|
341
|
+
var BLACKLIST_PLATFORM = "onebot";
|
|
342
|
+
function normalizeId(id) {
|
|
343
|
+
return parseGuildId(id) ?? id;
|
|
344
|
+
}
|
|
345
|
+
__name(normalizeId, "normalizeId");
|
|
346
|
+
function scopedId(id, selfId) {
|
|
347
|
+
return `${normalizeId(selfId)}#${normalizeId(id)}`;
|
|
348
|
+
}
|
|
349
|
+
__name(scopedId, "scopedId");
|
|
350
|
+
function unscopedId(id) {
|
|
351
|
+
const match = id.match(/^\d+#(.+)$/);
|
|
352
|
+
return match ? normalizeId(match[1]) : normalizeId(id);
|
|
353
|
+
}
|
|
354
|
+
__name(unscopedId, "unscopedId");
|
|
355
|
+
function isScopedId(id) {
|
|
356
|
+
return /^\d+#.+$/.test(id);
|
|
357
|
+
}
|
|
358
|
+
__name(isScopedId, "isScopedId");
|
|
359
|
+
function decodePendingInvite(row) {
|
|
360
|
+
return { ...row, groupId: unscopedId(row.groupId), userId: unscopedId(row.userId) };
|
|
361
|
+
}
|
|
362
|
+
__name(decodePendingInvite, "decodePendingInvite");
|
|
363
|
+
function decodePendingFriendRequest(row) {
|
|
364
|
+
return { ...row, userId: unscopedId(row.userId) };
|
|
365
|
+
}
|
|
366
|
+
__name(decodePendingFriendRequest, "decodePendingFriendRequest");
|
|
367
|
+
async function getRowsByNormalizedId(ctx, table, field, platform, id) {
|
|
368
|
+
const normalized = normalizeId(id);
|
|
369
|
+
const rows = await ctx.database.get(table, { platform });
|
|
370
|
+
return rows.filter((row) => normalizeId(String(row[field] ?? "")) === normalized);
|
|
371
|
+
}
|
|
372
|
+
__name(getRowsByNormalizedId, "getRowsByNormalizedId");
|
|
373
|
+
async function getBlacklistedGuild(ctx, guildId) {
|
|
374
|
+
guildId = normalizeId(guildId);
|
|
375
|
+
const rows = await ctx.database.get("blacklisted_guild", { platform: BLACKLIST_PLATFORM, guildId });
|
|
376
|
+
return rows.length > 0 ? rows : await getRowsByNormalizedId(ctx, "blacklisted_guild", "guildId", BLACKLIST_PLATFORM, guildId);
|
|
377
|
+
}
|
|
378
|
+
__name(getBlacklistedGuild, "getBlacklistedGuild");
|
|
379
|
+
async function removeBlacklistedGuild(ctx, guildId) {
|
|
380
|
+
guildId = normalizeId(guildId);
|
|
381
|
+
const rows = await getRowsByNormalizedId(ctx, "blacklisted_guild", "guildId", BLACKLIST_PLATFORM, guildId);
|
|
382
|
+
await ctx.database.remove("blacklisted_guild", { platform: BLACKLIST_PLATFORM, guildId });
|
|
383
|
+
for (const row of rows) {
|
|
384
|
+
if (row.guildId !== guildId) {
|
|
385
|
+
await ctx.database.remove("blacklisted_guild", { platform: BLACKLIST_PLATFORM, guildId: row.guildId });
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return rows.length > 0;
|
|
389
|
+
}
|
|
390
|
+
__name(removeBlacklistedGuild, "removeBlacklistedGuild");
|
|
391
|
+
async function createBlacklistedGuild(ctx, guildId, reason) {
|
|
392
|
+
guildId = normalizeId(guildId);
|
|
393
|
+
return await ctx.database.upsert("blacklisted_guild", [{
|
|
394
|
+
platform: BLACKLIST_PLATFORM,
|
|
395
|
+
guildId,
|
|
396
|
+
timestamp: Math.floor(Date.now() / 1e3),
|
|
397
|
+
reason
|
|
398
|
+
}]);
|
|
399
|
+
}
|
|
400
|
+
__name(createBlacklistedGuild, "createBlacklistedGuild");
|
|
401
|
+
async function getAllBlacklistedGuilds(ctx) {
|
|
402
|
+
return await ctx.database.get("blacklisted_guild", { platform: BLACKLIST_PLATFORM });
|
|
403
|
+
}
|
|
404
|
+
__name(getAllBlacklistedGuilds, "getAllBlacklistedGuilds");
|
|
405
|
+
async function clearBlacklistedGuilds(ctx) {
|
|
406
|
+
return await ctx.database.remove("blacklisted_guild", { platform: BLACKLIST_PLATFORM });
|
|
407
|
+
}
|
|
408
|
+
__name(clearBlacklistedGuilds, "clearBlacklistedGuilds");
|
|
409
|
+
async function blacklistKicked(ctx, guildId) {
|
|
410
|
+
guildId = normalizeId(guildId);
|
|
411
|
+
return await ctx.database.upsert("blacklisted_guild", [{
|
|
412
|
+
platform: BLACKLIST_PLATFORM,
|
|
413
|
+
guildId,
|
|
414
|
+
timestamp: Math.floor(Date.now() / 1e3),
|
|
415
|
+
reason: "kicked"
|
|
416
|
+
}]);
|
|
417
|
+
}
|
|
418
|
+
__name(blacklistKicked, "blacklistKicked");
|
|
419
|
+
async function markSelfLeft(ctx, guildId, selfId) {
|
|
420
|
+
selfId = normalizeId(selfId);
|
|
421
|
+
guildId = scopedId(guildId, selfId);
|
|
422
|
+
await ctx.database.upsert("self_left_guild", [{
|
|
423
|
+
platform: BLACKLIST_PLATFORM,
|
|
424
|
+
guildId,
|
|
425
|
+
selfId,
|
|
426
|
+
timestamp: Math.floor(Date.now() / 1e3)
|
|
427
|
+
}]);
|
|
428
|
+
}
|
|
429
|
+
__name(markSelfLeft, "markSelfLeft");
|
|
430
|
+
async function consumeSelfLeft(ctx, guildId, selfId, maxAgeSec = 120) {
|
|
431
|
+
selfId = normalizeId(selfId);
|
|
432
|
+
const normalizedGuildId = normalizeId(guildId);
|
|
433
|
+
const scopedQuery = { platform: BLACKLIST_PLATFORM, guildId: scopedId(guildId, selfId) };
|
|
434
|
+
const legacyQuery = { platform: BLACKLIST_PLATFORM, guildId: normalizedGuildId };
|
|
435
|
+
const [row] = await ctx.database.get("self_left_guild", scopedQuery);
|
|
436
|
+
const [legacyRow] = row ? [] : await ctx.database.get("self_left_guild", legacyQuery);
|
|
437
|
+
const [prefixedLegacyRow] = row || legacyRow ? [] : await getRowsByNormalizedId(ctx, "self_left_guild", "guildId", BLACKLIST_PLATFORM, guildId);
|
|
438
|
+
const target = row ?? legacyRow ?? prefixedLegacyRow;
|
|
439
|
+
const query = row ? scopedQuery : { platform: BLACKLIST_PLATFORM, guildId: target?.guildId ?? normalizedGuildId };
|
|
440
|
+
if (!target) return false;
|
|
441
|
+
await ctx.database.remove("self_left_guild", query);
|
|
442
|
+
return Math.floor(Date.now() / 1e3) - target.timestamp <= maxAgeSec;
|
|
443
|
+
}
|
|
444
|
+
__name(consumeSelfLeft, "consumeSelfLeft");
|
|
445
|
+
async function clearSelfLeft(ctx, guildId, selfId) {
|
|
446
|
+
const normalizedGuildId = normalizeId(guildId);
|
|
447
|
+
if (selfId) {
|
|
448
|
+
const normalizedSelfId = normalizeId(selfId);
|
|
449
|
+
await ctx.database.remove("self_left_guild", { platform: BLACKLIST_PLATFORM, guildId: scopedId(guildId, normalizedSelfId) });
|
|
450
|
+
await ctx.database.remove("self_left_guild", { platform: BLACKLIST_PLATFORM, guildId: normalizedGuildId });
|
|
451
|
+
const rows2 = await getRowsByNormalizedId(ctx, "self_left_guild", "guildId", BLACKLIST_PLATFORM, guildId);
|
|
452
|
+
for (const row of rows2) {
|
|
453
|
+
if (!isScopedId(row.guildId)) {
|
|
454
|
+
await ctx.database.remove("self_left_guild", { platform: BLACKLIST_PLATFORM, guildId: row.guildId });
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const rows = await ctx.database.get("self_left_guild", { platform: BLACKLIST_PLATFORM });
|
|
460
|
+
for (const row of rows) {
|
|
461
|
+
if (unscopedId(row.guildId) === normalizedGuildId) {
|
|
462
|
+
await ctx.database.remove("self_left_guild", { platform: BLACKLIST_PLATFORM, guildId: row.guildId });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
__name(clearSelfLeft, "clearSelfLeft");
|
|
467
|
+
async function clearExpiredSelfLeft(ctx, maxAgeSec = 300) {
|
|
468
|
+
const cutoff = Math.floor(Date.now() / 1e3) - maxAgeSec;
|
|
469
|
+
await ctx.database.remove("self_left_guild", { platform: BLACKLIST_PLATFORM, timestamp: { $lt: cutoff } });
|
|
470
|
+
}
|
|
471
|
+
__name(clearExpiredSelfLeft, "clearExpiredSelfLeft");
|
|
472
|
+
async function getCommandFrequencyRecord(ctx, platform, guildId) {
|
|
473
|
+
const records = await ctx.database.get("command_frequency_record", { platform, guildId });
|
|
474
|
+
return records.length > 0 ? records[0] : null;
|
|
475
|
+
}
|
|
476
|
+
__name(getCommandFrequencyRecord, "getCommandFrequencyRecord");
|
|
477
|
+
async function updateCommandFrequencyRecord(ctx, platform, guildId, data) {
|
|
478
|
+
await ctx.database.upsert("command_frequency_record", [{
|
|
479
|
+
platform,
|
|
480
|
+
guildId,
|
|
481
|
+
...data
|
|
482
|
+
}]);
|
|
483
|
+
}
|
|
484
|
+
__name(updateCommandFrequencyRecord, "updateCommandFrequencyRecord");
|
|
485
|
+
var GROUP_BOT_STATUS_CACHE_TTL = 5 * 60 * 1e3;
|
|
486
|
+
var GROUP_BOT_STATUS_CACHE_MAX = 1e3;
|
|
487
|
+
var groupBotStatusCache = new SimpleLRUCache(GROUP_BOT_STATUS_CACHE_TTL, GROUP_BOT_STATUS_CACHE_MAX);
|
|
488
|
+
var groupBotStatusCacheKey = /* @__PURE__ */ __name((platform, guildId) => `${platform}:${guildId}`, "groupBotStatusCacheKey");
|
|
489
|
+
function getCachedGroupBotStatus(key) {
|
|
490
|
+
return groupBotStatusCache.get(key);
|
|
491
|
+
}
|
|
492
|
+
__name(getCachedGroupBotStatus, "getCachedGroupBotStatus");
|
|
493
|
+
function setCachedGroupBotStatus(key, value) {
|
|
494
|
+
groupBotStatusCache.set(key, value);
|
|
495
|
+
}
|
|
496
|
+
__name(setCachedGroupBotStatus, "setCachedGroupBotStatus");
|
|
497
|
+
async function getGroupBotStatus(ctx, platform, guildId) {
|
|
498
|
+
guildId = normalizeId(guildId);
|
|
499
|
+
const key = groupBotStatusCacheKey(platform, guildId);
|
|
500
|
+
const cached = getCachedGroupBotStatus(key);
|
|
501
|
+
if (cached !== void 0) return cached;
|
|
502
|
+
const records = await ctx.database.get("group_bot_status", { platform, guildId });
|
|
503
|
+
const legacyRecords = records.length > 0 ? [] : await getRowsByNormalizedId(ctx, "group_bot_status", "guildId", platform, guildId);
|
|
504
|
+
const status = records.length > 0 ? records[0] : legacyRecords.length > 0 ? {
|
|
505
|
+
...legacyRecords[0],
|
|
506
|
+
guildId
|
|
507
|
+
} : null;
|
|
508
|
+
setCachedGroupBotStatus(key, status);
|
|
509
|
+
return status;
|
|
510
|
+
}
|
|
511
|
+
__name(getGroupBotStatus, "getGroupBotStatus");
|
|
512
|
+
async function setGroupBotStatus(ctx, platform, guildId, botEnabled) {
|
|
513
|
+
guildId = normalizeId(guildId);
|
|
514
|
+
await ctx.database.upsert("group_bot_status", [{ platform, guildId, botEnabled }]);
|
|
515
|
+
setCachedGroupBotStatus(groupBotStatusCacheKey(platform, guildId), { platform, guildId, botEnabled });
|
|
516
|
+
}
|
|
517
|
+
__name(setGroupBotStatus, "setGroupBotStatus");
|
|
518
|
+
async function isInSmallGroupWhitelist(ctx, guildId) {
|
|
519
|
+
guildId = normalizeId(guildId);
|
|
520
|
+
const records = await ctx.database.get("small_group_whitelist", { platform: BLACKLIST_PLATFORM, guildId });
|
|
521
|
+
return records.length > 0;
|
|
522
|
+
}
|
|
523
|
+
__name(isInSmallGroupWhitelist, "isInSmallGroupWhitelist");
|
|
524
|
+
async function addToSmallGroupWhitelist(ctx, guildId) {
|
|
525
|
+
guildId = normalizeId(guildId);
|
|
526
|
+
await ctx.database.upsert("small_group_whitelist", [{ platform: BLACKLIST_PLATFORM, guildId }]);
|
|
527
|
+
}
|
|
528
|
+
__name(addToSmallGroupWhitelist, "addToSmallGroupWhitelist");
|
|
529
|
+
async function removeFromSmallGroupWhitelist(ctx, guildId) {
|
|
530
|
+
guildId = normalizeId(guildId);
|
|
531
|
+
await ctx.database.remove("small_group_whitelist", { platform: BLACKLIST_PLATFORM, guildId });
|
|
532
|
+
}
|
|
533
|
+
__name(removeFromSmallGroupWhitelist, "removeFromSmallGroupWhitelist");
|
|
534
|
+
async function getAllSmallGroupWhitelist(ctx) {
|
|
535
|
+
return await ctx.database.get("small_group_whitelist", { platform: BLACKLIST_PLATFORM });
|
|
536
|
+
}
|
|
537
|
+
__name(getAllSmallGroupWhitelist, "getAllSmallGroupWhitelist");
|
|
538
|
+
async function markApprovedGuild(ctx, guildId, selfId) {
|
|
539
|
+
selfId = normalizeId(selfId);
|
|
540
|
+
guildId = scopedId(guildId, selfId);
|
|
541
|
+
await ctx.database.upsert("approved_guild", [{
|
|
542
|
+
platform: BLACKLIST_PLATFORM,
|
|
543
|
+
guildId,
|
|
544
|
+
selfId,
|
|
545
|
+
timestamp: Math.floor(Date.now() / 1e3)
|
|
546
|
+
}]);
|
|
547
|
+
}
|
|
548
|
+
__name(markApprovedGuild, "markApprovedGuild");
|
|
549
|
+
async function isApprovedGuild(ctx, guildId, selfId) {
|
|
550
|
+
selfId = normalizeId(selfId);
|
|
551
|
+
const records = await ctx.database.get("approved_guild", { platform: BLACKLIST_PLATFORM, guildId: scopedId(guildId, selfId) });
|
|
552
|
+
if (records.length > 0) return true;
|
|
553
|
+
const legacyRecords = await ctx.database.get("approved_guild", { platform: BLACKLIST_PLATFORM, guildId: normalizeId(guildId) });
|
|
554
|
+
if (legacyRecords.length > 0) return true;
|
|
555
|
+
const prefixedLegacyRecords = await getRowsByNormalizedId(ctx, "approved_guild", "guildId", BLACKLIST_PLATFORM, guildId);
|
|
556
|
+
return prefixedLegacyRecords.some((row) => !isScopedId(row.guildId));
|
|
557
|
+
}
|
|
558
|
+
__name(isApprovedGuild, "isApprovedGuild");
|
|
559
|
+
async function clearApprovedGuild(ctx, guildId, selfId) {
|
|
560
|
+
const normalizedGuildId = normalizeId(guildId);
|
|
561
|
+
if (selfId) {
|
|
562
|
+
await ctx.database.remove("approved_guild", { platform: BLACKLIST_PLATFORM, guildId: scopedId(guildId, selfId) });
|
|
563
|
+
await ctx.database.remove("approved_guild", { platform: BLACKLIST_PLATFORM, guildId: normalizedGuildId });
|
|
564
|
+
const rows2 = await getRowsByNormalizedId(ctx, "approved_guild", "guildId", BLACKLIST_PLATFORM, guildId);
|
|
565
|
+
for (const row of rows2) {
|
|
566
|
+
if (!isScopedId(row.guildId)) {
|
|
567
|
+
await ctx.database.remove("approved_guild", { platform: BLACKLIST_PLATFORM, guildId: row.guildId });
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const rows = await ctx.database.get("approved_guild", { platform: BLACKLIST_PLATFORM });
|
|
573
|
+
for (const row of rows) {
|
|
574
|
+
if (unscopedId(row.guildId) === normalizedGuildId) {
|
|
575
|
+
await ctx.database.remove("approved_guild", { platform: BLACKLIST_PLATFORM, guildId: row.guildId });
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
__name(clearApprovedGuild, "clearApprovedGuild");
|
|
580
|
+
async function getPendingInvite(ctx, platform, groupId, selfId) {
|
|
581
|
+
selfId = normalizeId(selfId);
|
|
582
|
+
const records = await ctx.database.get("pending_invite", { platform, groupId: scopedId(groupId, selfId) });
|
|
583
|
+
if (records.length > 0) return decodePendingInvite(records[0]);
|
|
584
|
+
const legacyRecords = await ctx.database.get("pending_invite", { platform, groupId: normalizeId(groupId) });
|
|
585
|
+
if (legacyRecords.length > 0) return decodePendingInvite(legacyRecords[0]);
|
|
586
|
+
const prefixedLegacyRecords = await getRowsByNormalizedId(ctx, "pending_invite", "groupId", platform, groupId);
|
|
587
|
+
const prefixedLegacyRecord = prefixedLegacyRecords.find((row) => !isScopedId(row.groupId));
|
|
588
|
+
return prefixedLegacyRecord ? decodePendingInvite(prefixedLegacyRecord) : null;
|
|
589
|
+
}
|
|
590
|
+
__name(getPendingInvite, "getPendingInvite");
|
|
591
|
+
async function addPendingInvite(ctx, platform, selfId, inviteUser) {
|
|
592
|
+
selfId = normalizeId(selfId);
|
|
593
|
+
await ctx.database.upsert("pending_invite", [{
|
|
594
|
+
platform,
|
|
595
|
+
selfId,
|
|
596
|
+
...inviteUser,
|
|
597
|
+
groupId: scopedId(inviteUser.groupId, selfId),
|
|
598
|
+
userId: normalizeId(inviteUser.userId)
|
|
599
|
+
}]);
|
|
600
|
+
}
|
|
601
|
+
__name(addPendingInvite, "addPendingInvite");
|
|
602
|
+
async function removePendingInvite(ctx, platform, groupId, selfId) {
|
|
603
|
+
selfId = normalizeId(selfId);
|
|
604
|
+
await ctx.database.remove("pending_invite", { platform, groupId: scopedId(groupId, selfId) });
|
|
605
|
+
await ctx.database.remove("pending_invite", { platform, groupId: normalizeId(groupId) });
|
|
606
|
+
const rows = await getRowsByNormalizedId(ctx, "pending_invite", "groupId", platform, groupId);
|
|
607
|
+
for (const row of rows) {
|
|
608
|
+
if (!isScopedId(row.groupId)) {
|
|
609
|
+
await ctx.database.remove("pending_invite", { platform, groupId: row.groupId });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
__name(removePendingInvite, "removePendingInvite");
|
|
614
|
+
async function getAllPendingInvites(ctx, platform, selfId) {
|
|
615
|
+
selfId = normalizeId(selfId);
|
|
616
|
+
const rows = await ctx.database.get("pending_invite", { platform });
|
|
617
|
+
return rows.filter((row) => row.selfId ? normalizeId(row.selfId) === selfId : !isScopedId(row.groupId)).map(decodePendingInvite);
|
|
618
|
+
}
|
|
619
|
+
__name(getAllPendingInvites, "getAllPendingInvites");
|
|
620
|
+
async function clearExpiredPendingInvites(ctx, platform, expireTimeMs) {
|
|
621
|
+
const cutoff = Math.floor((Date.now() - expireTimeMs) / 1e3);
|
|
622
|
+
await ctx.database.remove("pending_invite", { platform, time: { $lt: cutoff } });
|
|
623
|
+
}
|
|
624
|
+
__name(clearExpiredPendingInvites, "clearExpiredPendingInvites");
|
|
625
|
+
async function getPendingFriendRequest(ctx, platform, selfId, userId) {
|
|
626
|
+
selfId = normalizeId(selfId);
|
|
627
|
+
const records = await ctx.database.get("pending_friend_request", { platform, userId: scopedId(userId, selfId) });
|
|
628
|
+
if (records.length > 0) return decodePendingFriendRequest(records[0]);
|
|
629
|
+
const legacyRecords = await ctx.database.get("pending_friend_request", { platform, userId: normalizeId(userId) });
|
|
630
|
+
if (legacyRecords.length > 0) return decodePendingFriendRequest(legacyRecords[0]);
|
|
631
|
+
const prefixedLegacyRecords = await getRowsByNormalizedId(ctx, "pending_friend_request", "userId", platform, userId);
|
|
632
|
+
const prefixedLegacyRecord = prefixedLegacyRecords.find((row) => !isScopedId(row.userId));
|
|
633
|
+
return prefixedLegacyRecord ? decodePendingFriendRequest(prefixedLegacyRecord) : null;
|
|
634
|
+
}
|
|
635
|
+
__name(getPendingFriendRequest, "getPendingFriendRequest");
|
|
636
|
+
async function addPendingFriendRequest(ctx, platform, selfId, data) {
|
|
637
|
+
selfId = normalizeId(selfId);
|
|
638
|
+
await ctx.database.upsert("pending_friend_request", [{
|
|
639
|
+
platform,
|
|
640
|
+
selfId,
|
|
641
|
+
...data,
|
|
642
|
+
userId: scopedId(data.userId, selfId)
|
|
643
|
+
}]);
|
|
644
|
+
}
|
|
645
|
+
__name(addPendingFriendRequest, "addPendingFriendRequest");
|
|
646
|
+
async function removePendingFriendRequest(ctx, platform, selfId, userId) {
|
|
647
|
+
selfId = normalizeId(selfId);
|
|
648
|
+
await ctx.database.remove("pending_friend_request", { platform, userId: scopedId(userId, selfId) });
|
|
649
|
+
await ctx.database.remove("pending_friend_request", { platform, userId: normalizeId(userId) });
|
|
650
|
+
const rows = await getRowsByNormalizedId(ctx, "pending_friend_request", "userId", platform, userId);
|
|
651
|
+
for (const row of rows) {
|
|
652
|
+
if (!isScopedId(row.userId)) {
|
|
653
|
+
await ctx.database.remove("pending_friend_request", { platform, userId: row.userId });
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
__name(removePendingFriendRequest, "removePendingFriendRequest");
|
|
658
|
+
async function getAllPendingFriendRequests(ctx, platform, selfId) {
|
|
659
|
+
selfId = normalizeId(selfId);
|
|
660
|
+
const rows = await ctx.database.get("pending_friend_request", { platform });
|
|
661
|
+
return rows.filter((row) => row.selfId ? normalizeId(row.selfId) === selfId : !isScopedId(row.userId)).map(decodePendingFriendRequest);
|
|
662
|
+
}
|
|
663
|
+
__name(getAllPendingFriendRequests, "getAllPendingFriendRequests");
|
|
664
|
+
async function clearExpiredPendingFriendRequests(ctx, platform, selfId, expireTimeMs) {
|
|
665
|
+
const cutoff = Math.floor((Date.now() - expireTimeMs) / 1e3);
|
|
666
|
+
await ctx.database.remove("pending_friend_request", { platform, time: { $lt: cutoff } });
|
|
667
|
+
}
|
|
668
|
+
__name(clearExpiredPendingFriendRequests, "clearExpiredPendingFriendRequests");
|
|
669
|
+
|
|
670
|
+
// src/modules/basic.ts
|
|
671
|
+
var basic_exports = {};
|
|
672
|
+
__export(basic_exports, {
|
|
673
|
+
apply: () => apply2,
|
|
674
|
+
name: () => name2
|
|
675
|
+
});
|
|
676
|
+
var name2 = "group-control-basic";
|
|
677
|
+
function getBotSelfId(bot) {
|
|
678
|
+
const raw = String(bot?.selfId ?? bot?.userId ?? "");
|
|
679
|
+
return parseGuildId(raw) ?? raw;
|
|
680
|
+
}
|
|
681
|
+
__name(getBotSelfId, "getBotSelfId");
|
|
682
|
+
function makeGuildKey(platform, selfId, guildId) {
|
|
683
|
+
return `${platform}:${selfId}:${guildId}`;
|
|
684
|
+
}
|
|
685
|
+
__name(makeGuildKey, "makeGuildKey");
|
|
686
|
+
async function leaveGroup(bot, guildId) {
|
|
687
|
+
const groupId = toOneBotNumber(guildId);
|
|
688
|
+
if (groupId == null) throw new Error(`无效群号: ${guildId}`);
|
|
689
|
+
await bot.internal.setGroupLeave(groupId);
|
|
690
|
+
}
|
|
691
|
+
__name(leaveGroup, "leaveGroup");
|
|
692
|
+
async function getGroupName(bot, guildId) {
|
|
693
|
+
try {
|
|
694
|
+
const groupId = toOneBotNumber(guildId);
|
|
695
|
+
const info = groupId == null ? null : await bot.internal?.getGroupInfo?.(groupId);
|
|
696
|
+
const data = info?.data ?? info;
|
|
697
|
+
if (data?.group_name) return data.group_name;
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
const info = await bot.getGuild(guildId);
|
|
702
|
+
if (info?.name) return info.name;
|
|
703
|
+
if (info?.group_name) return info.group_name;
|
|
704
|
+
} catch {
|
|
705
|
+
}
|
|
706
|
+
return "未知";
|
|
707
|
+
}
|
|
708
|
+
__name(getGroupName, "getGroupName");
|
|
709
|
+
async function getGroupMemberList(bot, guildId) {
|
|
710
|
+
try {
|
|
711
|
+
const groupId = toOneBotNumber(guildId);
|
|
712
|
+
const raw = groupId == null ? null : await bot.internal?.getGroupMemberList?.(groupId);
|
|
713
|
+
const list = Array.isArray(raw) ? raw : Array.isArray(raw?.data) ? raw.data : [];
|
|
714
|
+
if (list.length > 0) return list;
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
try {
|
|
718
|
+
const raw = await bot.getGuildMemberList?.(guildId);
|
|
719
|
+
return Array.isArray(raw) ? raw : Array.isArray(raw?.data) ? raw.data : [];
|
|
720
|
+
} catch {
|
|
721
|
+
}
|
|
722
|
+
return [];
|
|
723
|
+
}
|
|
724
|
+
__name(getGroupMemberList, "getGroupMemberList");
|
|
725
|
+
function apply2(ctx, config) {
|
|
726
|
+
const quittingGuilds = /* @__PURE__ */ new Map();
|
|
727
|
+
const processedKicks = /* @__PURE__ */ new Map();
|
|
728
|
+
const processedAdds = /* @__PURE__ */ new Map();
|
|
729
|
+
const realtimeLastCheck = /* @__PURE__ */ new Map();
|
|
730
|
+
const QUITTING_EXPIRE_MS = 60 * 1e3;
|
|
731
|
+
const KICK_DEDUP_MS = 60 * 1e3;
|
|
732
|
+
const ADD_DEDUP_MS = 10 * 1e3;
|
|
733
|
+
const REALTIME_DEBOUNCE_MS = 1500;
|
|
734
|
+
const WELCOME_RETRY_DELAYS = [1e3, 3e3, 7e3];
|
|
735
|
+
function isRecent(map, key, ttl) {
|
|
736
|
+
const time = map.get(key);
|
|
737
|
+
if (!time) return false;
|
|
738
|
+
if (Date.now() - time <= ttl) return true;
|
|
739
|
+
map.delete(key);
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
__name(isRecent, "isRecent");
|
|
743
|
+
ctx.setInterval(() => {
|
|
744
|
+
const now = Date.now();
|
|
745
|
+
for (const [key, time] of quittingGuilds) {
|
|
746
|
+
if (now - time > QUITTING_EXPIRE_MS) quittingGuilds.delete(key);
|
|
747
|
+
}
|
|
748
|
+
for (const [key, time] of processedKicks) {
|
|
749
|
+
if (now - time > KICK_DEDUP_MS) processedKicks.delete(key);
|
|
750
|
+
}
|
|
751
|
+
for (const [key, time] of processedAdds) {
|
|
752
|
+
if (now - time > ADD_DEDUP_MS) processedAdds.delete(key);
|
|
753
|
+
}
|
|
754
|
+
for (const [key, time] of realtimeLastCheck) {
|
|
755
|
+
if (now - time > 5 * 60 * 1e3) realtimeLastCheck.delete(key);
|
|
756
|
+
}
|
|
757
|
+
}, 30 * 1e3);
|
|
758
|
+
ctx.setInterval(async () => {
|
|
759
|
+
try {
|
|
760
|
+
await clearExpiredSelfLeft(ctx);
|
|
761
|
+
} catch {
|
|
762
|
+
}
|
|
763
|
+
}, 5 * 60 * 1e3);
|
|
764
|
+
async function evaluateSmallGroup(bot, guildId) {
|
|
765
|
+
const threshold = config.basic.smallGroupThreshold;
|
|
766
|
+
const exclude = config.basic.smallGroupExcludeOfficialBots;
|
|
767
|
+
const maxBots = 20;
|
|
768
|
+
let total = 0;
|
|
769
|
+
let groupName = "未知";
|
|
770
|
+
try {
|
|
771
|
+
const groupId = toOneBotNumber(guildId);
|
|
772
|
+
const info = groupId == null ? null : await bot.internal?.getGroupInfo?.(groupId);
|
|
773
|
+
const data = info?.data ?? info;
|
|
774
|
+
total = Number(data?.member_count) || 0;
|
|
775
|
+
if (data?.group_name) groupName = data.group_name;
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
778
|
+
if (total === 0) {
|
|
779
|
+
try {
|
|
780
|
+
const guildInfo = await bot.getGuild(guildId);
|
|
781
|
+
total = Number(guildInfo?.member_count ?? guildInfo?.memberCount) || 0;
|
|
782
|
+
if (guildInfo?.name) groupName = guildInfo.name;
|
|
783
|
+
} catch {
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
if (total > 0 && total <= threshold) {
|
|
787
|
+
return { decision: "quit", count: total, groupName };
|
|
788
|
+
}
|
|
789
|
+
if (!exclude) {
|
|
790
|
+
if (total > 0) return { decision: total <= threshold ? "quit" : "keep", count: total, groupName };
|
|
791
|
+
} else if (total > threshold + maxBots) {
|
|
792
|
+
return { decision: "keep", count: total, groupName };
|
|
793
|
+
}
|
|
794
|
+
const list = await getGroupMemberList(bot, guildId);
|
|
795
|
+
if (list.length === 0) {
|
|
796
|
+
if (total > 0) return { decision: total <= threshold ? "quit" : "keep", count: total, groupName };
|
|
797
|
+
return { decision: "unknown", count: 0, groupName };
|
|
798
|
+
}
|
|
799
|
+
const N = list.length;
|
|
800
|
+
if (!exclude) return { decision: N <= threshold ? "quit" : "keep", count: N, groupName };
|
|
801
|
+
if (N <= threshold) return { decision: "quit", count: N, groupName };
|
|
802
|
+
const selfId = getBotSelfId(bot);
|
|
803
|
+
const botsNeeded = N - threshold;
|
|
804
|
+
let bots = 0;
|
|
805
|
+
for (const m of list) {
|
|
806
|
+
const uid = parseGuildId(String(m?.user_id ?? m?.userId ?? m?.user?.id ?? "")) ?? "";
|
|
807
|
+
if (m?.is_robot === true || m?.user?.isBot === true || selfId && uid === selfId) {
|
|
808
|
+
bots++;
|
|
809
|
+
if (bots >= botsNeeded) {
|
|
810
|
+
return { decision: "quit", count: N - bots, groupName };
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const real = N - bots;
|
|
815
|
+
return { decision: real <= threshold ? "quit" : "keep", count: real, groupName };
|
|
816
|
+
}
|
|
817
|
+
__name(evaluateSmallGroup, "evaluateSmallGroup");
|
|
818
|
+
async function performSmallGroupQuit(bot, platform, guildId, groupName, memberCount) {
|
|
819
|
+
const selfId = getBotSelfId(bot);
|
|
820
|
+
const dedupKey = makeGuildKey(platform, selfId, guildId);
|
|
821
|
+
const threshold = config.basic.smallGroupThreshold;
|
|
822
|
+
const quitMsg = config.basic.smallGroupQuitMessage.replaceAll("{memberCount}", memberCount.toString()).replaceAll("{threshold}", threshold.toString()).replaceAll("{groupName}", groupName).replaceAll("{groupId}", guildId);
|
|
823
|
+
try {
|
|
824
|
+
await bot.sendMessage(guildId, quitMsg, platform);
|
|
825
|
+
} catch (e) {
|
|
826
|
+
}
|
|
827
|
+
if (config.basic.smallGroupNotifyAdmin) {
|
|
828
|
+
const adminMsg = `小群自动退群
|
|
829
|
+
群名称:${groupName}
|
|
830
|
+
群号:${guildId}
|
|
831
|
+
群成员数:${memberCount}人(阈值:${threshold}人)
|
|
832
|
+
机器人已自动退出该群。`;
|
|
833
|
+
await notifyAdmins(bot, config, adminMsg);
|
|
834
|
+
}
|
|
835
|
+
quittingGuilds.set(dedupKey, Date.now());
|
|
836
|
+
await markSelfLeft(ctx, guildId, selfId);
|
|
837
|
+
try {
|
|
838
|
+
await leaveGroup(bot, guildId);
|
|
839
|
+
} catch (e) {
|
|
840
|
+
console.error(`小群自动退群失败 (群号: ${guildId}):`, e);
|
|
841
|
+
quittingGuilds.delete(dedupKey);
|
|
842
|
+
await clearSelfLeft(ctx, guildId, selfId);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
__name(performSmallGroupQuit, "performSmallGroupQuit");
|
|
846
|
+
function sendWelcomeMessage(bot, platform, guildId, message, shouldStop) {
|
|
847
|
+
const logger = ctx.logger("group-control-basic");
|
|
848
|
+
const attempt = /* @__PURE__ */ __name(async (attemptIndex) => {
|
|
849
|
+
if (shouldStop()) return;
|
|
850
|
+
try {
|
|
851
|
+
await bot.sendMessage(guildId, message, platform);
|
|
852
|
+
if (attemptIndex > 0) {
|
|
853
|
+
logger.info(`[guild-added] 欢迎消息重试成功 guildId=${guildId}, attempt=${attemptIndex + 1}`);
|
|
854
|
+
}
|
|
855
|
+
} catch (error) {
|
|
856
|
+
if (shouldStop()) return;
|
|
857
|
+
const retryDelay = WELCOME_RETRY_DELAYS[attemptIndex];
|
|
858
|
+
if (retryDelay == null) {
|
|
859
|
+
logger.warn(`[guild-added] 欢迎消息发送失败 guildId=${guildId}, attempts=${attemptIndex + 1}`, error);
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
logger.warn(`[guild-added] 欢迎消息发送失败,${retryDelay}ms 后重试 guildId=${guildId}, attempt=${attemptIndex + 1}`, error);
|
|
863
|
+
ctx.setTimeout(() => {
|
|
864
|
+
void attempt(attemptIndex + 1);
|
|
865
|
+
}, retryDelay);
|
|
866
|
+
}
|
|
867
|
+
}, "attempt");
|
|
868
|
+
void attempt(0);
|
|
869
|
+
}
|
|
870
|
+
__name(sendWelcomeMessage, "sendWelcomeMessage");
|
|
871
|
+
ctx.on("guild-added", async (session) => {
|
|
872
|
+
const { platform } = session;
|
|
873
|
+
const guildId = parseGuildId(session.guildId);
|
|
874
|
+
if (!guildId) return;
|
|
875
|
+
const selfId = getBotSelfId(session.bot);
|
|
876
|
+
ctx.logger("group-control-basic").info(`[guild-added] 触发!guildId=${guildId}, platform=${platform}`);
|
|
877
|
+
const addKey = makeGuildKey(platform, selfId, guildId);
|
|
878
|
+
if (isRecent(processedAdds, addKey, ADD_DEDUP_MS)) {
|
|
879
|
+
ctx.logger("group-control-basic").info(`[guild-added] 忽略重复事件 guildId=${guildId}`);
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
processedAdds.set(addKey, Date.now());
|
|
883
|
+
if (config.basic.enableBlacklist) {
|
|
884
|
+
const [blacklisted] = await getBlacklistedGuild(ctx, guildId);
|
|
885
|
+
if (blacklisted) {
|
|
886
|
+
try {
|
|
887
|
+
await session.bot.sendMessage(guildId, config.basic.blacklistMessage, platform);
|
|
888
|
+
} catch (e) {
|
|
889
|
+
}
|
|
890
|
+
quittingGuilds.set(addKey, Date.now());
|
|
891
|
+
await markSelfLeft(ctx, guildId, selfId);
|
|
892
|
+
try {
|
|
893
|
+
await leaveGroup(session.bot, guildId);
|
|
894
|
+
} catch (e) {
|
|
895
|
+
}
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
if (config.basic.smallGroupAutoQuit) {
|
|
900
|
+
const inWhitelist = await isInSmallGroupWhitelist(ctx, guildId);
|
|
901
|
+
const wasApproved = await isApprovedGuild(ctx, guildId, selfId);
|
|
902
|
+
const pendingInvite = await getPendingInvite(ctx, platform, guildId, selfId);
|
|
903
|
+
const hadPendingInvite = !!pendingInvite;
|
|
904
|
+
if (hadPendingInvite) await removePendingInvite(ctx, platform, guildId, selfId);
|
|
905
|
+
if (inWhitelist || wasApproved || hadPendingInvite) {
|
|
906
|
+
} else {
|
|
907
|
+
const delay = config.basic.smallGroupCheckDelay || 3e3;
|
|
908
|
+
ctx.setTimeout(async () => {
|
|
909
|
+
try {
|
|
910
|
+
const res = await evaluateSmallGroup(session.bot, guildId);
|
|
911
|
+
let groupName = res.groupName;
|
|
912
|
+
if (groupName === "未知") groupName = await getGroupName(session.bot, guildId);
|
|
913
|
+
if (res.decision === "quit") {
|
|
914
|
+
await performSmallGroupQuit(session.bot, platform, guildId, groupName, res.count);
|
|
915
|
+
} else if (res.decision === "keep") {
|
|
916
|
+
if (config.basic.smallGroupQualifiedNotifyAdmin) {
|
|
917
|
+
const qualifiedMsg = config.basic.smallGroupQualifiedMessage.replaceAll("{groupName}", groupName).replaceAll("{groupId}", guildId).replaceAll("{memberCount}", res.count.toString()).replaceAll("{threshold}", config.basic.smallGroupThreshold.toString());
|
|
918
|
+
await notifyAdmins(session.bot, config, qualifiedMsg);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
} catch (error) {
|
|
922
|
+
console.error(`小群自动退群检测失败 (群号: ${guildId}):`, error);
|
|
923
|
+
}
|
|
924
|
+
}, delay);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (config.basic.welcomeMessage) {
|
|
928
|
+
sendWelcomeMessage(
|
|
929
|
+
session.bot,
|
|
930
|
+
platform,
|
|
931
|
+
guildId,
|
|
932
|
+
config.basic.welcomeMessage,
|
|
933
|
+
() => isRecent(quittingGuilds, addKey, QUITTING_EXPIRE_MS)
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
if (config.basic.smallGroupAutoQuit && config.basic.smallGroupRealtimeMonitor) {
|
|
938
|
+
ctx.on("guild-member-removed", async (session) => {
|
|
939
|
+
const { platform } = session;
|
|
940
|
+
const guildId = parseGuildId(session.guildId);
|
|
941
|
+
if (!guildId) return;
|
|
942
|
+
const selfId = getBotSelfId(session.bot);
|
|
943
|
+
const dedupKey = makeGuildKey(platform, selfId, guildId);
|
|
944
|
+
if (parseGuildId(session.userId) === selfId) return;
|
|
945
|
+
if (quittingGuilds.has(dedupKey)) return;
|
|
946
|
+
const cooldown = (config.basic.smallGroupRecheckCooldown || 60) * 1e3;
|
|
947
|
+
const now = Date.now();
|
|
948
|
+
if (now - (realtimeLastCheck.get(dedupKey) || 0) < cooldown) return;
|
|
949
|
+
realtimeLastCheck.set(dedupKey, now);
|
|
950
|
+
if (await isInSmallGroupWhitelist(ctx, guildId)) return;
|
|
951
|
+
if (await isApprovedGuild(ctx, guildId, selfId)) return;
|
|
952
|
+
ctx.setTimeout(async () => {
|
|
953
|
+
try {
|
|
954
|
+
if (quittingGuilds.has(dedupKey)) return;
|
|
955
|
+
const res = await evaluateSmallGroup(session.bot, guildId);
|
|
956
|
+
if (res.decision === "quit") {
|
|
957
|
+
let groupName = res.groupName;
|
|
958
|
+
if (groupName === "未知") groupName = await getGroupName(session.bot, guildId);
|
|
959
|
+
await performSmallGroupQuit(session.bot, platform, guildId, groupName, res.count);
|
|
960
|
+
}
|
|
961
|
+
} catch (error) {
|
|
962
|
+
console.error(`实时小群检测失败 (群号: ${guildId}):`, error);
|
|
963
|
+
}
|
|
964
|
+
}, REALTIME_DEBOUNCE_MS);
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
ctx.on("guild-removed", async (session) => {
|
|
968
|
+
const guildId = parseGuildId(session.guildId);
|
|
969
|
+
if (!guildId) return;
|
|
970
|
+
const selfId = getBotSelfId(session.bot);
|
|
971
|
+
const platform = BLACKLIST_PLATFORM;
|
|
972
|
+
const dedupKey = makeGuildKey(platform, selfId, guildId);
|
|
973
|
+
try {
|
|
974
|
+
await clearApprovedGuild(ctx, guildId, selfId);
|
|
975
|
+
} catch {
|
|
976
|
+
}
|
|
977
|
+
if (quittingGuilds.has(dedupKey)) {
|
|
978
|
+
try {
|
|
979
|
+
await clearSelfLeft(ctx, guildId, selfId);
|
|
980
|
+
} catch {
|
|
981
|
+
}
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
const isSelfLeft = await consumeSelfLeft(ctx, guildId, selfId);
|
|
985
|
+
if (isSelfLeft) return;
|
|
986
|
+
const raw = session.event?._data || session.original || session.onebot || {};
|
|
987
|
+
const subType = String(raw.sub_type ?? session.subtype ?? "");
|
|
988
|
+
const eventTs = typeof raw.time === "number" ? raw.time : Math.floor(Date.now() / 1e3);
|
|
989
|
+
const ageSec = Math.floor(Date.now() / 1e3) - eventTs;
|
|
990
|
+
if (subType === "leave") return;
|
|
991
|
+
if (!subType && ageSec > 60) return;
|
|
992
|
+
if (isRecent(processedKicks, dedupKey, KICK_DEDUP_MS)) return;
|
|
993
|
+
processedKicks.set(dedupKey, Date.now());
|
|
994
|
+
if (config.basic.enableBlacklist || config.basic.notifyAdminOnKick) {
|
|
995
|
+
const [existing] = await getBlacklistedGuild(ctx, guildId);
|
|
996
|
+
if (existing && existing.reason === "kicked") {
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
if (config.basic.enableBlacklist) {
|
|
1001
|
+
await blacklistKicked(ctx, guildId);
|
|
1002
|
+
}
|
|
1003
|
+
if (config.basic.notifyAdminOnKick) {
|
|
1004
|
+
const groupName = await getGroupName(session.bot, guildId);
|
|
1005
|
+
const kickMsg = config.basic.kickNotificationMessage.replaceAll("{groupId}", guildId).replaceAll("{groupName}", groupName);
|
|
1006
|
+
await notifyAdmins(session.bot, config, kickMsg);
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
if (config.basic.notifyAdminOnMute || config.basic.muteAutoQuit) {
|
|
1010
|
+
ctx.on("guild-member-mute", async (session) => {
|
|
1011
|
+
const selfId = getBotSelfId(session.bot);
|
|
1012
|
+
if (parseGuildId(session.userId) !== selfId) return;
|
|
1013
|
+
if (!session.duration) return;
|
|
1014
|
+
const { platform } = session;
|
|
1015
|
+
const guildId = parseGuildId(session.guildId);
|
|
1016
|
+
if (!guildId) return;
|
|
1017
|
+
const operatorId = session.operatorId || "未知";
|
|
1018
|
+
const duration = session.duration ?? 0;
|
|
1019
|
+
const groupName = await getGroupName(session.bot, guildId);
|
|
1020
|
+
if (config.basic.muteAutoQuit && duration >= config.basic.muteAutoQuitThreshold) {
|
|
1021
|
+
const quitMsg = config.basic.muteQuitNotificationMessage.replaceAll("{groupId}", guildId).replaceAll("{groupName}", groupName).replaceAll("{operatorId}", operatorId).replaceAll("{duration}", duration.toString());
|
|
1022
|
+
await notifyAdmins(session.bot, config, quitMsg);
|
|
1023
|
+
try {
|
|
1024
|
+
await createBlacklistedGuild(ctx, guildId, "muted");
|
|
1025
|
+
} catch (e) {
|
|
1026
|
+
}
|
|
1027
|
+
const dedupKey = makeGuildKey(platform, selfId, guildId);
|
|
1028
|
+
quittingGuilds.set(dedupKey, Date.now());
|
|
1029
|
+
await markSelfLeft(ctx, guildId, selfId);
|
|
1030
|
+
try {
|
|
1031
|
+
await leaveGroup(session.bot, guildId);
|
|
1032
|
+
} catch (e) {
|
|
1033
|
+
console.error(`被禁言自动退群失败 (群号: ${guildId}):`, e);
|
|
1034
|
+
quittingGuilds.delete(dedupKey);
|
|
1035
|
+
await clearSelfLeft(ctx, guildId, selfId);
|
|
1036
|
+
}
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
if (config.basic.notifyAdminOnMute) {
|
|
1040
|
+
const msg = config.basic.muteNotificationMessage.replaceAll("{groupId}", guildId).replaceAll("{groupName}", groupName).replaceAll("{operatorId}", operatorId).replaceAll("{duration}", duration.toString());
|
|
1041
|
+
await notifyAdmins(session.bot, config, msg);
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
if (config.basic.quitCommandEnabled) {
|
|
1046
|
+
const cmdOpts = getAdminCommandOptions(config);
|
|
1047
|
+
ctx.command("quit", "让机器人主动退出当前群聊", cmdOpts).action(async ({ session }) => {
|
|
1048
|
+
if (!session.guildId) return "quit 指令只能在群聊中使用。";
|
|
1049
|
+
if (config.permission.mode === "builtin") {
|
|
1050
|
+
const hasPerm = await hasGuildPermission(session, config);
|
|
1051
|
+
if (!hasPerm) return "权限不足,只有群管理员可以使用此指令。";
|
|
1052
|
+
}
|
|
1053
|
+
const { platform, userId } = session;
|
|
1054
|
+
const guildId = parseGuildId(session.guildId);
|
|
1055
|
+
if (!guildId) return "当前群号格式无效,无法退出。";
|
|
1056
|
+
const selfId = getBotSelfId(session.bot);
|
|
1057
|
+
const dedupKey = makeGuildKey(platform, selfId, guildId);
|
|
1058
|
+
const groupName = await getGroupName(session.bot, guildId);
|
|
1059
|
+
const adminMsg = `收到来自 ${userId} 的退群指令
|
|
1060
|
+
群名称:${groupName}
|
|
1061
|
+
群号:${guildId}`;
|
|
1062
|
+
await notifyAdmins(session.bot, config, adminMsg);
|
|
1063
|
+
quittingGuilds.set(dedupKey, Date.now());
|
|
1064
|
+
await markSelfLeft(ctx, guildId, selfId);
|
|
1065
|
+
try {
|
|
1066
|
+
await session.bot.sendMessage(session.guildId, config.basic.quitMessage.replaceAll("{userId}", userId), platform);
|
|
1067
|
+
} catch (e) {
|
|
1068
|
+
}
|
|
1069
|
+
try {
|
|
1070
|
+
await leaveGroup(session.bot, guildId);
|
|
1071
|
+
} catch (e) {
|
|
1072
|
+
quittingGuilds.delete(dedupKey);
|
|
1073
|
+
await clearSelfLeft(ctx, guildId, selfId);
|
|
1074
|
+
return `退出失败: ${e.message}`;
|
|
1075
|
+
}
|
|
1076
|
+
return "";
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
__name(apply2, "apply");
|
|
1081
|
+
|
|
1082
|
+
// src/modules/invite.ts
|
|
1083
|
+
var invite_exports = {};
|
|
1084
|
+
__export(invite_exports, {
|
|
1085
|
+
apply: () => apply3,
|
|
1086
|
+
name: () => name3
|
|
1087
|
+
});
|
|
1088
|
+
var name3 = "group-control-invite";
|
|
1089
|
+
function getBotSelfId2(bot) {
|
|
1090
|
+
return parseGuildId(String(bot?.selfId ?? bot?.userId ?? ""));
|
|
1091
|
+
}
|
|
1092
|
+
__name(getBotSelfId2, "getBotSelfId");
|
|
1093
|
+
async function handleInviteRequest(bot, flag, approve, comment = "") {
|
|
1094
|
+
if (typeof bot.handleGuildRequest === "function") {
|
|
1095
|
+
try {
|
|
1096
|
+
await bot.handleGuildRequest(flag, approve, comment);
|
|
1097
|
+
return;
|
|
1098
|
+
} catch {
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
await bot.internal.setGroupAddRequest(flag, "invite", approve, comment);
|
|
1102
|
+
}
|
|
1103
|
+
__name(handleInviteRequest, "handleInviteRequest");
|
|
1104
|
+
function apply3(ctx, config) {
|
|
1105
|
+
if (!config.invite.enabled) return;
|
|
1106
|
+
ctx.setInterval(async () => {
|
|
1107
|
+
const expireMs = config.invite.inviteExpireDays * 24 * 60 * 60 * 1e3;
|
|
1108
|
+
try {
|
|
1109
|
+
for (const bot of ctx.bots) {
|
|
1110
|
+
await clearExpiredPendingInvites(ctx, bot.platform, expireMs);
|
|
1111
|
+
}
|
|
1112
|
+
if (config.invite.showDetailedLog) {
|
|
1113
|
+
console.log("已执行过期邀请清理");
|
|
1114
|
+
}
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
console.error("清理过期邀请失败:", error);
|
|
1117
|
+
}
|
|
1118
|
+
}, 60 * 60 * 1e3);
|
|
1119
|
+
ctx.logger("group-control-invite").info("invite 模块已加载,正在监听 guild-request 事件");
|
|
1120
|
+
ctx.on("guild-request", async (session) => {
|
|
1121
|
+
ctx.logger("group-control-invite").info(`[guild-request] 触发!userId=${session.userId}, guildId=${session.guildId}, messageId=${session.messageId}, type=${session.type}, subtype=${session.subtype}`);
|
|
1122
|
+
ctx.logger("group-control-invite").info(`[guild-request] event 对象: ${JSON.stringify(session.event, null, 2)}`);
|
|
1123
|
+
const raw = session.original || session.raw || session.event?._data || {};
|
|
1124
|
+
const subType = String(raw.sub_type ?? session.subtype ?? "");
|
|
1125
|
+
if (subType && subType !== "invite") return;
|
|
1126
|
+
const flag = raw.flag || session.flag || session.messageId;
|
|
1127
|
+
const rawUserId = raw.user_id ? String(raw.user_id) : session.userId;
|
|
1128
|
+
const rawGroupId = raw.group_id ? String(raw.group_id) : session.guildId;
|
|
1129
|
+
const userId = parseGuildId(rawUserId);
|
|
1130
|
+
const groupId = parseGuildId(rawGroupId);
|
|
1131
|
+
const selfId = getBotSelfId2(session.bot);
|
|
1132
|
+
if (!userId || !groupId || !selfId) {
|
|
1133
|
+
ctx.logger("group-control-invite").warn(`无法解析邀请事件 ID: userId=${rawUserId}, groupId=${rawGroupId}, selfId=${session.bot?.selfId}`);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const { platform } = session;
|
|
1137
|
+
if (config.basic.enableBlacklist) {
|
|
1138
|
+
const bl = await getBlacklistedGuild(ctx, groupId);
|
|
1139
|
+
if (bl.length > 0) {
|
|
1140
|
+
try {
|
|
1141
|
+
await handleInviteRequest(session.bot, flag, false, "该群已被机器人拉黑");
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
ctx.logger("group-control-invite").warn("拒绝黑名单群邀请失败 (flag 可能已失效):", e);
|
|
1144
|
+
}
|
|
1145
|
+
const rejectNotify = `已自动拒绝黑名单群邀请
|
|
1146
|
+
群号:${groupId}
|
|
1147
|
+
邀请者 QQ:${userId}
|
|
1148
|
+
如需放行请先执行 gc.unban ${groupId} 再让对方重新邀请。`;
|
|
1149
|
+
try {
|
|
1150
|
+
await notifyAdmins(session.bot, config, rejectNotify);
|
|
1151
|
+
} catch (e) {
|
|
1152
|
+
}
|
|
1153
|
+
try {
|
|
1154
|
+
const rejectMsg = `您邀请加入的群 ${groupId} 已被机器人拉黑,邀请已被自动拒绝。如有疑问请联系机器人管理员。`;
|
|
1155
|
+
await session.bot.sendPrivateMessage(userId, rejectMsg);
|
|
1156
|
+
} catch (e) {
|
|
1157
|
+
}
|
|
1158
|
+
if (config.invite.showDetailedLog) {
|
|
1159
|
+
console.log(`已自动拒绝黑名单群邀请: 群号 ${groupId}, 邀请者 ${userId}`);
|
|
1160
|
+
}
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
if (!flag && config.invite.showDetailedLog) {
|
|
1165
|
+
console.warn("未能提取到邀请 flag,可能导致无法处理邀请。Raw event:", JSON.stringify(raw));
|
|
1166
|
+
}
|
|
1167
|
+
if (config.invite.showDetailedLog) {
|
|
1168
|
+
console.log(`收到群邀请事件 - 原始数据: UserID=${raw.user_id}, GroupID=${raw.group_id}, Flag=${flag}`);
|
|
1169
|
+
}
|
|
1170
|
+
let userName = userId;
|
|
1171
|
+
try {
|
|
1172
|
+
const userInfo = await session.bot.getUser(userId);
|
|
1173
|
+
userName = userInfo?.nickname || userInfo?.name || userId;
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
console.error("获取用户信息失败:", error);
|
|
1176
|
+
}
|
|
1177
|
+
let groupName = groupId;
|
|
1178
|
+
try {
|
|
1179
|
+
const guildInfo = await session.bot.getGuild(groupId);
|
|
1180
|
+
groupName = guildInfo?.name || guildInfo?.group_name || groupId;
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
console.error("获取群信息失败:", error);
|
|
1183
|
+
}
|
|
1184
|
+
if (config.invite.autoApprove) {
|
|
1185
|
+
try {
|
|
1186
|
+
await handleInviteRequest(session.bot, flag, true);
|
|
1187
|
+
await markApprovedGuild(ctx, groupId, selfId);
|
|
1188
|
+
if (config.invite.showDetailedLog) {
|
|
1189
|
+
console.log(`自动同意群聊邀请: 群号 ${groupId}, 邀请者 ${userId}`);
|
|
1190
|
+
}
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
console.error("自动同意群聊邀请失败:", error);
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
try {
|
|
1196
|
+
const approveMessage = config.invite.inviteApproveMessage.replaceAll("{groupName}", groupName).replaceAll("{groupId}", groupId).replaceAll("{userName}", userName).replaceAll("{userId}", userId);
|
|
1197
|
+
await session.bot.sendPrivateMessage(userId, approveMessage);
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
console.error(`发送自动通过提示给 ${userId} 失败:`, error);
|
|
1200
|
+
}
|
|
1201
|
+
if (config.invite.notifyAdminOnApprove) {
|
|
1202
|
+
const notifyMessage = config.invite.inviteApproveNotificationMessage.replaceAll("{groupName}", groupName).replaceAll("{groupId}", groupId).replaceAll("{userName}", userName).replaceAll("{userId}", userId);
|
|
1203
|
+
await notifyAdmins(session.bot, config, notifyMessage);
|
|
1204
|
+
}
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
try {
|
|
1208
|
+
const waitMessage = config.invite.inviteWaitMessage.replaceAll("{groupName}", groupName).replaceAll("{groupId}", groupId).replaceAll("{userName}", userName).replaceAll("{userId}", userId);
|
|
1209
|
+
await session.bot.sendPrivateMessage(userId, waitMessage);
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
console.error(`发送等待审核提示给 ${userId} 失败:`, error);
|
|
1212
|
+
}
|
|
1213
|
+
if (!config.admin.adminQQs || config.admin.adminQQs.length === 0) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
await addPendingInvite(ctx, platform, selfId, {
|
|
1217
|
+
groupId,
|
|
1218
|
+
userId,
|
|
1219
|
+
userName,
|
|
1220
|
+
groupName,
|
|
1221
|
+
time: Math.floor(Date.now() / 1e3),
|
|
1222
|
+
flag
|
|
1223
|
+
});
|
|
1224
|
+
const requestMessage = config.invite.inviteRequestMessage.replaceAll("{groupName}", groupName).replaceAll("{groupId}", groupId).replaceAll("{userName}", userName).replaceAll("{userId}", userId);
|
|
1225
|
+
await notifyAdmins(session.bot, config, requestMessage);
|
|
1226
|
+
if (config.invite.showDetailedLog) {
|
|
1227
|
+
console.log("已发送群邀请审核通知");
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
const cmdOpts = getAdminCommandOptions(config);
|
|
1231
|
+
ctx.command("gc.approve <groupId:string>", "同意群聊邀请", cmdOpts).action(async ({ session }, groupId) => {
|
|
1232
|
+
if (!groupId) return "请指定群号。用法:gc.approve <群号>";
|
|
1233
|
+
groupId = parseGuildId(groupId);
|
|
1234
|
+
if (!groupId) return "输入格式错误,请输入群号。";
|
|
1235
|
+
const selfId = getBotSelfId2(session.bot);
|
|
1236
|
+
if (!selfId) return "无法识别当前机器人账号,已取消操作。";
|
|
1237
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有管理员可以审核邀请。";
|
|
1238
|
+
if (config.basic.enableBlacklist) {
|
|
1239
|
+
const bl = await getBlacklistedGuild(ctx, groupId);
|
|
1240
|
+
if (bl.length > 0) {
|
|
1241
|
+
return `群 ${groupId} 在黑名单中,无法通过审核。如需放行请先执行 gc.unban ${groupId}。`;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
const inviteData = await getPendingInvite(ctx, session.platform, groupId, selfId);
|
|
1245
|
+
if (!inviteData) {
|
|
1246
|
+
const allInvites = await getAllPendingInvites(ctx, session.platform, selfId);
|
|
1247
|
+
return `未找到群号 ${groupId} 的待处理邀请。当前待处理邀请:${allInvites.length > 0 ? allInvites.map((i) => `${i.groupId}(${i.groupName})`).join(", ") : "无"}`;
|
|
1248
|
+
}
|
|
1249
|
+
try {
|
|
1250
|
+
await handleInviteRequest(session.bot, inviteData.flag, true);
|
|
1251
|
+
await markApprovedGuild(ctx, groupId, selfId);
|
|
1252
|
+
try {
|
|
1253
|
+
await session.bot.sendPrivateMessage(inviteData.userId, `您的群聊邀请已通过管理员审核,机器人已加入群聊。`);
|
|
1254
|
+
} catch (error) {
|
|
1255
|
+
console.error("通知邀请者失败:", error);
|
|
1256
|
+
}
|
|
1257
|
+
await removePendingInvite(ctx, session.platform, groupId, selfId);
|
|
1258
|
+
return `已同意加入群 ${groupId}(${inviteData.groupName}),邀请者:${inviteData.userName}`;
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
console.error("处理同意邀请失败:", error);
|
|
1261
|
+
return `处理同意邀请失败: ${error.message}`;
|
|
1262
|
+
}
|
|
1263
|
+
});
|
|
1264
|
+
ctx.command("gc.reject <groupId:string>", "拒绝群聊邀请", cmdOpts).action(async ({ session }, groupId) => {
|
|
1265
|
+
if (!groupId) return "请指定群号。用法:gc.reject <群号>";
|
|
1266
|
+
groupId = parseGuildId(groupId);
|
|
1267
|
+
if (!groupId) return "输入格式错误,请输入群号。";
|
|
1268
|
+
const selfId = getBotSelfId2(session.bot);
|
|
1269
|
+
if (!selfId) return "无法识别当前机器人账号,已取消操作。";
|
|
1270
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有管理员可以审核邀请。";
|
|
1271
|
+
const inviteData = await getPendingInvite(ctx, session.platform, groupId, selfId);
|
|
1272
|
+
if (!inviteData) {
|
|
1273
|
+
const allInvites = await getAllPendingInvites(ctx, session.platform, selfId);
|
|
1274
|
+
return `未找到群号 ${groupId} 的待处理邀请。当前待处理邀请:${allInvites.length > 0 ? allInvites.map((i) => `${i.groupId}(${i.groupName})`).join(", ") : "无"}`;
|
|
1275
|
+
}
|
|
1276
|
+
try {
|
|
1277
|
+
await handleInviteRequest(session.bot, inviteData.flag, false, "已拒绝");
|
|
1278
|
+
try {
|
|
1279
|
+
await session.bot.sendPrivateMessage(inviteData.userId, `您的群聊邀请未通过管理员审核,机器人将不会加入该群聊。`);
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
console.error("通知邀请者失败:", error);
|
|
1282
|
+
}
|
|
1283
|
+
await removePendingInvite(ctx, session.platform, groupId, selfId);
|
|
1284
|
+
return `已拒绝加入群 ${groupId}(${inviteData.groupName}),邀请者:${inviteData.userName}`;
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
console.error("处理拒绝邀请失败:", error);
|
|
1287
|
+
return `处理拒绝邀请失败: ${error.message}`;
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
ctx.command("gc.pending", "查看待处理的群聊邀请", cmdOpts).action(async ({ session }) => {
|
|
1291
|
+
if (!hasGlobalPermission(session, config)) {
|
|
1292
|
+
return "权限不足,只有管理员可以查看待处理邀请。";
|
|
1293
|
+
}
|
|
1294
|
+
const selfId = getBotSelfId2(session.bot);
|
|
1295
|
+
if (!selfId) return "无法识别当前机器人账号,已取消操作。";
|
|
1296
|
+
const allInvites = await getAllPendingInvites(ctx, session.platform, selfId);
|
|
1297
|
+
if (allInvites.length === 0) {
|
|
1298
|
+
return "当前没有待处理的群聊邀请。";
|
|
1299
|
+
}
|
|
1300
|
+
const lines = ["待处理的群聊邀请列表:"];
|
|
1301
|
+
for (const invite of allInvites) {
|
|
1302
|
+
const elapsed = Math.floor((Date.now() / 1e3 - invite.time) / 60);
|
|
1303
|
+
lines.push(`- 群:${invite.groupName}(${invite.groupId})`);
|
|
1304
|
+
lines.push(` 邀请者:${invite.userName}(${invite.userId})`);
|
|
1305
|
+
lines.push(` ${elapsed} 分钟前`);
|
|
1306
|
+
lines.push(` 同意:gc.approve ${invite.groupId} | 拒绝:gc.reject ${invite.groupId}`);
|
|
1307
|
+
}
|
|
1308
|
+
return lines.join("\n");
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
__name(apply3, "apply");
|
|
1312
|
+
|
|
1313
|
+
// src/modules/frequency.ts
|
|
1314
|
+
var frequency_exports = {};
|
|
1315
|
+
__export(frequency_exports, {
|
|
1316
|
+
apply: () => apply4,
|
|
1317
|
+
name: () => name4
|
|
1318
|
+
});
|
|
1319
|
+
var name4 = "group-control-frequency";
|
|
1320
|
+
var PRIVATE_GUILD_PREFIX = "__private__:";
|
|
1321
|
+
var frequencyLocks = /* @__PURE__ */ new Map();
|
|
1322
|
+
function isBlocked(record) {
|
|
1323
|
+
if (!record || !record.blockExpiryTime) return false;
|
|
1324
|
+
return Date.now() < record.blockExpiryTime * 1e3;
|
|
1325
|
+
}
|
|
1326
|
+
__name(isBlocked, "isBlocked");
|
|
1327
|
+
function calcBlockDur(baseDur, expBase, blockCount) {
|
|
1328
|
+
if (expBase <= 1) return baseDur;
|
|
1329
|
+
return Math.round(baseDur * Math.pow(expBase, blockCount - 1));
|
|
1330
|
+
}
|
|
1331
|
+
__name(calcBlockDur, "calcBlockDur");
|
|
1332
|
+
function makeEmptyRecord(platform, guildId, now) {
|
|
1333
|
+
return { platform, guildId, commandCount: 1, lastCommandTime: now, warningSent: false, blockExpiryTime: 0, firstWarningTime: 0, blockCount: 0, lastBlockNotifyTime: 0 };
|
|
1334
|
+
}
|
|
1335
|
+
__name(makeEmptyRecord, "makeEmptyRecord");
|
|
1336
|
+
async function withFrequencyLock(key, task) {
|
|
1337
|
+
const previous = frequencyLocks.get(key) ?? Promise.resolve();
|
|
1338
|
+
let release;
|
|
1339
|
+
const current = new Promise((resolve) => {
|
|
1340
|
+
release = resolve;
|
|
1341
|
+
});
|
|
1342
|
+
const tail = previous.then(() => current, () => current);
|
|
1343
|
+
frequencyLocks.set(key, tail);
|
|
1344
|
+
await previous.catch(() => {
|
|
1345
|
+
});
|
|
1346
|
+
try {
|
|
1347
|
+
return await task();
|
|
1348
|
+
} finally {
|
|
1349
|
+
release();
|
|
1350
|
+
if (frequencyLocks.get(key) === tail) frequencyLocks.delete(key);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
__name(withFrequencyLock, "withFrequencyLock");
|
|
1354
|
+
async function handleTrigger(ctx, platform, guildId, limit, window, warnDelay, baseDur, expBase, expWindow, notifyCooldown) {
|
|
1355
|
+
let record = await getCommandFrequencyRecord(ctx, platform, guildId);
|
|
1356
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1357
|
+
const windowStart = now - window;
|
|
1358
|
+
if (!record) {
|
|
1359
|
+
record = makeEmptyRecord(platform, guildId, now);
|
|
1360
|
+
} else if (record.lastCommandTime < windowStart) {
|
|
1361
|
+
if (isBlocked(record)) {
|
|
1362
|
+
} else {
|
|
1363
|
+
const blockExpired = record.blockExpiryTime > 0 && now - record.blockExpiryTime > expWindow;
|
|
1364
|
+
record.commandCount = 1;
|
|
1365
|
+
record.lastCommandTime = now;
|
|
1366
|
+
record.warningSent = false;
|
|
1367
|
+
record.firstWarningTime = 0;
|
|
1368
|
+
if (blockExpired) record.blockCount = 0;
|
|
1369
|
+
}
|
|
1370
|
+
} else {
|
|
1371
|
+
record.commandCount += 1;
|
|
1372
|
+
record.lastCommandTime = now;
|
|
1373
|
+
}
|
|
1374
|
+
if (isBlocked(record)) {
|
|
1375
|
+
const remaining = Math.ceil((record.blockExpiryTime * 1e3 - Date.now()) / 1e3);
|
|
1376
|
+
const lastNotify = record.lastBlockNotifyTime || 0;
|
|
1377
|
+
if (now - lastNotify >= notifyCooldown) {
|
|
1378
|
+
record.lastBlockNotifyTime = now;
|
|
1379
|
+
await updateCommandFrequencyRecord(ctx, platform, guildId, record);
|
|
1380
|
+
return { result: "blocked", remaining };
|
|
1381
|
+
} else {
|
|
1382
|
+
await updateCommandFrequencyRecord(ctx, platform, guildId, record);
|
|
1383
|
+
return { result: "blocked-silent" };
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
if (warnDelay > 0 && record.warningSent && record.firstWarningTime > 0 && now - record.firstWarningTime >= warnDelay) {
|
|
1387
|
+
record.warningSent = false;
|
|
1388
|
+
record.firstWarningTime = 0;
|
|
1389
|
+
record.commandCount = 1;
|
|
1390
|
+
record.lastCommandTime = now;
|
|
1391
|
+
}
|
|
1392
|
+
if (record.commandCount > limit) {
|
|
1393
|
+
if (!record.warningSent) {
|
|
1394
|
+
record.warningSent = true;
|
|
1395
|
+
record.commandCount = 1;
|
|
1396
|
+
record.lastCommandTime = now;
|
|
1397
|
+
record.firstWarningTime = now;
|
|
1398
|
+
await updateCommandFrequencyRecord(ctx, platform, guildId, record);
|
|
1399
|
+
return { result: "warn" };
|
|
1400
|
+
} else {
|
|
1401
|
+
record.blockCount = (record.blockCount || 0) + 1;
|
|
1402
|
+
const dur = calcBlockDur(baseDur, expBase, record.blockCount);
|
|
1403
|
+
record.blockExpiryTime = now + dur;
|
|
1404
|
+
record.warningSent = false;
|
|
1405
|
+
record.commandCount = 0;
|
|
1406
|
+
record.firstWarningTime = 0;
|
|
1407
|
+
record.lastBlockNotifyTime = now;
|
|
1408
|
+
await updateCommandFrequencyRecord(ctx, platform, guildId, record);
|
|
1409
|
+
return { result: "new-blocked", dur };
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
await updateCommandFrequencyRecord(ctx, platform, guildId, record);
|
|
1413
|
+
return { result: "ok" };
|
|
1414
|
+
}
|
|
1415
|
+
__name(handleTrigger, "handleTrigger");
|
|
1416
|
+
function isSystemSession(session) {
|
|
1417
|
+
if (!session.userId) return true;
|
|
1418
|
+
if (session.userId === session.bot?.userId) return true;
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
__name(isSystemSession, "isSystemSession");
|
|
1422
|
+
function isUserInitiatedNonCommand(session) {
|
|
1423
|
+
if (!session.content) return false;
|
|
1424
|
+
const mentioned = session.elements?.some((e) => e.type === "at" && e.attrs?.id === session.bot?.userId);
|
|
1425
|
+
if (mentioned) return true;
|
|
1426
|
+
if (!session.guildId) return true;
|
|
1427
|
+
return false;
|
|
1428
|
+
}
|
|
1429
|
+
__name(isUserInitiatedNonCommand, "isUserInitiatedNonCommand");
|
|
1430
|
+
function apply4(ctx, config) {
|
|
1431
|
+
const freq = config.frequency;
|
|
1432
|
+
if (!freq.enabled && !freq.privateEnabled) return;
|
|
1433
|
+
const groupWhitelist = new Set((freq.whitelist ?? []).map((id) => parseGuildId(id) ?? id));
|
|
1434
|
+
const privateWhitelist = new Set((freq.privateWhitelist ?? []).map((id) => parseGuildId(id) ?? id));
|
|
1435
|
+
const countedSessions = /* @__PURE__ */ new WeakSet();
|
|
1436
|
+
async function checkFrequency(session, isCommand) {
|
|
1437
|
+
const isPrivate = !session.guildId;
|
|
1438
|
+
const platform = session.platform;
|
|
1439
|
+
if (isPrivate) {
|
|
1440
|
+
if (!freq.privateEnabled) return true;
|
|
1441
|
+
const userId = parseGuildId(session.userId) ?? session.userId;
|
|
1442
|
+
if (privateWhitelist.has(userId)) return true;
|
|
1443
|
+
const guildId = PRIVATE_GUILD_PREFIX + userId;
|
|
1444
|
+
const r = await withFrequencyLock(`${platform}:${guildId}`, () => handleTrigger(
|
|
1445
|
+
ctx,
|
|
1446
|
+
platform,
|
|
1447
|
+
guildId,
|
|
1448
|
+
freq.privateLimit,
|
|
1449
|
+
freq.privateWindow,
|
|
1450
|
+
freq.privateWarnDelay,
|
|
1451
|
+
freq.privateBlockDur,
|
|
1452
|
+
freq.blockExpBase,
|
|
1453
|
+
freq.blockExpWindow,
|
|
1454
|
+
freq.blockNotifyCooldown
|
|
1455
|
+
));
|
|
1456
|
+
if (r.result === "ok") return true;
|
|
1457
|
+
if (r.result === "warn") {
|
|
1458
|
+
try {
|
|
1459
|
+
await session.send(freq.warnMsg);
|
|
1460
|
+
} catch (e) {
|
|
1461
|
+
}
|
|
1462
|
+
return false;
|
|
1463
|
+
}
|
|
1464
|
+
if (r.result === "new-blocked") {
|
|
1465
|
+
try {
|
|
1466
|
+
await session.send(freq.blockMsg.replace("{duration}", r.dur.toString()));
|
|
1467
|
+
} catch (e) {
|
|
1468
|
+
}
|
|
1469
|
+
return false;
|
|
1470
|
+
}
|
|
1471
|
+
if (r.result === "blocked") {
|
|
1472
|
+
try {
|
|
1473
|
+
await session.send(freq.blockedMsg.replace("{time}", r.remaining.toString()));
|
|
1474
|
+
} catch (e) {
|
|
1475
|
+
}
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
return false;
|
|
1479
|
+
} else {
|
|
1480
|
+
if (!freq.enabled) return true;
|
|
1481
|
+
const normalizedGuildId = parseGuildId(session.guildId) ?? session.guildId;
|
|
1482
|
+
if (groupWhitelist.has(normalizedGuildId)) return true;
|
|
1483
|
+
const guildId = normalizedGuildId;
|
|
1484
|
+
const r = await withFrequencyLock(`${platform}:${guildId}`, () => handleTrigger(
|
|
1485
|
+
ctx,
|
|
1486
|
+
platform,
|
|
1487
|
+
guildId,
|
|
1488
|
+
freq.limit,
|
|
1489
|
+
freq.window,
|
|
1490
|
+
freq.warnDelay,
|
|
1491
|
+
freq.blockDur,
|
|
1492
|
+
freq.blockExpBase,
|
|
1493
|
+
freq.blockExpWindow,
|
|
1494
|
+
freq.blockNotifyCooldown
|
|
1495
|
+
));
|
|
1496
|
+
if (r.result === "ok") return true;
|
|
1497
|
+
if (r.result === "warn") {
|
|
1498
|
+
try {
|
|
1499
|
+
await session.bot.sendMessage(guildId, freq.warnMsg, platform);
|
|
1500
|
+
} catch (e) {
|
|
1501
|
+
}
|
|
1502
|
+
return false;
|
|
1503
|
+
}
|
|
1504
|
+
if (r.result === "new-blocked") {
|
|
1505
|
+
try {
|
|
1506
|
+
await session.bot.sendMessage(guildId, freq.blockMsg.replace("{duration}", r.dur.toString()), platform);
|
|
1507
|
+
} catch (e) {
|
|
1508
|
+
}
|
|
1509
|
+
return false;
|
|
1510
|
+
}
|
|
1511
|
+
if (r.result === "blocked") {
|
|
1512
|
+
try {
|
|
1513
|
+
await session.bot.sendMessage(guildId, freq.blockedMsg.replace("{time}", r.remaining.toString()), platform);
|
|
1514
|
+
} catch (e) {
|
|
1515
|
+
}
|
|
1516
|
+
return false;
|
|
1517
|
+
}
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
__name(checkFrequency, "checkFrequency");
|
|
1522
|
+
ctx.on("command/before-execute", async (argv) => {
|
|
1523
|
+
const session = argv.session;
|
|
1524
|
+
if (isSystemSession(session)) return;
|
|
1525
|
+
if (countedSessions.has(session)) return;
|
|
1526
|
+
const allowed = await checkFrequency(session, true);
|
|
1527
|
+
if (!allowed) return "";
|
|
1528
|
+
});
|
|
1529
|
+
ctx.middleware(async (session, next) => {
|
|
1530
|
+
if (isSystemSession(session)) return next();
|
|
1531
|
+
if (!isUserInitiatedNonCommand(session)) return next();
|
|
1532
|
+
countedSessions.add(session);
|
|
1533
|
+
const allowed = await checkFrequency(session, false);
|
|
1534
|
+
if (!allowed) return;
|
|
1535
|
+
return next();
|
|
1536
|
+
}, true);
|
|
1537
|
+
}
|
|
1538
|
+
__name(apply4, "apply");
|
|
1539
|
+
|
|
1540
|
+
// src/modules/commands.ts
|
|
1541
|
+
var commands_exports = {};
|
|
1542
|
+
__export(commands_exports, {
|
|
1543
|
+
apply: () => apply5,
|
|
1544
|
+
name: () => name5
|
|
1545
|
+
});
|
|
1546
|
+
var name5 = "group-control-commands";
|
|
1547
|
+
function chunk(arr, size) {
|
|
1548
|
+
const out = [];
|
|
1549
|
+
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
|
|
1550
|
+
return out;
|
|
1551
|
+
}
|
|
1552
|
+
__name(chunk, "chunk");
|
|
1553
|
+
async function sendAsForward(session, title, lines) {
|
|
1554
|
+
const groups = chunk(lines, 80);
|
|
1555
|
+
const botId = String(session.bot?.selfId || "10000");
|
|
1556
|
+
const nodes = groups.map((g) => ({
|
|
1557
|
+
type: "node",
|
|
1558
|
+
data: { user_id: botId, nickname: title, content: g.join("\n") }
|
|
1559
|
+
}));
|
|
1560
|
+
try {
|
|
1561
|
+
const internal = session.bot.internal;
|
|
1562
|
+
if (session.guildId) {
|
|
1563
|
+
if (typeof internal.sendGroupForwardMsg !== "function") throw new Error("no forward api");
|
|
1564
|
+
const guildId = toOneBotNumber(session.guildId);
|
|
1565
|
+
if (guildId == null) throw new Error("invalid guild id");
|
|
1566
|
+
await internal.sendGroupForwardMsg(guildId, nodes);
|
|
1567
|
+
} else {
|
|
1568
|
+
if (typeof internal.sendPrivateForwardMsg !== "function") throw new Error("no forward api");
|
|
1569
|
+
const userId = toOneBotNumber(session.userId);
|
|
1570
|
+
if (userId == null) throw new Error("invalid user id");
|
|
1571
|
+
await internal.sendPrivateForwardMsg(userId, nodes);
|
|
1572
|
+
}
|
|
1573
|
+
} catch {
|
|
1574
|
+
for (const g of groups) {
|
|
1575
|
+
try {
|
|
1576
|
+
await session.send(g.join("\n"));
|
|
1577
|
+
} catch {
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
__name(sendAsForward, "sendAsForward");
|
|
1583
|
+
async function deleteFriendCompat(bot, userId) {
|
|
1584
|
+
const internal = bot.internal;
|
|
1585
|
+
if (typeof internal?.deleteFriend !== "function") {
|
|
1586
|
+
throw new Error("当前适配器不支持 delete_friend 接口");
|
|
1587
|
+
}
|
|
1588
|
+
const n = toOneBotNumber(userId);
|
|
1589
|
+
if (n == null) throw new Error("输入格式错误,请输入要删除的好友 QQ 号。");
|
|
1590
|
+
try {
|
|
1591
|
+
await internal.deleteFriend(n);
|
|
1592
|
+
} catch (e) {
|
|
1593
|
+
await internal.deleteFriend({ user_id: n, friend_id: n, temp_block: false, both_del: false });
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
__name(deleteFriendCompat, "deleteFriendCompat");
|
|
1597
|
+
function apply5(ctx, config) {
|
|
1598
|
+
const cmdOpts = getAdminCommandOptions(config);
|
|
1599
|
+
ctx.command("gc", "群控管理员指令", cmdOpts);
|
|
1600
|
+
ctx.command("gc.ban <groupId:text>", "添加群聊到黑名单", cmdOpts).action(async ({ session }, input) => {
|
|
1601
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1602
|
+
const errorMsg = isBlacklistEnabled(config.basic);
|
|
1603
|
+
if (errorMsg) return errorMsg;
|
|
1604
|
+
const guildId = parseGuildId(input);
|
|
1605
|
+
if (!guildId) return `输入格式错误。`;
|
|
1606
|
+
const existing = await getBlacklistedGuild(ctx, guildId);
|
|
1607
|
+
if (existing.length > 0) return `群聊 ${guildId} 已在黑名单中。`;
|
|
1608
|
+
await createBlacklistedGuild(ctx, guildId, "manual_add");
|
|
1609
|
+
return `已添加群聊 ${guildId} 到黑名单。`;
|
|
1610
|
+
});
|
|
1611
|
+
ctx.command("gc.unban <groupId:text>", "从黑名单移除群聊", cmdOpts).action(async ({ session }, input) => {
|
|
1612
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1613
|
+
const errorMsg = isBlacklistEnabled(config.basic);
|
|
1614
|
+
if (errorMsg) return errorMsg;
|
|
1615
|
+
const guildId = parseGuildId(input);
|
|
1616
|
+
if (!guildId) return `输入格式错误。`;
|
|
1617
|
+
const removed = await removeBlacklistedGuild(ctx, guildId);
|
|
1618
|
+
await clearSelfLeft(ctx, guildId);
|
|
1619
|
+
return removed ? `已移除群聊 ${guildId}` : `群聊 ${guildId} 不在黑名单中。`;
|
|
1620
|
+
});
|
|
1621
|
+
ctx.command("gc.banlist", "查看黑名单", cmdOpts).action(async ({ session }) => {
|
|
1622
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1623
|
+
const errorMsg = isBlacklistEnabled(config.basic);
|
|
1624
|
+
if (errorMsg) return errorMsg;
|
|
1625
|
+
const records = await getAllBlacklistedGuilds(ctx);
|
|
1626
|
+
if (records.length === 0) return "黑名单为空。";
|
|
1627
|
+
return "黑名单列表:\n" + records.map((r) => `- ${r.guildId} (时间: ${formatDate(r.timestamp)})`).join("\n");
|
|
1628
|
+
});
|
|
1629
|
+
ctx.command("gc.clearban", "清空黑名单", cmdOpts).action(async ({ session }) => {
|
|
1630
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1631
|
+
const errorMsg = isBlacklistEnabled(config.basic);
|
|
1632
|
+
if (errorMsg) return errorMsg;
|
|
1633
|
+
const records = await getAllBlacklistedGuilds(ctx);
|
|
1634
|
+
if (records.length === 0) return "黑名单已是空的。";
|
|
1635
|
+
await clearBlacklistedGuilds(ctx);
|
|
1636
|
+
return `已清空黑名单,共移除 ${records.length} 个群聊。`;
|
|
1637
|
+
});
|
|
1638
|
+
ctx.command("gc.sg-add <groupId:text>", "解除指定群聊的小群人数限制", cmdOpts).action(async ({ session }, input) => {
|
|
1639
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1640
|
+
const guildId = parseGuildId(input);
|
|
1641
|
+
if (!guildId) return "输入格式错误,请输入群号。";
|
|
1642
|
+
const exists = await isInSmallGroupWhitelist(ctx, guildId);
|
|
1643
|
+
if (exists) return `群聊 ${guildId} 已在小群白名单中。`;
|
|
1644
|
+
await addToSmallGroupWhitelist(ctx, guildId);
|
|
1645
|
+
return `已将群聊 ${guildId} 加入小群白名单,该群不再受小群人数限制。`;
|
|
1646
|
+
});
|
|
1647
|
+
ctx.command("gc.sg-rm <groupId:text>", "恢复指定群聊的小群人数限制", cmdOpts).action(async ({ session }, input) => {
|
|
1648
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1649
|
+
const guildId = parseGuildId(input);
|
|
1650
|
+
if (!guildId) return "输入格式错误,请输入群号。";
|
|
1651
|
+
const exists = await isInSmallGroupWhitelist(ctx, guildId);
|
|
1652
|
+
if (!exists) return `群聊 ${guildId} 不在小群白名单中。`;
|
|
1653
|
+
await removeFromSmallGroupWhitelist(ctx, guildId);
|
|
1654
|
+
return `已将群聊 ${guildId} 从小群白名单移除,该群将恢复小群人数限制。`;
|
|
1655
|
+
});
|
|
1656
|
+
ctx.command("gc.sg-list", "查看小群白名单", cmdOpts).action(async ({ session }) => {
|
|
1657
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1658
|
+
const records = await getAllSmallGroupWhitelist(ctx);
|
|
1659
|
+
if (records.length === 0) return "小群白名单为空。";
|
|
1660
|
+
return "小群白名单列表(以下群不受小群人数限制):\n" + records.map((r) => `- ${r.guildId}`).join("\n");
|
|
1661
|
+
});
|
|
1662
|
+
ctx.command("gc.friends", "列出机器人的好友(合并转发)", cmdOpts).action(async ({ session }) => {
|
|
1663
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1664
|
+
let list = [];
|
|
1665
|
+
try {
|
|
1666
|
+
const raw = await session.bot.internal.getFriendList();
|
|
1667
|
+
list = Array.isArray(raw) ? raw : Array.isArray(raw?.data) ? raw.data : [];
|
|
1668
|
+
} catch (e) {
|
|
1669
|
+
return `获取好友列表失败:${e.message}`;
|
|
1670
|
+
}
|
|
1671
|
+
if (list.length === 0) return "好友列表为空。";
|
|
1672
|
+
const lines = list.map((f, i) => {
|
|
1673
|
+
const uid = f.user_id ?? f.userId ?? "";
|
|
1674
|
+
const name9 = f.remark || f.nickname || f.nick || String(uid);
|
|
1675
|
+
return `${i + 1}. ${name9} (${uid})`;
|
|
1676
|
+
});
|
|
1677
|
+
await sendAsForward(session, `好友列表(共 ${list.length} 个)`, lines);
|
|
1678
|
+
return "";
|
|
1679
|
+
});
|
|
1680
|
+
ctx.command("gc.delfriend <userId:text>", "删除指定好友", cmdOpts).action(async ({ session }, input) => {
|
|
1681
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1682
|
+
const userId = parseGuildId(input);
|
|
1683
|
+
if (!userId) return "输入格式错误,请输入要删除的好友 QQ 号。";
|
|
1684
|
+
try {
|
|
1685
|
+
await deleteFriendCompat(session.bot, userId);
|
|
1686
|
+
return `已删除好友 ${userId}。`;
|
|
1687
|
+
} catch (e) {
|
|
1688
|
+
return `删除好友失败:${e.message}`;
|
|
1689
|
+
}
|
|
1690
|
+
});
|
|
1691
|
+
ctx.command("gc.groups", "列出机器人所在的群(合并转发)", cmdOpts).action(async ({ session }) => {
|
|
1692
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1693
|
+
let list = [];
|
|
1694
|
+
try {
|
|
1695
|
+
const raw = await session.bot.internal.getGroupList();
|
|
1696
|
+
list = Array.isArray(raw) ? raw : Array.isArray(raw?.data) ? raw.data : [];
|
|
1697
|
+
} catch (e) {
|
|
1698
|
+
return `获取群列表失败:${e.message}`;
|
|
1699
|
+
}
|
|
1700
|
+
if (list.length === 0) return "机器人尚未加入任何群。";
|
|
1701
|
+
const lines = list.map((g, i) => {
|
|
1702
|
+
const gid = g.group_id ?? g.groupId ?? "";
|
|
1703
|
+
const gname = g.group_name ?? g.groupName ?? String(gid);
|
|
1704
|
+
const count = g.member_count ?? g.memberCount;
|
|
1705
|
+
const max = g.max_member_count ?? g.maxMemberCount;
|
|
1706
|
+
const sizeInfo = count != null ? `(${count}${max != null ? `/${max}` : ""}人)` : "";
|
|
1707
|
+
return `${i + 1}. ${gname} (${gid})${sizeInfo}`;
|
|
1708
|
+
});
|
|
1709
|
+
await sendAsForward(session, `群列表(共 ${list.length} 个)`, lines);
|
|
1710
|
+
return "";
|
|
1711
|
+
});
|
|
1712
|
+
ctx.command("gc.leave <groupId:text>", "让机器人退出指定群", cmdOpts).action(async ({ session }, input) => {
|
|
1713
|
+
if (!hasGlobalPermission(session, config)) return "权限不足,只有全局管理员可以执行此操作。";
|
|
1714
|
+
const guildId = parseGuildId(input);
|
|
1715
|
+
if (!guildId) return "输入格式错误,请输入要退出的群号。";
|
|
1716
|
+
const selfId = parseGuildId(String(session.bot?.selfId ?? session.bot?.userId ?? ""));
|
|
1717
|
+
if (!selfId) return "无法识别当前机器人账号,已取消退群。";
|
|
1718
|
+
await markSelfLeft(ctx, guildId, selfId);
|
|
1719
|
+
try {
|
|
1720
|
+
const groupId = toOneBotNumber(guildId);
|
|
1721
|
+
if (groupId == null) throw new Error("无效群号");
|
|
1722
|
+
await session.bot.internal.setGroupLeave(groupId);
|
|
1723
|
+
return `已退出群 ${guildId}。`;
|
|
1724
|
+
} catch (e) {
|
|
1725
|
+
await clearSelfLeft(ctx, guildId, selfId);
|
|
1726
|
+
return `退出群 ${guildId} 失败:${e.message}`;
|
|
1727
|
+
}
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
__name(apply5, "apply");
|
|
1731
|
+
|
|
1732
|
+
// src/modules/switch.ts
|
|
1733
|
+
var switch_exports = {};
|
|
1734
|
+
__export(switch_exports, {
|
|
1735
|
+
apply: () => apply6,
|
|
1736
|
+
name: () => name6
|
|
1737
|
+
});
|
|
1738
|
+
var name6 = "group-control-switch";
|
|
1739
|
+
function normalizeCommandName(name9, prefixes = []) {
|
|
1740
|
+
let source = name9.trim();
|
|
1741
|
+
for (const prefix of prefixes.filter(Boolean).sort((a, b) => b.length - a.length)) {
|
|
1742
|
+
if (source.startsWith(prefix)) {
|
|
1743
|
+
source = source.slice(prefix.length);
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
return source.replace(/^[/.!!。]+/, "").toLowerCase().replace(/_/g, "-");
|
|
1748
|
+
}
|
|
1749
|
+
__name(normalizeCommandName, "normalizeCommandName");
|
|
1750
|
+
function getCommandNames(command) {
|
|
1751
|
+
const names = /* @__PURE__ */ new Set();
|
|
1752
|
+
if (command?.name) names.add(normalizeCommandName(command.name));
|
|
1753
|
+
if (command?.displayName) names.add(normalizeCommandName(command.displayName));
|
|
1754
|
+
for (const alias of Object.keys(command?._aliases ?? {})) {
|
|
1755
|
+
names.add(normalizeCommandName(alias));
|
|
1756
|
+
}
|
|
1757
|
+
return [...names];
|
|
1758
|
+
}
|
|
1759
|
+
__name(getCommandNames, "getCommandNames");
|
|
1760
|
+
function isAdminCommand(command) {
|
|
1761
|
+
return getCommandNames(command).some((commandName) => ADMIN_COMMANDS.has(commandName));
|
|
1762
|
+
}
|
|
1763
|
+
__name(isAdminCommand, "isAdminCommand");
|
|
1764
|
+
function getCommandPrefixes(ctx, session) {
|
|
1765
|
+
const configured = session.resolve?.(ctx.root.config.prefix) ?? ctx.root.config.prefix;
|
|
1766
|
+
const prefixes = Array.isArray(configured) ? configured : [configured || ""];
|
|
1767
|
+
return [...prefixes, "/", ".", "!", "!", "。"];
|
|
1768
|
+
}
|
|
1769
|
+
__name(getCommandPrefixes, "getCommandPrefixes");
|
|
1770
|
+
function stripLeadingBotMentions(content) {
|
|
1771
|
+
return content.replace(/^(?:\s*<at\b[^>]*(?:\/>|>\s*<\/at>))+\s*/i, "").replace(/^(?:\s*\[CQ:at,[^\]]+\])+\s*/i, "");
|
|
1772
|
+
}
|
|
1773
|
+
__name(stripLeadingBotMentions, "stripLeadingBotMentions");
|
|
1774
|
+
function getMessageCommandName(ctx, session) {
|
|
1775
|
+
const strippedContent = session.stripped?.content?.trim();
|
|
1776
|
+
const content = stripLeadingBotMentions(strippedContent ? session.stripped.content : session.content ?? "");
|
|
1777
|
+
const firstWord = content.match(/^\s*(\S+)/)?.[1];
|
|
1778
|
+
return firstWord ? normalizeCommandName(firstWord, getCommandPrefixes(ctx, session)) : null;
|
|
1779
|
+
}
|
|
1780
|
+
__name(getMessageCommandName, "getMessageCommandName");
|
|
1781
|
+
function apply6(ctx, config) {
|
|
1782
|
+
if (config.permission.protectedCommands?.length > 0) {
|
|
1783
|
+
const protectedSet = new Set(config.permission.protectedCommands.map((commandName) => normalizeCommandName(commandName)));
|
|
1784
|
+
ctx.on("command/before-execute", async (argv) => {
|
|
1785
|
+
const session = argv.session;
|
|
1786
|
+
if (!session.guildId) return;
|
|
1787
|
+
const commandNames = getCommandNames(argv.command);
|
|
1788
|
+
if (!commandNames.some((commandName) => protectedSet.has(commandName))) return;
|
|
1789
|
+
const hasPerm = await hasGuildPermission(session, config);
|
|
1790
|
+
if (!hasPerm) {
|
|
1791
|
+
return "权限不足,只有群管理员可以使用此指令。";
|
|
1792
|
+
}
|
|
1793
|
+
}, true);
|
|
1794
|
+
}
|
|
1795
|
+
if (!config.botSwitch?.enabled) return;
|
|
1796
|
+
const cmdOpts = {};
|
|
1797
|
+
if (config.permission.mode === "koishi") {
|
|
1798
|
+
cmdOpts.authority = config.permission.koishiAuthority;
|
|
1799
|
+
}
|
|
1800
|
+
ctx.command("bot-on", "开启机器人", cmdOpts).action(async ({ session }) => {
|
|
1801
|
+
if (!session.guildId) return "该指令只能在群聊中使用。";
|
|
1802
|
+
if (config.permission.mode === "builtin") {
|
|
1803
|
+
const hasPerm = await hasGuildPermission(session, config);
|
|
1804
|
+
if (!hasPerm) return "权限不足,只有群管理员可以使用此指令。";
|
|
1805
|
+
}
|
|
1806
|
+
await setGroupBotStatus(ctx, session.platform, session.guildId, true);
|
|
1807
|
+
return "机器人已在此群开启。";
|
|
1808
|
+
});
|
|
1809
|
+
ctx.command("bot-off", "关闭机器人", cmdOpts).action(async ({ session }) => {
|
|
1810
|
+
if (!session.guildId) return "该指令只能在群聊中使用。";
|
|
1811
|
+
if (config.permission.mode === "builtin") {
|
|
1812
|
+
const hasPerm = await hasGuildPermission(session, config);
|
|
1813
|
+
if (!hasPerm) return "权限不足,只有群管理员可以使用此指令。";
|
|
1814
|
+
}
|
|
1815
|
+
await setGroupBotStatus(ctx, session.platform, session.guildId, false);
|
|
1816
|
+
return "机器人已在此群关闭。所有指令和主动响应(入群欢迎、链接解析等)将被阻止。使用 bot-on 重新开启。";
|
|
1817
|
+
});
|
|
1818
|
+
ctx.on("command/before-execute", async (argv) => {
|
|
1819
|
+
const session = argv.session;
|
|
1820
|
+
if (!session.guildId) return;
|
|
1821
|
+
if (isAdminCommand(argv.command)) {
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
const status = await getGroupBotStatus(ctx, session.platform, session.guildId);
|
|
1825
|
+
const isBotEnabled = status ? status.botEnabled : config.botSwitch.defaultState;
|
|
1826
|
+
if (!isBotEnabled) {
|
|
1827
|
+
const isMentioned = session.elements?.some((e) => e.type === "at" && e.attrs.id === session.bot.userId);
|
|
1828
|
+
if (isMentioned && config.botSwitch.disabledMessage) {
|
|
1829
|
+
try {
|
|
1830
|
+
await session.send(config.botSwitch.disabledMessage);
|
|
1831
|
+
} catch (e) {
|
|
1832
|
+
ctx.logger("group-control-switch").warn("发送关闭提示失败", e);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
return "";
|
|
1836
|
+
}
|
|
1837
|
+
}, true);
|
|
1838
|
+
ctx.middleware(async (session, next) => {
|
|
1839
|
+
if (!session.guildId) return next();
|
|
1840
|
+
const status = await getGroupBotStatus(ctx, session.platform, session.guildId);
|
|
1841
|
+
const isBotEnabled = status ? status.botEnabled : config.botSwitch.defaultState;
|
|
1842
|
+
if (isBotEnabled) {
|
|
1843
|
+
return next();
|
|
1844
|
+
}
|
|
1845
|
+
const commandName = session.argv?.command && isAdminCommand(session.argv.command) ? session.argv.command.name : getMessageCommandName(ctx, session);
|
|
1846
|
+
if (commandName && ADMIN_COMMANDS.has(normalizeCommandName(commandName))) {
|
|
1847
|
+
return next();
|
|
1848
|
+
}
|
|
1849
|
+
const isMentioned = session.elements?.some((e) => e.type === "at" && e.attrs.id === session.bot.userId);
|
|
1850
|
+
if (isMentioned && config.botSwitch.disabledMessage) {
|
|
1851
|
+
try {
|
|
1852
|
+
await session.send(config.botSwitch.disabledMessage);
|
|
1853
|
+
} catch (e) {
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
return;
|
|
1857
|
+
}, true);
|
|
1858
|
+
}
|
|
1859
|
+
__name(apply6, "apply");
|
|
1860
|
+
|
|
1861
|
+
// src/modules/friend.ts
|
|
1862
|
+
var friend_exports = {};
|
|
1863
|
+
__export(friend_exports, {
|
|
1864
|
+
apply: () => apply7,
|
|
1865
|
+
name: () => name7
|
|
1866
|
+
});
|
|
1867
|
+
var name7 = "group-control-friend";
|
|
1868
|
+
function getBotSelfId3(bot) {
|
|
1869
|
+
return parseGuildId(String(bot?.selfId ?? bot?.userId ?? ""));
|
|
1870
|
+
}
|
|
1871
|
+
__name(getBotSelfId3, "getBotSelfId");
|
|
1872
|
+
function apply7(ctx, config) {
|
|
1873
|
+
if (!config.friend.enabled) return;
|
|
1874
|
+
ctx.setInterval(async () => {
|
|
1875
|
+
const expireMs = config.friend.requestExpireDays * 24 * 60 * 60 * 1e3;
|
|
1876
|
+
try {
|
|
1877
|
+
for (const bot of ctx.bots) {
|
|
1878
|
+
const selfId = getBotSelfId3(bot);
|
|
1879
|
+
if (selfId) await clearExpiredPendingFriendRequests(ctx, bot.platform, selfId, expireMs);
|
|
1880
|
+
}
|
|
1881
|
+
} catch (e) {
|
|
1882
|
+
}
|
|
1883
|
+
}, 60 * 60 * 1e3);
|
|
1884
|
+
ctx.on("friend-request", async (session) => {
|
|
1885
|
+
const raw = session.original || session.raw || session.event?._data || {};
|
|
1886
|
+
const flag = raw.flag || session.flag || session.messageId;
|
|
1887
|
+
const userId = parseGuildId(raw.user_id ? String(raw.user_id) : session.userId);
|
|
1888
|
+
const comment = raw.comment || session.comment || "";
|
|
1889
|
+
const { platform } = session;
|
|
1890
|
+
const selfId = getBotSelfId3(session.bot);
|
|
1891
|
+
if (!userId || !selfId) {
|
|
1892
|
+
ctx.logger("group-control-friend").warn(`无法解析好友申请 ID: userId=${raw.user_id ?? session.userId}, selfId=${session.bot?.selfId}`);
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
let nickname = userId;
|
|
1896
|
+
try {
|
|
1897
|
+
const userNumber = toOneBotNumber(userId);
|
|
1898
|
+
const info = userNumber == null ? null : await session.bot.internal?.getStrangerInfo?.(userNumber);
|
|
1899
|
+
nickname = info?.nickname || nickname;
|
|
1900
|
+
} catch (e) {
|
|
1901
|
+
}
|
|
1902
|
+
if (config.friend.autoApprove) {
|
|
1903
|
+
try {
|
|
1904
|
+
await session.bot.internal?.setFriendAddRequest?.(flag, true, "");
|
|
1905
|
+
if (config.friend.notifyAdminOnApprove) {
|
|
1906
|
+
const msg2 = config.friend.approveNotificationMessage.replaceAll("{userId}", userId).replaceAll("{nickname}", nickname).replaceAll("{comment}", comment);
|
|
1907
|
+
await notifyAdmins(session.bot, config, msg2);
|
|
1908
|
+
}
|
|
1909
|
+
} catch (e) {
|
|
1910
|
+
ctx.logger("group-control-friend").warn("自动通过好友申请失败", e);
|
|
1911
|
+
}
|
|
1912
|
+
return;
|
|
1913
|
+
}
|
|
1914
|
+
await addPendingFriendRequest(ctx, platform, selfId, { userId, nickname, comment, flag, time: Math.floor(Date.now() / 1e3) });
|
|
1915
|
+
const msg = config.friend.requestMessage.replaceAll("{userId}", userId).replaceAll("{nickname}", nickname).replaceAll("{comment}", comment);
|
|
1916
|
+
await notifyAdmins(session.bot, config, msg);
|
|
1917
|
+
});
|
|
1918
|
+
const cmdOpts = getAdminCommandOptions(config);
|
|
1919
|
+
ctx.command("gc.friend-pending", "查看待处理的好友申请", cmdOpts).alias("gc.fp").action(async ({ session }) => {
|
|
1920
|
+
if (!hasGlobalPermission(session, config)) return "权限不足。";
|
|
1921
|
+
const selfId = getBotSelfId3(session.bot);
|
|
1922
|
+
if (!selfId) return "无法识别当前机器人账号,已取消操作。";
|
|
1923
|
+
const all = await getAllPendingFriendRequests(ctx, session.platform, selfId);
|
|
1924
|
+
if (all.length === 0) return "当前没有待处理的好友申请。";
|
|
1925
|
+
const lines = ["待处理好友申请列表:"];
|
|
1926
|
+
for (const r of all) {
|
|
1927
|
+
const elapsed = Math.floor((Date.now() / 1e3 - r.time) / 60);
|
|
1928
|
+
lines.push(`- ${r.nickname}(${r.userId})附言:${r.comment || "无"} · ${elapsed} 分钟前`);
|
|
1929
|
+
lines.push(` 同意:gc.friend-approve ${r.userId} | 拒绝:gc.friend-reject ${r.userId}`);
|
|
1930
|
+
}
|
|
1931
|
+
return lines.join("\n");
|
|
1932
|
+
});
|
|
1933
|
+
ctx.command("gc.friend-approve <userId:string>", "同意好友申请", cmdOpts).alias("gc.fa").action(async ({ session }, userId) => {
|
|
1934
|
+
if (!hasGlobalPermission(session, config)) return "权限不足。";
|
|
1935
|
+
if (!userId) return "请指定QQ号。用法:gc.friend-approve <QQ号>";
|
|
1936
|
+
userId = parseGuildId(userId);
|
|
1937
|
+
if (!userId) return "输入格式错误,请输入 QQ 号。";
|
|
1938
|
+
const selfId = getBotSelfId3(session.bot);
|
|
1939
|
+
if (!selfId) return "无法识别当前机器人账号,已取消操作。";
|
|
1940
|
+
const record = await getPendingFriendRequest(ctx, session.platform, selfId, userId);
|
|
1941
|
+
if (!record) return `未找到来自 ${userId} 的待处理好友申请。`;
|
|
1942
|
+
try {
|
|
1943
|
+
await session.bot.internal?.setFriendAddRequest?.(record.flag, true, "");
|
|
1944
|
+
await removePendingFriendRequest(ctx, session.platform, selfId, userId);
|
|
1945
|
+
return `已同意 ${record.nickname}(${userId})的好友申请。`;
|
|
1946
|
+
} catch (e) {
|
|
1947
|
+
return `处理失败:${e.message}`;
|
|
1948
|
+
}
|
|
1949
|
+
});
|
|
1950
|
+
ctx.command("gc.friend-reject <userId:string>", "拒绝好友申请", cmdOpts).alias("gc.fr").action(async ({ session }, userId) => {
|
|
1951
|
+
if (!hasGlobalPermission(session, config)) return "权限不足。";
|
|
1952
|
+
if (!userId) return "请指定QQ号。用法:gc.friend-reject <QQ号>";
|
|
1953
|
+
userId = parseGuildId(userId);
|
|
1954
|
+
if (!userId) return "输入格式错误,请输入 QQ 号。";
|
|
1955
|
+
const selfId = getBotSelfId3(session.bot);
|
|
1956
|
+
if (!selfId) return "无法识别当前机器人账号,已取消操作。";
|
|
1957
|
+
const record = await getPendingFriendRequest(ctx, session.platform, selfId, userId);
|
|
1958
|
+
if (!record) return `未找到来自 ${userId} 的待处理好友申请。`;
|
|
1959
|
+
try {
|
|
1960
|
+
await session.bot.internal?.setFriendAddRequest?.(record.flag, false, "");
|
|
1961
|
+
await removePendingFriendRequest(ctx, session.platform, selfId, userId);
|
|
1962
|
+
return `已拒绝 ${record.nickname}(${userId})的好友申请。`;
|
|
1963
|
+
} catch (e) {
|
|
1964
|
+
return `处理失败:${e.message}`;
|
|
1965
|
+
}
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
__name(apply7, "apply");
|
|
1969
|
+
|
|
1970
|
+
// src/config.ts
|
|
1971
|
+
var import_koishi = require("koishi");
|
|
1972
|
+
var Config = import_koishi.Schema.intersect([
|
|
1973
|
+
import_koishi.Schema.object({
|
|
1974
|
+
admin: import_koishi.Schema.object({
|
|
1975
|
+
adminQQs: import_koishi.Schema.array(String).default([]).description("管理员QQ号列表(权限验证及通知)"),
|
|
1976
|
+
notificationGroupId: import_koishi.Schema.string().description("通知群号(填写后发到此群,否则私聊管理员)")
|
|
1977
|
+
}).description("管理员配置")
|
|
1978
|
+
}),
|
|
1979
|
+
import_koishi.Schema.object({
|
|
1980
|
+
permission: import_koishi.Schema.object({
|
|
1981
|
+
mode: import_koishi.Schema.union([
|
|
1982
|
+
import_koishi.Schema.const("koishi").description("使用 Koishi 自带权限系统 (authority)"),
|
|
1983
|
+
import_koishi.Schema.const("builtin").description("使用插件内置权限管理 (群管理员/群主)")
|
|
1984
|
+
]).default("builtin").description("权限管理模式"),
|
|
1985
|
+
koishiAuthority: import_koishi.Schema.natural().default(3).description("Koishi 模式下管理指令所需的最低权限等级"),
|
|
1986
|
+
protectedCommands: import_koishi.Schema.array(String).default([]).description("需要群管理员权限才能使用的自定义指令名列表")
|
|
1987
|
+
}).description("权限管理")
|
|
1988
|
+
}),
|
|
1989
|
+
import_koishi.Schema.object({
|
|
1990
|
+
basic: import_koishi.Schema.object({
|
|
1991
|
+
welcomeMessage: import_koishi.Schema.string().default("你好,我是机器人。").description("加入群聊时发送的欢迎消息"),
|
|
1992
|
+
quitCommandEnabled: import_koishi.Schema.boolean().default(true).description("启用 quit 指令"),
|
|
1993
|
+
quitMessage: import_koishi.Schema.string().default("收到来自{userId}的指令,即将退出群聊。").description("quit 指令触发后的群内提示,支持变量 {userId}"),
|
|
1994
|
+
enableBlacklist: import_koishi.Schema.boolean().default(true).description("启用被踢出自动拉黑"),
|
|
1995
|
+
blacklistMessage: import_koishi.Schema.string().role("textarea").default("此群聊已被拉黑,机器人将自动退出,请联系管理员移出黑名单。").description("被拉入黑名单群后的提示"),
|
|
1996
|
+
notifyAdminOnKick: import_koishi.Schema.boolean().default(true).description("被踢出群时通知管理员"),
|
|
1997
|
+
kickNotificationMessage: import_koishi.Schema.string().role("textarea").default("机器人已被踢出群聊\n群名称:{groupName}\n群号:{groupId}\n该群已被自动加入黑名单。").description("被踢出群通知模板,支持变量 {groupId}, {groupName}"),
|
|
1998
|
+
smallGroupAutoQuit: import_koishi.Schema.boolean().default(false).description("启用小群自动退群"),
|
|
1999
|
+
smallGroupThreshold: import_koishi.Schema.natural().default(30).description("小群人数阈值(低于等于此值时自动退群)"),
|
|
2000
|
+
smallGroupCheckDelay: import_koishi.Schema.natural().default(3e3).description("加入后延迟检测时间(毫秒)"),
|
|
2001
|
+
smallGroupExcludeOfficialBots: import_koishi.Schema.boolean().default(true).description("统计群人数时排除 QQ 官方机器人(is_robot)及机器人自身,仅统计真人成员"),
|
|
2002
|
+
smallGroupRealtimeMonitor: import_koishi.Schema.boolean().default(true).description("实时监控群人数(监听成员退群事件,群缩小到阈值以下时自动退群)"),
|
|
2003
|
+
smallGroupRecheckCooldown: import_koishi.Schema.natural().default(60).description("实时监控:同一群两次复检的最小间隔(秒),避免成员批量退群时频繁调用接口"),
|
|
2004
|
+
smallGroupQuitMessage: import_koishi.Schema.string().role("textarea").default("该群人数过少({memberCount}人),不满足最低人数要求({threshold}人),机器人将自动退出。").description("小群退群提示,支持变量 {memberCount}, {threshold}, {groupName}, {groupId}"),
|
|
2005
|
+
smallGroupNotifyAdmin: import_koishi.Schema.boolean().default(true).description("小群自动退群时通知管理员"),
|
|
2006
|
+
smallGroupQualifiedNotifyAdmin: import_koishi.Schema.boolean().default(true).description("未经审核被拉入人数达标的群时通知管理员"),
|
|
2007
|
+
smallGroupQualifiedMessage: import_koishi.Schema.string().role("textarea").default("机器人被未经审核地拉入群聊\n群名称:{groupName}\n群号:{groupId}\n当前人数:{memberCount}人(阈值:{threshold}人)\n请确认是否保留。").description("合格小群通知模板,支持变量 {groupName}, {groupId}, {memberCount}, {threshold}"),
|
|
2008
|
+
notifyAdminOnMute: import_koishi.Schema.boolean().default(false).description("机器人被禁言时通知管理员"),
|
|
2009
|
+
muteNotificationMessage: import_koishi.Schema.string().role("textarea").default("机器人在群聊中被禁言\n群名称:{groupName}\n群号:{groupId}\n操作者:{operatorId}\n禁言时长:{duration}秒").description("被禁言通知模板,支持变量 {groupId}, {groupName}, {operatorId}, {duration}"),
|
|
2010
|
+
muteAutoQuit: import_koishi.Schema.boolean().default(false).description("机器人被禁言达到阈值时自动退群并拉黑"),
|
|
2011
|
+
muteAutoQuitThreshold: import_koishi.Schema.natural().default(600).description("触发自动退群的禁言时长阈值(秒),被禁言时长 ≥ 此值即退群并拉黑"),
|
|
2012
|
+
muteQuitNotificationMessage: import_koishi.Schema.string().role("textarea").default("机器人被长时间禁言,已自动退群并拉黑\n群名称:{groupName}\n群号:{groupId}\n操作者:{operatorId}\n禁言时长:{duration}秒").description("被禁言自动退群时发给管理员的通知模板,支持变量 {groupId}, {groupName}, {operatorId}, {duration}")
|
|
2013
|
+
}).description("基础群组管理")
|
|
2014
|
+
}),
|
|
2015
|
+
import_koishi.Schema.object({
|
|
2016
|
+
invite: import_koishi.Schema.object({
|
|
2017
|
+
enabled: import_koishi.Schema.boolean().default(false).description("启用群聊邀请审核"),
|
|
2018
|
+
autoApprove: import_koishi.Schema.boolean().default(false).description("自动同意邀请"),
|
|
2019
|
+
notifyAdminOnApprove: import_koishi.Schema.boolean().default(true).description("自动同意时是否仍通知管理员"),
|
|
2020
|
+
inviteWaitMessage: import_koishi.Schema.string().role("textarea").default("已收到您的群聊邀请,正在等待管理员审核,请耐心等待。").description("发给邀请者的等待提示"),
|
|
2021
|
+
inviteApproveMessage: import_koishi.Schema.string().role("textarea").default("已自动通过您的群聊邀请,机器人正在加入群聊。").description("自动同意时发给邀请者的提示,支持变量 {groupName}, {groupId}, {userName}, {userId}"),
|
|
2022
|
+
inviteRequestMessage: import_koishi.Schema.string().role("textarea").default("收到新的群聊邀请请求:\n群名称:{groupName}\n群号:{groupId}\n邀请者:{userName} (QQ: {userId})\n\n请使用指令 gc.approve {groupId} 同意或 gc.reject {groupId} 拒绝。").description("发给管理员的请求消息模板,支持变量 {groupName}, {groupId}, {userName}, {userId}"),
|
|
2023
|
+
inviteApproveNotificationMessage: import_koishi.Schema.string().role("textarea").default("已自动通过群聊邀请\n群名称:{groupName}\n群号:{groupId}\n邀请者:{userName} (QQ: {userId})").description("自动同意时发给管理员的通知模板,支持变量 {groupName}, {groupId}, {userName}, {userId}"),
|
|
2024
|
+
inviteExpireDays: import_koishi.Schema.natural().default(3).description("邀请记录过期天数"),
|
|
2025
|
+
showDetailedLog: import_koishi.Schema.boolean().default(false).description("显示详细日志")
|
|
2026
|
+
}).description("群聊邀请审核")
|
|
2027
|
+
}),
|
|
2028
|
+
import_koishi.Schema.object({
|
|
2029
|
+
friend: import_koishi.Schema.object({
|
|
2030
|
+
enabled: import_koishi.Schema.boolean().default(false).description("启用好友申请管理"),
|
|
2031
|
+
autoApprove: import_koishi.Schema.boolean().default(false).description("自动通过好友申请(否则通知管理员手动处理)"),
|
|
2032
|
+
notifyAdminOnApprove: import_koishi.Schema.boolean().default(true).description("自动通过时是否仍通知管理员"),
|
|
2033
|
+
requestExpireDays: import_koishi.Schema.natural().default(7).description("待处理申请的过期天数"),
|
|
2034
|
+
requestMessage: import_koishi.Schema.string().role("textarea").default("收到新的好友申请\nQQ:{userId}\n昵称:{nickname}\n附言:{comment}\n\n使用 gc.friend-approve {userId} 同意或 gc.friend-reject {userId} 拒绝。").description("通知管理员的消息模板,支持变量 {userId}, {nickname}, {comment}"),
|
|
2035
|
+
approveNotificationMessage: import_koishi.Schema.string().role("textarea").default("已自动通过好友申请\nQQ:{userId}\n昵称:{nickname}\n附言:{comment}").description("自动通过时的通知模板,支持变量 {userId}, {nickname}, {comment}")
|
|
2036
|
+
}).description("好友申请管理")
|
|
2037
|
+
}),
|
|
2038
|
+
import_koishi.Schema.object({
|
|
2039
|
+
frequency: import_koishi.Schema.object({
|
|
2040
|
+
enabled: import_koishi.Schema.boolean().default(false).description("启用群聊频率控制(指令及 @ 对话均受限)"),
|
|
2041
|
+
limit: import_koishi.Schema.natural().default(5).description("群聊:时间窗口内允许的最大触发次数"),
|
|
2042
|
+
window: import_koishi.Schema.natural().default(60).description("群聊:时间窗口(秒)"),
|
|
2043
|
+
warnDelay: import_koishi.Schema.natural().default(30).description("群聊:警告后再次触发的时间阈值(秒),超出则进入屏蔽"),
|
|
2044
|
+
blockDur: import_koishi.Schema.natural().default(300).description("群聊:首次屏蔽的基础时长(秒)"),
|
|
2045
|
+
whitelist: import_koishi.Schema.array(String).default([]).description("群聊:不受频率限制的群号列表"),
|
|
2046
|
+
privateEnabled: import_koishi.Schema.boolean().default(false).description("启用私聊频率控制"),
|
|
2047
|
+
privateLimit: import_koishi.Schema.natural().default(10).description("私聊:时间窗口内允许的最大触发次数"),
|
|
2048
|
+
privateWindow: import_koishi.Schema.natural().default(60).description("私聊:时间窗口(秒)"),
|
|
2049
|
+
privateWarnDelay: import_koishi.Schema.natural().default(30).description("私聊:警告后再次触发的时间阈值(秒)"),
|
|
2050
|
+
privateBlockDur: import_koishi.Schema.natural().default(300).description("私聊:首次屏蔽的基础时长(秒)"),
|
|
2051
|
+
privateWhitelist: import_koishi.Schema.array(String).default([]).description("私聊:不受频率限制的用户ID列表"),
|
|
2052
|
+
blockExpBase: import_koishi.Schema.natural().min(1).default(2).description("屏蔽时长指数增长底数(时长 = blockDur × base^(次数-1)),设为 1 禁用"),
|
|
2053
|
+
blockExpWindow: import_koishi.Schema.natural().default(3600).description("指数增长重置窗口(秒),从最后一次屏蔽结束计算,超出则重置次数"),
|
|
2054
|
+
blockNotifyCooldown: import_koishi.Schema.natural().default(60).description("屏蔽期间提示消息的冷却时间(秒),避免刷屏"),
|
|
2055
|
+
warnMsg: import_koishi.Schema.string().default("发言频率过高,请慢一点~").description("首次超限警告消息"),
|
|
2056
|
+
blockMsg: import_koishi.Schema.string().default("发言频率过高,已被禁用 {duration} 秒。").description("进入屏蔽时的通知,支持变量 {duration}"),
|
|
2057
|
+
blockedMsg: import_koishi.Schema.string().default("暂时被禁用,还有 {time} 秒解禁。").description("屏蔽期间再次触发时的提示,支持变量 {time}")
|
|
2058
|
+
}).description("频率控制")
|
|
2059
|
+
}),
|
|
2060
|
+
import_koishi.Schema.object({
|
|
2061
|
+
botSwitch: import_koishi.Schema.object({
|
|
2062
|
+
enabled: import_koishi.Schema.boolean().default(true).description("启用群聊 bot 开关"),
|
|
2063
|
+
defaultState: import_koishi.Schema.boolean().default(true).description("默认开启状态"),
|
|
2064
|
+
disabledMessage: import_koishi.Schema.string().role("textarea").default("机器人当前在此群处于关闭状态,请使用 bot-on 开启。").description("关闭状态下被 @ 时的提示")
|
|
2065
|
+
}).description("机器人开关控制")
|
|
2066
|
+
})
|
|
2067
|
+
]);
|
|
2068
|
+
|
|
2069
|
+
// src/index.ts
|
|
2070
|
+
var name8 = "group-control";
|
|
2071
|
+
var inject = ["database"];
|
|
2072
|
+
function apply8(ctx, config) {
|
|
2073
|
+
ctx.on("dispose", () => {
|
|
2074
|
+
clearGuildAdminCache();
|
|
2075
|
+
});
|
|
2076
|
+
ctx.plugin(database_exports);
|
|
2077
|
+
ctx.plugin(basic_exports, config);
|
|
2078
|
+
ctx.plugin(invite_exports, config);
|
|
2079
|
+
ctx.plugin(frequency_exports, config);
|
|
2080
|
+
ctx.plugin(commands_exports, config);
|
|
2081
|
+
ctx.plugin(switch_exports, config);
|
|
2082
|
+
ctx.plugin(friend_exports, config);
|
|
2083
|
+
}
|
|
2084
|
+
__name(apply8, "apply");
|
|
2085
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2086
|
+
0 && (module.exports = {
|
|
2087
|
+
Config,
|
|
2088
|
+
apply,
|
|
2089
|
+
inject,
|
|
2090
|
+
name
|
|
2091
|
+
});
|