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.
- package/CHANGELOG.md +48 -0
- package/lib/build-metadata.json +5 -5
- package/lib/index.js +8 -3
- package/lib/karin/module/db/bilibili.js +185 -393
- package/lib/karin/module/db/douyin.js +219 -500
- package/lib/karin/module/db/index.js +30 -5
- package/lib/karin/module/db/model.js +200 -0
- package/lib/karin/module/db/statistics.js +197 -242
- package/lib/karin/module/utils/amagiClient.js +40 -15
- package/lib/karin/platform/douyin/douyin.js +10 -0
- package/lib/karin/setup.js +2 -4
- package/package.json +3 -2
- package/scripts/build.mjs +38 -5
- package/scripts/deploy.mjs +76 -27
- package/scripts/smoke-amagi-capability.cjs +97 -0
- package/scripts/smoke-comments.cjs +164 -0
- package/scripts/smoke-db-koishi.cjs +170 -0
- package/src/index.ts +8 -3
- package/src/karin/module/db/bilibili.ts +189 -448
- package/src/karin/module/db/douyin.ts +219 -587
- package/src/karin/module/db/index.ts +30 -4
- package/src/karin/module/db/model.ts +439 -0
- package/src/karin/module/db/statistics.ts +213 -380
- package/src/karin/module/utils/amagiClient.ts +49 -27
- package/src/karin/platform/douyin/douyin.ts +10 -0
- package/src/karin/setup.ts +2 -4
- package/lib/compat/sqlite3.js +0 -117
- package/lib/karin/module/db/migration.js +0 -199
- package/src/compat/sqlite3.ts +0 -123
- package/src/karin/module/db/migration.ts +0 -249
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.DouyinDBBase = void 0;
|
|
7
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
9
4
|
const node_karin_1 = require("../../../compat/node-karin.js");
|
|
10
|
-
const root_1 = require("../../../compat/root.js");
|
|
11
|
-
const sqlite3_1 = __importDefault(require("../../../compat/sqlite3.js"));
|
|
12
|
-
const utils_1 = require("../../module/utils");
|
|
13
5
|
const Config_1 = require("../../module/utils/Config");
|
|
14
|
-
|
|
6
|
+
const model_1 = require("./model");
|
|
7
|
+
/** 当前时间戳(ISO 字符串,与旧版 sqlite 里的口径一致) */
|
|
8
|
+
const now = () => new Date().toISOString();
|
|
9
|
+
/** 数据库操作类(基于 Koishi 原生数据库服务) */
|
|
15
10
|
class DouyinDBBase {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
ctx;
|
|
12
|
+
constructor(ctx) {
|
|
13
|
+
this.ctx = ctx;
|
|
14
|
+
}
|
|
15
|
+
get db() {
|
|
16
|
+
return this.ctx.database;
|
|
20
17
|
}
|
|
21
18
|
/**
|
|
22
19
|
* 初始化数据库
|
|
@@ -24,14 +21,7 @@ class DouyinDBBase {
|
|
|
24
21
|
async init() {
|
|
25
22
|
try {
|
|
26
23
|
node_karin_1.logger.debug(node_karin_1.logger.green('--------------------------[DouyinDB] 开始初始化数据库--------------------------'));
|
|
27
|
-
node_karin_1.logger.debug('[DouyinDB]
|
|
28
|
-
// 创建数据库连接
|
|
29
|
-
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(this.dbPath), { recursive: true });
|
|
30
|
-
this.db = new sqlite3_1.default.Database(this.dbPath);
|
|
31
|
-
// 创建表结构
|
|
32
|
-
await this.createTables();
|
|
33
|
-
// 检查并升级数据库结构
|
|
34
|
-
await this.migrate();
|
|
24
|
+
node_karin_1.logger.debug('[DouyinDB] 使用 Koishi 原生数据库服务,表已由 ctx.model.extend 注册');
|
|
35
25
|
node_karin_1.logger.debug('[DouyinDB] 数据库模型同步成功');
|
|
36
26
|
node_karin_1.logger.debug('[DouyinDB] 正在同步配置订阅...');
|
|
37
27
|
node_karin_1.logger.debug('[DouyinDB] 配置项数量:', Config_1.Config.pushlist.douyin?.length || 0);
|
|
@@ -45,227 +35,16 @@ class DouyinDBBase {
|
|
|
45
35
|
}
|
|
46
36
|
return this;
|
|
47
37
|
}
|
|
48
|
-
/**
|
|
49
|
-
* 数据库迁移
|
|
50
|
-
*/
|
|
51
|
-
async migrate() {
|
|
52
|
-
try {
|
|
53
|
-
// 检查 AwemeCaches 表是否有 pushType 字段
|
|
54
|
-
try {
|
|
55
|
-
await this.runQuery('SELECT pushType FROM AwemeCaches LIMIT 1');
|
|
56
|
-
}
|
|
57
|
-
catch {
|
|
58
|
-
node_karin_1.logger.info('[DouyinDB] 检测到 AwemeCaches 表缺少 pushType 字段,开始执行迁移...');
|
|
59
|
-
// 1. 添加 pushType 字段(默认值为 'post')
|
|
60
|
-
await this.runQuery("ALTER TABLE AwemeCaches ADD COLUMN pushType TEXT DEFAULT 'post'");
|
|
61
|
-
// 2. 由于 SQLite 不支持修改 UNIQUE 约束,我们需要重建表
|
|
62
|
-
// 步骤:重命名旧表 -> 创建新表 -> 复制数据 -> 删除旧表
|
|
63
|
-
await this.runQuery('ALTER TABLE AwemeCaches RENAME TO AwemeCaches_old');
|
|
64
|
-
await this.runQuery(`
|
|
65
|
-
CREATE TABLE IF NOT EXISTS AwemeCaches (
|
|
66
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
-
aweme_id TEXT NOT NULL,
|
|
68
|
-
sec_uid TEXT NOT NULL,
|
|
69
|
-
groupId TEXT NOT NULL,
|
|
70
|
-
pushType TEXT DEFAULT 'post',
|
|
71
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
72
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
73
|
-
FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
|
|
74
|
-
UNIQUE(aweme_id, sec_uid, groupId, pushType)
|
|
75
|
-
)
|
|
76
|
-
`);
|
|
77
|
-
await this.runQuery(`
|
|
78
|
-
INSERT INTO AwemeCaches (id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt)
|
|
79
|
-
SELECT id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt
|
|
80
|
-
FROM AwemeCaches_old
|
|
81
|
-
`);
|
|
82
|
-
await this.runQuery('DROP TABLE AwemeCaches_old');
|
|
83
|
-
node_karin_1.logger.info('[DouyinDB] AwemeCaches 表迁移完成');
|
|
84
|
-
}
|
|
85
|
-
// 检查并修复外键约束问题
|
|
86
|
-
try {
|
|
87
|
-
// 尝试获取表结构
|
|
88
|
-
const tableInfo = await this.allQuery("SELECT sql FROM sqlite_master WHERE type='table' AND name='AwemeCaches'");
|
|
89
|
-
if (tableInfo.length > 0 && tableInfo[0].sql.includes('FOREIGN KEY (groupId) REFERENCES Groups(id)')) {
|
|
90
|
-
node_karin_1.logger.info('[DouyinDB] 检测到 AwemeCaches 表存在错误的外键约束,开始修复...');
|
|
91
|
-
// 重建表以移除错误的外键约束
|
|
92
|
-
await this.runQuery('ALTER TABLE AwemeCaches RENAME TO AwemeCaches_old');
|
|
93
|
-
await this.runQuery(`
|
|
94
|
-
CREATE TABLE IF NOT EXISTS AwemeCaches (
|
|
95
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
96
|
-
aweme_id TEXT NOT NULL,
|
|
97
|
-
sec_uid TEXT NOT NULL,
|
|
98
|
-
groupId TEXT NOT NULL,
|
|
99
|
-
pushType TEXT DEFAULT 'post',
|
|
100
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
101
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
102
|
-
FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
|
|
103
|
-
UNIQUE(aweme_id, sec_uid, groupId, pushType)
|
|
104
|
-
)
|
|
105
|
-
`);
|
|
106
|
-
await this.runQuery(`
|
|
107
|
-
INSERT INTO AwemeCaches (id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt)
|
|
108
|
-
SELECT id, aweme_id, sec_uid, groupId, pushType, createdAt, updatedAt
|
|
109
|
-
FROM AwemeCaches_old
|
|
110
|
-
`);
|
|
111
|
-
await this.runQuery('DROP TABLE AwemeCaches_old');
|
|
112
|
-
node_karin_1.logger.info('[DouyinDB] AwemeCaches 表外键约束修复完成');
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
node_karin_1.logger.debug('[DouyinDB] 外键约束检查/修复跳过:', error);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
node_karin_1.logger.error('[DouyinDB] 数据库迁移失败:', error);
|
|
121
|
-
// 不抛出错误,以免影响程序启动,但记录日志
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* 创建数据库表结构
|
|
126
|
-
*/
|
|
127
|
-
async createTables() {
|
|
128
|
-
const queries = [
|
|
129
|
-
// 创建机器人表
|
|
130
|
-
`CREATE TABLE IF NOT EXISTS Bots (
|
|
131
|
-
id TEXT PRIMARY KEY,
|
|
132
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
133
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
134
|
-
)`,
|
|
135
|
-
// 创建群组表
|
|
136
|
-
`CREATE TABLE IF NOT EXISTS Groups (
|
|
137
|
-
id TEXT NOT NULL,
|
|
138
|
-
botId TEXT NOT NULL,
|
|
139
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
140
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
141
|
-
PRIMARY KEY (id, botId),
|
|
142
|
-
FOREIGN KEY (botId) REFERENCES Bots(id)
|
|
143
|
-
)`,
|
|
144
|
-
// 创建抖音用户表
|
|
145
|
-
`CREATE TABLE IF NOT EXISTS DouyinUsers (
|
|
146
|
-
sec_uid TEXT PRIMARY KEY,
|
|
147
|
-
short_id TEXT,
|
|
148
|
-
remark TEXT,
|
|
149
|
-
living INTEGER DEFAULT 0,
|
|
150
|
-
filterMode TEXT DEFAULT 'blacklist',
|
|
151
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
152
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
|
|
153
|
-
)`,
|
|
154
|
-
// 创建群组用户订阅关系表
|
|
155
|
-
`CREATE TABLE IF NOT EXISTS GroupUserSubscriptions (
|
|
156
|
-
groupId TEXT,
|
|
157
|
-
sec_uid TEXT,
|
|
158
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
159
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
160
|
-
PRIMARY KEY (groupId, sec_uid),
|
|
161
|
-
FOREIGN KEY (groupId) REFERENCES Groups(id),
|
|
162
|
-
FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid)
|
|
163
|
-
)`,
|
|
164
|
-
// 创建作品缓存表
|
|
165
|
-
`CREATE TABLE IF NOT EXISTS AwemeCaches (
|
|
166
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
167
|
-
aweme_id TEXT NOT NULL,
|
|
168
|
-
sec_uid TEXT NOT NULL,
|
|
169
|
-
groupId TEXT NOT NULL,
|
|
170
|
-
pushType TEXT DEFAULT 'post',
|
|
171
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
172
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
173
|
-
FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
|
|
174
|
-
UNIQUE(aweme_id, sec_uid, groupId, pushType)
|
|
175
|
-
)`,
|
|
176
|
-
// 创建过滤词表
|
|
177
|
-
`CREATE TABLE IF NOT EXISTS FilterWords (
|
|
178
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
179
|
-
sec_uid TEXT NOT NULL,
|
|
180
|
-
word TEXT NOT NULL,
|
|
181
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
182
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
183
|
-
FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
|
|
184
|
-
UNIQUE(sec_uid, word)
|
|
185
|
-
)`,
|
|
186
|
-
// 创建过滤标签表
|
|
187
|
-
`CREATE TABLE IF NOT EXISTS FilterTags (
|
|
188
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
189
|
-
sec_uid TEXT NOT NULL,
|
|
190
|
-
tag TEXT NOT NULL,
|
|
191
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
192
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
193
|
-
FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
|
|
194
|
-
UNIQUE(sec_uid, tag)
|
|
195
|
-
)`,
|
|
196
|
-
// 创建列表快照表(用于喜欢列表和推荐列表)
|
|
197
|
-
`CREATE TABLE IF NOT EXISTS ListSnapshots (
|
|
198
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
199
|
-
sec_uid TEXT NOT NULL,
|
|
200
|
-
pushType TEXT NOT NULL,
|
|
201
|
-
aweme_id TEXT NOT NULL,
|
|
202
|
-
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
203
|
-
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
204
|
-
FOREIGN KEY (sec_uid) REFERENCES DouyinUsers(sec_uid),
|
|
205
|
-
UNIQUE(sec_uid, pushType, aweme_id)
|
|
206
|
-
)`
|
|
207
|
-
];
|
|
208
|
-
for (const query of queries) {
|
|
209
|
-
await this.runQuery(query);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* 执行SQL查询
|
|
214
|
-
*/
|
|
215
|
-
runQuery(sql, params = []) {
|
|
216
|
-
return new Promise((resolve, reject) => {
|
|
217
|
-
this.db.run(sql, params, function (err) {
|
|
218
|
-
if (err) {
|
|
219
|
-
reject(err);
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
resolve({ lastID: this.lastID, changes: this.changes });
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* 执行SQL查询并获取单个结果
|
|
229
|
-
*/
|
|
230
|
-
getQuery(sql, params = []) {
|
|
231
|
-
return new Promise((resolve, reject) => {
|
|
232
|
-
this.db.get(sql, params, (err, row) => {
|
|
233
|
-
if (err) {
|
|
234
|
-
reject(err);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
resolve(row);
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* 执行SQL查询并获取所有结果
|
|
244
|
-
*/
|
|
245
|
-
allQuery(sql, params = []) {
|
|
246
|
-
return new Promise((resolve, reject) => {
|
|
247
|
-
this.db.all(sql, params, (err, rows) => {
|
|
248
|
-
if (err) {
|
|
249
|
-
reject(err);
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
resolve(rows);
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
38
|
/**
|
|
258
39
|
* 获取或创建机器人记录
|
|
259
40
|
* @param botId 机器人ID
|
|
260
41
|
*/
|
|
261
42
|
async getOrCreateBot(botId) {
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
return bot;
|
|
43
|
+
const [bot] = await this.db.get(model_1.TABLE.douyinBot, { id: botId });
|
|
44
|
+
if (bot)
|
|
45
|
+
return bot;
|
|
46
|
+
const time = now();
|
|
47
|
+
return await this.db.create(model_1.TABLE.douyinBot, { id: botId, createdAt: time, updatedAt: time });
|
|
269
48
|
}
|
|
270
49
|
/**
|
|
271
50
|
* 获取或创建群组记录
|
|
@@ -274,13 +53,16 @@ class DouyinDBBase {
|
|
|
274
53
|
*/
|
|
275
54
|
async getOrCreateGroup(groupId, botId) {
|
|
276
55
|
await this.getOrCreateBot(botId);
|
|
277
|
-
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
56
|
+
const [group] = await this.db.get(model_1.TABLE.douyinGroup, { id: groupId, botId });
|
|
57
|
+
if (group)
|
|
58
|
+
return group;
|
|
59
|
+
const time = now();
|
|
60
|
+
return await this.db.create(model_1.TABLE.douyinGroup, {
|
|
61
|
+
id: groupId,
|
|
62
|
+
botId,
|
|
63
|
+
createdAt: time,
|
|
64
|
+
updatedAt: time
|
|
65
|
+
});
|
|
284
66
|
}
|
|
285
67
|
/**
|
|
286
68
|
* 获取或创建抖音用户记录
|
|
@@ -289,47 +71,32 @@ class DouyinDBBase {
|
|
|
289
71
|
* @param remark 用户昵称
|
|
290
72
|
*/
|
|
291
73
|
async getOrCreateDouyinUser(sec_uid, short_id = '', remark = '') {
|
|
292
|
-
|
|
74
|
+
const [user] = await this.db.get(model_1.TABLE.douyinUser, { sec_uid });
|
|
293
75
|
if (!user) {
|
|
294
|
-
const
|
|
295
|
-
await this.
|
|
296
|
-
user = {
|
|
76
|
+
const time = now();
|
|
77
|
+
const created = await this.db.create(model_1.TABLE.douyinUser, {
|
|
297
78
|
sec_uid,
|
|
298
79
|
short_id,
|
|
299
80
|
remark,
|
|
300
81
|
living: false,
|
|
301
82
|
filterMode: 'blacklist',
|
|
302
|
-
createdAt:
|
|
303
|
-
updatedAt:
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
else {
|
|
307
|
-
// 如果提供了新的信息,更新用户记录
|
|
308
|
-
let needUpdate = false;
|
|
309
|
-
const updates = [];
|
|
310
|
-
const params = [];
|
|
311
|
-
if (remark && user.remark !== remark) {
|
|
312
|
-
updates.push('remark = ?');
|
|
313
|
-
params.push(remark);
|
|
314
|
-
user.remark = remark;
|
|
315
|
-
needUpdate = true;
|
|
316
|
-
}
|
|
317
|
-
if (short_id && user.short_id !== short_id) {
|
|
318
|
-
updates.push('short_id = ?');
|
|
319
|
-
params.push(short_id);
|
|
320
|
-
user.short_id = short_id;
|
|
321
|
-
needUpdate = true;
|
|
322
|
-
}
|
|
323
|
-
if (needUpdate) {
|
|
324
|
-
const now = new Date().toISOString();
|
|
325
|
-
updates.push('updatedAt = ?');
|
|
326
|
-
params.push(now);
|
|
327
|
-
params.push(sec_uid);
|
|
328
|
-
await this.runQuery(`UPDATE DouyinUsers SET ${updates.join(', ')} WHERE sec_uid = ?`, params);
|
|
329
|
-
user.updatedAt = now;
|
|
330
|
-
}
|
|
83
|
+
createdAt: time,
|
|
84
|
+
updatedAt: time
|
|
85
|
+
});
|
|
86
|
+
return created;
|
|
331
87
|
}
|
|
332
|
-
|
|
88
|
+
// 如果提供了新的信息,更新用户记录
|
|
89
|
+
const patch = {};
|
|
90
|
+
if (remark && user.remark !== remark)
|
|
91
|
+
patch.remark = remark;
|
|
92
|
+
if (short_id && user.short_id !== short_id)
|
|
93
|
+
patch.short_id = short_id;
|
|
94
|
+
if (Object.keys(patch).length === 0)
|
|
95
|
+
return user;
|
|
96
|
+
const time = now();
|
|
97
|
+
patch.updatedAt = time;
|
|
98
|
+
await this.db.set(model_1.TABLE.douyinUser, { sec_uid }, patch);
|
|
99
|
+
return { ...user, ...patch };
|
|
333
100
|
}
|
|
334
101
|
/**
|
|
335
102
|
* 订阅抖音用户
|
|
@@ -342,18 +109,16 @@ class DouyinDBBase {
|
|
|
342
109
|
async subscribeDouyinUser(groupId, botId, sec_uid, short_id = '', remark = '') {
|
|
343
110
|
await this.getOrCreateGroup(groupId, botId);
|
|
344
111
|
await this.getOrCreateDouyinUser(sec_uid, short_id, remark);
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
return subscription;
|
|
112
|
+
const [subscription] = await this.db.get(model_1.TABLE.douyinSubscription, { groupId, sec_uid });
|
|
113
|
+
if (subscription)
|
|
114
|
+
return subscription;
|
|
115
|
+
const time = now();
|
|
116
|
+
return await this.db.create(model_1.TABLE.douyinSubscription, {
|
|
117
|
+
groupId,
|
|
118
|
+
sec_uid,
|
|
119
|
+
createdAt: time,
|
|
120
|
+
updatedAt: time
|
|
121
|
+
});
|
|
357
122
|
}
|
|
358
123
|
/**
|
|
359
124
|
* 取消订阅抖音用户
|
|
@@ -361,24 +126,21 @@ class DouyinDBBase {
|
|
|
361
126
|
* @param sec_uid 抖音用户sec_uid
|
|
362
127
|
*/
|
|
363
128
|
async unsubscribeDouyinUser(groupId, sec_uid) {
|
|
364
|
-
const result = await this.
|
|
129
|
+
const result = await this.db.remove(model_1.TABLE.douyinSubscription, { groupId, sec_uid });
|
|
365
130
|
// 清除相关的作品缓存
|
|
366
|
-
await this.
|
|
131
|
+
await this.db.remove(model_1.TABLE.douyinAwemeCache, { groupId, sec_uid });
|
|
367
132
|
// 检查该用户是否还有其他群组订阅
|
|
368
|
-
const
|
|
133
|
+
const remaining = await this.db.get(model_1.TABLE.douyinSubscription, { sec_uid }, { limit: 1 });
|
|
369
134
|
// 如果没有任何群组订阅该用户,删除用户记录及相关数据
|
|
370
|
-
if (
|
|
135
|
+
if (remaining.length === 0) {
|
|
371
136
|
node_karin_1.logger.info(`[DouyinDB] 用户 ${sec_uid} 已无任何群组订阅,清理相关数据`);
|
|
372
|
-
|
|
373
|
-
await this.
|
|
374
|
-
|
|
375
|
-
await this.runQuery('DELETE FROM FilterWords WHERE sec_uid = ?', [sec_uid]);
|
|
376
|
-
// 删除过滤标签
|
|
377
|
-
await this.runQuery('DELETE FROM FilterTags WHERE sec_uid = ?', [sec_uid]);
|
|
137
|
+
await this.db.remove(model_1.TABLE.douyinUser, { sec_uid });
|
|
138
|
+
await this.db.remove(model_1.TABLE.douyinFilterWord, { sec_uid });
|
|
139
|
+
await this.db.remove(model_1.TABLE.douyinFilterTag, { sec_uid });
|
|
378
140
|
// 删除所有相关的作品缓存(所有群组的)
|
|
379
|
-
await this.
|
|
141
|
+
await this.db.remove(model_1.TABLE.douyinAwemeCache, { sec_uid });
|
|
380
142
|
}
|
|
381
|
-
return result.
|
|
143
|
+
return (result.removed ?? 0) > 0;
|
|
382
144
|
}
|
|
383
145
|
/**
|
|
384
146
|
* 添加作品缓存
|
|
@@ -388,21 +150,18 @@ class DouyinDBBase {
|
|
|
388
150
|
* @param pushType 推送类型:post(作品列表)、favorite(喜欢列表)、recommend(推荐列表)、live(直播)
|
|
389
151
|
*/
|
|
390
152
|
async addAwemeCache(aweme_id, sec_uid, groupId, pushType = 'post') {
|
|
391
|
-
|
|
392
|
-
if (
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
return cache;
|
|
153
|
+
const [cache] = await this.db.get(model_1.TABLE.douyinAwemeCache, { aweme_id, sec_uid, groupId, pushType });
|
|
154
|
+
if (cache)
|
|
155
|
+
return cache;
|
|
156
|
+
const time = now();
|
|
157
|
+
return await this.db.create(model_1.TABLE.douyinAwemeCache, {
|
|
158
|
+
aweme_id,
|
|
159
|
+
sec_uid,
|
|
160
|
+
groupId,
|
|
161
|
+
pushType,
|
|
162
|
+
createdAt: time,
|
|
163
|
+
updatedAt: time
|
|
164
|
+
});
|
|
406
165
|
}
|
|
407
166
|
/**
|
|
408
167
|
* 检查作品是否已推送
|
|
@@ -412,8 +171,8 @@ class DouyinDBBase {
|
|
|
412
171
|
* @param pushType 推送类型:post(作品列表)、favorite(喜欢列表)、recommend(推荐列表)、live(直播)
|
|
413
172
|
*/
|
|
414
173
|
async isAwemePushed(aweme_id, sec_uid, groupId, pushType = 'post') {
|
|
415
|
-
const
|
|
416
|
-
return
|
|
174
|
+
const rows = await this.db.get(model_1.TABLE.douyinAwemeCache, { aweme_id, sec_uid, groupId, pushType }, { limit: 1 });
|
|
175
|
+
return rows.length > 0;
|
|
417
176
|
}
|
|
418
177
|
/**
|
|
419
178
|
* 检查群组是否有推送历史(用于判断是否为新订阅)
|
|
@@ -422,15 +181,15 @@ class DouyinDBBase {
|
|
|
422
181
|
* @param pushType 推送类型
|
|
423
182
|
*/
|
|
424
183
|
async hasHistory(sec_uid, groupId, pushType) {
|
|
425
|
-
const
|
|
426
|
-
return
|
|
184
|
+
const rows = await this.db.get(model_1.TABLE.douyinAwemeCache, { sec_uid, groupId, pushType }, { limit: 1 });
|
|
185
|
+
return rows.length > 0;
|
|
427
186
|
}
|
|
428
187
|
/**
|
|
429
188
|
* 获取机器人管理的所有群组
|
|
430
189
|
* @param botId 机器人ID
|
|
431
190
|
*/
|
|
432
191
|
async getBotGroups(botId) {
|
|
433
|
-
return await this.
|
|
192
|
+
return await this.db.get(model_1.TABLE.douyinGroup, { botId });
|
|
434
193
|
}
|
|
435
194
|
/**
|
|
436
195
|
* 更新群组的机器人ID
|
|
@@ -440,45 +199,51 @@ class DouyinDBBase {
|
|
|
440
199
|
*/
|
|
441
200
|
async updateGroupBotId(groupId, oldBotId, newBotId) {
|
|
442
201
|
await this.getOrCreateBot(newBotId);
|
|
443
|
-
|
|
444
|
-
|
|
202
|
+
await this.db.set(model_1.TABLE.douyinGroup, { id: groupId, botId: oldBotId }, {
|
|
203
|
+
botId: newBotId,
|
|
204
|
+
updatedAt: now()
|
|
205
|
+
});
|
|
445
206
|
}
|
|
446
207
|
/**
|
|
447
208
|
* 获取群组订阅的所有抖音用户
|
|
448
209
|
* @param groupId 群组ID
|
|
449
210
|
*/
|
|
450
211
|
async getGroupSubscriptions(groupId) {
|
|
451
|
-
const subscriptions = await this.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
createdAt: sub.createdAt,
|
|
462
|
-
updatedAt: sub.updatedAt,
|
|
463
|
-
douyinUser: {
|
|
212
|
+
const subscriptions = await this.db.get(model_1.TABLE.douyinSubscription, { groupId });
|
|
213
|
+
if (!subscriptions.length)
|
|
214
|
+
return [];
|
|
215
|
+
const secUids = [...new Set(subscriptions.map((item) => item.sec_uid))];
|
|
216
|
+
const users = await this.db.get(model_1.TABLE.douyinUser, { sec_uid: { $in: secUids } });
|
|
217
|
+
const userMap = new Map(users.map((user) => [user.sec_uid, user]));
|
|
218
|
+
return subscriptions.map((sub) => {
|
|
219
|
+
const user = userMap.get(sub.sec_uid);
|
|
220
|
+
return {
|
|
221
|
+
groupId: sub.groupId,
|
|
464
222
|
sec_uid: sub.sec_uid,
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
223
|
+
createdAt: sub.createdAt,
|
|
224
|
+
updatedAt: sub.updatedAt,
|
|
225
|
+
douyinUser: {
|
|
226
|
+
sec_uid: sub.sec_uid,
|
|
227
|
+
short_id: user?.short_id ?? '',
|
|
228
|
+
remark: user?.remark ?? '',
|
|
229
|
+
living: !!user?.living,
|
|
230
|
+
filterMode: (user?.filterMode ?? 'blacklist'),
|
|
231
|
+
createdAt: user?.createdAt ?? sub.createdAt,
|
|
232
|
+
updatedAt: user?.updatedAt ?? sub.updatedAt
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
});
|
|
473
236
|
}
|
|
474
237
|
/**
|
|
475
238
|
* 获取抖音用户的所有订阅群组
|
|
476
239
|
* @param sec_uid 抖音用户sec_uid
|
|
477
240
|
*/
|
|
478
241
|
async getUserSubscribedGroups(sec_uid) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
242
|
+
const subscriptions = await this.db.get(model_1.TABLE.douyinSubscription, { sec_uid });
|
|
243
|
+
if (!subscriptions.length)
|
|
244
|
+
return [];
|
|
245
|
+
const groupIds = [...new Set(subscriptions.map((item) => item.groupId))];
|
|
246
|
+
return (await this.db.get(model_1.TABLE.douyinGroup, { id: { $in: groupIds } }));
|
|
482
247
|
}
|
|
483
248
|
/**
|
|
484
249
|
* 检查群组是否已订阅抖音用户
|
|
@@ -486,8 +251,8 @@ class DouyinDBBase {
|
|
|
486
251
|
* @param groupId 群组ID
|
|
487
252
|
*/
|
|
488
253
|
async isSubscribed(sec_uid, groupId) {
|
|
489
|
-
const
|
|
490
|
-
return
|
|
254
|
+
const rows = await this.db.get(model_1.TABLE.douyinSubscription, { sec_uid, groupId }, { limit: 1 });
|
|
255
|
+
return rows.length > 0;
|
|
491
256
|
}
|
|
492
257
|
/**
|
|
493
258
|
* 获取抖音用户信息
|
|
@@ -495,11 +260,10 @@ class DouyinDBBase {
|
|
|
495
260
|
* @returns 返回用户信息,如果不存在则返回null
|
|
496
261
|
*/
|
|
497
262
|
async getDouyinUser(sec_uid) {
|
|
498
|
-
const user = await this.
|
|
499
|
-
if (user)
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
return user || null;
|
|
263
|
+
const [user] = await this.db.get(model_1.TABLE.douyinUser, { sec_uid });
|
|
264
|
+
if (!user)
|
|
265
|
+
return null;
|
|
266
|
+
return { ...user, living: !!user.living };
|
|
503
267
|
}
|
|
504
268
|
/**
|
|
505
269
|
* 更新用户直播状态
|
|
@@ -510,9 +274,8 @@ class DouyinDBBase {
|
|
|
510
274
|
const user = await this.getDouyinUser(sec_uid);
|
|
511
275
|
if (!user)
|
|
512
276
|
return false;
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
return result.changes > 0;
|
|
277
|
+
const result = await this.db.set(model_1.TABLE.douyinUser, { sec_uid }, { living, updatedAt: now() });
|
|
278
|
+
return (result.matched ?? 0) > 0;
|
|
516
279
|
}
|
|
517
280
|
/**
|
|
518
281
|
* 获取用户直播状态
|
|
@@ -527,68 +290,52 @@ class DouyinDBBase {
|
|
|
527
290
|
* @param configItems 配置文件中的订阅项
|
|
528
291
|
*/
|
|
529
292
|
async syncConfigSubscriptions(configItems) {
|
|
293
|
+
const items = configItems ?? [];
|
|
530
294
|
// 1. 收集配置文件中的所有订阅关系
|
|
531
295
|
const configSubscriptions = new Map();
|
|
532
|
-
|
|
533
|
-
for (const item of configItems) {
|
|
296
|
+
for (const item of items) {
|
|
534
297
|
const sec_uid = item.sec_uid;
|
|
535
298
|
const short_id = item.short_id ?? '';
|
|
536
299
|
const remark = item.remark ?? '';
|
|
537
300
|
// 创建或更新抖音用户记录
|
|
538
301
|
await this.getOrCreateDouyinUser(sec_uid, short_id, remark);
|
|
539
|
-
// 处理该用户的所有群组订阅
|
|
540
302
|
for (const groupWithBot of item.group_id) {
|
|
541
303
|
const [groupId, botId] = groupWithBot.split(':');
|
|
542
304
|
if (!groupId || !botId)
|
|
543
305
|
continue;
|
|
544
306
|
// 确保群组存在
|
|
545
307
|
await this.getOrCreateGroup(groupId, botId);
|
|
546
|
-
|
|
547
|
-
if (!configSubscriptions.has(groupId)) {
|
|
308
|
+
if (!configSubscriptions.has(groupId))
|
|
548
309
|
configSubscriptions.set(groupId, new Set());
|
|
549
|
-
}
|
|
550
310
|
configSubscriptions.get(groupId)?.add(sec_uid);
|
|
551
|
-
|
|
552
|
-
const isSubscribed = await this.isSubscribed(sec_uid, groupId);
|
|
553
|
-
// 如果未订阅,创建订阅关系
|
|
554
|
-
if (!isSubscribed) {
|
|
311
|
+
if (!await this.isSubscribed(sec_uid, groupId)) {
|
|
555
312
|
await this.subscribeDouyinUser(groupId, botId, sec_uid, short_id, remark);
|
|
556
313
|
}
|
|
557
314
|
}
|
|
558
315
|
}
|
|
559
|
-
// 2.
|
|
560
|
-
|
|
561
|
-
const allGroups = await this.allQuery('SELECT * FROM Groups');
|
|
316
|
+
// 2. 删除数据库里存在、但配置文件里没有的订阅
|
|
317
|
+
const allGroups = await this.db.get(model_1.TABLE.douyinGroup, {});
|
|
562
318
|
for (const group of allGroups) {
|
|
563
319
|
const groupId = group.id;
|
|
564
320
|
const configUsers = configSubscriptions.get(groupId) ?? new Set();
|
|
565
|
-
|
|
566
|
-
const dbSubscriptions = await this.getGroupSubscriptions(groupId);
|
|
567
|
-
// 找出需要删除的订阅(在数据库中存在但配置文件中不存在)
|
|
321
|
+
const dbSubscriptions = await this.db.get(model_1.TABLE.douyinSubscription, { groupId });
|
|
568
322
|
for (const subscription of dbSubscriptions) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
node_karin_1.logger.mark(`已删除群组 ${groupId} 对抖音用户 ${sec_uid} 的订阅`);
|
|
574
|
-
}
|
|
323
|
+
if (configUsers.has(subscription.sec_uid))
|
|
324
|
+
continue;
|
|
325
|
+
await this.unsubscribeDouyinUser(groupId, subscription.sec_uid);
|
|
326
|
+
node_karin_1.logger.mark(`已删除群组 ${groupId} 对抖音用户 ${subscription.sec_uid} 的订阅`);
|
|
575
327
|
}
|
|
576
328
|
}
|
|
577
329
|
// 3. 清理不再被任何群组订阅的抖音用户记录及其过滤词和过滤标签
|
|
578
|
-
|
|
579
|
-
const allUsers = await this.allQuery('SELECT * FROM DouyinUsers');
|
|
330
|
+
const allUsers = await this.db.get(model_1.TABLE.douyinUser, {});
|
|
580
331
|
for (const user of allUsers) {
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
// 删除该用户记录
|
|
589
|
-
await this.runQuery('DELETE FROM DouyinUsers WHERE sec_uid = ?', [sec_uid]);
|
|
590
|
-
node_karin_1.logger.mark(`已删除抖音用户 ${sec_uid} 的记录及相关过滤设置(不再被任何群组订阅)`);
|
|
591
|
-
}
|
|
332
|
+
const subscribedGroups = await this.getUserSubscribedGroups(user.sec_uid);
|
|
333
|
+
if (subscribedGroups.length > 0)
|
|
334
|
+
continue;
|
|
335
|
+
await this.db.remove(model_1.TABLE.douyinFilterWord, { sec_uid: user.sec_uid });
|
|
336
|
+
await this.db.remove(model_1.TABLE.douyinFilterTag, { sec_uid: user.sec_uid });
|
|
337
|
+
await this.db.remove(model_1.TABLE.douyinUser, { sec_uid: user.sec_uid });
|
|
338
|
+
node_karin_1.logger.mark(`已删除抖音用户 ${user.sec_uid} 的记录及相关过滤设置(不再被任何群组订阅)`);
|
|
592
339
|
}
|
|
593
340
|
}
|
|
594
341
|
/**
|
|
@@ -596,7 +343,8 @@ class DouyinDBBase {
|
|
|
596
343
|
* @param groupId 群组ID
|
|
597
344
|
*/
|
|
598
345
|
async getGroupById(groupId) {
|
|
599
|
-
|
|
346
|
+
const [group] = await this.db.get(model_1.TABLE.douyinGroup, { id: groupId });
|
|
347
|
+
return group ?? null;
|
|
600
348
|
}
|
|
601
349
|
/**
|
|
602
350
|
* 更新用户的过滤模式
|
|
@@ -605,9 +353,9 @@ class DouyinDBBase {
|
|
|
605
353
|
*/
|
|
606
354
|
async updateFilterMode(sec_uid, filterMode) {
|
|
607
355
|
const user = await this.getOrCreateDouyinUser(sec_uid);
|
|
608
|
-
const
|
|
609
|
-
await this.
|
|
610
|
-
return { ...user, filterMode, updatedAt:
|
|
356
|
+
const time = now();
|
|
357
|
+
await this.db.set(model_1.TABLE.douyinUser, { sec_uid }, { filterMode, updatedAt: time });
|
|
358
|
+
return { ...user, filterMode, updatedAt: time };
|
|
611
359
|
}
|
|
612
360
|
/**
|
|
613
361
|
* 添加过滤词
|
|
@@ -616,24 +364,11 @@ class DouyinDBBase {
|
|
|
616
364
|
*/
|
|
617
365
|
async addFilterWord(sec_uid, word) {
|
|
618
366
|
await this.getOrCreateDouyinUser(sec_uid);
|
|
619
|
-
|
|
620
|
-
if (
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
word,
|
|
625
|
-
now,
|
|
626
|
-
now
|
|
627
|
-
]);
|
|
628
|
-
filterWord = {
|
|
629
|
-
id: result.lastID,
|
|
630
|
-
sec_uid,
|
|
631
|
-
word,
|
|
632
|
-
createdAt: now,
|
|
633
|
-
updatedAt: now
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
return filterWord;
|
|
367
|
+
const [existing] = await this.db.get(model_1.TABLE.douyinFilterWord, { sec_uid, word });
|
|
368
|
+
if (existing)
|
|
369
|
+
return existing;
|
|
370
|
+
const time = now();
|
|
371
|
+
return await this.db.create(model_1.TABLE.douyinFilterWord, { sec_uid, word, createdAt: time, updatedAt: time });
|
|
637
372
|
}
|
|
638
373
|
/**
|
|
639
374
|
* 删除过滤词
|
|
@@ -641,8 +376,8 @@ class DouyinDBBase {
|
|
|
641
376
|
* @param word 过滤词
|
|
642
377
|
*/
|
|
643
378
|
async removeFilterWord(sec_uid, word) {
|
|
644
|
-
const result = await this.
|
|
645
|
-
return result.
|
|
379
|
+
const result = await this.db.remove(model_1.TABLE.douyinFilterWord, { sec_uid, word });
|
|
380
|
+
return (result.removed ?? 0) > 0;
|
|
646
381
|
}
|
|
647
382
|
/**
|
|
648
383
|
* 添加过滤标签
|
|
@@ -651,24 +386,11 @@ class DouyinDBBase {
|
|
|
651
386
|
*/
|
|
652
387
|
async addFilterTag(sec_uid, tag) {
|
|
653
388
|
await this.getOrCreateDouyinUser(sec_uid);
|
|
654
|
-
|
|
655
|
-
if (
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
tag,
|
|
660
|
-
now,
|
|
661
|
-
now
|
|
662
|
-
]);
|
|
663
|
-
filterTag = {
|
|
664
|
-
id: result.lastID,
|
|
665
|
-
sec_uid,
|
|
666
|
-
tag,
|
|
667
|
-
createdAt: now,
|
|
668
|
-
updatedAt: now
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
return filterTag;
|
|
389
|
+
const [existing] = await this.db.get(model_1.TABLE.douyinFilterTag, { sec_uid, tag });
|
|
390
|
+
if (existing)
|
|
391
|
+
return existing;
|
|
392
|
+
const time = now();
|
|
393
|
+
return await this.db.create(model_1.TABLE.douyinFilterTag, { sec_uid, tag, createdAt: time, updatedAt: time });
|
|
672
394
|
}
|
|
673
395
|
/**
|
|
674
396
|
* 删除过滤标签
|
|
@@ -676,24 +398,24 @@ class DouyinDBBase {
|
|
|
676
398
|
* @param tag 过滤标签
|
|
677
399
|
*/
|
|
678
400
|
async removeFilterTag(sec_uid, tag) {
|
|
679
|
-
const result = await this.
|
|
680
|
-
return result.
|
|
401
|
+
const result = await this.db.remove(model_1.TABLE.douyinFilterTag, { sec_uid, tag });
|
|
402
|
+
return (result.removed ?? 0) > 0;
|
|
681
403
|
}
|
|
682
404
|
/**
|
|
683
405
|
* 获取用户的所有过滤词
|
|
684
406
|
* @param sec_uid 抖音用户sec_uid
|
|
685
407
|
*/
|
|
686
408
|
async getFilterWords(sec_uid) {
|
|
687
|
-
const
|
|
688
|
-
return
|
|
409
|
+
const rows = await this.db.get(model_1.TABLE.douyinFilterWord, { sec_uid });
|
|
410
|
+
return rows.map((row) => row.word);
|
|
689
411
|
}
|
|
690
412
|
/**
|
|
691
413
|
* 获取用户的所有过滤标签
|
|
692
414
|
* @param sec_uid 抖音用户sec_uid
|
|
693
415
|
*/
|
|
694
416
|
async getFilterTags(sec_uid) {
|
|
695
|
-
const
|
|
696
|
-
return
|
|
417
|
+
const rows = await this.db.get(model_1.TABLE.douyinFilterTag, { sec_uid });
|
|
418
|
+
return rows.map((row) => row.tag);
|
|
697
419
|
}
|
|
698
420
|
/**
|
|
699
421
|
* 获取用户的过滤配置
|
|
@@ -782,9 +504,10 @@ class DouyinDBBase {
|
|
|
782
504
|
async cleanOldAwemeCache(days = 7) {
|
|
783
505
|
const cutoffDate = new Date();
|
|
784
506
|
cutoffDate.setDate(cutoffDate.getDate() - days);
|
|
785
|
-
const
|
|
786
|
-
|
|
787
|
-
|
|
507
|
+
const result = await this.db.remove(model_1.TABLE.douyinAwemeCache, {
|
|
508
|
+
createdAt: { $lt: cutoffDate.toISOString() }
|
|
509
|
+
});
|
|
510
|
+
return result.removed ?? 0;
|
|
788
511
|
}
|
|
789
512
|
/** 为了向后兼容,保留groupRepository和awemeCacheRepository属性 */
|
|
790
513
|
get groupRepository() {
|
|
@@ -793,7 +516,7 @@ class DouyinDBBase {
|
|
|
793
516
|
if (options?.where?.botId) {
|
|
794
517
|
return await this.getBotGroups(options.where.botId);
|
|
795
518
|
}
|
|
796
|
-
return await this.
|
|
519
|
+
return (await this.db.get(model_1.TABLE.douyinGroup, {}));
|
|
797
520
|
}
|
|
798
521
|
};
|
|
799
522
|
}
|
|
@@ -801,46 +524,28 @@ class DouyinDBBase {
|
|
|
801
524
|
return {
|
|
802
525
|
find: async (options = {}) => {
|
|
803
526
|
const { where = {}, order, take, relations } = options;
|
|
804
|
-
let sql = 'SELECT * FROM AwemeCaches';
|
|
805
|
-
const params = [];
|
|
806
527
|
// 构建WHERE条件
|
|
807
|
-
const
|
|
808
|
-
if (where.groupId)
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if (where.
|
|
813
|
-
|
|
814
|
-
|
|
528
|
+
const query = {};
|
|
529
|
+
if (where.groupId)
|
|
530
|
+
query.groupId = where.groupId;
|
|
531
|
+
if (where.sec_uid)
|
|
532
|
+
query.sec_uid = where.sec_uid;
|
|
533
|
+
if (where.aweme_id)
|
|
534
|
+
query.aweme_id = where.aweme_id;
|
|
535
|
+
// 构建排序(minato 只认 asc / desc)
|
|
536
|
+
const sort = {};
|
|
537
|
+
const allowedFields = ['id', 'aweme_id', 'sec_uid', 'groupId', 'createdAt', 'updatedAt'];
|
|
538
|
+
for (const [field, direction] of Object.entries(order ?? {})) {
|
|
539
|
+
if (!allowedFields.includes(field))
|
|
540
|
+
continue;
|
|
541
|
+
sort[field] = String(direction).toLowerCase() === 'asc' ? 'asc' : 'desc';
|
|
815
542
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
}
|
|
823
|
-
// 构建ORDER BY
|
|
824
|
-
if (order) {
|
|
825
|
-
const orderClauses = [];
|
|
826
|
-
const allowedFields = ['id', 'aweme_id', 'sec_uid', 'groupId', 'createdAt', 'updatedAt'];
|
|
827
|
-
const allowedDirections = ['ASC', 'DESC'];
|
|
828
|
-
for (const [field, direction] of Object.entries(order)) {
|
|
829
|
-
// 验证字段名和排序方向,防止SQL注入
|
|
830
|
-
if (allowedFields.includes(field) && allowedDirections.includes(direction)) {
|
|
831
|
-
orderClauses.push(`${field} ${direction}`);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (orderClauses.length > 0) {
|
|
835
|
-
sql += ' ORDER BY ' + orderClauses.join(', ');
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
// 构建LIMIT
|
|
839
|
-
if (take) {
|
|
840
|
-
sql += ' LIMIT ?';
|
|
841
|
-
params.push(take.toString());
|
|
842
|
-
}
|
|
843
|
-
const caches = await this.allQuery(sql, params);
|
|
543
|
+
const cursor = {};
|
|
544
|
+
if (Object.keys(sort).length)
|
|
545
|
+
cursor.sort = sort;
|
|
546
|
+
if (take)
|
|
547
|
+
cursor.limit = take;
|
|
548
|
+
const caches = await this.db.get(model_1.TABLE.douyinAwemeCache, query, cursor);
|
|
844
549
|
// 如果需要关联douyinUser数据
|
|
845
550
|
if (relations && relations.includes('douyinUser')) {
|
|
846
551
|
const result = [];
|
|
@@ -866,24 +571,24 @@ class DouyinDBBase {
|
|
|
866
571
|
const { groupId, sec_uid, aweme_id } = conditions;
|
|
867
572
|
// 优先处理 aweme_id + groupId 的精确删除(单条记录)
|
|
868
573
|
if (aweme_id && groupId) {
|
|
869
|
-
const result = await this.
|
|
870
|
-
return { affected: result.
|
|
574
|
+
const result = await this.db.remove(model_1.TABLE.douyinAwemeCache, { aweme_id, groupId });
|
|
575
|
+
return { affected: result.removed ?? 0 };
|
|
871
576
|
}
|
|
872
577
|
if (groupId && sec_uid) {
|
|
873
|
-
const result = await this.
|
|
874
|
-
return { affected: result.
|
|
578
|
+
const result = await this.db.remove(model_1.TABLE.douyinAwemeCache, { groupId, sec_uid });
|
|
579
|
+
return { affected: result.removed ?? 0 };
|
|
875
580
|
}
|
|
876
581
|
if (groupId) {
|
|
877
|
-
const result = await this.
|
|
878
|
-
return { affected: result.
|
|
582
|
+
const result = await this.db.remove(model_1.TABLE.douyinAwemeCache, { groupId });
|
|
583
|
+
return { affected: result.removed ?? 0 };
|
|
879
584
|
}
|
|
880
585
|
if (sec_uid) {
|
|
881
|
-
const result = await this.
|
|
882
|
-
return { affected: result.
|
|
586
|
+
const result = await this.db.remove(model_1.TABLE.douyinAwemeCache, { sec_uid });
|
|
587
|
+
return { affected: result.removed ?? 0 };
|
|
883
588
|
}
|
|
884
589
|
if (aweme_id) {
|
|
885
|
-
const result = await this.
|
|
886
|
-
return { affected: result.
|
|
590
|
+
const result = await this.db.remove(model_1.TABLE.douyinAwemeCache, { aweme_id });
|
|
591
|
+
return { affected: result.removed ?? 0 };
|
|
887
592
|
}
|
|
888
593
|
return { affected: 0 };
|
|
889
594
|
}
|
|
@@ -896,8 +601,8 @@ class DouyinDBBase {
|
|
|
896
601
|
* @param pushType 推送类型
|
|
897
602
|
*/
|
|
898
603
|
async isAwemeInList(aweme_id, sec_uid, pushType) {
|
|
899
|
-
const
|
|
900
|
-
return
|
|
604
|
+
const rows = await this.db.get(model_1.TABLE.douyinListSnapshot, { aweme_id, sec_uid, pushType }, { limit: 1 });
|
|
605
|
+
return rows.length > 0;
|
|
901
606
|
}
|
|
902
607
|
/**
|
|
903
608
|
* 更新列表快照(用于喜欢列表和推荐列表)
|
|
@@ -906,12 +611,26 @@ class DouyinDBBase {
|
|
|
906
611
|
* @param aweme_ids 作品ID列表
|
|
907
612
|
*/
|
|
908
613
|
async updateListSnapshot(sec_uid, pushType, aweme_ids) {
|
|
909
|
-
const
|
|
614
|
+
const time = now();
|
|
910
615
|
// 先删除该用户该类型的所有旧快照
|
|
911
|
-
await this.
|
|
912
|
-
//
|
|
913
|
-
|
|
914
|
-
|
|
616
|
+
await this.db.remove(model_1.TABLE.douyinListSnapshot, { sec_uid, pushType });
|
|
617
|
+
// 插入新快照(去重)
|
|
618
|
+
const existing = await this.db.get(model_1.TABLE.douyinListSnapshot, {
|
|
619
|
+
sec_uid,
|
|
620
|
+
pushType,
|
|
621
|
+
aweme_id: { $in: [...new Set(aweme_ids)] }
|
|
622
|
+
});
|
|
623
|
+
const known = new Set(existing.map((row) => row.aweme_id));
|
|
624
|
+
for (const aweme_id of new Set(aweme_ids)) {
|
|
625
|
+
if (known.has(aweme_id))
|
|
626
|
+
continue;
|
|
627
|
+
await this.db.create(model_1.TABLE.douyinListSnapshot, {
|
|
628
|
+
sec_uid,
|
|
629
|
+
pushType,
|
|
630
|
+
aweme_id,
|
|
631
|
+
createdAt: time,
|
|
632
|
+
updatedAt: time
|
|
633
|
+
});
|
|
915
634
|
}
|
|
916
635
|
}
|
|
917
636
|
}
|