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 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, userId: string): Promise<boolean>;
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
- const batchSize = 100;
62
- for (let i = 0; i < data.length; i += batchSize) {
63
- await ctx.database.upsert('blacklist_users', data.slice(i, i + batchSize));
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', { userId: data.deletes });
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, userId) {
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(userId))
163
+ if (protectedSet.has(user_id))
159
164
  return false;
160
165
  // 2. 查库
161
- const entries = await ctx.database.get('blacklist_users', { userId, disabled: false });
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, userId)) {
168
- logger.info(`🛡️ 跳过黑名单管理员 ${userId}`);
172
+ if (await isUserAdmin(session, config, user_id)) {
173
+ logger.info(`🛡️ 跳过黑名单管理员 ${user_id}`);
169
174
  return false;
170
175
  }
171
- logger.info(`🎯 [群: ${session.guildId}] 发现黑名单用户: ${userId} - 原因: ${reason}`);
176
+ logger.info(`🎯 [群: ${session.guildId}] 发现黑名单用户: ${user_id} - 原因: ${reason}`);
172
177
  // 获取显示名
173
- let displayName = userId;
178
+ let displayName = user_id;
174
179
  try {
175
- const member = await session.bot.getGuildMember(session.guildId, userId);
176
- displayName = member.nick || member.user?.name || userId;
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}', 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, userId);
201
+ await session.bot.kickGuildMember(session.guildId, user_id);
197
202
  kicked = true;
198
- logger.info(`✅ [群: ${session.guildId}] 成功踢出: ${userId}`);
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, userId);
219
+ await session.bot.getGuildMember(session.guildId, user_id);
215
220
  // 如果还能获取到,说明没踢掉
216
- logger.warn(`⚠️ [群: ${session.guildId}] 踢出验证失败,用户仍在群内: ${userId}`);
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.userId));
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
- userId: 'string',
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: 'userId' });
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', { userId: session.userId, disabled: false });
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
@@ -16,7 +16,7 @@ export interface PluginConfig {
16
16
  autoRejectNotifyMessage: string;
17
17
  }
18
18
  export interface BlacklistEntry {
19
- userId: string;
19
+ user_id: string;
20
20
  reason: string;
21
21
  operator_id?: string;
22
22
  source_id?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-blacklist-online",
3
3
  "description": "自用插件",
4
- "version": "0.1.1",
4
+ "version": "0.1.3",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [