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 +83 -21
- package/lib/membershipSystem.d.ts +5 -0
- package/lib/queueSystem.d.ts +4 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
1600
|
-
|
|
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"
|
|
1637
|
-
const usedTokens = Object.entries(this.
|
|
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}.
|
|
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"
|
|
3251
|
-
const
|
|
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"
|
|
3270
|
-
const
|
|
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;
|
package/lib/queueSystem.d.ts
CHANGED
|
@@ -28,10 +28,13 @@ export declare class QueueSystem {
|
|
|
28
28
|
private redrawWaitQueue;
|
|
29
29
|
maxConcurrentTasks: number;
|
|
30
30
|
private membershipSystem;
|
|
31
|
-
|
|
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): {
|