koishi-plugin-blacklist-online 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/core.d.ts +1 -1
- package/lib/core.js +25 -20
- package/lib/index.js +3 -3
- package/lib/types.d.ts +1 -1
- package/package.json +1 -1
package/lib/core.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare function isUserAdmin(session: Session, config: PluginConfig, user
|
|
|
5
5
|
export declare function syncBlacklist(ctx: Context, config: PluginConfig): Promise<void>;
|
|
6
6
|
export declare function queueRequest(ctx: Context, type: 'ADD' | 'REMOVE' | 'CANCEL', payload: any): Promise<any>;
|
|
7
7
|
export declare function processOfflineQueue(ctx: Context, config: PluginConfig): Promise<void>;
|
|
8
|
-
export declare function checkAndHandleUser(ctx: Context, config: PluginConfig, session: Session,
|
|
8
|
+
export declare function checkAndHandleUser(ctx: Context, config: PluginConfig, session: Session, user_id: string): Promise<boolean>;
|
|
9
9
|
export declare function parseUserId(input: string): string;
|
|
10
10
|
export declare function scanGuild(ctx: Context, config: PluginConfig, bot: any, // 传入具体的 bot 实例
|
|
11
11
|
guildId: string): Promise<{
|
package/lib/core.js
CHANGED
|
@@ -57,10 +57,15 @@ async function syncBlacklist(ctx, config) {
|
|
|
57
57
|
if (strategy === 'full_replace') {
|
|
58
58
|
logger.info(`📥 全量同步 -> ${newRevision} (条数: ${data.length})`);
|
|
59
59
|
await ctx.database.remove('blacklist_users', {});
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
if (data.length > 0) {
|
|
61
|
+
// 修复:使用 upsert 明确告知数据库如果主键冲突则更新
|
|
62
|
+
// 注意:Koishi 的 upsert 在处理大量数据时,建议分批以保证稳定性
|
|
63
|
+
const batchSize = 50;
|
|
64
|
+
for (let i = 0; i < data.length; i += batchSize) {
|
|
65
|
+
const batch = data.slice(i, i + batchSize);
|
|
66
|
+
// 这里的字段名必须与后端返回的 snake_case 保持完全一致
|
|
67
|
+
await ctx.database.upsert('blacklist_users', batch);
|
|
68
|
+
}
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
else if (strategy === 'incremental') {
|
|
@@ -68,7 +73,7 @@ async function syncBlacklist(ctx, config) {
|
|
|
68
73
|
if (data.upserts?.length)
|
|
69
74
|
await ctx.database.upsert('blacklist_users', data.upserts);
|
|
70
75
|
if (data.deletes?.length)
|
|
71
|
-
await ctx.database.remove('blacklist_users', {
|
|
76
|
+
await ctx.database.remove('blacklist_users', { user_id: data.deletes });
|
|
72
77
|
}
|
|
73
78
|
await ctx.database.upsert('blacklist_meta', [{ key: 'sync_revision', value: newRevision }]);
|
|
74
79
|
logger.info('✅ 同步完成');
|
|
@@ -146,7 +151,7 @@ async function processOfflineQueue(ctx, config) {
|
|
|
146
151
|
}
|
|
147
152
|
}
|
|
148
153
|
// --- 5. 用户检查核心 ---
|
|
149
|
-
async function checkAndHandleUser(ctx, config, session,
|
|
154
|
+
async function checkAndHandleUser(ctx, config, session, user_id) {
|
|
150
155
|
if (!session.guildId)
|
|
151
156
|
return false;
|
|
152
157
|
const guildSettings = await ctx.database.get('blacklist_guild_settings', { guildId: session.guildId });
|
|
@@ -155,25 +160,25 @@ async function checkAndHandleUser(ctx, config, session, userId) {
|
|
|
155
160
|
return false;
|
|
156
161
|
// 1. 本地白名单 (最高优先级)
|
|
157
162
|
const protectedSet = new Set(config.protectedUsers || []);
|
|
158
|
-
if (protectedSet.has(
|
|
163
|
+
if (protectedSet.has(user_id))
|
|
159
164
|
return false;
|
|
160
165
|
// 2. 查库
|
|
161
|
-
const entries = await ctx.database.get('blacklist_users', {
|
|
166
|
+
const entries = await ctx.database.get('blacklist_users', { user_id, disabled: false });
|
|
162
167
|
if (entries.length === 0)
|
|
163
168
|
return false;
|
|
164
169
|
const entry = entries[0];
|
|
165
170
|
const reason = entry.reason || 'QQ号黑名单';
|
|
166
171
|
// 3. 查管理员
|
|
167
|
-
if (await isUserAdmin(session, config,
|
|
168
|
-
logger.info(`🛡️ 跳过黑名单管理员 ${
|
|
172
|
+
if (await isUserAdmin(session, config, user_id)) {
|
|
173
|
+
logger.info(`🛡️ 跳过黑名单管理员 ${user_id}`);
|
|
169
174
|
return false;
|
|
170
175
|
}
|
|
171
|
-
logger.info(`🎯 [群: ${session.guildId}] 发现黑名单用户: ${
|
|
176
|
+
logger.info(`🎯 [群: ${session.guildId}] 发现黑名单用户: ${user_id} - 原因: ${reason}`);
|
|
172
177
|
// 获取显示名
|
|
173
|
-
let displayName =
|
|
178
|
+
let displayName = user_id;
|
|
174
179
|
try {
|
|
175
|
-
const member = await session.bot.getGuildMember(session.guildId,
|
|
176
|
-
displayName = member.nick || member.user?.name ||
|
|
180
|
+
const member = await session.bot.getGuildMember(session.guildId, user_id);
|
|
181
|
+
displayName = member.nick || member.user?.name || user_id;
|
|
177
182
|
}
|
|
178
183
|
catch {
|
|
179
184
|
}
|
|
@@ -182,7 +187,7 @@ async function checkAndHandleUser(ctx, config, session, userId) {
|
|
|
182
187
|
const tpl = mode === 'notify' ? config.adminNotifyMessage : config.kickNotifyMessage;
|
|
183
188
|
const msg = tpl
|
|
184
189
|
.replace('{user}', displayName)
|
|
185
|
-
.replace('{userId}',
|
|
190
|
+
.replace('{userId}', user_id)
|
|
186
191
|
.replace('{reason}', reason)
|
|
187
192
|
.replace('{guild}', session.guildId);
|
|
188
193
|
// 引用回复
|
|
@@ -193,9 +198,9 @@ async function checkAndHandleUser(ctx, config, session, userId) {
|
|
|
193
198
|
if (mode === 'kick' || mode === 'both') {
|
|
194
199
|
for (let i = 0; i < config.retryAttempts; i++) {
|
|
195
200
|
try {
|
|
196
|
-
await session.bot.kickGuildMember(session.guildId,
|
|
201
|
+
await session.bot.kickGuildMember(session.guildId, user_id);
|
|
197
202
|
kicked = true;
|
|
198
|
-
logger.info(`✅ [群: ${session.guildId}] 成功踢出: ${
|
|
203
|
+
logger.info(`✅ [群: ${session.guildId}] 成功踢出: ${user_id}`);
|
|
199
204
|
break;
|
|
200
205
|
}
|
|
201
206
|
catch (e) {
|
|
@@ -211,9 +216,9 @@ async function checkAndHandleUser(ctx, config, session, userId) {
|
|
|
211
216
|
if (kicked && config.verifyKickResult) {
|
|
212
217
|
await (0, exports.sleep)(2000);
|
|
213
218
|
try {
|
|
214
|
-
await session.bot.getGuildMember(session.guildId,
|
|
219
|
+
await session.bot.getGuildMember(session.guildId, user_id);
|
|
215
220
|
// 如果还能获取到,说明没踢掉
|
|
216
|
-
logger.warn(`⚠️ [群: ${session.guildId}] 踢出验证失败,用户仍在群内: ${
|
|
221
|
+
logger.warn(`⚠️ [群: ${session.guildId}] 踢出验证失败,用户仍在群内: ${user_id}`);
|
|
217
222
|
}
|
|
218
223
|
catch {
|
|
219
224
|
// 获取不到说明踢出成功
|
|
@@ -240,7 +245,7 @@ guildId) {
|
|
|
240
245
|
const members = await bot.getGuildMemberList(guildId);
|
|
241
246
|
// 2. 获取本地黑名单缓存
|
|
242
247
|
const blacklist = await ctx.database.get('blacklist_users', { disabled: false });
|
|
243
|
-
const blacklistSet = new Set(blacklist.map(b => b.
|
|
248
|
+
const blacklistSet = new Set(blacklist.map(b => b.user_id));
|
|
244
249
|
// 3. 筛选目标 (内存操作,极快)
|
|
245
250
|
const targets = members.data.filter((m) => {
|
|
246
251
|
if (!m.user?.id)
|
package/lib/index.js
CHANGED
|
@@ -50,13 +50,13 @@ function apply(ctx, config) {
|
|
|
50
50
|
const logger = ctx.logger('blacklist-online');
|
|
51
51
|
// 1. 扩展数据库
|
|
52
52
|
ctx.model.extend('blacklist_users', {
|
|
53
|
-
|
|
53
|
+
user_id: 'string',
|
|
54
54
|
reason: 'string',
|
|
55
55
|
disabled: { type: 'boolean', initial: false },
|
|
56
56
|
operator_id: 'string',
|
|
57
57
|
source_id: 'string',
|
|
58
58
|
updated_at: 'timestamp'
|
|
59
|
-
}, { primary: '
|
|
59
|
+
}, { primary: 'user_id' });
|
|
60
60
|
ctx.model.extend('blacklist_request_queue', {
|
|
61
61
|
id: 'string', type: 'string', payload: 'json', createdAt: 'timestamp', retryCount: 'unsigned'
|
|
62
62
|
}, { primary: 'id' });
|
|
@@ -95,7 +95,7 @@ function apply(ctx, config) {
|
|
|
95
95
|
if (config.protectedUsers.includes(session.userId))
|
|
96
96
|
return;
|
|
97
97
|
// 查库
|
|
98
|
-
const entries = await ctx.database.get('blacklist_users', {
|
|
98
|
+
const entries = await ctx.database.get('blacklist_users', { user_id: session.userId, disabled: false });
|
|
99
99
|
if (entries.length > 0) {
|
|
100
100
|
try {
|
|
101
101
|
await session.bot.handleGuildRequest(session.messageId, false, config.rejectionMessage);
|
package/lib/types.d.ts
CHANGED