UNPKG

koishi-plugin-best-jrrp

Version:

支持真随机的今日人品(运势),可任意自定义消息,支持随机图片,还带有排行、分析、货币等功能。

984 lines (977 loc) 62.3 kB
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