koishi-plugin-kkk 3.7.2 → 3.9.0

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.
@@ -1,16 +1,21 @@
1
- import fs from 'node:fs'
2
- import path from 'node:path'
3
-
4
1
  import { DynamicType } from '@ikenxuan/amagi'
2
+ import { Context } from 'koishi'
5
3
  import { logger } from 'node-karin'
6
- import { karinPathBase } from 'node-karin/root'
7
- import sqlite3, { sqlite3 as sqlite3Types } from 'node-karin/sqlite3'
8
4
 
9
- import { Root } from '@/module/utils'
10
5
  import { Config } from '@/module/utils/Config'
11
6
  import { BilibiliPushItem } from '@/platform/bilibili/push'
12
7
  import { bilibiliPushItem } from '@/types/config/pushlist'
13
8
 
9
+ import {
10
+ TABLE,
11
+ type BilibiliFilterTagRow,
12
+ type BilibiliFilterWordRow,
13
+ type BilibiliSubscriptionRow,
14
+ type BilibiliUserRow,
15
+ type DynamicCacheRow,
16
+ type GroupRow
17
+ } from './model'
18
+
14
19
  /**
15
20
  * 机器人接口 - 存储机器人信息
16
21
  */
@@ -119,13 +124,19 @@ interface FilterTag {
119
124
  updatedAt: string
120
125
  }
121
126
 
122
- /** 数据库操作类 */
127
+ /** 当前时间戳(ISO 字符串,与旧版 sqlite 里的口径一致) */
128
+ const now = () => new Date().toISOString()
129
+
130
+ /** 数据库操作类(基于 Koishi 原生数据库服务) */
123
131
  export class BilibiliDBBase {
124
- private db!: sqlite3Types['Database']
125
- private dbPath: string
132
+ private ctx: Context
126
133
 
127
- constructor() {
128
- this.dbPath = path.join(`${karinPathBase}/${Root.pluginName}/data`, 'bilibili.db')
134
+ constructor(ctx: Context) {
135
+ this.ctx = ctx
136
+ }
137
+
138
+ private get db() {
139
+ return this.ctx.database
129
140
  }
130
141
 
131
142
  /**
@@ -134,16 +145,7 @@ export class BilibiliDBBase {
134
145
  async init(): Promise<BilibiliDBBase> {
135
146
  try {
136
147
  logger.debug(logger.green('--------------------------[BilibiliDB] 开始初始化数据库--------------------------'))
137
- logger.debug('[BilibiliDB] 正在连接数据库...')
138
-
139
- // 创建数据库连接
140
- await fs.promises.mkdir(path.dirname(this.dbPath), { recursive: true })
141
- this.db = new sqlite3.Database(this.dbPath)
142
-
143
- // 创建表结构
144
- await this.createTables()
145
-
146
- logger.debug('[BilibiliDB] 数据库模型同步成功')
148
+ logger.debug('[BilibiliDB] 使用 Koishi 原生数据库服务,表已由 ctx.model.extend 注册')
147
149
 
148
150
  logger.debug('[BilibiliDB] 正在同步配置订阅...')
149
151
  logger.debug('[BilibiliDB] 配置项数量:', Config.pushlist.bilibili?.length || 0)
@@ -158,149 +160,16 @@ export class BilibiliDBBase {
158
160
  return this
159
161
  }
160
162
 
161
- /**
162
- * 创建数据库表结构
163
- */
164
- private async createTables(): Promise<void> {
165
- const queries = [
166
- // 创建机器人表
167
- `CREATE TABLE IF NOT EXISTS Bots (
168
- id TEXT PRIMARY KEY,
169
- createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
170
- updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
171
- )`,
172
-
173
- // 创建群组表
174
- `CREATE TABLE IF NOT EXISTS Groups (
175
- id TEXT NOT NULL,
176
- botId TEXT NOT NULL,
177
- createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
178
- updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
179
- PRIMARY KEY (id, botId),
180
- FOREIGN KEY (botId) REFERENCES Bots(id)
181
- )`,
182
-
183
- // 创建B站用户表
184
- `CREATE TABLE IF NOT EXISTS BilibiliUsers (
185
- host_mid INTEGER PRIMARY KEY,
186
- remark TEXT,
187
- filterMode TEXT DEFAULT 'blacklist',
188
- createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
189
- updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
190
- )`,
191
-
192
- // 创建群组用户订阅关系表
193
- `CREATE TABLE IF NOT EXISTS GroupUserSubscriptions (
194
- groupId TEXT,
195
- host_mid INTEGER,
196
- createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
197
- updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
198
- PRIMARY KEY (groupId, host_mid),
199
- FOREIGN KEY (groupId) REFERENCES Groups(id),
200
- FOREIGN KEY (host_mid) REFERENCES BilibiliUsers(host_mid)
201
- )`,
202
-
203
- // 创建动态缓存表
204
- `CREATE TABLE IF NOT EXISTS DynamicCaches (
205
- id INTEGER PRIMARY KEY AUTOINCREMENT,
206
- dynamic_id TEXT NOT NULL,
207
- host_mid INTEGER NOT NULL,
208
- groupId TEXT NOT NULL,
209
- dynamic_type TEXT,
210
- createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
211
- updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
212
- FOREIGN KEY (host_mid) REFERENCES BilibiliUsers(host_mid),
213
- FOREIGN KEY (groupId) REFERENCES Groups(id),
214
- UNIQUE(dynamic_id, host_mid, groupId)
215
- )`,
216
-
217
- // 创建过滤词表
218
- `CREATE TABLE IF NOT EXISTS FilterWords (
219
- id INTEGER PRIMARY KEY AUTOINCREMENT,
220
- host_mid INTEGER NOT NULL,
221
- word TEXT NOT NULL,
222
- createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
223
- updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
224
- FOREIGN KEY (host_mid) REFERENCES BilibiliUsers(host_mid),
225
- UNIQUE(host_mid, word)
226
- )`,
227
-
228
- // 创建过滤标签表
229
- `CREATE TABLE IF NOT EXISTS FilterTags (
230
- id INTEGER PRIMARY KEY AUTOINCREMENT,
231
- host_mid INTEGER NOT NULL,
232
- tag TEXT NOT NULL,
233
- createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
234
- updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
235
- FOREIGN KEY (host_mid) REFERENCES BilibiliUsers(host_mid),
236
- UNIQUE(host_mid, tag)
237
- )`
238
- ]
239
-
240
- for (const query of queries) {
241
- await this.runQuery(query)
242
- }
243
- }
244
-
245
- /**
246
- * 执行SQL查询
247
- */
248
- private runQuery(sql: string, params: any[] = []): Promise<any> {
249
- return new Promise((resolve, reject) => {
250
- this.db.run(sql, params, function (err) {
251
- if (err) {
252
- reject(err)
253
- } else {
254
- resolve({ lastID: this.lastID, changes: this.changes })
255
- }
256
- })
257
- })
258
- }
259
-
260
- /**
261
- * 执行SQL查询并获取单个结果
262
- */
263
- private getQuery<T>(sql: string, params: any[] = []): Promise<T | undefined> {
264
- return new Promise((resolve, reject) => {
265
- this.db.get(sql, params, (err, row) => {
266
- if (err) {
267
- reject(err)
268
- } else {
269
- resolve(row as T)
270
- }
271
- })
272
- })
273
- }
274
-
275
- /**
276
- * 执行SQL查询并获取所有结果
277
- */
278
- private allQuery<T>(sql: string, params: any[] = []): Promise<T[]> {
279
- return new Promise((resolve, reject) => {
280
- this.db.all(sql, params, (err, rows) => {
281
- if (err) {
282
- reject(err)
283
- } else {
284
- resolve(rows as T[])
285
- }
286
- })
287
- })
288
- }
289
-
290
163
  /**
291
164
  * 获取或创建机器人记录
292
165
  * @param botId 机器人ID
293
166
  */
294
167
  async getOrCreateBot(botId: string): Promise<Bot> {
295
- let bot = await this.getQuery<Bot>('SELECT * FROM Bots WHERE id = ?', [botId])
296
-
297
- if (!bot) {
298
- const now = new Date().toISOString()
299
- await this.runQuery('INSERT INTO Bots (id, createdAt, updatedAt) VALUES (?, ?, ?)', [botId, now, now])
300
- bot = { id: botId, createdAt: now, updatedAt: now }
301
- }
168
+ const [bot] = await this.db.get(TABLE.bilibiliBot, { id: botId })
169
+ if (bot) return bot
302
170
 
303
- return bot
171
+ const time = now()
172
+ return await this.db.create(TABLE.bilibiliBot, { id: botId, createdAt: time, updatedAt: time })
304
173
  }
305
174
 
306
175
  /**
@@ -311,15 +180,16 @@ export class BilibiliDBBase {
311
180
  async getOrCreateGroup(groupId: string, botId: string): Promise<Group> {
312
181
  await this.getOrCreateBot(botId)
313
182
 
314
- let group = await this.getQuery<Group>('SELECT * FROM Groups WHERE id = ? AND botId = ?', [groupId, botId])
183
+ const [group] = await this.db.get(TABLE.bilibiliGroup, { id: groupId, botId })
184
+ if (group) return group
315
185
 
316
- if (!group) {
317
- const now = new Date().toISOString()
318
- await this.runQuery('INSERT INTO Groups (id, botId, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [groupId, botId, now, now])
319
- group = { id: groupId, botId, createdAt: now, updatedAt: now }
320
- }
321
-
322
- return group
186
+ const time = now()
187
+ return await this.db.create(TABLE.bilibiliGroup, {
188
+ id: groupId,
189
+ botId,
190
+ createdAt: time,
191
+ updatedAt: time
192
+ })
323
193
  }
324
194
 
325
195
  /**
@@ -328,8 +198,8 @@ export class BilibiliDBBase {
328
198
  * @returns 返回用户信息,如果不存在则返回null
329
199
  */
330
200
  async getBilibiliUser(host_mid: number): Promise<BilibiliUser | null> {
331
- const user = await this.getQuery<BilibiliUser>('SELECT * FROM BilibiliUsers WHERE host_mid = ?', [host_mid])
332
- return user || null
201
+ const [user] = await this.db.get(TABLE.bilibiliUser, { host_mid })
202
+ return (user as BilibiliUser | undefined) ?? null
333
203
  }
334
204
 
335
205
  /**
@@ -338,35 +208,28 @@ export class BilibiliDBBase {
338
208
  * @param remark UP主昵称
339
209
  */
340
210
  async getOrCreateBilibiliUser(host_mid: number, remark: string = ''): Promise<BilibiliUser> {
341
- let user = await this.getQuery<BilibiliUser>('SELECT * FROM BilibiliUsers WHERE host_mid = ?', [host_mid])
211
+ const [row] = await this.db.get(TABLE.bilibiliUser, { host_mid })
342
212
 
343
- if (!user) {
344
- const now = new Date().toISOString()
345
- await this.runQuery('INSERT INTO BilibiliUsers (host_mid, remark, filterMode, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?)', [
346
- host_mid,
347
- remark,
348
- 'blacklist',
349
- now,
350
- now
351
- ])
352
- user = {
213
+ if (!row) {
214
+ const time = now()
215
+ const created = await this.db.create(TABLE.bilibiliUser, {
353
216
  host_mid,
354
217
  remark,
355
218
  filterMode: 'blacklist',
356
- createdAt: now,
357
- updatedAt: now
358
- }
359
- } else {
360
- // 如果提供了新的remark,更新用户记录
361
- if (remark && user.remark !== remark) {
362
- const now = new Date().toISOString()
363
- await this.runQuery('UPDATE BilibiliUsers SET remark = ?, updatedAt = ? WHERE host_mid = ?', [remark, now, host_mid])
364
- user.remark = remark
365
- user.updatedAt = now
366
- }
219
+ createdAt: time,
220
+ updatedAt: time
221
+ })
222
+ return created as BilibiliUser
367
223
  }
368
224
 
369
- return user
225
+ // 如果提供了新的remark,更新用户记录
226
+ if (remark && row.remark !== remark) {
227
+ const time = now()
228
+ await this.db.set(TABLE.bilibiliUser, { host_mid }, { remark, updatedAt: time })
229
+ return { ...row, remark, updatedAt: time } as BilibiliUser
230
+ }
231
+
232
+ return row as BilibiliUser
370
233
  }
371
234
 
372
235
  /**
@@ -380,23 +243,16 @@ export class BilibiliDBBase {
380
243
  await this.getOrCreateGroup(groupId, botId)
381
244
  await this.getOrCreateBilibiliUser(host_mid, remark)
382
245
 
383
- let subscription = await this.getQuery<GroupUserSubscription>(
384
- 'SELECT * FROM GroupUserSubscriptions WHERE groupId = ? AND host_mid = ?',
385
- [groupId, host_mid]
386
- )
387
-
388
- if (!subscription) {
389
- const now = new Date().toISOString()
390
- await this.runQuery('INSERT INTO GroupUserSubscriptions (groupId, host_mid, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [
391
- groupId,
392
- host_mid,
393
- now,
394
- now
395
- ])
396
- subscription = { groupId, host_mid, createdAt: now, updatedAt: now }
397
- }
246
+ const [subscription] = await this.db.get(TABLE.bilibiliSubscription, { groupId, host_mid })
247
+ if (subscription) return subscription
398
248
 
399
- return subscription
249
+ const time = now()
250
+ return await this.db.create(TABLE.bilibiliSubscription, {
251
+ groupId,
252
+ host_mid,
253
+ createdAt: time,
254
+ updatedAt: time
255
+ })
400
256
  }
401
257
 
402
258
  /**
@@ -405,35 +261,26 @@ export class BilibiliDBBase {
405
261
  * @param host_mid B站用户UID
406
262
  */
407
263
  async unsubscribeBilibiliUser(groupId: string, host_mid: number): Promise<boolean> {
408
- const result = await this.runQuery('DELETE FROM GroupUserSubscriptions WHERE groupId = ? AND host_mid = ?', [groupId, host_mid])
264
+ const result = await this.db.remove(TABLE.bilibiliSubscription, { groupId, host_mid })
409
265
 
410
266
  // 清除相关的动态缓存
411
- await this.runQuery('DELETE FROM DynamicCaches WHERE groupId = ? AND host_mid = ?', [groupId, host_mid])
267
+ await this.db.remove(TABLE.bilibiliDynamicCache, { groupId, host_mid })
412
268
 
413
269
  // 检查该用户是否还有其他群组订阅
414
- const remainingSubscriptions = await this.getQuery<{ count: number }>(
415
- 'SELECT COUNT(*) as count FROM GroupUserSubscriptions WHERE host_mid = ?',
416
- [host_mid]
417
- )
270
+ const remaining = await this.db.get(TABLE.bilibiliSubscription, { host_mid }, { limit: 1 })
418
271
 
419
272
  // 如果没有任何群组订阅该用户,删除用户记录及相关数据
420
- if (remainingSubscriptions && remainingSubscriptions.count === 0) {
273
+ if (remaining.length === 0) {
421
274
  logger.info(`[BilibiliDB] 用户 ${host_mid} 已无任何群组订阅,清理相关数据`)
422
275
 
423
- // 删除用户记录
424
- await this.runQuery('DELETE FROM BilibiliUsers WHERE host_mid = ?', [host_mid])
425
-
426
- // 删除过滤词
427
- await this.runQuery('DELETE FROM FilterWords WHERE host_mid = ?', [host_mid])
428
-
429
- // 删除过滤标签
430
- await this.runQuery('DELETE FROM FilterTags WHERE host_mid = ?', [host_mid])
431
-
276
+ await this.db.remove(TABLE.bilibiliUser, { host_mid })
277
+ await this.db.remove(TABLE.bilibiliFilterWord, { host_mid })
278
+ await this.db.remove(TABLE.bilibiliFilterTag, { host_mid })
432
279
  // 删除所有相关的动态缓存(所有群组的)
433
- await this.runQuery('DELETE FROM DynamicCaches WHERE host_mid = ?', [host_mid])
280
+ await this.db.remove(TABLE.bilibiliDynamicCache, { host_mid })
434
281
  }
435
282
 
436
- return result.changes > 0
283
+ return (result.removed ?? 0) > 0
437
284
  }
438
285
 
439
286
  /**
@@ -444,30 +291,18 @@ export class BilibiliDBBase {
444
291
  * @param dynamic_type 动态类型
445
292
  */
446
293
  async addDynamicCache(dynamic_id: string, host_mid: number, groupId: string, dynamic_type: string): Promise<DynamicCache> {
447
- let cache = await this.getQuery<DynamicCache>('SELECT * FROM DynamicCaches WHERE dynamic_id = ? AND host_mid = ? AND groupId = ?', [
294
+ const [cache] = await this.db.get(TABLE.bilibiliDynamicCache, { dynamic_id, host_mid, groupId })
295
+ if (cache) return cache
296
+
297
+ const time = now()
298
+ return await this.db.create(TABLE.bilibiliDynamicCache, {
448
299
  dynamic_id,
449
300
  host_mid,
450
- groupId
451
- ])
452
-
453
- if (!cache) {
454
- const now = new Date().toISOString()
455
- const result = await this.runQuery(
456
- 'INSERT INTO DynamicCaches (dynamic_id, host_mid, groupId, dynamic_type, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)',
457
- [dynamic_id, host_mid, groupId, dynamic_type, now, now]
458
- )
459
- cache = {
460
- id: result.lastID,
461
- dynamic_id,
462
- host_mid,
463
- groupId,
464
- dynamic_type,
465
- createdAt: now,
466
- updatedAt: now
467
- }
468
- }
469
-
470
- return cache
301
+ groupId,
302
+ dynamic_type,
303
+ createdAt: time,
304
+ updatedAt: time
305
+ })
471
306
  }
472
307
 
473
308
  /**
@@ -477,12 +312,8 @@ export class BilibiliDBBase {
477
312
  * @param groupId 群组ID
478
313
  */
479
314
  async isDynamicPushed(dynamic_id: string, host_mid: number, groupId: string): Promise<boolean> {
480
- const result = await this.getQuery<{ count: number }>(
481
- 'SELECT COUNT(*) as count FROM DynamicCaches WHERE dynamic_id = ? AND host_mid = ? AND groupId = ?',
482
- [dynamic_id, host_mid, groupId]
483
- )
484
-
485
- return (result?.count || 0) > 0
315
+ const rows = await this.db.get(TABLE.bilibiliDynamicCache, { dynamic_id, host_mid, groupId }, { limit: 1 })
316
+ return rows.length > 0
486
317
  }
487
318
 
488
319
  /**
@@ -490,7 +321,7 @@ export class BilibiliDBBase {
490
321
  * @param botId 机器人ID
491
322
  */
492
323
  async getBotGroups(botId: string): Promise<Group[]> {
493
- return await this.allQuery<Group>('SELECT * FROM Groups WHERE botId = ?', [botId])
324
+ return await this.db.get(TABLE.bilibiliGroup, { botId })
494
325
  }
495
326
 
496
327
  /**
@@ -501,8 +332,10 @@ export class BilibiliDBBase {
501
332
  */
502
333
  async updateGroupBotId(groupId: string, oldBotId: string, newBotId: string): Promise<void> {
503
334
  await this.getOrCreateBot(newBotId)
504
- const now = new Date().toISOString()
505
- await this.runQuery('UPDATE Groups SET botId = ?, updatedAt = ? WHERE id = ? AND botId = ?', [newBotId, now, groupId, oldBotId])
335
+ await this.db.set(TABLE.bilibiliGroup, { id: groupId, botId: oldBotId }, {
336
+ botId: newBotId,
337
+ updatedAt: now()
338
+ })
506
339
  }
507
340
 
508
341
  /**
@@ -510,30 +343,29 @@ export class BilibiliDBBase {
510
343
  * @param groupId 群组ID
511
344
  */
512
345
  async getGroupSubscriptions(groupId: string): Promise<(GroupUserSubscription & { bilibiliUser: BilibiliUser })[]> {
513
- const subscriptions = await this.allQuery<any>(
514
- `SELECT
515
- gus.groupId, gus.host_mid, gus.createdAt, gus.updatedAt,
516
- bu.remark, bu.filterMode,
517
- bu.createdAt as bu_createdAt, bu.updatedAt as bu_updatedAt
518
- FROM GroupUserSubscriptions gus
519
- LEFT JOIN BilibiliUsers bu ON gus.host_mid = bu.host_mid
520
- WHERE gus.groupId = ?`,
521
- [groupId]
522
- )
523
-
524
- return subscriptions.map((sub) => ({
525
- groupId: sub.groupId,
526
- host_mid: sub.host_mid,
527
- createdAt: sub.createdAt,
528
- updatedAt: sub.updatedAt,
529
- bilibiliUser: {
346
+ const subscriptions: BilibiliSubscriptionRow[] = await this.db.get(TABLE.bilibiliSubscription, { groupId })
347
+ if (!subscriptions.length) return []
348
+
349
+ const hosts = [...new Set(subscriptions.map((item) => item.host_mid))]
350
+ const users: BilibiliUserRow[] = await this.db.get(TABLE.bilibiliUser, { host_mid: { $in: hosts } })
351
+ const userMap = new Map(users.map((user) => [user.host_mid, user]))
352
+
353
+ return subscriptions.map((sub) => {
354
+ const user = userMap.get(sub.host_mid)
355
+ return {
356
+ groupId: sub.groupId,
530
357
  host_mid: sub.host_mid,
531
- remark: sub.remark,
532
- filterMode: sub.filterMode as 'blacklist' | 'whitelist',
533
- createdAt: sub.bu_createdAt,
534
- updatedAt: sub.bu_updatedAt
358
+ createdAt: sub.createdAt,
359
+ updatedAt: sub.updatedAt,
360
+ bilibiliUser: {
361
+ host_mid: sub.host_mid,
362
+ remark: user?.remark ?? '',
363
+ filterMode: (user?.filterMode ?? 'blacklist') as 'blacklist' | 'whitelist',
364
+ createdAt: user?.createdAt ?? sub.createdAt,
365
+ updatedAt: user?.updatedAt ?? sub.updatedAt
366
+ }
535
367
  }
536
- }))
368
+ })
537
369
  }
538
370
 
539
371
  /**
@@ -541,12 +373,11 @@ export class BilibiliDBBase {
541
373
  * @param host_mid B站用户UID
542
374
  */
543
375
  async getUserSubscribedGroups(host_mid: number): Promise<Group[]> {
544
- return await this.allQuery<Group>(
545
- `SELECT g.* FROM Groups g
546
- INNER JOIN GroupUserSubscriptions gus ON g.id = gus.groupId
547
- WHERE gus.host_mid = ?`,
548
- [host_mid]
549
- )
376
+ const subscriptions: BilibiliSubscriptionRow[] = await this.db.get(TABLE.bilibiliSubscription, { host_mid })
377
+ if (!subscriptions.length) return []
378
+
379
+ const groupIds = [...new Set(subscriptions.map((item) => item.groupId))]
380
+ return (await this.db.get(TABLE.bilibiliGroup, { id: { $in: groupIds } })) as GroupRow[]
550
381
  }
551
382
 
552
383
  /**
@@ -555,17 +386,9 @@ export class BilibiliDBBase {
555
386
  * @param host_mid 可选的B站用户UID过滤
556
387
  */
557
388
  async getGroupDynamicCache(groupId: string, host_mid?: number): Promise<DynamicCache[]> {
558
- let sql = 'SELECT * FROM DynamicCaches WHERE groupId = ?'
559
- const params: any[] = [groupId]
560
-
561
- if (host_mid) {
562
- sql += ' AND host_mid = ?'
563
- params.push(host_mid)
564
- }
565
-
566
- sql += ' ORDER BY createdAt DESC'
567
-
568
- return await this.allQuery<DynamicCache>(sql, params)
389
+ return await this.db.get(TABLE.bilibiliDynamicCache, host_mid ? { groupId, host_mid } : { groupId }, {
390
+ sort: { createdAt: 'desc' }
391
+ })
569
392
  }
570
393
 
571
394
  /**
@@ -574,12 +397,8 @@ export class BilibiliDBBase {
574
397
  * @param groupId 群组ID
575
398
  */
576
399
  async isSubscribed(host_mid: number, groupId: string): Promise<boolean> {
577
- const result = await this.getQuery<{ count: number }>(
578
- 'SELECT COUNT(*) as count FROM GroupUserSubscriptions WHERE host_mid = ? AND groupId = ?',
579
- [host_mid, groupId]
580
- )
581
-
582
- return (result?.count || 0) > 0
400
+ const rows = await this.db.get(TABLE.bilibiliSubscription, { host_mid, groupId }, { limit: 1 })
401
+ return rows.length > 0
583
402
  }
584
403
 
585
404
  /**
@@ -587,18 +406,18 @@ export class BilibiliDBBase {
587
406
  * @param configItems 配置文件中的订阅项
588
407
  */
589
408
  async syncConfigSubscriptions(configItems: bilibiliPushItem[]): Promise<void> {
409
+ const items = configItems ?? []
410
+
590
411
  // 1. 收集配置文件中的所有订阅关系
591
412
  const configSubscriptions: Map<string, Set<number>> = new Map()
592
413
 
593
- // 初始化每个群组的订阅UP集合
594
- for (const item of configItems) {
414
+ for (const item of items) {
595
415
  const host_mid = item.host_mid
596
416
  const remark = item.remark ?? ''
597
417
 
598
418
  // 创建或更新B站用户记录
599
419
  await this.getOrCreateBilibiliUser(host_mid, remark)
600
420
 
601
- // 处理该UP主的所有群组订阅
602
421
  for (const groupWithBot of item.group_id) {
603
422
  const [groupId, botId] = groupWithBot.split(':')
604
423
  if (!groupId || !botId) continue
@@ -606,65 +425,42 @@ export class BilibiliDBBase {
606
425
  // 确保群组存在
607
426
  await this.getOrCreateGroup(groupId, botId)
608
427
 
609
- // 记录配置文件中的订阅关系
610
- if (!configSubscriptions.has(groupId)) {
611
- configSubscriptions.set(groupId, new Set())
612
- }
428
+ if (!configSubscriptions.has(groupId)) configSubscriptions.set(groupId, new Set())
613
429
  configSubscriptions.get(groupId)?.add(host_mid)
614
430
 
615
- // 检查是否已订阅
616
- const isSubscribed = await this.isSubscribed(host_mid, groupId)
617
-
618
- // 如果未订阅,创建订阅关系
619
- if (!isSubscribed) {
431
+ if (!await this.isSubscribed(host_mid, groupId)) {
620
432
  await this.subscribeBilibiliUser(groupId, botId, host_mid, remark)
621
433
  }
622
434
  }
623
435
  }
624
436
 
625
- // 2. 获取数据库中的所有订阅关系,并与配置文件比较,删除不在配置文件中的订阅
626
- // 获取所有群组
627
- const allGroups = await this.allQuery<Group>('SELECT * FROM Groups')
437
+ // 2. 删除数据库里存在、但配置文件里没有的订阅
438
+ const allGroups: GroupRow[] = await this.db.get(TABLE.bilibiliGroup, {})
628
439
 
629
440
  for (const group of allGroups) {
630
441
  const groupId = group.id
631
- const configUps = configSubscriptions.get(groupId) ?? new Set()
632
-
633
- // 获取该群组在数据库中的所有订阅
634
- const dbSubscriptions = await this.getGroupSubscriptions(groupId)
442
+ const configUps = configSubscriptions.get(groupId) ?? new Set<number>()
443
+ const dbSubscriptions: BilibiliSubscriptionRow[] = await this.db.get(TABLE.bilibiliSubscription, { groupId })
635
444
 
636
- // 找出需要删除的订阅(在数据库中存在但配置文件中不存在)
637
445
  for (const subscription of dbSubscriptions) {
638
- const host_mid = subscription.host_mid
639
-
640
- if (!configUps.has(host_mid)) {
641
- // 删除订阅关系
642
- await this.unsubscribeBilibiliUser(groupId, host_mid)
643
- logger.mark(`已删除群组 ${groupId} 对UP主 ${host_mid} 的订阅`)
644
- }
446
+ if (configUps.has(subscription.host_mid)) continue
447
+ await this.unsubscribeBilibiliUser(groupId, subscription.host_mid)
448
+ logger.mark(`已删除群组 ${groupId} 对UP主 ${subscription.host_mid} 的订阅`)
645
449
  }
646
450
  }
647
451
 
648
452
  // 3. 清理不再被任何群组订阅的UP主记录及其过滤词和过滤标签
649
- // 获取所有B站用户
650
- const allUsers = await this.allQuery<BilibiliUser>('SELECT * FROM BilibiliUsers')
453
+ const allUsers: BilibiliUserRow[] = await this.db.get(TABLE.bilibiliUser, {})
651
454
 
652
455
  for (const user of allUsers) {
653
- const host_mid = user.host_mid
654
-
655
- // 检查该UP主是否还有群组订阅
656
- const subscribedGroups = await this.getUserSubscribedGroups(host_mid)
657
-
658
- if (subscribedGroups.length === 0) {
659
- // 删除该UP主的过滤词和过滤标签
660
- await this.runQuery('DELETE FROM FilterWords WHERE host_mid = ?', [host_mid])
661
- await this.runQuery('DELETE FROM FilterTags WHERE host_mid = ?', [host_mid])
456
+ const subscribedGroups = await this.getUserSubscribedGroups(user.host_mid)
457
+ if (subscribedGroups.length > 0) continue
662
458
 
663
- // 删除该UP主记录
664
- await this.runQuery('DELETE FROM BilibiliUsers WHERE host_mid = ?', [host_mid])
459
+ await this.db.remove(TABLE.bilibiliFilterWord, { host_mid: user.host_mid })
460
+ await this.db.remove(TABLE.bilibiliFilterTag, { host_mid: user.host_mid })
461
+ await this.db.remove(TABLE.bilibiliUser, { host_mid: user.host_mid })
665
462
 
666
- logger.mark(`已删除UP主 ${host_mid} 的记录及相关过滤设置(不再被任何群组订阅)`)
667
- }
463
+ logger.mark(`已删除UP主 ${user.host_mid} 的记录及相关过滤设置(不再被任何群组订阅)`)
668
464
  }
669
465
  }
670
466
 
@@ -675,11 +471,11 @@ export class BilibiliDBBase {
675
471
  */
676
472
  async updateFilterMode(host_mid: number, filterMode: 'blacklist' | 'whitelist'): Promise<BilibiliUser> {
677
473
  const user = await this.getOrCreateBilibiliUser(host_mid)
678
- const now = new Date().toISOString()
474
+ const time = now()
679
475
 
680
- await this.runQuery('UPDATE BilibiliUsers SET filterMode = ?, updatedAt = ? WHERE host_mid = ?', [filterMode, now, host_mid])
476
+ await this.db.set(TABLE.bilibiliUser, { host_mid }, { filterMode, updatedAt: time })
681
477
 
682
- return { ...user, filterMode, updatedAt: now }
478
+ return { ...user, filterMode, updatedAt: time }
683
479
  }
684
480
 
685
481
  /**
@@ -690,26 +486,11 @@ export class BilibiliDBBase {
690
486
  async addFilterWord(host_mid: number, word: string): Promise<FilterWord> {
691
487
  await this.getOrCreateBilibiliUser(host_mid)
692
488
 
693
- let filterWord = await this.getQuery<FilterWord>('SELECT * FROM FilterWords WHERE host_mid = ? AND word = ?', [host_mid, word])
489
+ const [existing] = await this.db.get(TABLE.bilibiliFilterWord, { host_mid, word })
490
+ if (existing) return existing
694
491
 
695
- if (!filterWord) {
696
- const now = new Date().toISOString()
697
- const result = await this.runQuery('INSERT INTO FilterWords (host_mid, word, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [
698
- host_mid,
699
- word,
700
- now,
701
- now
702
- ])
703
- filterWord = {
704
- id: result.lastID,
705
- host_mid,
706
- word,
707
- createdAt: now,
708
- updatedAt: now
709
- }
710
- }
711
-
712
- return filterWord
492
+ const time = now()
493
+ return await this.db.create(TABLE.bilibiliFilterWord, { host_mid, word, createdAt: time, updatedAt: time })
713
494
  }
714
495
 
715
496
  /**
@@ -718,8 +499,8 @@ export class BilibiliDBBase {
718
499
  * @param word 过滤词
719
500
  */
720
501
  async removeFilterWord(host_mid: number, word: string): Promise<boolean> {
721
- const result = await this.runQuery('DELETE FROM FilterWords WHERE host_mid = ? AND word = ?', [host_mid, word])
722
- return result.changes > 0
502
+ const result = await this.db.remove(TABLE.bilibiliFilterWord, { host_mid, word })
503
+ return (result.removed ?? 0) > 0
723
504
  }
724
505
 
725
506
  /**
@@ -730,26 +511,11 @@ export class BilibiliDBBase {
730
511
  async addFilterTag(host_mid: number, tag: string): Promise<FilterTag> {
731
512
  await this.getOrCreateBilibiliUser(host_mid)
732
513
 
733
- let filterTag = await this.getQuery<FilterTag>('SELECT * FROM FilterTags WHERE host_mid = ? AND tag = ?', [host_mid, tag])
514
+ const [existing] = await this.db.get(TABLE.bilibiliFilterTag, { host_mid, tag })
515
+ if (existing) return existing
734
516
 
735
- if (!filterTag) {
736
- const now = new Date().toISOString()
737
- const result = await this.runQuery('INSERT INTO FilterTags (host_mid, tag, createdAt, updatedAt) VALUES (?, ?, ?, ?)', [
738
- host_mid,
739
- tag,
740
- now,
741
- now
742
- ])
743
- filterTag = {
744
- id: result.lastID,
745
- host_mid,
746
- tag,
747
- createdAt: now,
748
- updatedAt: now
749
- }
750
- }
751
-
752
- return filterTag
517
+ const time = now()
518
+ return await this.db.create(TABLE.bilibiliFilterTag, { host_mid, tag, createdAt: time, updatedAt: time })
753
519
  }
754
520
 
755
521
  /**
@@ -758,8 +524,8 @@ export class BilibiliDBBase {
758
524
  * @param tag 过滤标签
759
525
  */
760
526
  async removeFilterTag(host_mid: number, tag: string): Promise<boolean> {
761
- const result = await this.runQuery('DELETE FROM FilterTags WHERE host_mid = ? AND tag = ?', [host_mid, tag])
762
- return result.changes > 0
527
+ const result = await this.db.remove(TABLE.bilibiliFilterTag, { host_mid, tag })
528
+ return (result.removed ?? 0) > 0
763
529
  }
764
530
 
765
531
  /**
@@ -767,8 +533,8 @@ export class BilibiliDBBase {
767
533
  * @param host_mid B站用户UID
768
534
  */
769
535
  async getFilterWords(host_mid: number): Promise<string[]> {
770
- const filterWords = await this.allQuery<FilterWord>('SELECT * FROM FilterWords WHERE host_mid = ?', [host_mid])
771
- return filterWords.map((word) => word.word)
536
+ const rows: BilibiliFilterWordRow[] = await this.db.get(TABLE.bilibiliFilterWord, { host_mid })
537
+ return rows.map((row) => row.word)
772
538
  }
773
539
 
774
540
  /**
@@ -776,8 +542,8 @@ export class BilibiliDBBase {
776
542
  * @param host_mid B站用户UID
777
543
  */
778
544
  async getFilterTags(host_mid: number): Promise<string[]> {
779
- const filterTags = await this.allQuery<FilterTag>('SELECT * FROM FilterTags WHERE host_mid = ?', [host_mid])
780
- return filterTags.map((tag) => tag.tag)
545
+ const rows: BilibiliFilterTagRow[] = await this.db.get(TABLE.bilibiliFilterTag, { host_mid })
546
+ return rows.map((row) => row.tag)
781
547
  }
782
548
 
783
549
  /**
@@ -951,10 +717,11 @@ export class BilibiliDBBase {
951
717
  async cleanOldDynamicCache(days: number = 7): Promise<number> {
952
718
  const cutoffDate = new Date()
953
719
  cutoffDate.setDate(cutoffDate.getDate() - days)
954
- const cutoffDateStr = cutoffDate.toISOString()
955
720
 
956
- const result = await this.runQuery('DELETE FROM DynamicCaches WHERE createdAt < ?', [cutoffDateStr])
957
- return result.changes ?? 0
721
+ const result = await this.db.remove(TABLE.bilibiliDynamicCache, {
722
+ createdAt: { $lt: cutoffDate.toISOString() }
723
+ })
724
+ return result.removed ?? 0
958
725
  }
959
726
 
960
727
  /** 为了向后兼容,保留groupRepository和dynamicCacheRepository属性 */
@@ -969,7 +736,7 @@ export class BilibiliDBBase {
969
736
  if (options?.where?.botId) {
970
737
  return await this.getBotGroups(options.where.botId)
971
738
  }
972
- return await this.allQuery<Group>('SELECT * FROM Groups')
739
+ return (await this.db.get(TABLE.bilibiliGroup, {})) as GroupRow[]
973
740
  }
974
741
  }
975
742
  }
@@ -989,58 +756,32 @@ export class BilibiliDBBase {
989
756
  } = {}
990
757
  ): Promise<T[]> => {
991
758
  const { where = {}, order, take, relations } = options
992
- let sql = 'SELECT * FROM DynamicCaches'
993
- const params: any[] = []
994
759
 
995
760
  // 构建WHERE条件
996
- const conditions: string[] = []
997
- if (where.groupId) {
998
- conditions.push('groupId = ?')
999
- params.push(where.groupId)
1000
- }
1001
- if (where.host_mid) {
1002
- conditions.push('host_mid = ?')
1003
- params.push(where.host_mid)
1004
- }
1005
- if (where.dynamic_id) {
1006
- conditions.push('dynamic_id = ?')
1007
- params.push(where.dynamic_id)
1008
- }
1009
-
1010
- if (conditions.length > 0) {
1011
- sql += ' WHERE ' + conditions.join(' AND ')
1012
- }
1013
-
1014
- // 构建ORDER BY
1015
- if (order) {
1016
- const orderClauses: string[] = []
1017
- const allowedFields = ['id', 'dynamic_id', 'host_mid', 'groupId', 'dynamic_type', 'createdAt', 'updatedAt']
1018
- const allowedDirections = ['ASC', 'DESC']
1019
-
1020
- for (const [field, direction] of Object.entries(order)) {
1021
- // 验证字段名和排序方向,防止SQL注入
1022
- if (allowedFields.includes(field) && allowedDirections.includes(direction)) {
1023
- orderClauses.push(`${field} ${direction}`)
1024
- }
1025
- }
1026
- if (orderClauses.length > 0) {
1027
- sql += ' ORDER BY ' + orderClauses.join(', ')
1028
- }
761
+ const query: Record<string, any> = {}
762
+ if (where.groupId) query.groupId = where.groupId
763
+ if (where.host_mid) query.host_mid = where.host_mid
764
+ if (where.dynamic_id) query.dynamic_id = where.dynamic_id
765
+
766
+ // 构建排序(minato 只认 asc / desc)
767
+ const sort: Record<string, 'asc' | 'desc'> = {}
768
+ const allowedFields = ['id', 'dynamic_id', 'host_mid', 'groupId', 'dynamic_type', 'createdAt', 'updatedAt']
769
+ for (const [field, direction] of Object.entries(order ?? {})) {
770
+ if (!allowedFields.includes(field)) continue
771
+ sort[field] = String(direction).toLowerCase() === 'asc' ? 'asc' : 'desc'
1029
772
  }
1030
773
 
1031
- // 构建LIMIT
1032
- if (take) {
1033
- sql += ' LIMIT ?'
1034
- params.push(take.toString())
1035
- }
774
+ const cursor: Record<string, any> = {}
775
+ if (Object.keys(sort).length) cursor.sort = sort
776
+ if (take) cursor.limit = take
1036
777
 
1037
- const caches = await this.allQuery<DynamicCache>(sql, params)
778
+ const caches: DynamicCacheRow[] = await this.db.get(TABLE.bilibiliDynamicCache, query, cursor)
1038
779
 
1039
780
  // 如果需要关联bilibiliUser数据
1040
781
  if (relations && relations.includes('bilibiliUser')) {
1041
782
  const result = []
1042
783
  for (const cache of caches) {
1043
- const bilibiliUser = await this.getQuery<BilibiliUser>('SELECT * FROM BilibiliUsers WHERE host_mid = ?', [cache.host_mid])
784
+ const bilibiliUser = await this.getBilibiliUser(cache.host_mid)
1044
785
  result.push({
1045
786
  ...cache,
1046
787
  bilibiliUser,
@@ -1063,24 +804,24 @@ export class BilibiliDBBase {
1063
804
 
1064
805
  // 优先处理 dynamic_id + groupId 的精确删除(单条记录)
1065
806
  if (dynamic_id && groupId) {
1066
- const result = await this.runQuery('DELETE FROM DynamicCaches WHERE dynamic_id = ? AND groupId = ?', [dynamic_id, groupId])
1067
- return { affected: result.changes }
807
+ const result = await this.db.remove(TABLE.bilibiliDynamicCache, { dynamic_id, groupId })
808
+ return { affected: result.removed ?? 0 }
1068
809
  }
1069
810
  if (groupId && host_mid) {
1070
- const result = await this.runQuery('DELETE FROM DynamicCaches WHERE groupId = ? AND host_mid = ?', [groupId, host_mid])
1071
- return { affected: result.changes }
811
+ const result = await this.db.remove(TABLE.bilibiliDynamicCache, { groupId, host_mid })
812
+ return { affected: result.removed ?? 0 }
1072
813
  }
1073
814
  if (groupId) {
1074
- const result = await this.runQuery('DELETE FROM DynamicCaches WHERE groupId = ?', [groupId])
1075
- return { affected: result.changes }
815
+ const result = await this.db.remove(TABLE.bilibiliDynamicCache, { groupId })
816
+ return { affected: result.removed ?? 0 }
1076
817
  }
1077
818
  if (host_mid) {
1078
- const result = await this.runQuery('DELETE FROM DynamicCaches WHERE host_mid = ?', [host_mid])
1079
- return { affected: result.changes }
819
+ const result = await this.db.remove(TABLE.bilibiliDynamicCache, { host_mid })
820
+ return { affected: result.removed ?? 0 }
1080
821
  }
1081
822
  if (dynamic_id) {
1082
- const result = await this.runQuery('DELETE FROM DynamicCaches WHERE dynamic_id = ?', [dynamic_id])
1083
- return { affected: result.changes }
823
+ const result = await this.db.remove(TABLE.bilibiliDynamicCache, { dynamic_id })
824
+ return { affected: result.removed ?? 0 }
1084
825
  }
1085
826
  return { affected: 0 }
1086
827
  }