koishi-plugin-hhs-huatu 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -471,9 +471,6 @@ async function login(ctx, email, password) {
471
471
  let startIndex = ctx.runtime.currentTokenIndex;
472
472
  let strategy = ctx.config.tokenStrategy || "round-robin";
473
473
  logger.debug(`使用token策略: ${strategy}`);
474
- if (strategy === "parallel" && !ctx.runtime.tokenUsage) {
475
- ctx.runtime.tokenUsage = {};
476
- }
477
474
  if (strategy === "random") {
478
475
  startIndex = Math.floor(Math.random() * ctx.config.token.length);
479
476
  ctx.runtime.currentTokenIndex = startIndex;
@@ -481,9 +478,9 @@ async function login(ctx, email, password) {
481
478
  let foundUnusedToken = false;
482
479
  for (let i = 0; i < ctx.config.token.length; i++) {
483
480
  const checkIndex = (ctx.runtime.currentTokenIndex + i) % ctx.config.token.length;
484
- if (!ctx.runtime.tokenUsage[checkIndex]) {
481
+ const tokenUsage = ctx.runtime.tokenUsage || {};
482
+ if (!tokenUsage[checkIndex]) {
485
483
  startIndex = checkIndex;
486
- ctx.runtime.tokenUsage[checkIndex] = true;
487
484
  ctx.runtime.currentTokenIndex = (checkIndex + 1) % ctx.config.token.length;
488
485
  foundUnusedToken = true;
489
486
  logger.debug(`并行策略选择token索引 ${checkIndex},下一次从索引 ${ctx.runtime.currentTokenIndex} 开始检查`);
@@ -1523,12 +1520,19 @@ __name(auditImage, "auditImage");
1523
1520
 
1524
1521
  // src/queueSystem.ts
1525
1522
  var QueueSystem = class {
1526
- constructor(ctx, config, generateImageFn, membershipSystem) {
1523
+ constructor(ctx, config, generateImageFn, membershipSystem, initialTokenUsage) {
1527
1524
  this.ctx = ctx;
1528
1525
  this.config = config;
1529
1526
  this.generateImageFn = generateImageFn;
1530
1527
  this.membershipSystem = membershipSystem || null;
1531
1528
  this.maxConcurrentTasks = config.tokenStrategy === "parallel" && Array.isArray(config.token) ? Math.min(config.maxConcurrentRequests || 1, config.token.length) : config.maxConcurrentRequests || 1;
1529
+ if (initialTokenUsage) {
1530
+ this.tokenUsage = { ...initialTokenUsage };
1531
+ } else if (config.tokenStrategy === "parallel" && Array.isArray(config.token)) {
1532
+ for (let i = 0; i < config.token.length; i++) {
1533
+ this.tokenUsage[i] = false;
1534
+ }
1535
+ }
1532
1536
  }
1533
1537
  static {
1534
1538
  __name(this, "QueueSystem");
@@ -1552,6 +1556,9 @@ var QueueSystem = class {
1552
1556
  maxConcurrentTasks;
1553
1557
  // 会员系统引用
1554
1558
  membershipSystem = null;
1559
+ // Token管理相关
1560
+ tokenLock = Promise.resolve();
1561
+ tokenUsage = /* @__PURE__ */ Object.create(null);
1555
1562
  // 获取锁
1556
1563
  async acquireRedrawLock() {
1557
1564
  if (!this.redrawLock) {
@@ -1589,6 +1596,20 @@ var QueueSystem = class {
1589
1596
  this.usedTokenIndices.add(newIndex);
1590
1597
  return newIndex;
1591
1598
  }
1599
+ // 获取token锁,确保token选择的原子性
1600
+ async acquireTokenLock() {
1601
+ const release = this.tokenLock;
1602
+ let releaseFn = null;
1603
+ this.tokenLock = new Promise((resolve3) => {
1604
+ releaseFn = resolve3;
1605
+ });
1606
+ await release;
1607
+ return () => {
1608
+ if (releaseFn) {
1609
+ releaseFn();
1610
+ }
1611
+ };
1612
+ }
1592
1613
  // 处理队列
1593
1614
  async processQueue() {
1594
1615
  if (this.taskQueue.length === 0) return;
@@ -1596,10 +1617,8 @@ var QueueSystem = class {
1596
1617
  const tasksToProcess = Math.min(this.maxConcurrentTasks - this.processingTasks, this.taskQueue.length);
1597
1618
  if (this.config.tokenStrategy === "parallel" && Array.isArray(this.ctx.config.token)) {
1598
1619
  this.ctx.logger.debug(`处理队列中的任务,当前处理中: ${this.processingTasks},即将处理: ${tasksToProcess},队列剩余: ${this.taskQueue.length},最大并发: ${this.maxConcurrentTasks}`);
1599
- if (this.ctx.runtime?.tokenUsage) {
1600
- const usedTokens = Object.entries(this.ctx.runtime.tokenUsage).filter(([_, used]) => used).map(([index]) => index);
1601
- this.ctx.logger.debug(`当前token使用情况: 已用 ${usedTokens.length}/${this.ctx.config.token.length} (索引: ${usedTokens.join(", ")})`);
1602
- }
1620
+ const usedTokens = Object.entries(this.tokenUsage).filter(([_, used]) => used).map(([index]) => index);
1621
+ this.ctx.logger.debug(`当前token使用情况: 已用 ${usedTokens.length}/${this.ctx.config.token.length} (索引: ${usedTokens.join(", ")})`);
1603
1622
  }
1604
1623
  for (let i = 0; i < tasksToProcess; i++) {
1605
1624
  this.processingTasks++;
@@ -1625,6 +1644,7 @@ var QueueSystem = class {
1625
1644
  }
1626
1645
  }
1627
1646
  Promise.resolve().then(async () => {
1647
+ const releaseTokenLock = await this.acquireTokenLock();
1628
1648
  try {
1629
1649
  const result = await this.generateImageFn(task.session, task.options, task.input);
1630
1650
  task.resolve(result);
@@ -1632,9 +1652,10 @@ var QueueSystem = class {
1632
1652
  task.reject(err);
1633
1653
  } finally {
1634
1654
  this.processingTasks--;
1655
+ releaseTokenLock();
1635
1656
  if (Array.isArray(this.ctx.config.token)) {
1636
- if (this.config.tokenStrategy === "parallel" && this.ctx.runtime?.tokenUsage) {
1637
- const usedTokens = Object.entries(this.ctx.runtime.tokenUsage).filter(([_, used]) => used).map(([index]) => index);
1657
+ if (this.config.tokenStrategy === "parallel") {
1658
+ const usedTokens = Object.entries(this.tokenUsage).filter(([_, used]) => used).map(([index]) => index);
1638
1659
  this.ctx.logger.debug(`任务完成后token使用情况(parallel): 已用 ${usedTokens.length}/${this.ctx.config.token.length} (索引: ${usedTokens.join(", ")})`);
1639
1660
  } else if (this.config.tokenStrategy === "round-robin") {
1640
1661
  this.ctx.logger.debug(`任务完成后token索引(round-robin): ${this.ctx.runtime?.currentTokenIndex}/${this.ctx.config.token.length - 1}`);
@@ -1852,7 +1873,7 @@ var MembershipSystem = class {
1852
1873
  let message = "【会员到期提醒】\n以下会员即将到期:\n\n";
1853
1874
  expiringMembers.forEach((member, index) => {
1854
1875
  const expireDate = new Date(this.userData[member.userId].membershipExpiry).toLocaleString();
1855
- message += `${index + 1}. 用户ID: ${member.userId}
1876
+ message += `${index + 1}. @${member.userId}
1856
1877
  剩余时间: ${member.remainingHours} 小时
1857
1878
  到期时间: ${expireDate}
1858
1879
 
@@ -1865,7 +1886,7 @@ var MembershipSystem = class {
1865
1886
  for (const bot of bots) {
1866
1887
  try {
1867
1888
  await bot.sendMessage(groupId, message);
1868
- this.ctx.logger.info(`已向群组 ${groupId} 发送会员到期提醒`);
1889
+ this.ctx.logger.info(`已向群组 ${groupId} 发送会员到期提醒(包含@功能)`);
1869
1890
  sent = true;
1870
1891
  break;
1871
1892
  } catch (err) {
@@ -1919,6 +1940,28 @@ var MembershipSystem = class {
1919
1940
  scheduleReminder();
1920
1941
  }
1921
1942
  }
1943
+ // 给所有会员增加天数
1944
+ async addDaysToAllMembers(days) {
1945
+ if (!this.config.membershipEnabled) {
1946
+ return { success: false, count: 0, message: "会员系统未启用" };
1947
+ }
1948
+ const now = Date.now();
1949
+ let updatedCount = 0;
1950
+ const daysInMs = days * 24 * 60 * 60 * 1e3;
1951
+ for (const userId in this.userData) {
1952
+ const user = this.userData[userId];
1953
+ if (user.isMember && user.membershipExpiry > now) {
1954
+ user.membershipExpiry += daysInMs;
1955
+ updatedCount++;
1956
+ this.ctx.logger.info(`已为会员 ${userId} 增加 ${days} 天,到期时间:${new Date(user.membershipExpiry).toLocaleString()}`);
1957
+ }
1958
+ }
1959
+ if (updatedCount > 0) {
1960
+ await this.saveUserData();
1961
+ }
1962
+ const message = updatedCount > 0 ? `✅ 成功为 ${updatedCount} 位会员增加 ${days} 天会员时长` : "⚠️ 当前没有有效会员可增加天数";
1963
+ return { success: true, count: updatedCount, message };
1964
+ }
1922
1965
  // 检查并重置每日使用次数
1923
1966
  checkAndResetDailyUsage(userId) {
1924
1967
  if (!this.userData[userId]) {
@@ -2168,6 +2211,16 @@ function apply(ctx, config) {
2168
2211
  const tasks = /* @__PURE__ */ Object.create(null);
2169
2212
  const globalTasks = /* @__PURE__ */ new Set();
2170
2213
  let queueSystem;
2214
+ const syncTokenUsage = /* @__PURE__ */ __name(() => {
2215
+ if (ctx.runtime && Array.isArray(ctx.config.token)) {
2216
+ if (!ctx.runtime.tokenUsage) {
2217
+ ctx.runtime.tokenUsage = {};
2218
+ for (let i = 0; i < ctx.config.token.length; i++) {
2219
+ ctx.runtime.tokenUsage[i] = false;
2220
+ }
2221
+ }
2222
+ }
2223
+ }, "syncTokenUsage");
2171
2224
  let tokenTask = null;
2172
2225
  const getToken = /* @__PURE__ */ __name(async (session) => {
2173
2226
  try {
@@ -2284,6 +2337,7 @@ function apply(ctx, config) {
2284
2337
  }
2285
2338
  return { width, height, custom: true };
2286
2339
  }, "resolution");
2340
+ syncTokenUsage();
2287
2341
  async function generateImage(session, options2, input) {
2288
2342
  ctx.logger.debug(`generateImage开始处理,sessionId=${session.id},userId=${session.userId}`);
2289
2343
  const sessionObj = session;
@@ -2940,7 +2994,7 @@ function apply(ctx, config) {
2940
2994
  }
2941
2995
  }
2942
2996
  __name(generateImage, "generateImage");
2943
- queueSystem = new QueueSystem(ctx, config, generateImage, membershipSystem);
2997
+ queueSystem = new QueueSystem(ctx, config, generateImage, membershipSystem, ctx.runtime?.tokenUsage);
2944
2998
  const cmd = ctx.command("novelai [prompts...]").alias("nai").alias("imagine").alias("nai4", { options: { model: "nai-v4-full", sampler: "k_euler_a", iterations: 1, batch: 1 } }).alias("nai4c", { options: { model: "nai-v4-curated-preview", sampler: "k_euler_a", iterations: 1, batch: 1 } }).alias("nai4-5c", { options: { model: "nai-v4-5-curated", sampler: "k_euler_a", iterations: 1, batch: 1 } }).alias("nai4-5", { options: { model: "nai-v4-5-full", sampler: "k_euler_a", iterations: 1, batch: 1 } }).userFields(["authority"]).shortcut("imagine", { i18n: true, fuzzy: true }).shortcut("enhance", { i18n: true, fuzzy: true, options: { enhance: true } }).option("enhance", "-e", { hidden: some(restricted, thirdParty, noImage) }).option("model", "-m <model>", { type: models, hidden: thirdParty }).option("resolution", "-r <resolution>", { type: resolution2 }).option("output", "-o", { type: ["minimal", "default", "verbose"] }).option("override", "-O", { hidden: restricted }).option("sampler", "-s <sampler>").option("seed", "-x <seed:number>").option("steps", "-t <step>", { type: step, hidden: restricted }).option("scale", "-c <scale:number>").option("rescale", "-R <rescale:number>").option("noise", "-n <noise:number>", { hidden: some(restricted, thirdParty) }).option("strength", "-N <strength:number>", { hidden: restricted }).option("hiresFix", "-H", { hidden: /* @__PURE__ */ __name(() => config.type !== "sd-webui", "hidden") }).option("hiresFixSteps", "<step>", { type: step, hidden: /* @__PURE__ */ __name(() => config.type !== "sd-webui", "hidden") }).option("smea", "-S", { hidden: /* @__PURE__ */ __name(() => config.model !== "nai-v3", "hidden") }).option("smeaDyn", "-d", { hidden: /* @__PURE__ */ __name(() => config.model !== "nai-v3", "hidden") }).option("scheduler", "-C <scheduler:string>", {
2945
2999
  hidden: /* @__PURE__ */ __name(() => config.type === "naifu", "hidden"),
2946
3000
  type: ["token", "login"].includes(config.type) ? scheduler.nai : config.type === "sd-webui" ? scheduler.sd : config.type === "stable-horde" ? scheduler.horde : []
@@ -3247,8 +3301,9 @@ function apply(ctx, config) {
3247
3301
  queueSystem.userTasks[currentUserId]--;
3248
3302
  targetBot.sendMessage(targetChannelId, value);
3249
3303
  if (Array.isArray(ctx.config.token)) {
3250
- if (config.tokenStrategy === "parallel" && ctx.runtime?.tokenUsage) {
3251
- const usedTokens = Object.entries(ctx.runtime.tokenUsage).filter(([_, used]) => used).map(([index2]) => index2);
3304
+ if (config.tokenStrategy === "parallel") {
3305
+ const queueTokenUsage = queueSystem.tokenUsage || {};
3306
+ const usedTokens = Object.entries(queueTokenUsage).filter(([_, used]) => used).map(([index2]) => index2);
3252
3307
  ctx.logger.debug(`重画任务 ${taskUniqueId} 完成,当前token使用情况: ${usedTokens.join(", ")}`);
3253
3308
  } else if (config.tokenStrategy === "round-robin") {
3254
3309
  ctx.logger.debug(`重画任务 ${taskUniqueId} 完成,当前token索引: ${ctx.runtime?.currentTokenIndex}`);
@@ -3266,8 +3321,9 @@ function apply(ctx, config) {
3266
3321
  handleError(ctx, session, err)
3267
3322
  );
3268
3323
  if (Array.isArray(ctx.config.token)) {
3269
- if (config.tokenStrategy === "parallel" && ctx.runtime?.tokenUsage) {
3270
- const usedTokens = Object.entries(ctx.runtime.tokenUsage).filter(([_, used]) => used).map(([index2]) => index2);
3324
+ if (config.tokenStrategy === "parallel") {
3325
+ const queueTokenUsage = queueSystem.tokenUsage || {};
3326
+ const usedTokens = Object.entries(queueTokenUsage).filter(([_, used]) => used).map(([index2]) => index2);
3271
3327
  ctx.logger.debug(`重画任务 ${taskUniqueId} 失败,当前token使用情况: ${usedTokens.join(", ")}`);
3272
3328
  } else if (config.tokenStrategy === "round-robin") {
3273
3329
  ctx.logger.debug(`重画任务 ${taskUniqueId} 失败,当前token索引: ${ctx.runtime?.currentTokenIndex}`);
@@ -3490,13 +3546,19 @@ function apply(ctx, config) {
3490
3546
  ]);
3491
3547
  });
3492
3548
  if (config.memberDebugCommandEnabled) {
3493
- ctx.command("novelai.member-debug", "会员系统调试指令").userFields(["authority"]).alias("会员调试").option("cleanup", "-c 立即执行会员信息清理").option("remind", "-r 立即执行会员到期提醒").option("status", "-s 查看定时任务状态").option("resetUsage", "-u <userId:string> 重置指定用户的使用次数").action(async ({ session, options: options2 }) => {
3549
+ ctx.command("novelai.member-debug", "会员系统调试指令").userFields(["authority"]).alias("会员调试").option("cleanup", "-c 立即执行会员信息清理").option("remind", "-r 立即执行会员到期提醒").option("status", "-s 查看定时任务状态").option("resetUsage", "-u <userId:string> 重置指定用户的使用次数").option("addDaysAll", "-a <days:number> 给所有会员增加天数").action(async ({ session, options: options2 }) => {
3494
3550
  if (session.user.authority < config.memberDebugCommandAuthLv) {
3495
3551
  return `权限不足,需要权限等级 ${config.memberDebugCommandAuthLv} 或以上`;
3496
3552
  }
3497
3553
  if (!config.membershipEnabled) {
3498
3554
  return "会员系统未启用";
3499
3555
  }
3556
+ if (options2.addDaysAll !== void 0) {
3557
+ const days = options2.addDaysAll;
3558
+ await session.send(`正在为所有会员增加 ${days} 天会员时长...`);
3559
+ const result = await membershipSystem.addDaysToAllMembers(days);
3560
+ return result.message;
3561
+ }
3500
3562
  if (options2.resetUsage) {
3501
3563
  const targetId = options2.resetUsage;
3502
3564
  if (!userData[targetId]) {
@@ -3608,7 +3670,7 @@ function apply(ctx, config) {
3608
3670
  await membershipSystem.checkAndRemindExpiringMembers();
3609
3671
  return "✅ 提醒完成!请查看控制台日志获取详细信息。";
3610
3672
  }
3611
- return "请使用以下选项:\n-c 立即执行会员信息清理\n-r 立即执行会员到期提醒\n-s 查看定时任务状态\n-u 重置指定用户的使用次数";
3673
+ return "请使用以下选项:\n-c 立即执行会员信息清理\n-r 立即执行会员到期提醒\n-s 查看定时任务状态\n-u 重置指定用户的使用次数\n-a <天数> 给所有会员增加天数";
3612
3674
  });
3613
3675
  }
3614
3676
  if (config.directorToolsEnabled !== false) {
@@ -17,6 +17,11 @@ export declare class MembershipSystem {
17
17
  checkAndRemindExpiringMembers(): Promise<void>;
18
18
  private setupCleanupTask;
19
19
  private setupReminderTask;
20
+ addDaysToAllMembers(days: number): Promise<{
21
+ success: boolean;
22
+ count: number;
23
+ message: string;
24
+ }>;
20
25
  checkAndResetDailyUsage(userId: string): void;
21
26
  canUseDrawing(userId: string, session: Session): boolean | string;
22
27
  incrementUsage(userId: string, drawCount?: number): void;
@@ -28,10 +28,13 @@ export declare class QueueSystem {
28
28
  private redrawWaitQueue;
29
29
  maxConcurrentTasks: number;
30
30
  private membershipSystem;
31
- constructor(ctx: Context, config: Config, generateImageFn: (session: Session<'authority'>, options: any, input: string) => Promise<any>, membershipSystem?: MembershipSystem);
31
+ private tokenLock;
32
+ private tokenUsage;
33
+ constructor(ctx: Context, config: Config, generateImageFn: (session: Session<'authority'>, options: any, input: string) => Promise<any>, membershipSystem?: MembershipSystem, initialTokenUsage?: Dict<boolean>);
32
34
  acquireRedrawLock(): Promise<void>;
33
35
  releaseRedrawLock(): void;
34
36
  getUniqueTokenIndex(currentIndex: number, tokenCount: number): number;
37
+ private acquireTokenLock;
35
38
  processQueue(): Promise<void>;
36
39
  addTask(task: TaskQueueItem): Promise<any>;
37
40
  getQueueStatus(userId: string): {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-hhs-huatu",
3
3
  "description": "Generate images by diffusion models",
4
- "version": "0.3.2",
4
+ "version": "0.3.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [