koishi-plugin-best-jrrp
Version:
支持真随机的今日人品(运势),可任意自定义消息,支持随机图片,还带有排行、分析、货币等功能。
984 lines (977 loc) • 62.3 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name2 in all)
__defProp(target, name2, { get: all[name2], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
Config: () => Config,
JrrpAlgorithm: () => JrrpAlgorithm,
apply: () => apply,
inject: () => inject,
name: () => name,
usage: () => usage
});
module.exports = __toCommonJS(src_exports);
var import_koishi = require("koishi");
// src/fortunecalc.ts
var FortuneCalc = class {
static {
__name(this, "FortuneCalc");
}
algorithm;
apiKey;
/**
* 创建人品计算器实例
* @param {JrrpAlgorithm} algorithm - 计算算法
* @param {string} [apiKey] - Random.org API密钥
*/
constructor(algorithm, apiKey) {
this.algorithm = algorithm;
this.apiKey = apiKey;
}
/**
* 计算人品值
* @param {string} userId - 用户ID
* @param {string} date - 日期字符串
* @returns {Promise<FortuneResult>} 计算结果
*/
async calculate(userId, date) {
if (this.algorithm === "randomorg" /* RANDOMORG */ && this.apiKey) {
const score2 = await this.fetchRandom();
if (score2 !== null) {
return { score: score2, actualAlgorithm: "randomorg" /* RANDOMORG */ };
}
}
const seed = this.generateSeed(userId, date);
let score;
if (this.algorithm === "gaussian" /* GAUSSIAN */) {
const u1 = Math.abs(Math.sin(seed) * 1e4 % 1) || 1e-4, u2 = Math.abs(Math.sin(seed + 127) * 1e4 % 1) || 1e-4, z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
score = Math.max(0, Math.min(100, Math.round(z * 15 + 50)));
} else {
score = Math.floor((1664525 * seed + 1013904223) % 2 ** 32 / 2 ** 32 * 101);
}
return { score, actualAlgorithm: this.algorithm };
}
/**
* 从Random.org获取真随机数
* @private
* @returns {Promise<number|null>} 随机数,失败返回null
*/
async fetchRandom() {
if (!this.apiKey) return null;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 2e3);
const response = await fetch("https://api.random.org/json-rpc/4/invoke", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
method: "generateIntegers",
params: { apiKey: this.apiKey, n: 1, min: 0, max: 100 },
id: 1
}),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) return null;
const data = await response.json();
return data?.result?.random?.data?.[0] ?? null;
} catch {
return null;
}
}
/**
* 根据用户ID和日期生成种子
* @private
* @param {string} userId - 用户ID
* @param {string} dateStr - 日期字符串
* @returns {number} 生成的种子
*/
generateSeed(userId, dateStr) {
let hash = 0, str = userId + dateStr;
for (let i = 0; i < str.length; i++)
hash = (hash << 5) - hash + str.charCodeAt(i), hash |= 0;
return Math.abs(hash);
}
};
// src/fortunestore.ts
var FortuneStore = class {
/**
* 构造函数,初始化数据库表结构
* @param {Context} ctx Koishi 上下文
*/
constructor(ctx) {
this.ctx = ctx;
ctx.model.extend("jrrp", {
userId: "string",
username: "string",
algorithm: "string",
date: "string",
score: "integer"
}, {
primary: ["userId", "date"],
indexes: ["date"]
});
}
static {
__name(this, "FortuneStore");
}
/**
* 清理字符串,移除不可见字符和特殊字符,限制长度
* @param {string} input 输入字符串
* @returns {string} 清理后的字符串
*/
sanitizeString(input) {
return input ? String(input).replace(/[\x00-\x1F\x7F\u200B-\u200F\u2028-\u202F\uFEFF]|[<>`$()[\]{};'"\\\=]|\s+/g, " ").replace(/(.)\1{3,}/g, "$1$1$1$1$1…").trim().slice(0, 64) : "";
}
/**
* 查询用户人品数据
* @param {string} userId 用户ID
* @param {string} [date] 可选,指定日期,默认为今天
* @returns {Promise<FortuneData|null>} 查询结果
*/
async getFortune(userId, date) {
const actualDate = date || (/* @__PURE__ */ new Date()).toLocaleDateString("sv-SE");
const records = await this.ctx.database.get("jrrp", { userId, date: actualDate });
if (!records.length) return null;
const { username, algorithm, score } = records[0];
return { username, algorithm, score };
}
/**
* 保存人品数据
* @param {string} userId 用户ID
* @param {FortuneData} fortune 人品数据
* @returns {Promise<boolean>} 是否保存成功
*/
async save(userId, fortune) {
try {
await this.ctx.database.upsert("jrrp", [{
userId,
date: (/* @__PURE__ */ new Date()).toLocaleDateString("sv-SE"),
username: this.sanitizeString(fortune.username),
algorithm: fortune.algorithm,
score: fortune.score
}]);
return true;
} catch (e) {
return false;
}
}
/**
* 获取所有今日人品数据,按分数排序
* @returns {Promise<Array<{userId: string, data: FortuneData}>>}
*/
async getAllTodayFortunes() {
const today = (/* @__PURE__ */ new Date()).toLocaleDateString("sv-SE");
const records = await this.ctx.database.select("jrrp").where({ date: today }).orderBy("score", "desc").execute();
return records.map((r) => ({
userId: r.userId,
data: { username: r.username, algorithm: r.algorithm, score: r.score }
}));
}
/**
* 清除人品数据
* @param {string} [userId] 可选,指定用户ID
* @param {string} [date] 可选,指定日期
* @param {boolean} [dropTable] 可选,是否直接删除数据表
* @returns {Promise<number>} 清除的记录数量
*/
async clearData(userId, date, dropTable) {
if (dropTable) {
try {
await this.ctx.database.drop("jrrp");
return -1;
} catch (e) {
return 0;
}
}
const query = {};
if (userId) query.userId = userId;
if (date) query.date = date;
if (Object.keys(query).length === 0) return 0;
try {
const result = await this.ctx.database.remove("jrrp", query);
return result.matched;
} catch {
return 0;
}
}
/**
* 获取用户和全局统计数据的比较
* @param {string} userId 用户ID
* @returns {Promise<StatsComparison>} 用户和全局统计数据比较结果
*/
async getStatsComparison(userId) {
try {
const recentCutoffDateStr = new Date(Date.now() - 30 * 24 * 3600 * 1e3).toLocaleDateString("sv-SE");
const [userAllTimeRecords, userRecentRecords, globalRecentScores] = await Promise.all([
this.ctx.database.select("jrrp").where({ userId }).orderBy("date", "desc").execute(),
this.ctx.database.select("jrrp").where({ userId, date: { $gte: recentCutoffDateStr } }).orderBy("date", "desc").execute(),
this.ctx.database.select("jrrp").where({ date: { $gte: recentCutoffDateStr } }).project(["score"]).execute()
]);
const globalRecentRecords = globalRecentScores.map((s) => ({ score: s.score }));
const userAllTime = this.analyzeData(userAllTimeRecords, 10);
const userRecent = this.analyzeData(userRecentRecords);
const globalRecent = this.analyzeData(globalRecentRecords);
return { userAllTime, userRecent, globalRecent };
} catch (error) {
const emptyResult = { count: 0, mean: 0, median: 0, stdDev: 0, min: 0, max: 0, recentScores: [] };
return { userAllTime: emptyResult, userRecent: emptyResult, globalRecent: emptyResult };
}
}
/**
* 分析人品数据集合
* @param {JrrpEntry[]} records 人品记录集合
* @param {number} [recentLimit] 最近记录的限制数量
* @returns {AnalysisResult} 分析结果
*/
analyzeData(records, recentLimit) {
if (!records?.length) return {
count: 0,
mean: 0,
median: 0,
stdDev: 0,
min: 0,
max: 0,
recentScores: [],
distribution: { low: 0, medium: 0, high: 0 }
};
const scores = records.map((e) => e.score);
const count = scores.length;
const sorted = [...scores].sort((a, b) => a - b);
const sum = scores.reduce((a, b) => a + b, 0);
const mean = sum / count;
const stdDev = Math.sqrt(scores.reduce((a, s) => a + Math.pow(s - mean, 2), 0) / count);
const distribution = {
low: scores.filter((s) => s <= 33).length / count,
medium: scores.filter((s) => s > 33 && s <= 66).length / count,
high: scores.filter((s) => s > 66).length / count
};
const histogram = Array(10).fill(0);
scores.forEach((s) => histogram[Math.min(Math.floor(s / 10), 9)]++);
const maxBin = Math.max(...histogram);
const normalizedHistogram = maxBin > 0 ? histogram.map((bin) => bin / maxBin) : histogram;
let luckType = "均衡";
if (distribution.high > 0.5) luckType = "较高";
else if (distribution.low > 0.5) luckType = "较低";
else if (stdDev > 30) luckType = "极端";
else if (distribution.medium > 0.6) luckType = "中庸";
const entropy = this.calculateEntropy(normalizedHistogram, count);
const balance = (mean - 50) * 2;
const extremeRate = scores.filter((s) => s <= 10 || s >= 90).length / count * 100;
const heatmap = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
scores.forEach((s) => heatmap[Math.min(Math.floor(s / 10), 9)]++);
const trendGraph = this.generateTrendGraph(scores.slice(0, Math.min(recentLimit || 10, scores.length)));
return {
count,
mean,
stdDev,
min: sorted[0],
max: sorted[count - 1],
median: count % 2 ? sorted[Math.floor(count / 2)] : (sorted[count / 2 - 1] + sorted[count / 2]) / 2,
recentScores: recentLimit ? scores.slice(0, recentLimit) : void 0,
luckType,
distribution,
histogram: normalizedHistogram,
entropy,
balance,
extremeRate,
heatmap,
trendGraph
};
}
/**
* 计算熵指数
* @param {number[]} histogram 直方图数据
* @param {number} count 总记录数
* @returns {number} 熵指数(0-100)
*/
calculateEntropy(histogram, count) {
if (count === 0) return 0;
const probabilities = histogram.map((h) => h / count).filter((p) => p > 0);
if (probabilities.length === 0) return 0;
const rawEntropy = -probabilities.reduce((sum, p) => sum + p * Math.log2(p), 0);
const maxEntropy = Math.log2(probabilities.length);
if (maxEntropy === 0) return 0;
return Math.min(100, rawEntropy / maxEntropy * 100);
}
/**
* 生成走势图
* @param {number[]} scores 分数列表
* @returns {string} 走势图字符串
*/
generateTrendGraph(scores) {
if (!scores.length) return "";
const chars = " ▂▃▄▅▆▇█";
const min = Math.min(...scores);
const max = Math.max(...scores);
const range = max - min || 1;
return scores.map((score) => {
const normalized = (score - min) / range;
const charIndex = Math.min(Math.floor(normalized * chars.length), chars.length - 1);
return chars[charIndex];
}).join("");
}
};
// src/expressions.ts
var expressions = {
/**
* 简单表达式集合
* 每个分数对应多个简单的等价数学表达式
* @type {Record<number, string[]>}
*/
simple: {
0: ["(4-4)/(7+6)", "2/(1*2)-1", "(8-8)/(3+7)", "(9-9)/(5*3)", "(9-9)/5*3"],
1: ["(1/9)*(6+3)", "1/5*(4+1)", "1/9*(6+3)", "8*(4-5)+9", "(1/5)*(4+1)"],
2: ["4-(5/1)+3", "(7+9)/(9-1)", "(2/1)*(2-1)", "2/1*(2-1)", "(4-5)/1+3"],
3: ["(6*4)/2-9", "6*(4/2)-9", "1*(4+1)-2", "(7+8)/(7-2)", "(1*4)+1-2"],
4: ["(7-3)/6*6", "(7+5)/6*2", "3*2+(7-9)", "7-(3/6)*6", "(3*2)+7-9"],
5: ["(9*2)-(9+4)", "3+(8*1)/4", "3+8*(1/4)", "9*2-(9+4)", "(5*2)+1-6"],
6: ["7-2+(8/8)", "(5*3)/3+1", "(9-7)/2+5", "(7-2)+8/8", "5*(3/3)+1"],
7: ["(9/9)*3+4", "2/(4-3)+5", "(9/9)*(3+4)", "7+(9/9)-1", "9/9*(3+4)"],
8: ["8+(8-8)/7", "(2*4)-9+9", "2+2*(6/2)", "2+(2*6)/2", "9-7/(2+5)"],
9: ["(2/1)*1+7", "2*(8+1)/2", "2/(1*1)+7", "5*(2+1)-6", "5-(2*2)+8"],
10: ["(5*2)+(8-8)", "5*2+(8-8)", "9+3/(1*3)", "(6/2)*6-8", "(5*2)+8-8"],
11: ["4+(8-7)*7", "(4+7)*(5-4)", "4+7*(5-4)", "2*(4+7)/2", "6+(3*2)-1"],
12: ["9+3*(2-1)", "(2+2)*(6/2)", "4*(9+6)/5", "6+(8-6)*3", "(2+2)*6/2"],
13: ["4*(4-2)+5", "(4-9)+(3*6)", "(9*2)-9+4", "(4-9)+3*6", "4-9+(3*6)"],
14: ["7/(7-6)*2", "(6*3)+4-8", "9+(3*2)-1", "6*3+(4-8)", "(6*3)+(4-8)"],
15: ["(9*8)/9+7", "7+(8/9)*9", "9*(8/9)+7", "(7+8)/9*9", "5+2*(7-2)"],
16: ["(8/4)+2*7", "2/1*(1+7)", "(8/4)+(2*7)", "(2/1)*(1+7)", "8/4+(2*7)"],
17: ["5/5+(2*8)", "5+(2*7)-2", "(6+3)*2-1", "(5/5)+(2*8)", "(5/5)+2*8"],
18: ["3*(2+7)-9", "(8*4)-(5+9)", "8*4-(5+9)", "4*5+(4-6)", "(4*5)+4-6"],
19: ["(4*4)-2+5", "6*(4-1)+1", "(8-1)+4*3", "8-1+(4*3)", "(8-1)+(4*3)"],
20: ["(2*8)+8-4", "2*8+(8-4)", "(4*6)-(1+3)", "(2*8)+(8-4)", "4*6-(1+3)"],
21: ["9*3-(5+1)", "(8*2)-3+8", "(5*6)-(7+2)", "5*6-(7+2)", "(9*3)-(5+1)"],
22: ["6+4*(6-2)", "4*6+(1-3)", "6*4-(1+1)", "(6*4)-(1+1)", "(4*6)+1-3"],
23: ["(9/3)+4*5", "9/3+(4*5)", "(9/3)+(4*5)", "4*(6-1)+3", "(9*3)-5+1"],
24: ["(6*4)-1+1", "8*(1+5)/2", "6*(9+7)/4", "(5+3)*(6/2)", "(5+3)*6/2"],
25: ["(9/1)+4*4", "(9/1)+(4*4)", "(5*6)-7+2", "9/1+(4*4)", "4*(6+1)-3"],
26: ["(7*3)-2+7", "(4*6)+7-5", "(5-3)*(4+9)", "(4*6)-1+3", "(5*5)-4+5"],
27: ["(7*3)+9-3", "(7*3)+(9-3)", "7*3+(8-2)", "(7*3)+8-2", "7*3+(9-3)"],
28: ["7/3*(7+5)", "(1-5)+4*8", "(7/3)*(7+5)", "2*(8+8)-4", "6+(4*6)-2"],
29: ["(6-2)+(5*5)", "(6-2)+5*5", "2+(6/2)*9", "6-2+(5*5)", "1+(9-5)*7"],
30: ["(5-2)*(2+8)", "(8*5)-(6+4)", "4*(5+4)-6", "7*(6-2)+2", "8*5-(6+4)"],
31: ["(9*3)-2+6", "(9-3)*4+7", "5*(8-3)+6", "5*8-(3+6)", "(5*8)-(3+6)"],
32: ["8-6+(5*6)", "(8-6)+(5*6)", "2*(8+9)-2", "(8-6)+5*6", "(7*4)+4/1"],
33: ["9*(3/1)+6", "9/3*(3+8)", "(9*3)/1+6", "4+(9*4)-7", "7+(9*3)-1"],
34: ["(4/2)+4*8", "4/2+(4*8)", "9*(3/1)+7", "(4/2)+(4*8)", "(9*3)/1+7"],
35: ["4+(7*5)-4", "(4*9)+4-5", "4*9+(4-5)", "(5+2)*(7-2)", "(4*9)+(4-5)"],
36: ["(7*5)-7+8", "(6-2)*(2+7)", "4+(9*4)-4", "(8*4)-5+9", "(9+3)/1*3"],
37: ["(3-2)+(6*6)", "(3-2)+6*6", "3-2+(6*6)", "(4*9)-4+5", "(3+2)*9-8"],
38: ["7*6-(2+2)", "(7*6)-(2+2)", "6*(3+4)-4", "(8*5)-6+4", "2+9*(9-5)"],
39: ["7*(9-4)+4", "(9-2)+4*8", "9-2+(4*8)", "(9-2)+(4*8)", "1+(9*5)-7"],
40: ["(7-3)*8+8", "(6+4)*(6-2)", "5*(9-1)/1", "(3+5)*(5/1)", "(3+5)*5/1"],
41: ["7*(2+4)-1", "(5*8)+7-6", "5+(4*9)/1", "(5*8)+(7-6)", "5*8+(7-6)"],
42: ["3-3+(6*7)", "5*(2+8)-8", "(3-3)+(6*7)", "(7*6)-2+2", "(3-3)+6*7"],
43: ["(7/1)*5+8", "(1+9)*5-7", "5*9-(2/1)", "(5*8)-3+6", "(5*9)-2/1"],
44: ["8*(7+4)/2", "5*(8/1)+4", "(5*8)/1+4", "(2+9)*(9-5)", "(9-3)*7+2"],
45: ["3*(7+9)-3", "(7-2)*(4+5)", "8*6+(4-7)", "(8*6)+(4-7)", "(8*6)+4-7"],
46: ["(7*6)+5-1", "6*9+(1-9)", "7*6+(5-1)", "(6*9)+1-9", "(6*9)+(1-9)"],
47: ["7-8+(6*8)", "(5+2)*7-2", "4*(9+4)-5", "(7-8)+(6*8)", "(7-8)+6*8"],
48: ["8*8-(7+9)", "(3-3)+8*6", "(8*8)-(7+9)", "3-3+(8*6)", "(4+9)*4-4"],
49: ["(7-1)*8+1", "4*(6+7)-3", "(9/1)*5+4", "7*(2+6)-7", "8*(6-1)+9"],
50: ["(8+2)*5/1", "(8+2)*(5/1)", "(9*5)+7-2", "4*(5+8)-2", "9*5+(7-2)"],
51: ["(4+7)*5-4", "6*(2+7)-3", "5*(7+5)-9", "6*(9+1)-9", "(9-3)*7+9"],
52: ["8*7-(3+1)", "(8*7)-(3+1)", "7+(8-3)*9", "7+(8*6)-3", "(6-1)*9+7"],
53: ["(5*9)+9-1", "7+(9*6)-8", "(5*9)+(9-1)", "(9*6)-5+4", "5*9+(9-1)"],
54: ["(2-2)+6*9", "(2-2)+(6*9)", "(8*7)-3+1", "2-2+(6*9)", "(9-3)*(7+2)"],
55: ["(3-2)+6*9", "(3-2)+(6*9)", "(7*9)-(4+4)", "7*9-(4+4)", "3-2+(6*9)"],
56: ["7*(4+4)/1", "8*(4+3)/1", "9*(4+3)-7", "8*(6+2)-8", "(9-2)*8/1"],
57: ["(8*7)+(5-4)", "(8*7)+5-4", "8*7+(5-4)", "3+(8*7)-2", "8*8-(5+2)"],
58: ["8*7+(4/2)", "(8*7)+(4/2)", "(6+4)*6-2", "(8*7)+4/2", "(7*9)+4-9"],
59: ["6*(5+5)-1", "(8*7)-5+8", "8*(8-1)+3", "5+(8-2)*9", "(4+6)*6-1"],
60: ["(6+4)*(6/1)", "(6+4)*6/1", "8*8-(1+3)", "(8*8)-(1+3)", "8/2*(7+8)"],
61: ["(9-1)*7+5", "(9*6)-2+9", "(7-2)+(8*7)", "7-2+(8*7)", "(7-2)+8*7"],
62: ["8*8-(1+1)", "(8*8)+7-9", "7*(2+7)-1", "8*8+(7-9)", "(8*8)+(7-9)"],
63: ["7-7+(7*9)", "9*(8/1)-9", "(9*8)/1-9", "(7-7)+7*9", "(7*9)-4+4"],
64: ["9*7+(8/8)", "(7-3)*(8+8)", "(9*7)+8/8", "(9*7)+3/3", "(9*7)+(8/8)"],
65: ["6-5+(8*8)", "(6-5)+8*8", "9+(9-1)*7", "(2+9)*6-1", "(7+6)*(9-4)"],
66: ["(9-3)*(4+7)", "3/1+(7*9)", "(3/1)+7*9", "(8*8)-7+9", "(3/1)+(7*9)"],
67: ["(4-1)+8*8", "(4-1)+(8*8)", "4-1+(8*8)", "9*(5+3)-5", "(6+9)*5-8"],
68: ["6*(7+5)-4", "9*(8/1)-4", "(9*8)-5+1", "(9*8)/1-4", "7*(4+6)-2"],
69: ["(8*9)+(3-6)", "8*9+(3-6)", "(8*9)+3-6", "8*(8+1)-3", "5*(8+7)-6"],
70: ["7/1*(7+3)", "(6-1)*(5+9)", "(7/1)*(7+3)", "7*(5+5)/1", "(7+7)*(9-4)"],
71: ["8+(9/1)*7", "(7*9)+(9-1)", "7*9+(9-1)", "(7*9)+9-1", "4*(9+9)-1"],
72: ["(9*8)+6-6", "9*8+(6-6)", "4*(9+9)/1", "(9*8)+(6-6)", "5*(7+8)-3"],
73: ["6+(9*8)-5", "8*(6+4)-7", "8/8+(9*8)", "(8/8)+(9*8)", "(8/8)+9*8"],
74: ["9+(9*8)-7", "(4/2)+9*8", "8-6+(9*8)", "(8-6)+9*8", "(8-6)+(9*8)"],
75: ["(3+8)*7-2", "(5-2)+8*9", "(5-2)+(8*9)", "5-2+(8*9)", "7*(3+8)-2"],
76: ["9*(2+7)-5", "(7-3)+8*9", "(9+3)*7-8", "7*(6+5)-1", "(5+5)*8-4"],
77: ["(6+8)*6-7", "7+(8*9)-2", "(3-7)+(9*9)", "3-7+(9*9)", "(3-7)+9*9"],
78: ["5*(8+9)-7", "(9*8)-2+8", "(7+5)*7-6", "2+(9*9)-5", "(8+9)*5-7"],
79: ["(9-1)*9+7", "8*(7+3)-1", "7+(9*9)-9", "(5+9)*6-5", "9*(4+5)-2"],
80: ["(2+8)*(8/1)", "8*(1+9)/1", "(2+8)*8/1", "8+(9/1)*8", "(6-1)*(9+7)"],
81: ["9/1*(5+4)", "(9/1)*(5+4)", "(5+4)*9/1", "9*(2+8)-9", "7*(3+9)-3"],
82: ["(9+9)*5-8", "9*(7+3)-8", "7*(9+4)-9", "8*(7+4)-6", "(6+8)*6-2"],
83: ["(9+1)*9-7", "(8+9)*5-2", "(8+7)*6-7", "7+(9*9)-5", "9*(9+1)-7"],
84: ["(8-1)*(9+3)", "(4+8)*7/1", "(4+8)*(7/1)", "(9+3)/1*7", "(9-3)*(5+9)"],
85: ["4+9*(9/1)", "(6-2)+(9*9)", "4+(9*9)/1", "(6-2)+9*9", "6-2+(9*9)"],
86: ["9*(9/1)+5", "(9*9)/1+5", "7*(8+5)-5", "(3+8)*8-2", "6*(7+8)-4"],
87: ["6+9*(9/1)", "6+(9*9)/1", "6*(8+7)-3", "6*(8+8)-9", "(7+8)*6-3"],
88: ["5*(9+9)-2", "(7+4)*8/1", "(8+8)*6-8", "9*(2+8)-2", "(7+9)*6-8"],
89: ["(9+1)*9-1", "(2+8)*9-1", "5*(9+9)-1", "(3+9)*8-7", "7*(7+6)-2"],
90: ["(9/1)*(2+8)", "(9+6)*(6/1)", "9/1*(2+8)", "(9+6)*6/1", "(7+3)/1*9"],
91: ["7/1*(5+8)", "6*(9+7)-5", "(8+5)*(7/1)", "(7/1)*(5+8)", "(8+5)*7/1"],
92: ["8*(7+5)-4", "(4+7)*9-7", "9*(7+4)-7", "(8+8)*6-4", "(9+5)*7-6"],
93: ["7*(6+8)-5", "(4+8)*8-3", "(4+7)*9-6", "9*(8+3)-6", "(6+6)*8-3"],
94: ["9*(4+7)-5", "(5+6)*9-5", "(2+9)*9-5", "(8+3)*9-5", "6*(8+8)-2"],
95: ["6*(8+8)-1", "(7+9)*6-1", "9*(3+8)-4", "(4+9)*8-9", "(5+7)*8-1"],
96: ["(9-1)*(7+5)", "(9-3)*(7+9)", "(9-3)*(8+8)", "6*(9+8)-6", "8*(6+7)-8"],
97: ["8*(5+8)-7", "(9+5)*7-1", "(7+4)*9-2", "8*(7+6)-7", "7*(7+8)-8"],
98: ["(9+5)*7/1", "(9+5)*(7/1)", "7*(8+6)/1", "8*(9+4)-6", "(7+7)*(9-2)"],
99: ["7*(7+8)-6", "9*(8+3)/1", "(8+3)*9/1", "(8+3)*(9/1)", "(9+3)*9-9"],
100: ["(9+8)*6-2", "(6+9)*7-5", "(9+4)*8-4", "9*(3+9)-8", "7*(8+7)-5"]
},
/**
* 复杂表达式集合
* 每个分数对应多个较复杂的等价数学表达式
* @type {Record<number, string[]>}
*/
complex: {
0: ["2-2", "3-3", "4-4", "5-5", "6-6", "7-7", "8-8", "9-9"],
1: ["2/2", "3/3", "4/4", "5/5", "6/6", "7/7", "8/8", "9/9"],
2: ["2+2-2", "(3+3)/3", "(4+4)/4", "(5+5)/5", "(6+6)/6", "(7+7)/7", "(8+8)/8", "(9+9)/9"],
3: ["2+2/2", "3+3-3", "4-4/4", "(5+5+5)/5", "(6+6+6)/6", "(7+7+7)/7", "(8+8+8)/8", "(9+9+9)/9"],
4: ["2+2", "3+3/3", "4+4-4", "5-5/5", "6-((6+6)/6)", "(7+7+7+7)/7", "8/((8+8)/8)", "(9+9+9+9)/9"],
5: ["2+2+2/2", "3+3-3/3", "4+4/4", "5+5-5", "6-6/6", "7-((7+7)/7)", "8-((8+8+8)/8)", "(9+9+9+9+9)/9"],
6: ["2+2+2", "3+3", "(4+4)/4+4", "5+5/5", "6+6-6", "7-7/7", "8-((8+8)/8)", "9-((9+9+9)/9)"],
7: ["2+2+2+2/2", "3+3+3/3", "4+4-4/4", "(5+5)/5+5", "6+6/6", "7+7-7", "8-8/8", "9-((9+9)/9)"],
8: ["(2+2)*2", "3*3-3/3", "4+4", "(5+5+5)/5+5", "(6+6)/6+6", "7+7/7", "8+8-8", "9-9/9"],
9: ["(2+2)*2+2/2", "3*3", "4+4+4/4", "5+5-5/5", "(6+6+6)/6+6", "(7+7)/7+7", "8+8/8", "9+9-9"],
10: ["2+2*2*2", "3*3+3/3", "(4+4)/4+4+4", "5+5", "6-((6+6)/6-6)", "(7+7+7)/7+7", "(8+8)/8+8", "9+9/9"],
11: ["(2+2+2)*2-2/2", "3+3*3-3/3", "4+4+4-4/4", "5+5+5/5", "6+6-6/6", "(7+7+7+7)/7+7", "(8+8+8)/8+8", "(9+9)/9+9"],
12: ["(2+2+2)*2", "3+3*3", "4+4+4", "(5+5)/5+5+5", "6+6", "7-((7+7)/7-7)", "8/((8+8)/8)+8", "(9+9+9)/9+9"],
13: ["(2+2+2)*2+2/2", "3+3*3+3/3", "4+4+4+4/4", "(5+5+5)/5+5+5", "6+6+6/6", "7+7-7/7", "8-((8+8+8)/8-8)", "(9+9+9+9)/9+9"],
14: ["(2+2+2)*2+2", "3+3+3*3-3/3", "(4+4)/4+4+4+4", "5+5+5-5/5", "(6+6)/6+6+6", "7+7", "8-((8+8)/8-8)", "(9+9+9+9+9)/9+9"],
15: ["(2+2)*2*2-2/2", "3+3+3*3", "4*4-4/4", "5+5+5", "(6+6+6)/6+6+6", "7+7+7/7", "8+8-8/8", "9-((9+9+9)/9-9)"],
16: ["(2+2)*2*2", "3+3+3*3+3/3", "4*4", "5+5+5+5/5", "6-((6+6)/6-6-6)", "(7+7)/7+7+7", "8+8", "9-((9+9)/9-9)"],
17: ["(2+2)*2*2+2/2", "(3+3)*3-3/3", "4*4+4/4", "(5+5)/5+5+5+5", "6+6+6-6/6", "(7+7+7)/7+7+7", "8+8+8/8", "9+9-9/9"],
18: ["2+2*2*2*2", "(3+3)*3", "(4+4)/4+4*4", "(5+5+5)/5+5+5+5", "6+6+6", "(7+7+7+7)/7+7+7", "(8+8)/8+8+8", "9+9"],
19: ["2+2*2*2*2+2/2", "(3+3)*3+3/3", "4+4*4-4/4", "5*5-5-5/5", "6+6+6+6/6", "7-((7+7)/7-7-7)", "(8+8+8)/8+8+8", "9+9+9/9"],
20: ["(2+2*2*2)*2", "(3+3)*3+3-3/3", "4+4*4", "5*5-5", "(6+6)/6+6+6+6", "7+7+7-7/7", "8/((8+8)/8)+8+8", "(9+9)/9+9+9"],
21: ["(2+2*2*2)*2+2/2", "(3+3)*3+3", "4+4*4+4/4", "5*5+5/5-5", "(6+6+6)/6+6+6+6", "7+7+7", "8-((8+8+8)/8-8-8)", "(9+9+9)/9+9+9"],
22: ["(2+2+2)*2*2-2", "(3+3)*3+3+3/3", "(4+4)/4+4+4*4", "(5+5)/5+5*5-5", "6-((6+6)/6-6-6-6)", "7+7+7+7/7", "8-((8+8)/8-8-8)", "(9+9+9+9)/9+9+9"],
23: ["(2+2+2)*2*2-2/2", "3*3*3-3-3/3", "4+4+4*4-4/4", "(5-(5+5)/5/5)*5", "6+6+6+6-6/6", "(7+7)/7+7+7+7", "8+8+8-8/8", "(9+9+9+9+9)/9+9+9"],
24: ["(2+2+2)*2*2", "3*3*3-3", "4+4+4*4", "5*5-5/5", "6+6+6+6", "(7+7+7)/7+7+7+7", "8+8+8", "9-((9+9+9)/9-9-9)"],
25: ["(2+2+2)*2*2+2/2", "3*3*3+3/3-3", "4+4+4*4+4/4", "5*5", "6+6+6+6+6/6", "(7+7+7+7)/7+7+7+7", "8+8+8+8/8", "9-((9+9)/9-9-9)"],
26: ["(2+2+2)*2*2+2", "3*3*3-3/3", "(4+4)/4+4+4+4*4", "5*5+5/5", "(6+6)/6+6+6+6+6", "7-((7+7)/7-7-7-7)", "(8+8)/8+8+8+8", "9+9+9-9/9"],
27: ["((2+2+2)*2+2)*2-2/2", "3*3*3", "(4+4)*4-4-4/4", "(5+5)/5+5*5", "6/((6+6)/6+6)*6*6", "7+7+7+7-7/7", "(8+8+8)/8+8+8+8", "9+9+9"],
28: ["((2+2+2)*2+2)*2", "3*3*3+3/3", "(4+4)*4-4", "(5+5+5)/5+5*5", "(6-(6+6)/6/6)*6-6", "7+7+7+7", "8/((8+8)/8)+8+8+8", "9+9+9+9/9"],
29: ["((2+2+2)*2+2)*2+2/2", "3+3*3*3-3/3", "(4+4)*4+4/4-4", "5+5*5-5/5", "6*6-6-6/6", "7+7+7+7+7/7", "8-((8+8+8)/8-8-8-8)", "(9+9)/9+9+9+9"],
30: ["(2+2)*2*2*2-2", "3+3*3*3", "(4+4)*4-4/4-4/4", "5+5*5", "6*6-6", "(7+7)/7+7+7+7+7", "8-((8+8)/8-8-8-8)", "(9+9+9)/9+9+9+9"],
31: ["(2+2)*2*2*2-2/2", "3+3*3*3+3/3", "(4+4)*4-4/4", "5+5*5+5/5", "6*6+6/6-6", "(7+7+7)/7+7+7+7+7", "8+8+8+8-8/8", "(9+9+9+9)/9+9+9+9"],
32: ["(2+2)*2*2*2", "3+3+3*3*3-3/3", "(4+4)*4", "(5+5)/5+5+5*5", "(6+6)/6+6*6-6", "8+8+8+8"],
33: ["(2+2)*2*2*2+2/2", "3+3+3*3*3", "(4+4)*4+4/4", "(5+5+5)/5+5+5*5", "(6/(6+6)+6)*6-6", "7-((7+7)/7-7-7-7-7)", "8+8+8+8+8/8", "9-((9+9+9)/9-9-9-9)"],
34: ["2+2*2*2*2*2", "3+3+3*3*3+3/3", "(4/(4+4)+4+4)*4", "5+5+5*5-5/5", "(6-(6+6)/6/6)*6", "7*7-7-7-7/7", "(8+8)/8+8+8+8+8", "9-((9+9)/9-9-9-9)"],
35: ["2+2*2*2*2*2+2/2", "(3+3*3)*3-3/3", "(4+4)*4+4-4/4", "5+5+5*5", "6*6-6/6", "7*7-7-7", "(8+8+8)/8+8+8+8+8", "9+9+9+9-9/9"],
36: ["(2+2*2*2*2)*2", "(3+3*3)*3", "(4+4)*4+4", "5+5+5*5+5/5", "6*6", "7*7+7/7-7-7", "8/((8+8)/8)+8+8+8+8", "9+9+9+9"],
37: ["(2+2*2*2*2)*2+2/2", "(3+3*3)*3+3/3", "(4+4)*4+4+4/4", "(5+5)/5+5+5+5*5", "6*6+6/6", "(7+7)/7+7*7-7-7", "9+9+9+9+9/9"],
38: ["(2+2*2*2*2)*2+2", "(3+3*3)*3+3-3/3", "(4/(4+4)+4+4)*4+4", "(5+5+5)/5+5+5+5*5", "(6+6)/6+6*6", "(7+7+7)/7+7*7-7-7", "8-((8+8)/8-8-8-8-8)", "(9+9)/9+9+9+9+9"],
39: ["(2+2*2*2)*2*2-2/2", "(3+3*3)*3+3", "(4+4)*4+4+4-4/4", "5+5+5+5*5-5/5", "(6/(6+6)+6)*6", "(7-(7+7+7)/7/7)*7-7", "8+8+8+8+8-8/8", "(9+9+9)/9+9+9+9+9"],
40: ["(2+2*2*2)*2*2", "(3+3*3)*3+3+3/3", "(4+4)*4+4+4", "5+5+5+5*5", "6-((6+6)/6-6*6)", "(7-(7+7)/7/7)*7-7", "8+8+8+8+8"],
41: ["(2+2*2*2)*2*2+2/2", "(3+3+3*3)*3-3-3/3", "(4+4)*4+4+4+4/4", "5+5+5+5*5+5/5", "6+6*6-6/6", "7*7-7-7/7", "8+8+8+8+8+8/8"],
42: ["(2+2*2*2)*2*2+2", "(3+3+3*3)*3-3", "(4/(4+4)+4+4)*4+4+4", "(5+5)/5+5+5+5+5*5", "6+6*6", "7*7-7", "(8+8)/8+8+8+8+8+8"],
43: ["(3+3+3*3)*3+3/3-3", "(4+4+4)*4-4-4/4", "(5+5)*5-5-5/5-5/5", "6+6*6+6/6", "7*7+7/7-7", "9-((9+9)/9-9-9-9-9)"],
44: ["((2+2+2)*2*2-2)*2", "(3+3+3*3)*3-3/3", "(4+4+4)*4-4", "(5+5)*5-5-5/5", "(6+6)/6+6+6*6", "(7+7)/7+7*7-7", "(8/(8+8)+8)*8-8-8-8", "9+9+9+9+9-9/9"],
45: ["(3+3+3*3)*3", "(4+4+4)*4+4/4-4", "(5+5)*5-5", "(6/(6+6)+6)*6+6", "(7+7+7)/7+7*7-7", "9+9+9+9+9"],
46: ["(2+2+2)*2*2*2-2", "(3+3+3*3)*3+3/3", "(4+4+4)*4-4/4-4/4", "(5+5)*5+5/5-5", "6-((6+6)/6-6-6*6)", "(7-(7+7+7)/7/7)*7", "(8-(8+8)/8/8)*8-8-8", "9+9+9+9+9+9/9"],
47: ["(2+2+2)*2*2*2-2/2", "(3+3+3*3)*3+3-3/3", "(4+4+4)*4-4/4", "(5-(5+5+5)/5/5+5)*5", "6+6+6*6-6/6", "(7-(7+7)/7/7)*7", "8*8-8-8-8/8", "(9+9)/9+9+9+9+9+9"],
48: ["(2+2+2)*2*2*2", "(3+3+3*3)*3+3", "(4+4+4)*4", "(5+5)*5-5/5-5/5", "6+6+6*6", "7*7-7/7", "8*8-8-8"],
49: ["(2+2+2)*2*2*2+2/2", "(3+3+3*3)*3+3+3/3", "(4+4+4)*4+4/4", "(5+5)*5-5/5", "6+6+6*6+6/6", "7*7", "8*8+8/8-8-8"],
50: ["(2+2+2)*2*2*2+2", "(3+3)*3*3-3-3/3", "(4/(4+4)+4+4+4)*4", "(5+5)*5", "(6+6)/6+6+6+6*6", "7*7+7/7", "(8+8)/8+8*8-8-8"],
51: ["(3+3)*3*3-3", "(4+4+4)*4+4-4/4", "(5+5)*5+5/5", "(6/(6+6)+6)*6+6+6", "(7+7)/7+7*7", "(8+8+8)/8+8*8-8-8"],
52: ["((2+2+2)*2*2+2)*2", "(3+3)*3*3+3/3-3", "(4+4+4)*4+4", "(5+5)*5+5/5+5/5", "6-((6+6)/6-6-6-6*6)", "(7+7+7)/7+7*7", "(8/(8+8)+8)*8-8-8"],
53: ["(3+3)*3*3-3/3", "(4+4+4)*4+4+4/4", "(5+5+5)/5+5*5+5*5", "6+6+6+6*6-6/6", "(7+7+7+7)/7+7*7", "(8-(8+8+8)/8/8)*8-8", "9*9-9-9-9-9/9"],
54: ["((2+2+2)*2+2)*2*2-2", "(3+3)*3*3", "(4/(4+4)+4+4+4)*4+4", "(5+5)*5+5-5/5", "6+6+6+6*6", "7-((7+7)/7-7*7)", "(8-(8+8)/8/8)*8-8", "9*9-9-9-9"],
55: ["(3+3)*3*3+3/3", "4*4*4-4-4-4/4", "(5+5)*5+5", "6+6+6+6*6+6/6", "7+7*7-7/7", "8*8-8-8/8", "9*9+9/9-9-9-9"],
56: ["((2+2+2)*2+2)*2*2", "(3+3)*3*3+3-3/3", "4*4*4-4-4", "(5+5)*5+5+5/5", "(6+6)/6+6+6+6+6*6", "7+7*7", "8*8-8", "(9+9)/9+9*9-9-9-9"],
57: ["(3+3)*3*3+3", "4*4*4+4/4-4-4", "(5+5)*5+5+5/5+5/5", "(6/(6+6)+6)*6+6+6+6", "7+7*7+7/7", "8*8+8/8-8"],
58: ["((2+2+2)*2+2)*2*2+2", "(3+3)*3*3+3+3/3", "(4+4)/4+4*4*4-4-4", "(7+7)/7+7+7*7", "(8+8)/8+8*8-8"],
59: ["((3+3)*3+3)*3-3-3/3", "4*4*4-4-4/4", "(5+5)*5+5+5-5/5", "(6+6)*6-6-6-6/6", "(7+7+7)/7+7+7*7", "(8+8+8)/8+8*8-8"],
60: ["((2+2)*2*2*2-2)*2", "((3+3)*3+3)*3-3", "4*4*4-4", "(5+5)*5+5+5", "(6+6)*6-6-6", "(7+7+7+7)/7+7+7*7", "(8/(8+8)+8)*8-8"],
61: ["((3+3)*3+3)*3+3/3-3", "4*4*4+4/4-4", "(5+5)*5+5+5+5/5", "(6+6)*6+6/6-6-6", "7-((7+7)/7-7-7*7)", "(8-(8+8+8)/8/8)*8", "(9-(9+9)/9/9)*9-9-9"],
62: ["(2+2)*2*2*2*2-2", "((3+3)*3+3)*3-3/3", "(4+4)/4+4*4*4-4", "7+7+7*7-7/7", "(8-(8+8)/8/8)*8", "9*9-9-9-9/9"],
63: ["(2+2)*2*2*2*2-2/2", "((3+3)*3+3)*3", "4*4*4-4/4", "(6/(6+6)+6+6)*6-6-6", "7+7+7*7", "8*8-8/8", "9*9-9-9"],
64: ["(2+2)*2*2*2*2", "((3+3)*3+3)*3+3/3", "4*4*4", "(5+5+5)*5-5-5-5/5", "(6+6)*6-6-6/6-6/6", "7+7+7*7+7/7", "8*8", "9*9+9/9-9-9"],
65: ["(2+2)*2*2*2*2+2/2", "((3+3)*3+3)*3+3-3/3", "4*4*4+4/4", "(5+5+5)*5-5-5", "(6+6)*6-6-6/6", "(7+7)/7+7+7+7*7", "8*8+8/8", "(9+9)/9+9*9-9-9"],
66: ["2+2*2*2*2*2*2", "((3+3)*3+3)*3+3", "(4+4)/4+4*4*4", "(5+5+5)*5+5/5-5-5", "(6+6)*6-6", "(7+7+7)/7+7+7+7*7", "(8+8)/8+8*8", "(9+9+9)/9+9*9-9-9"],
67: ["((3+3)*3+3)*3+3+3/3", "4+4*4*4-4/4", "(6+6)*6+6/6-6", "(8+8+8)/8+8*8"],
68: ["(2+2*2*2*2*2)*2", "(3*3*3-3)*3-3-3/3", "4+4*4*4", "(6/(6+6+6)+6+6)*6-6", "7-((7+7)/7-7-7-7*7)", "(8/(8+8)+8)*8"],
69: ["(3*3*3-3)*3-3", "4+4*4*4+4/4", "(5+5+5)*5-5-5/5", "(6/(6+6)+6+6)*6-6", "7+7+7+7*7-7/7", "8-((8+8+8)/8-8*8)", "(9-(9+9+9)/9/9)*9-9"],
70: ["(2+2*2*2*2*2)*2+2", "(3*3*3-3)*3+3/3-3", "(4+4)/4+4+4*4*4", "(5+5+5)*5-5", "(6+6)*6-6/6-6/6", "7+7+7+7*7", "8-((8+8)/8-8*8)", "(9-(9+9)/9/9)*9-9"],
71: ["(3*3*3-3)*3-3/3", "4+4+4*4*4-4/4", "(5+5+5)*5+5/5-5", "(6+6)*6-6/6", "7+7+7+7*7+7/7", "8+8*8-8/8", "9*9-9-9/9"],
72: ["(2+2*2*2*2)*2*2", "(3*3*3-3)*3", "4+4+4*4*4", "(6+6)*6", "(7+7)/7+7+7+7+7*7", "8+8*8", "9*9-9"],
73: ["(3*3*3-3)*3+3/3", "4+4+4*4*4+4/4", "(5+5+5)*5-5/5-5/5", "(6+6)*6+6/6", "8+8*8+8/8", "9*9+9/9-9"],
74: ["(2+2*2*2*2)*2*2+2", "3-((3-3*3*3)*3+3/3)", "(4+4)/4+4+4+4*4*4", "(5+5+5)*5-5/5", "(6/(6+6+6)+6+6)*6", "(8+8)/8+8+8*8", "(9+9)/9+9*9-9"],
75: ["3-((3-3*3*3)*3)", "(4+4*4)*4-4-4/4", "(5+5+5)*5", "(6/(6+6)+6+6)*6", "(8+8+8)/8+8+8*8", "(9+9+9)/9+9*9-9"],
76: ["((2+2*2*2*2)*2+2)*2", "3-((3-3*3*3)*3-3/3)", "(4+4*4)*4-4", "(5+5+5)*5+5/5", "6-(6/(6+6+6)-6-6)*6", "7+7+7+7+7*7-7/7", "(8/(8+8)+8)*8+8", "(9+9+9+9)/9+9*9-9"],
77: ["3*3*3*3-3-3/3", "(4+4*4)*4+4/4-4", "(5+5+5)*5+5/5+5/5", "(6+6)*6+6-6/6", "7+7+7+7+7*7", "8-((8+8+8)/8-8-8*8)", "(9-(9+9+9+9)/9/9)*9"],
78: ["(2+2*2*2)*2*2*2-2", "3*3*3*3-3", "(4+4*4)*4-4/4-4/4", "(6+6)*6+6", "7+7+7+7+7*7+7/7", "8-((8+8)/8-8-8*8)", "(9-(9+9+9)/9/9)*9"],
79: ["3*3*3*3+3/3-3", "(4+4*4)*4-4/4", "(5+5+5)*5+5-5/5", "(6+6)*6+6+6/6", "8+8+8*8-8/8", "(9-(9+9)/9/9)*9"],
80: ["(2+2*2*2)*2*2*2", "3*3*3*3-3/3", "(4+4*4)*4", "(5+5+5)*5+5", "(6/(6+6+6)+6+6)*6+6", "8+8+8*8", "9*9-9/9"],
81: ["3*3*3*3", "(4+4*4)*4+4/4", "(5+5+5)*5+5+5/5", "(6/(6+6)+6+6)*6+6", "8+8+8*8+8/8", "9*9"],
82: ["(2+2*2*2)*2*2*2+2", "3*3*3*3+3/3", "(4/(4+4)+4+4*4)*4", "(8+8)/8+8+8+8*8", "9*9+9/9"],
83: ["3+3*3*3*3-3/3", "(4+4*4)*4+4-4/4", "(6+6)*6+6+6-6/6", "(7+7)*7-7-7-7/7", "(8+8+8)/8+8+8+8*8", "(9+9)/9+9*9"],
84: ["((2+2*2*2)*2*2+2)*2", "3+3*3*3*3", "(4+4*4)*4+4", "(5+5+5)*5+5+5-5/5", "(6+6)*6+6+6", "(7+7)*7-7-7", "(8/(8+8)+8)*8+8+8", "(9+9+9)/9+9*9"],
85: ["3+3*3*3*3+3/3", "(4+4*4)*4+4+4/4", "(5+5+5)*5+5+5", "(6+6)*6+6+6+6/6", "(7+7)*7+7/7-7-7", "(9+9+9+9)/9+9*9"],
86: ["3+3+3*3*3*3-3/3", "(4/(4+4)+4+4*4)*4+4", "(5+5+5)*5+5+5+5/5", "8-((8+8)/8-8-8-8*8)", "(9+9+9+9+9)/9+9*9"],
87: ["3+3+3*3*3*3", "(4+4*4)*4+4+4-4/4", "(6/(6+6)+6+6)*6+6+6", "8+8+8+8*8-8/8", "9-((9+9+9)/9-9*9)"],
88: ["((2+2+2)*2*2-2)*2*2", "3+3+3*3*3*3+3/3", "(4+4*4)*4+4+4", "8+8+8+8*8", "9-((9+9)/9-9*9)"],
89: ["(3+3*3*3)*3-3/3", "(4+4*4)*4+4+4+4/4", "(5*5-5)*5-5-5-5/5", "(6+6)*6+6+6+6-6/6", "(7+7)*7-7-7/7-7/7", "8+8+8+8*8+8/8", "9+9*9-9/9"],
90: ["(3+3*3*3)*3", "(5*5-5)*5-5-5", "(6+6)*6+6+6+6", "(7+7)*7-7-7/7", "(8+8)/8+8+8+8+8*8", "9+9*9"],
91: ["(3+3*3*3)*3+3/3", "(4+4+4*4)*4-4-4/4", "(5*5-5)*5+5/5-5-5", "(6+6)*6+6+6+6+6/6", "(7+7)*7-7", "9+9*9+9/9"],
92: ["((2+2+2)*2*2*2-2)*2", "(3+3*3*3)*3+3-3/3", "(4+4+4*4)*4-4", "(7+7)*7+7/7-7", "(8/(8+8)+8)*8+8+8+8", "(9+9)/9+9+9*9"],
93: ["(3+3*3*3)*3+3", "(4+4+4*4)*4+4/4-4", "(7+7)*7+7/7+7/7-7", "(9+9+9)/9+9+9*9"],
94: ["(2+2+2)*2*2*2*2-2", "(3+3*3*3)*3+3+3/3", "(5*5-5)*5-5-5/5", "(9+9+9+9)/9+9+9*9"],
95: ["(4+4+4*4)*4-4/4", "(5*5-5)*5-5", "(6+6+6)*6-6-6-6/6", "(7-(7+7+7)/7/7+7)*7", "8+8+8+8+8*8-8/8"],
96: ["(2+2+2)*2*2*2*2", "(3+3+3*3*3)*3-3", "(4+4+4*4)*4", "(5*5-5)*5+5/5-5", "(6+6+6)*6-6-6", "(7+7)*7-7/7-7/7", "8+8+8+8+8*8", "9-((9+9+9)/9-9-9*9)"],
97: ["(4+4+4*4)*4+4/4", "(6+6+6)*6+6/6-6-6", "(7+7)*7-7/7", "8+8+8+8+8*8+8/8", "9-((9+9)/9-9-9*9)"],
98: ["(2+2+2)*2*2*2*2+2", "(3+3+3*3*3)*3-3/3", "(4/(4+4)+4+4+4*4)*4", "(5*5-5)*5-5/5-5/5", "(7+7)*7", "9+9+9*9-9/9"],
99: ["(3+3+3*3*3)*3", "(4+4+4*4)*4+4-4/4", "(5*5-5)*5-5/5", "(7+7)*7+7/7", "9+9+9*9"],
100: ["((2+2+2)*2*2*2+2)*2", "(3+3+3*3*3)*3+3/3", "(4+4+4*4)*4+4", "(5*5-5)*5", "(7+7)*7+7/7+7/7", "9+9+9*9+9/9"]
}
};
// src/msgbuilder.ts
var import_fs = require("fs");
var import_path = require("path");
var MsgBuilder = class {
static {
__name(this, "MsgBuilder");
}
config;
dateSpecialMsgsMap = /* @__PURE__ */ new Map();
scoreSpecialMsgsMap = /* @__PURE__ */ new Map();
rangesCache = [];
pixivConfig;
/**
* 创建消息构建器实例
* @param {MsgConfig} config 消息配置
* @param {PixivConfig} [pixivConfig] Pixiv配置
*/
constructor(config, pixivConfig) {
this.config = config;
this.pixivConfig = pixivConfig || {};
this.initializeCache();
}
/**
* 初始化缓存数据
* @private
*/
initializeCache() {
if (this.config.specialMessages?.length && this.config.enableSpecial) {
for (const msg of this.config.specialMessages) {
const condition = String(msg.condition);
if (/^\d{1,2}-\d{1,2}$/.test(condition)) {
const msgs = this.dateSpecialMsgsMap.get(condition) || [];
msgs.push(msg);
this.dateSpecialMsgsMap.set(condition, msgs);
} else if (/^\d+$/.test(condition)) {
const score = parseInt(condition, 10);
const msgs = this.scoreSpecialMsgsMap.get(score) || [];
msgs.push(msg);
this.scoreSpecialMsgsMap.set(score, msgs);
}
}
}
if (this.config.rangeMessages?.length && this.config.enableRange) {
this.rangesCache = Array.from({ length: 101 }, () => []);
for (const range of this.config.rangeMessages) {
const min = Math.max(0, range.min);
const max = Math.min(100, range.max);
for (let i = min; i <= max; i++) {
this.rangesCache[i].push(range);
}
}
}
}
/**
* 获取一言内容
* @private
* @param {string} [params] 一言参数,直接拼接到API URL
* @returns {Promise<string>} 一言内容,包含出处和作者
*/
async getHitokoto(params) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3e3);
const url = `https://v1.hitokoto.cn/${params ? `?${params}` : ""}`;
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);
if (!response.ok) return "";
const data = await response.json();
if (!data?.hitokoto) return "";
if (!data.from) return data.hitokoto;
const citation = `——${data.from_who && data.from_who !== data.from ? ` ${data.from_who}` : ""}《${data.from}》`;
const getTextWidth = /* @__PURE__ */ __name((text) => [...text].reduce((w, c) => w + (/[\u4e00-\u9fa5\u3000-\u30ff\u3130-\u318f\uac00-\ud7af]/.test(c) ? 2 : 1), 0), "getTextWidth");
const spacesNeeded = Math.max(0, Math.min(getTextWidth(data.hitokoto), 36) - getTextWidth(citation));
return `${data.hitokoto}
${" ".repeat(spacesNeeded)}${citation}`;
} catch {
return "";
}
}
/**
* 获取Pixiv图片链接数组(本地无则自动下载)
*/
async getPixivLinks() {
const { baseDir = process.cwd(), imagesPath, logger = console } = this.pixivConfig;
if (!imagesPath) return [];
const isLocalPath = !imagesPath.startsWith("http://") && !imagesPath.startsWith("https://");
if (isLocalPath) {
try {
let dirPath = imagesPath;
if (!dirPath.startsWith("/") && !dirPath.match(/^[A-Za-z]:\\/)) {
dirPath = (0, import_path.resolve)(baseDir, dirPath);
}
const files = await import_fs.promises.readdir(dirPath, { withFileTypes: true });
const imageFiles = files.filter((file) => file.isFile() && /\.(jpe?g|png|gif|webp)$/i.test(file.name)).map((file) => (0, import_path.join)(dirPath, file.name));
return imageFiles;
} catch (e) {
logger.error("读取本地图片目录失败:", e);
return [];
}
} else {
const { resolve: resolve2 } = await import("path");
const { existsSync } = await import("fs");
const { readFile, writeFile } = await import("fs/promises");
const filePath = resolve2(baseDir, "data", "pixiv.json");
if (!existsSync(filePath)) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3e4);
const res = await fetch(imagesPath, { signal: controller.signal });
clearTimeout(timeout);
if (!res.ok) throw new Error(`下载失败: ${res.status}`);
await writeFile(filePath, await res.text(), "utf8");
} catch (e) {
logger.error("下载JSON文件失败:", e);
return [];
}
}
try {
const arr = JSON.parse(await readFile(filePath, "utf8"));
return Array.isArray(arr) ? arr : [];
} catch (e) {
logger.error("读取链接失败:", e);
return [];
}
}
}
/**
* 构建JRRP结果消息
* @param {number} score 今日人品分数
* @param {string} userId 用户ID
* @param {string} username 用户名
* @param {number} [money=0] 获得的货币数量
* @returns {Promise<string|string[]>} 格式化后的消息字符串或消息队列
*/
async build(score, userId, username, money = 0) {
const todayStr = `${(/* @__PURE__ */ new Date()).getMonth() + 1}-${(/* @__PURE__ */ new Date()).getDate()}`;
const formattedScore = this.formatScore(score, todayStr);
let template = this.config.template || "";
const message = template.includes("{message}") ? this.getMessage(score, todayStr).replace(/\\n/g, "\n") : "";
if (template.includes("{hitokoto")) {
template = await this.replaceHitokotoPlaceholders(template);
}
if (template.includes("{pixiv}")) {
template = await this.replacePixivPlaceholders(template);
}
let result = template.replace(/{at}/g, `<at id="${userId}"/>`).replace(/{username}/g, username).replace(/{score}/g, formattedScore).replace(/{message}/g, message).replace(/{money}/g, money > 0 ? String(money) : "").replace(/{image:([^}]+)}/g, '<image url="$1"/>');
if (result.includes("{~}")) {
return result.split("{~}").map((s) => s.trim()).filter(Boolean);
}
return result;
}
/**
* 替换所有一言占位符
* @private
* @param {string} template 模板字符串
* @returns {Promise<string>} 替换后的字符串
*/
async replaceHitokotoPlaceholders(template) {
const matches = [...template.matchAll(/{hitokoto(?::([^}]*))?}/g)];
if (!matches.length) return template;
const replacements = await Promise.all(
matches.map((match) => this.getHitokoto(match[1] || "").then((content) => ({ pattern: match[0], content })))
);
return replacements.reduce((result, { pattern, content }) => result.replace(pattern, content), template);
}
/**
* 替换所有Pixiv占位符
* @private
* @param {string} template 模板字符串
* @returns {Promise<string>} 替换后的字符串
*/
async replacePixivPlaceholders(template) {
const matches = [...template.matchAll(/{pixiv}/g)];
if (!matches.length) return template;
const arr = await this.getPixivLinks();
const replacements = await Promise.all(matches.map(async (m) => {
let content = "";
if (Array.isArray(arr) && arr.length) {
const candidate = arr[Math.floor(Math.random() * arr.length)];
try {
let buffer;
let mime;
if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
const res = await fetch(candidate, { headers: { "Referer": "https://www.pixiv.net/" } });
if (res.ok) {
buffer = Buffer.from(await res.arrayBuffer());
const ext = candidate.split(".").pop()?.toLowerCase() || "jpg";
mime = ext === "png" ? "image/png" : ext === "gif" ? "image/gif" : "image/jpeg";
} else {
throw new Error(`请求失败: ${res.status}`);
}
} else {
buffer = await import_fs.promises.readFile(candidate);
const ext = (0, import_path.extname)(candidate).slice(1).toLowerCase();
mime = ext === "png" ? "image/png" : ext === "gif" ? "image/gif" : ext === "webp" ? "image/webp" : "image/jpeg";
}
content = `<image src="base64://${buffer.toString("base64")}" type="${mime}"/>`;
} catch (e) {
this.pixivConfig.logger?.error("图片处理失败:", e);
}
}
return { pattern: m[0], content };
}));
return replacements.reduce((result, { pattern, content }) => result.replace(pattern, content), template);
}
/**
* 按优先级获取消息内容
* @private
* @param {number} score 分数
* @param {string} date 日期字符串
* @returns {string} 消息文本
*/
getMessage(score, date) {
if (this.config.enableSpecial) {
const dateMessages = this.dateSpecialMsgsMap.get(date);
if (dateMessages?.length) return dateMessages[Math.random() * dateMessages.length | 0].message;
const scoreMessages = this.scoreSpecialMsgsMap.get(score);
if (scoreMessages?.length) return scoreMessages[Math.random() * scoreMessages.length | 0].message;
}
if (this.config.enableRange && score >= 0 && score <= 100) {
const rangeMessages = this.rangesCache[score];
if (rangeMessages?.length) return rangeMessages[Math.random() * rangeMessages.length | 0].message;
}
return "";
}
/**
* 格式化分数显示
* @private
* @param {number} score 分数
* @param {string} todayStr 今日日期字符串
* @returns {string} 格式化后的分数字符串
*/
formatScore(score, todayStr) {
const { enabled, date, mode } = this.config.display;
if (!enabled || date && todayStr !== date) return score.toString();
switch (mode) {
case "binary":
return score.toString(2);
case "octal":
return score.toString(8);
case "hex":
return score.toString(16).toUpperCase();
case "simple":
case "complex": {
const exprList = (mode === "simple" ? expressions.simple : expressions.complex)[score];
return exprList?.length ? exprList[Math.random() * exprList.length | 0] : score.toString();
}
default:
return score.toString();
}
}
};
// src/index.ts
var name = "best-jrrp";
var inject = {
optional: ["monetary"],
required: ["database"]
};
var usage = `
<div style="border-radius: 10px; border: 1px solid #ddd; padding: 16px; margin-bottom: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);">
<h2 style="margin-top: 0; color: #4a6ee0;">📌 插件说明</h2>
<p>📖 <strong>使用文档</strong>:请点击左上角的 <strong>插件主页</strong> 查看插件使用文档</p>
<p>🔍 <strong>更多插件</strong>:可访问 <a href="https://github.com/YisRime" style="color:#4a6ee0;text-decoration:none;">苡淞的 GitHub</a> 查看本人的所有插件</p>
</div>
<div style="border-radius: 10px; border: 1px solid #ddd; padding: 16px; margin-bottom: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);">
<h2 style="margin-top: 0; color: #e0574a;">❤️ 支持与反馈</h2>
<p>🌟 喜欢这个插件?请在 <a href="https://github.com/YisRime" style="color:#e0574a;text-decoration:none;">GitHub</a> 上给我一个 Star!</p>
<p>🐛 遇到问题?请通过 <strong>Issues</strong> 提交反馈,或加入 QQ 群 <a href="https://qm.qq.com/q/PdLMx9Jowq" style="color:#e0574a;text-decoration:none;"><strong>855571375</strong></a> 进行交流</p>
</div>
`;
var JrrpAlgorithm = /* @__PURE__ */ ((JrrpAlgorithm2) => {
JrrpAlgorithm2["GAUSSIAN"] = "gaussian";
JrrpAlgorithm2["LINEAR"] = "linear";
JrrpAlgorithm2["RANDOMORG"] = "randomorg";
return JrrpAlgorithm2;
})(JrrpAlgorithm || {});
var Config = import_koishi.Schema.intersect([
import_koishi.Schema.object({
enableDate: import_koishi.Schema.boolean().description("启用日期查询").default(true),
enableScore: import_koishi.Schema.boolean().description("启用人品预测").default(true),
enableRank: import_koishi.Schema.boolean().description("启用人品排行").default(true),
enableAnalyse: import_koishi.Schema.boolean().description("启用数据分析").default(true),
algorithm: import_koishi.Schema.union([
import_koishi.Schema.const("gaussian" /* GAUSSIAN */).description("算法 - 正态分布"),
import_koishi.Schema.const("linear" /* LINEAR */).description("算法 - 线性同余"),
import_koishi.Schema.const("randomorg" /* RANDOMORG */).description("真随机 - Random.org")
]).default("linear" /* LINEAR */).description("计算方式"),
apiKey: import_koishi.Schema.string().description("密钥 - Random.org").role("secret")
}).description("基础配置"),
import_koishi.Schema.object({
enableMonetary: import_koishi.Schema.boolean().description("启用货币支持").default(false),
currencyName: import_koishi.Schema.string().description("货币名称").default("default"),
rewardRule: import_koishi.Schema.string().description("签到奖励").default("0.5x-1.5x"),
enablePenalty: import_koishi.Schema.boolean().description("启用累签惩罚").default(false),
penaltyRule: import_koishi.Schema.string().description("累签惩罚").default("0.1x-0.3x")
}).description("货币配置"),
import_koishi.Schema.object({
enableScoreFormat: import_koishi.Schema.boolean().description("数值格式化").default(true),
formatDate: import_koishi.Schema.string().description("启用日期(可留空)").pattern(/^\d{1,2}-\d{1,2}$/).default("4-1"),
scoreFormat: import_koishi.Schema.union([
import_koishi.Schema.const("binary").description("二进制"),
import_koishi.Schema.const("octal").description("八进制"),
import_koishi.Schema.const("hex").description("十六进制"),
import_koishi.Schema.const("simple").description("简单表达式"),
import_koishi.Schema.const("complex").description("复杂表达式")
]).description("样式").default("simple")
}).description("样式配置"),
import_koishi.Schema.object({
template: import_koishi.Schema.string().description("消息内容(支持占位符)").default("{at}你今天的人品值是:{score}{message}").role("textarea"),
imagesPath: import_koishi.Schema.string().description("图源地址").default("https://raw.githubusercontent.com/YisRime/koishi-plugin-onebot-tool/main/resource/pixiv.json"),
enableRange: import_koishi.Schema.boolean().description("启用区间消息").default(true),
rangeMessages: import_koishi.Schema.array(import_koishi.Schema.object({
min: import_koishi.Schema.number().description("最小值").min(0).max(100).default(0),
max: import_koishi.Schema.number().description("最大值").min(0).max(100).default(100),
message: import_koishi.Schema.string().description("对应消息")
})).description("区间消息配置").default([
{ min: 0, max: 10, message: "……(是百分制哦)" },
{ min: 11, max: 19, message: "?!不会吧……" },
{ min: 20, max: 39, message: "!呜……" },
{ min: 40, max: 49, message: "!勉强还行吧……?" },
{ min: 50, max: 64, message: "!还行啦,还行啦。" },
{ min: 65, max: 89, message: "!今天运气不错呢!" },
{ min: 90, max: 97, message: "!好评如潮!" },
{ min: 98, max: 100, message: "!差点就到 100 了呢……" }
]).role("table"),
enableSpecial: import_koishi.Schema.boolean().description("启用特殊消息").default(true),
specialMessages: import_koishi.Schema.array(import_koishi.Schema.object({
condition: import_koishi.Schema.string().description("触发条件(数值/日期)").pattern(/^(\d+|\d{1,2}-\d{1,2})$/),
message: import_koishi.Schema.string().description("对应消息")
})).description("特殊消息配置").default([
{ condition: "0", message: "?!" },
{ condition: "50", message: "!五五开……" },
{ condition: "100", message: "!100!100!!!!!" },
{ condition: "1-1", message: "!新年快乐!" },
{ condition: "4-1", message: "!愚人节快乐!" },
{ condition: "12-25", message: "!圣诞快乐!" }
]).role("table")
}).description("消息配置")
]);
function getNumericId(userId) {
if (!userId) return null;
const directId = Number(userId);
if (!isNaN(directId) && isFinite(directId)) return directId;
let hash = 0;
for (let i = 0; i < userId.length; i++) {
const char = userId.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash |= 0;
}
return Math.abs(hash);
}
__name(getNumericId, "getNumericId");
function calculateMonetaryValue(rule, score, ctx) {
rule = rule.trim().toLowerCase();
const parsers = [
{
regex: /^(-?\d*\.?\d+)x-(-?\d*\.?\d+)x$/,
handler: /* @__PURE__ */ __name((match) => {
let min = parseFloat(match[1]);
let max = parseFloat(match[2]);
if (min > max) [min, max] = [max, min];
const multiplier = Math.random() * (max - min) + min;
return Math.round(score * multiplier);
}, "handler")
},
{
regex: /^(-?\d*\.?\d+)x$/,
handler: /* @__PURE__ */ __name((match) => {
const multiplier = parseFloat(match[1]);
return Math.round(score * multiplier);
}, "handler")
},
{
regex: /^(-?\d+)-(-?\d+)$/,
handler: /* @__PURE__ */ __name((match) => {
let min = parseInt(match[1], 10);
let max = parseInt(match[2], 10);
if (min > max) [min, max] = [max, min];
return Math.floor(Math.random() * (max - min + 1)) + min;
}, "handler")
},
{
regex: /^(-?\d+)$/,
handler: /* @__PURE__ */ __name((match) => {
return parseInt(match[1], 10);
}, "handler")
}
];
for (const parser of parsers) {
const match = rule.match(parser.regex);
if (match) {
const value = parser.handler(match);
return isNaN(value) ? 0 : value;
}
}
return 0;
}
__name(calculateMonetaryValue, "calculateMonetaryValue");
async function autoRecall(session, message, delay = 1e4) {
if (!message) return;
setTimeout(async () => {
try {
const messageIds = (Array.isArray(message) ? message : [message]).map((msg) => typeof msg === "string" ? msg : msg?.id).filter(Boolean);
for (const msgId of messageIds) {
if (session.bot && session.channelId)
await session.bot.deleteMessage(session.channelId, msgId);
}
} catch {
}
}, delay);
}
__name(autoRecall, "autoRecall");
function apply(ctx, config) {
const calc = new FortuneCalc(config.algorithm, config.apiKey);
const store = new FortuneStore(ctx);
const builder = new MsgBuilder({
rangeMessages: config.rangeMes