karin-plugin-kkk 2.22.0 → 2.23.1

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,6 +1,6 @@
1
1
  import { n as __esmMin, o as __toESM, r as __export } from "./rolldown-runtime-BMXAG3ag.js";
2
- import { $ as init_date_fns, C as zhCN, S as init_locale, _n as init_dist, a as Window, an as require_png, cn as require_heic_decode, dn as Chalk, et as fromUnixTime, fn as init_source, gn as Xhshow, hn as axios_default, i as init_lib, ln as require_express, mn as init_axios, n as require_lib, nt as format, on as require_jsQR, pn as AxiosError$1, r as require_qr_code_styling, rt as differenceInSeconds, sn as require_jpeg_js, t as require_dist, tt as formatDistanceToNow, un as require_protobufjs, vn as init_zod, yn as zod_default } from "./vendor-BcMLByns.js";
3
- import { n as init_client, r as reactServerRender } from "./template-B9tgdrVz.js";
2
+ import { A as init_locale, An as init_zod, Cn as Chalk, Dn as axios_default, En as init_axios, On as Xhshow, Sn as require_protobufjs, Tn as AxiosError$1, _n as require_png, a as Window, bn as require_heic_decode, dt as init_date_fns, ft as fromUnixTime, ht as differenceInSeconds, i as init_lib, j as zhCN, jn as zod_default, kn as init_dist, mt as format, n as require_lib, pt as formatDistanceToNow, r as require_qr_code_styling, t as require_dist, vn as require_jsQR, wn as init_source, xn as require_express, yn as require_jpeg_js } from "./vendor-DxfKHvj-.js";
3
+ import { n as init_client, r as reactServerRender } from "./template-CnW8M_F0.js";
4
4
  import { createRequire } from "node:module";
5
5
  import karin$1, { BOT_CONNECT, app, authMiddleware, checkPkgUpdate, checkPort, common, components, config, copyConfigSync, createBadRequestResponse, createNotFoundResponse, createServerErrorResponse, createSuccessResponse, db, defineConfig, ffmpeg, ffprobe, filesByExt, getBot, hooks, karin, karinPathHtml, karinPathTemp, logger, logs, mkdirSync, range, render, requireFileSync, restart, segment, updatePkg, watch } from "node-karin";
6
6
  import fs from "node:fs";
@@ -6621,7 +6621,7 @@ var init_QRCodeScanner = __esmMin(() => {
6621
6621
  }
6622
6622
  static async parseHEIC(buffer) {
6623
6623
  try {
6624
- const decoded = await (0, import_heic_decode$1.default)({ buffer });
6624
+ const decoded = await (0, import_heic_decode$1.default)({ buffer: buffer.buffer });
6625
6625
  logger.debug(`HEIC 解析成功: ${decoded.width}x${decoded.height}`);
6626
6626
  return {
6627
6627
  width: decoded.width,
@@ -9885,9 +9885,323 @@ var init_douyin = __esmMin(async () => {
9885
9885
  }
9886
9886
  };
9887
9887
  });
9888
+ var MigrationManager;
9889
+ var init_migration = __esmMin(() => {
9890
+ MigrationManager = class {
9891
+ dbPath;
9892
+ constructor(dbPath) {
9893
+ this.dbPath = dbPath;
9894
+ }
9895
+ runQuery(db$1, sql, params = []) {
9896
+ return new Promise((resolve$1, reject) => {
9897
+ db$1.run(sql, params, function(err) {
9898
+ if (err) reject(err);
9899
+ else resolve$1({
9900
+ lastID: this.lastID,
9901
+ changes: this.changes
9902
+ });
9903
+ });
9904
+ });
9905
+ }
9906
+ getQuery(db$1, sql, params = []) {
9907
+ return new Promise((resolve$1, reject) => {
9908
+ db$1.get(sql, params, (err, row) => {
9909
+ if (err) reject(err);
9910
+ else resolve$1(row);
9911
+ });
9912
+ });
9913
+ }
9914
+ allQuery(db$1, sql, params = []) {
9915
+ return new Promise((resolve$1, reject) => {
9916
+ db$1.all(sql, params, (err, rows) => {
9917
+ if (err) reject(err);
9918
+ else resolve$1(rows);
9919
+ });
9920
+ });
9921
+ }
9922
+ async initMigrationTable(db$1) {
9923
+ await this.runQuery(db$1, `\n CREATE TABLE IF NOT EXISTS _migrations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n version INTEGER NOT NULL UNIQUE,\n name TEXT NOT NULL,\n executedAt TEXT DEFAULT CURRENT_TIMESTAMP\n )\n `);
9924
+ }
9925
+ async getExecutedMigrations(db$1) {
9926
+ try {
9927
+ return await this.allQuery(db$1, "SELECT * FROM _migrations ORDER BY version");
9928
+ } catch {
9929
+ return [];
9930
+ }
9931
+ }
9932
+ async recordMigration(db$1, migration) {
9933
+ await this.runQuery(db$1, "INSERT INTO _migrations (version, name, executedAt) VALUES (?, ?, ?)", [
9934
+ migration.version,
9935
+ migration.name,
9936
+ (/* @__PURE__ */ new Date()).toISOString()
9937
+ ]);
9938
+ }
9939
+ async executeMigration(db$1, migration) {
9940
+ logger.info(`[Migration] 执行迁移 v${migration.version}: ${migration.name}`);
9941
+ try {
9942
+ await this.runQuery(db$1, "BEGIN TRANSACTION");
9943
+ for (const sql of migration.up) await this.runQuery(db$1, sql);
9944
+ await this.recordMigration(db$1, migration);
9945
+ await this.runQuery(db$1, "COMMIT");
9946
+ logger.info(`[Migration] ✓ 迁移 v${migration.version} 执行成功`);
9947
+ } catch (error) {
9948
+ await this.runQuery(db$1, "ROLLBACK");
9949
+ logger.error(`[Migration] ✗ 迁移 v${migration.version} 执行失败:`, error);
9950
+ throw error;
9951
+ }
9952
+ }
9953
+ async runMigrations(db$1, migrations) {
9954
+ await this.initMigrationTable(db$1);
9955
+ const executedMigrations = await this.getExecutedMigrations(db$1);
9956
+ const executedVersions = new Set(executedMigrations.map((m) => m.version));
9957
+ const versions = migrations.map((m) => m.version);
9958
+ const uniqueVersions = new Set(versions);
9959
+ if (versions.length !== uniqueVersions.size) throw new Error("[Migration] 迁移版本号必须唯一");
9960
+ const pendingMigrations = [...migrations].sort((a, b) => a.version - b.version).filter((m) => !executedVersions.has(m.version));
9961
+ if (pendingMigrations.length === 0) {
9962
+ logger.debug("[Migration] 数据库已是最新版本,无需迁移");
9963
+ return;
9964
+ }
9965
+ logger.info(`[Migration] 发现 ${pendingMigrations.length} 个待执行的迁移`);
9966
+ for (const migration of pendingMigrations) await this.executeMigration(db$1, migration);
9967
+ logger.info("[Migration] 所有迁移执行完成");
9968
+ }
9969
+ async getCurrentVersion(db$1) {
9970
+ try {
9971
+ await this.initMigrationTable(db$1);
9972
+ return (await this.getQuery(db$1, "SELECT MAX(version) as version FROM _migrations"))?.version || 0;
9973
+ } catch {
9974
+ return 0;
9975
+ }
9976
+ }
9977
+ async rollbackTo(db$1, targetVersion, migrations) {
9978
+ const currentVersion = await this.getCurrentVersion(db$1);
9979
+ if (targetVersion >= currentVersion) {
9980
+ logger.warn("[Migration] 目标版本不低于当前版本,无需回滚");
9981
+ return;
9982
+ }
9983
+ const migrationsToRollback = migrations.filter((m) => m.version > targetVersion && m.version <= currentVersion).sort((a, b) => b.version - a.version);
9984
+ logger.info(`[Migration] 开始回滚到版本 ${targetVersion}`);
9985
+ for (const migration of migrationsToRollback) {
9986
+ if (!migration.down || migration.down.length === 0) throw new Error(`[Migration] 迁移 v${migration.version} 没有提供回滚脚本`);
9987
+ logger.info(`[Migration] 回滚迁移 v${migration.version}: ${migration.name}`);
9988
+ try {
9989
+ await this.runQuery(db$1, "BEGIN TRANSACTION");
9990
+ for (const sql of migration.down) await this.runQuery(db$1, sql);
9991
+ await this.runQuery(db$1, "DELETE FROM _migrations WHERE version = ?", [migration.version]);
9992
+ await this.runQuery(db$1, "COMMIT");
9993
+ logger.info(`[Migration] ✓ 迁移 v${migration.version} 回滚成功`);
9994
+ } catch (error) {
9995
+ await this.runQuery(db$1, "ROLLBACK");
9996
+ logger.error(`[Migration] ✗ 迁移 v${migration.version} 回滚失败:`, error);
9997
+ throw error;
9998
+ }
9999
+ }
10000
+ logger.info("[Migration] 回滚完成");
10001
+ }
10002
+ };
10003
+ });
10004
+ var StatisticsDBBase;
10005
+ var init_statistics = __esmMin(async () => {
10006
+ await init_utils$1();
10007
+ await init_migration();
10008
+ StatisticsDBBase = class {
10009
+ db;
10010
+ dbPath;
10011
+ migrationManager;
10012
+ constructor() {
10013
+ this.dbPath = path.join(`${karinPathBase}/${Root.pluginName}/data`, "statistics.db");
10014
+ this.migrationManager = new MigrationManager(this.dbPath);
10015
+ }
10016
+ async init() {
10017
+ try {
10018
+ logger.debug(logger.green("--------------------------[StatisticsDB] 开始初始化数据库--------------------------"));
10019
+ logger.debug("[StatisticsDB] 正在连接数据库...");
10020
+ await (await import("node:fs/promises")).mkdir(path.dirname(this.dbPath), { recursive: true });
10021
+ this.db = new sqlite3.Database(this.dbPath);
10022
+ await this.createTables();
10023
+ logger.debug("[StatisticsDB] 数据库模型同步成功");
10024
+ await this.initGlobalStatistics();
10025
+ await this.syncHistoryFromStats();
10026
+ logger.debug(logger.green("--------------------------[StatisticsDB] 初始化数据库完成--------------------------"));
10027
+ } catch (error) {
10028
+ logger.error("[StatisticsDB] 数据库初始化失败:", error);
10029
+ throw error;
10030
+ }
10031
+ return this;
10032
+ }
10033
+ async createTables() {
10034
+ for (const query of [
10035
+ `CREATE TABLE IF NOT EXISTS ParseStatistics (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n groupId TEXT NOT NULL,\n userId TEXT NOT NULL,\n platform TEXT NOT NULL,\n parseCount INTEGER DEFAULT 0,\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(groupId, userId, platform)\n )`,
10036
+ `CREATE TABLE IF NOT EXISTS ParseHistory (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n date TEXT NOT NULL UNIQUE,\n totalParses INTEGER DEFAULT 0,\n douyin INTEGER DEFAULT 0,\n bilibili INTEGER DEFAULT 0,\n kuaishou INTEGER DEFAULT 0,\n xiaohongshu INTEGER DEFAULT 0,\n createdAt TEXT DEFAULT CURRENT_TIMESTAMP\n )`,
10037
+ `CREATE TABLE IF NOT EXISTS GlobalStatistics (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n updatedAt TEXT DEFAULT CURRENT_TIMESTAMP\n )`
10038
+ ]) await this.runQuery(query);
10039
+ }
10040
+ async initGlobalStatistics() {
10041
+ for (const key of ["totalGroups", "totalParses"]) if (!await this.getQuery("SELECT * FROM GlobalStatistics WHERE key = ?", [key])) await this.runQuery("INSERT INTO GlobalStatistics (key, value, updatedAt) VALUES (?, ?, ?)", [
10042
+ key,
10043
+ "0",
10044
+ (/* @__PURE__ */ new Date()).toISOString()
10045
+ ]);
10046
+ }
10047
+ runQuery(sql, params = []) {
10048
+ return new Promise((resolve$1, reject) => {
10049
+ this.db.run(sql, params, function(err) {
10050
+ if (err) reject(err);
10051
+ else resolve$1({
10052
+ lastID: this.lastID,
10053
+ changes: this.changes
10054
+ });
10055
+ });
10056
+ });
10057
+ }
10058
+ getQuery(sql, params = []) {
10059
+ return new Promise((resolve$1, reject) => {
10060
+ this.db.get(sql, params, (err, row) => {
10061
+ if (err) reject(err);
10062
+ else resolve$1(row);
10063
+ });
10064
+ });
10065
+ }
10066
+ allQuery(sql, params = []) {
10067
+ return new Promise((resolve$1, reject) => {
10068
+ this.db.all(sql, params, (err, rows) => {
10069
+ if (err) reject(err);
10070
+ else resolve$1(rows);
10071
+ });
10072
+ });
10073
+ }
10074
+ async recordParse(groupId, userId, platform$1) {
10075
+ const now = (/* @__PURE__ */ new Date()).toISOString();
10076
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
10077
+ if (await this.getQuery("SELECT * FROM ParseStatistics WHERE groupId = ? AND userId = ? AND platform = ?", [
10078
+ groupId,
10079
+ userId,
10080
+ platform$1
10081
+ ])) await this.runQuery("UPDATE ParseStatistics SET parseCount = parseCount + 1, updatedAt = ? WHERE groupId = ? AND userId = ? AND platform = ?", [
10082
+ now,
10083
+ groupId,
10084
+ userId,
10085
+ platform$1
10086
+ ]);
10087
+ else {
10088
+ await this.runQuery("INSERT INTO ParseStatistics (groupId, userId, platform, parseCount, createdAt, updatedAt) VALUES (?, ?, ?, 1, ?, ?)", [
10089
+ groupId,
10090
+ userId,
10091
+ platform$1,
10092
+ now,
10093
+ now
10094
+ ]);
10095
+ const groupExists = await this.getQuery("SELECT COUNT(DISTINCT groupId) as count FROM ParseStatistics WHERE groupId = ?", [groupId]);
10096
+ if (groupExists && groupExists.count === 1) await this.incrementTotalGroups();
10097
+ }
10098
+ await this.incrementTotalParses();
10099
+ await this.updateDailyHistory(today, platform$1);
10100
+ }
10101
+ async updateDailyHistory(date, platform$1) {
10102
+ const now = (/* @__PURE__ */ new Date()).toISOString();
10103
+ try {
10104
+ if (await this.getQuery("SELECT * FROM ParseHistory WHERE date = ?", [date])) await this.runQuery(`UPDATE ParseHistory SET totalParses = totalParses + 1, ${platform$1} = ${platform$1} + 1 WHERE date = ?`, [date]);
10105
+ else await this.runQuery("INSERT INTO ParseHistory (date, totalParses, douyin, bilibili, kuaishou, xiaohongshu, createdAt) VALUES (?, 1, ?, ?, ?, ?, ?)", [
10106
+ date,
10107
+ platform$1 === "douyin" ? 1 : 0,
10108
+ platform$1 === "bilibili" ? 1 : 0,
10109
+ platform$1 === "kuaishou" ? 1 : 0,
10110
+ platform$1 === "xiaohongshu" ? 1 : 0,
10111
+ now
10112
+ ]);
10113
+ } catch (error) {
10114
+ logger.error("[StatisticsDB] 更新每日历史记录失败:", error);
10115
+ }
10116
+ }
10117
+ async getRecentHistory(days = 30) {
10118
+ return await this.allQuery("SELECT * FROM ParseHistory ORDER BY date DESC LIMIT ?", [days]);
10119
+ }
10120
+ async syncHistoryFromStats() {
10121
+ try {
10122
+ const historyCount = await this.getQuery("SELECT COUNT(*) as count FROM ParseHistory");
10123
+ if (historyCount && historyCount.count > 0) return;
10124
+ const allStats = await this.getAllStatistics();
10125
+ const dateMap = /* @__PURE__ */ new Map();
10126
+ for (const stat of allStats) {
10127
+ const date = stat.createdAt.split("T")[0];
10128
+ if (!dateMap.has(date)) dateMap.set(date, {
10129
+ douyin: 0,
10130
+ bilibili: 0,
10131
+ kuaishou: 0,
10132
+ xiaohongshu: 0
10133
+ });
10134
+ const dateData = dateMap.get(date);
10135
+ dateData[stat.platform] += stat.parseCount;
10136
+ }
10137
+ for (const [date, platforms] of dateMap.entries()) {
10138
+ const totalParses = platforms.douyin + platforms.bilibili + platforms.kuaishou + platforms.xiaohongshu;
10139
+ await this.runQuery("INSERT OR IGNORE INTO ParseHistory (date, totalParses, douyin, bilibili, kuaishou, xiaohongshu, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?)", [
10140
+ date,
10141
+ totalParses,
10142
+ platforms.douyin,
10143
+ platforms.bilibili,
10144
+ platforms.kuaishou,
10145
+ platforms.xiaohongshu,
10146
+ (/* @__PURE__ */ new Date()).toISOString()
10147
+ ]);
10148
+ }
10149
+ logger.info(`[StatisticsDB] 已同步 ${dateMap.size} 天的历史数据`);
10150
+ } catch (error) {
10151
+ logger.error("[StatisticsDB] 同步历史数据失败:", error);
10152
+ }
10153
+ }
10154
+ async getGroupStatistics(groupId) {
10155
+ return await this.allQuery("SELECT * FROM ParseStatistics WHERE groupId = ? ORDER BY platform, userId", [groupId]);
10156
+ }
10157
+ async getGroupUniqueUsers(groupId) {
10158
+ return (await this.getQuery("SELECT COUNT(DISTINCT userId) as count FROM ParseStatistics WHERE groupId = ?", [groupId]))?.count || 0;
10159
+ }
10160
+ async getTotalUniqueUsers() {
10161
+ return (await this.getQuery("SELECT COUNT(DISTINCT userId) as count FROM ParseStatistics"))?.count || 0;
10162
+ }
10163
+ async getAllStatistics() {
10164
+ return await this.allQuery("SELECT * FROM ParseStatistics ORDER BY groupId, platform");
10165
+ }
10166
+ async getPlatformTotalParses(platform$1) {
10167
+ return (await this.getQuery("SELECT SUM(parseCount) as total FROM ParseStatistics WHERE platform = ?", [platform$1]))?.total || 0;
10168
+ }
10169
+ async getTotalGroups() {
10170
+ return (await this.getQuery("SELECT COUNT(DISTINCT groupId) as count FROM ParseStatistics"))?.count || 0;
10171
+ }
10172
+ async getTotalParses() {
10173
+ const result = await this.getQuery("SELECT value FROM GlobalStatistics WHERE key = ?", ["totalParses"]);
10174
+ return parseInt(result?.value || "0", 10);
10175
+ }
10176
+ async incrementTotalGroups() {
10177
+ const totalGroups = await this.getTotalGroups();
10178
+ await this.runQuery("UPDATE GlobalStatistics SET value = ?, updatedAt = ? WHERE key = ?", [
10179
+ totalGroups.toString(),
10180
+ (/* @__PURE__ */ new Date()).toISOString(),
10181
+ "totalGroups"
10182
+ ]);
10183
+ }
10184
+ async incrementTotalParses() {
10185
+ await this.runQuery("UPDATE GlobalStatistics SET value = value + 1, updatedAt = ? WHERE key = ?", [(/* @__PURE__ */ new Date()).toISOString(), "totalParses"]);
10186
+ }
10187
+ async getGlobalSummary() {
10188
+ return {
10189
+ totalGroups: await this.getTotalGroups(),
10190
+ totalParses: await this.getTotalParses(),
10191
+ platformStats: {
10192
+ douyin: await this.getPlatformTotalParses("douyin"),
10193
+ bilibili: await this.getPlatformTotalParses("bilibili"),
10194
+ kuaishou: await this.getPlatformTotalParses("kuaishou"),
10195
+ xiaohongshu: await this.getPlatformTotalParses("xiaohongshu")
10196
+ }
10197
+ };
10198
+ }
10199
+ };
10200
+ });
9888
10201
  var db_exports = __export({
9889
10202
  BilibiliDBBase: () => BilibiliDBBase,
9890
10203
  DouyinDBBase: () => DouyinDBBase,
10204
+ StatisticsDBBase: () => StatisticsDBBase,
9891
10205
  bilibiliDB: () => bilibiliDBInstance,
9892
10206
  bilibiliDBInstance: () => bilibiliDBInstance,
9893
10207
  cleanOldDynamicCache: () => cleanOldDynamicCache,
@@ -9895,18 +10209,25 @@ var db_exports = __export({
9895
10209
  douyinDBInstance: () => douyinDBInstance,
9896
10210
  getBilibiliDB: () => getBilibiliDB,
9897
10211
  getDouyinDB: () => getDouyinDB,
9898
- initAllDatabases: () => initAllDatabases$1
10212
+ getStatisticsDB: () => getStatisticsDB,
10213
+ initAllDatabases: () => initAllDatabases$1,
10214
+ statisticsDB: () => statisticsDBInstance,
10215
+ statisticsDBInstance: () => statisticsDBInstance
9899
10216
  }, 1);
9900
- var douyinDB, douyinInitializing, bilibiliDB, bilibiliInitializing, getDouyinDB, getBilibiliDB, initAllDatabases$1, douyinDBInstance, bilibiliDBInstance, cleanOldDynamicCache;
10217
+ var douyinDB, douyinInitializing, bilibiliDB, bilibiliInitializing, statisticsDB, statisticsInitializing, getDouyinDB, getBilibiliDB, getStatisticsDB, initAllDatabases$1, douyinDBInstance, bilibiliDBInstance, statisticsDBInstance, cleanOldDynamicCache;
9901
10218
  var init_db = __esmMin(async () => {
9902
10219
  await init_bilibili();
9903
10220
  await init_douyin();
10221
+ await init_statistics();
9904
10222
  init_bilibili();
9905
10223
  init_douyin();
10224
+ init_statistics();
9906
10225
  douyinDB = null;
9907
10226
  douyinInitializing = false;
9908
10227
  bilibiliDB = null;
9909
10228
  bilibiliInitializing = false;
10229
+ statisticsDB = null;
10230
+ statisticsInitializing = false;
9910
10231
  getDouyinDB = async () => {
9911
10232
  if (douyinDB) return douyinDB;
9912
10233
  if (douyinInitializing) {
@@ -9935,15 +10256,35 @@ var init_db = __esmMin(async () => {
9935
10256
  bilibiliInitializing = false;
9936
10257
  }
9937
10258
  };
10259
+ getStatisticsDB = async () => {
10260
+ if (statisticsDB) return statisticsDB;
10261
+ if (statisticsInitializing) {
10262
+ await new Promise((resolve$1) => setTimeout(resolve$1, 100));
10263
+ return statisticsDB;
10264
+ }
10265
+ statisticsInitializing = true;
10266
+ try {
10267
+ statisticsDB = await new StatisticsDBBase().init();
10268
+ return statisticsDB;
10269
+ } finally {
10270
+ statisticsInitializing = false;
10271
+ }
10272
+ };
9938
10273
  initAllDatabases$1 = async () => {
9939
- const [douyin$2, bilibili$2] = await Promise.all([getDouyinDB(), getBilibiliDB()]);
10274
+ const [douyin$2, bilibili$2, statistics] = await Promise.all([
10275
+ getDouyinDB(),
10276
+ getBilibiliDB(),
10277
+ getStatisticsDB()
10278
+ ]);
9940
10279
  return {
9941
10280
  douyinDB: douyin$2,
9942
- bilibiliDB: bilibili$2
10281
+ bilibiliDB: bilibili$2,
10282
+ statisticsDB: statistics
9943
10283
  };
9944
10284
  };
9945
10285
  douyinDBInstance = await getDouyinDB();
9946
10286
  bilibiliDBInstance = await getBilibiliDB();
10287
+ statisticsDBInstance = await getStatisticsDB();
9947
10288
  cleanOldDynamicCache = async (platform$1, days = 7) => {
9948
10289
  if (platform$1 === "douyin") return await (await getDouyinDB()).cleanOldAwemeCache(days);
9949
10290
  else return await (await getBilibiliDB()).cleanOldDynamicCache(days);
@@ -17441,6 +17782,7 @@ var Bilibilipush = class extends Base {
17441
17782
  const imgArray = [];
17442
17783
  const title = data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major?.opus?.title || "bilibili_dynamic";
17443
17784
  const images = data$1[dynamicId].Dynamic_Data.modules.module_dynamic.major && data$1[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.draw?.items || data$1[dynamicId].Dynamic_Data.modules.module_dynamic?.major?.opus.pics;
17785
+ if (images.length === 0) break;
17444
17786
  for (const [index, img2] of images.entries()) {
17445
17787
  const imageUrl = await processImageUrl(img2.src ?? img2.url, title, index);
17446
17788
  imgArray.push(segment.image(imageUrl));
@@ -18241,7 +18583,7 @@ var DouYin = class extends Base {
18241
18583
  gender: userProfile.data.user.gender ?? 0,
18242
18584
  user_age: userProfile.data.user.user_age ?? 0
18243
18585
  } : void 0,
18244
- image_url: isArticle ? VideoData.data.aweme_detail.video.origin_cover.url_list[0] : this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover_original_scale?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0],
18586
+ image_url: isArticle ? VideoData.data.aweme_detail.video.origin_cover.url_list[0] : this.is_mp4 ? VideoData.data.aweme_detail.video.animated_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.dynamic_cover?.url_list[0] ?? VideoData.data.aweme_detail.video.cover_original_scale?.url_list[0] ?? VideoData.data.aweme_detail.video.cover.url_list[0] : VideoData.data.aweme_detail.images[0].url_list[0],
18245
18587
  cover_size: isArticle ? VideoData.data.aweme_detail.video.origin_cover ? {
18246
18588
  width: VideoData.data.aweme_detail.video.origin_cover.width,
18247
18589
  height: VideoData.data.aweme_detail.video.origin_cover.height
@@ -20138,7 +20480,7 @@ var waitQrcode = async (page) => {
20138
20480
  const response = await fetch(originalImage);
20139
20481
  imageBuffer = Buffer.from(await response.arrayBuffer());
20140
20482
  }
20141
- const qrContent = QRCodeScanner.scanFromBuffer(imageBuffer);
20483
+ const qrContent = await QRCodeScanner.scanFromBuffer(imageBuffer);
20142
20484
  if (qrContent) {
20143
20485
  logger.mark("二维码解码成功:", qrContent);
20144
20486
  return {
@@ -20209,24 +20551,39 @@ await init_Config();
20209
20551
  var HELP_MENU_CONFIG = [
20210
20552
  {
20211
20553
  title: "常用功能",
20212
- items: [{
20213
- title: "自动识别分享链接进行解析",
20214
- description: (() => {
20215
- const platforms = [];
20216
- if (Config.douyin?.switch) platforms.push("抖音");
20217
- if (Config.bilibili?.switch) platforms.push("哔哩哔哩");
20218
- if (Config.kuaishou?.switch) platforms.push("快手");
20219
- if (Config.xiaohongshu?.switch) platforms.push("小红书");
20220
- return platforms.length > 0 ? `支持「${platforms.join("」「")}」` : "暂无可用平台";
20221
- })(),
20222
- icon: "Link",
20223
- roles: ["member", "master"]
20224
- }, {
20225
- title: "「#解析」「#kkk解析」「#弹幕解析」",
20226
- description: "在解析功能关闭的情况下,可对引用消息进行解析;弹幕解析仅使用于「抖音」「哔哩哔哩」",
20227
- icon: "Sparkles",
20228
- roles: ["member", "master"]
20229
- }]
20554
+ items: [
20555
+ {
20556
+ title: "自动识别分享链接进行解析",
20557
+ description: (() => {
20558
+ const platforms = [];
20559
+ if (Config.douyin?.switch) platforms.push("抖音");
20560
+ if (Config.bilibili?.switch) platforms.push("哔哩哔哩");
20561
+ if (Config.kuaishou?.switch) platforms.push("快手");
20562
+ if (Config.xiaohongshu?.switch) platforms.push("小红书");
20563
+ return platforms.length > 0 ? `支持「${platforms.join("」「")}」` : "暂无可用平台";
20564
+ })(),
20565
+ icon: "Link",
20566
+ roles: ["member", "master"]
20567
+ },
20568
+ {
20569
+ title: "「#解析」「#kkk解析」「#弹幕解析」",
20570
+ description: "在解析功能关闭的情况下,可对引用消息进行解析;弹幕解析仅使用于「抖音」「哔哩哔哩」",
20571
+ icon: "Sparkles",
20572
+ roles: ["member", "master"]
20573
+ },
20574
+ {
20575
+ title: "#kkk解析统计",
20576
+ description: "查看当前群组的解析统计数据,包括各平台解析次数、使用用户数等",
20577
+ icon: "BarChart",
20578
+ roles: ["member", "master"]
20579
+ },
20580
+ {
20581
+ title: "#kkk全局解析统计",
20582
+ description: "查看全局解析统计数据,包括所有群组的解析情况、趋势分析和群组排行",
20583
+ icon: "TrendingUp",
20584
+ roles: ["master"]
20585
+ }
20586
+ ]
20230
20587
  },
20231
20588
  {
20232
20589
  title: "推送相关",
@@ -20664,6 +21021,86 @@ const qrLogin = karin$1.command(/^#?(kkk)?登录$/i, async (e) => {
20664
21021
  perm: "master",
20665
21022
  name: "kkk-APP扫码登录"
20666
21023
  });
21024
+ await init_module();
21025
+ await init_db();
21026
+ const groupStatistics = karin$1.command(/^#?kkk解析统计$/, async (e) => {
21027
+ try {
21028
+ const groupId = e.isGroup ? e.contact?.peer || "" : "";
21029
+ if (!groupId) {
21030
+ await e.reply("此命令仅支持在群聊中使用");
21031
+ return true;
21032
+ }
21033
+ let groupName = "";
21034
+ let groupMemberCount;
21035
+ let groupAvatar;
21036
+ try {
21037
+ const groupInfo = await e.bot.getGroupInfo(groupId);
21038
+ groupName = groupInfo?.groupName || "";
21039
+ groupMemberCount = groupInfo?.memberCount;
21040
+ groupAvatar = groupInfo?.avatar;
21041
+ } catch (error) {
21042
+ logger.debug("[统计] 获取群组信息失败:", error);
21043
+ }
21044
+ const statisticsDB$1 = await getStatisticsDB();
21045
+ const groupStats = await statisticsDB$1.getGroupStatistics(groupId);
21046
+ const groupUniqueUsers = await statisticsDB$1.getGroupUniqueUsers(groupId);
21047
+ const globalSummary = await statisticsDB$1.getGlobalSummary();
21048
+ const groupTotalParses = groupStats.reduce((sum, stat) => sum + stat.parseCount, 0);
21049
+ const platformData = {
21050
+ douyin: groupStats.filter((s) => s.platform === "douyin").reduce((sum, s) => sum + s.parseCount, 0),
21051
+ bilibili: groupStats.filter((s) => s.platform === "bilibili").reduce((sum, s) => sum + s.parseCount, 0),
21052
+ kuaishou: groupStats.filter((s) => s.platform === "kuaishou").reduce((sum, s) => sum + s.parseCount, 0),
21053
+ xiaohongshu: groupStats.filter((s) => s.platform === "xiaohongshu").reduce((sum, s) => sum + s.parseCount, 0)
21054
+ };
21055
+ const img$2 = await Render("statistics/group", {
21056
+ groupId,
21057
+ groupName,
21058
+ groupMemberCount,
21059
+ groupAvatar,
21060
+ groupTotalParses,
21061
+ groupUniqueUsers,
21062
+ platformData,
21063
+ globalTotalGroups: globalSummary.totalGroups,
21064
+ globalTotalParses: globalSummary.totalParses
21065
+ });
21066
+ await e.reply(img$2);
21067
+ return true;
21068
+ } catch (error) {
21069
+ await e.reply(`获取解析统计失败:${error}`);
21070
+ return true;
21071
+ }
21072
+ }, { name: "kkk-解析统计" });
21073
+ const globalStatistics = karin$1.command(/^#?kkk全局解析统计$/, async (e) => {
21074
+ try {
21075
+ const statisticsDB$1 = await getStatisticsDB();
21076
+ const allStats = await statisticsDB$1.getAllStatistics();
21077
+ const historyData = await statisticsDB$1.getRecentHistory(30);
21078
+ const groupIds = [...new Set(allStats.map((s) => s.groupId))];
21079
+ const groupInfoMap = /* @__PURE__ */ new Map();
21080
+ for (const groupId of groupIds) try {
21081
+ const groupInfo = await e.bot.getGroupInfo(groupId);
21082
+ groupInfoMap.set(groupId, {
21083
+ groupName: groupInfo?.groupName,
21084
+ groupAvatar: groupInfo?.avatar
21085
+ });
21086
+ } catch (error) {
21087
+ logger.debug(`[统计] 获取群组 ${groupId} 信息失败:`, error);
21088
+ }
21089
+ const img$2 = await Render("statistics/global", {
21090
+ allStats,
21091
+ historyData: historyData.reverse(),
21092
+ groupInfoMap: Object.fromEntries(groupInfoMap)
21093
+ });
21094
+ await e.reply(img$2);
21095
+ return true;
21096
+ } catch (error) {
21097
+ await e.reply(`获取全局解析统计失败:${error}`);
21098
+ return true;
21099
+ }
21100
+ }, {
21101
+ name: "kkk-全局解析统计",
21102
+ perm: "master"
21103
+ });
20667
21104
  const getXiaohongshuID = async (url, log = true) => {
20668
21105
  const longLink = (await axios.get(url, { headers: { "User-Agent": "Apifox/1.0.0 (https://apifox.com)" } }))?.request?.res?.responseUrl ?? url;
20669
21106
  const normalizedLink = (() => {
@@ -21055,6 +21492,7 @@ var processXiaohongshuEmojis = (text, emojiData) => {
21055
21492
  }).join("");
21056
21493
  };
21057
21494
  await init_module();
21495
+ await init_db();
21058
21496
  await init_Config();
21059
21497
  await init_ErrorHandler();
21060
21498
  var reg = {
@@ -21074,6 +21512,13 @@ var handleDouyin = wrapWithErrorHandler(async (e) => {
21074
21512
  }
21075
21513
  const iddata = await getDouyinID(e, String(urlMatch[0]));
21076
21514
  await new DouYin(e, iddata, { forceBurnDanmaku }).DouyinHandler(iddata);
21515
+ const groupId = e.isGroup ? e.contact?.peer || "" : "";
21516
+ const userId = e.userId || "";
21517
+ if (groupId && userId) try {
21518
+ await (await getStatisticsDB()).recordParse(groupId, userId, "douyin");
21519
+ } catch (error) {
21520
+ logger.debug("[统计] 记录抖音解析统计失败:", error);
21521
+ }
21077
21522
  return true;
21078
21523
  }, { businessName: "抖音视频解析" });
21079
21524
  var handleBilibili = wrapWithErrorHandler(async (e) => {
@@ -21093,6 +21538,13 @@ var handleBilibili = wrapWithErrorHandler(async (e) => {
21093
21538
  }
21094
21539
  const iddata = await getBilibiliID(url);
21095
21540
  await new Bilibili(e, iddata, { forceBurnDanmaku }).BilibiliHandler(iddata);
21541
+ const groupId = e.isGroup ? e.contact?.peer || "" : "";
21542
+ const userId = e.userId || "";
21543
+ if (groupId && userId) try {
21544
+ await (await getStatisticsDB()).recordParse(groupId, userId, "bilibili");
21545
+ } catch (error) {
21546
+ logger.debug("[统计] 记录B站解析统计失败:", error);
21547
+ }
21096
21548
  return true;
21097
21549
  }, { businessName: "B站视频解析" });
21098
21550
  var handleKuaishou = wrapWithErrorHandler(async (e) => {
@@ -21100,6 +21552,13 @@ var handleKuaishou = wrapWithErrorHandler(async (e) => {
21100
21552
  const iddata = await getKuaishouID(String(kuaishouUrl));
21101
21553
  const WorkData = await fetchKuaishouData(iddata.type, iddata);
21102
21554
  await new Kuaishou(e, iddata).KuaishouHandler(WorkData);
21555
+ const groupId = e.isGroup ? e.contact?.peer || "" : "";
21556
+ const userId = e.userId || "";
21557
+ if (groupId && userId) try {
21558
+ await (await getStatisticsDB()).recordParse(groupId, userId, "kuaishou");
21559
+ } catch (error) {
21560
+ logger.debug("[统计] 记录快手解析统计失败:", error);
21561
+ }
21103
21562
  }, { businessName: "快手视频解析" });
21104
21563
  var handleXiaohongshu = wrapWithErrorHandler(async (e) => {
21105
21564
  const url = e.msg.replaceAll("\\", "").match(/https?:\/\/[^\s"'<>]+/)?.[0];
@@ -21109,6 +21568,13 @@ var handleXiaohongshu = wrapWithErrorHandler(async (e) => {
21109
21568
  }
21110
21569
  const iddata = await getXiaohongshuID(url);
21111
21570
  await new Xiaohongshu(e, iddata).XiaohongshuHandler(iddata);
21571
+ const groupId = e.isGroup ? e.contact?.peer || "" : "";
21572
+ const userId = e.userId || "";
21573
+ if (groupId && userId) try {
21574
+ await (await getStatisticsDB()).recordParse(groupId, userId, "xiaohongshu");
21575
+ } catch (error) {
21576
+ logger.debug("[统计] 记录小红书解析统计失败:", error);
21577
+ }
21112
21578
  return true;
21113
21579
  }, { businessName: "小红书视频解析" });
21114
21580
  var handlePrefix = wrapWithErrorHandler(async (e, next) => {
@@ -21353,4 +21819,4 @@ const update = karin$1.task("kkk-更新检测", "*/5 * * * *", Handler, {
21353
21819
  name: "kkk-更新检测",
21354
21820
  log: false
21355
21821
  });
21356
- export { removeOldFiles as C, Root as D, web_config_default as E, init_root as O, dylogin as S, webConfig as T, setdyPush as _, douyinAPP as a, version as b, xiaohongshuAPP as c, bilibiliPushList as d, changeBotID as f, setbiliPush as g, forcePush as h, bilibiliAPP as i, qrLogin as l, douyinPushList as m, kkkUpdateCommand as n, kuaishouAPP as o, douyinPush as p, update as r, prefix as s, kkkUpdate as t, bilibiliPush as u, testDouyinPush as v, task as w, biLogin as x, help as y };
21822
+ export { init_root as A, biLogin as C, webConfig as D, task as E, web_config_default as O, version as S, removeOldFiles as T, forcePush as _, douyinAPP as a, testDouyinPush as b, xiaohongshuAPP as c, qrLogin as d, bilibiliPush as f, douyinPushList as g, douyinPush as h, bilibiliAPP as i, Root as k, globalStatistics as l, changeBotID as m, kkkUpdateCommand as n, kuaishouAPP as o, bilibiliPushList as p, update as r, prefix as s, kkkUpdate as t, groupStatistics as u, setbiliPush as v, dylogin as w, help as x, setdyPush as y };