From 28432cd0a5416a4e1b7c15daae3c11fa0fd65430 Mon Sep 17 00:00:00 2001 From: xyz <15380362192@163.com> Date: Thu, 3 Apr 2025 00:01:31 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86enka=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/panel.js | 365 ++++++++++++++++++++--- defSet/config.yaml | 3 +- lib/ekapi/PartnerScore.json | 512 ++++++++++++++++++++++++++++++++ lib/ekapi/alias/char_alias.json | 173 +++++++++++ lib/ekapi/name_convert.js | 313 +++++++++++++++++++ lib/ekapi/query.js | 39 +++ 6 files changed, 1359 insertions(+), 46 deletions(-) create mode 100644 lib/ekapi/PartnerScore.json create mode 100644 lib/ekapi/alias/char_alias.json create mode 100644 lib/ekapi/name_convert.js create mode 100644 lib/ekapi/query.js diff --git a/apps/panel.js b/apps/panel.js index bb26643..2ed7dec 100644 --- a/apps/panel.js +++ b/apps/panel.js @@ -10,6 +10,8 @@ import { import settings from '../lib/settings.js'; import _ from 'lodash'; import { rulePrefix } from '../lib/common.js'; +import { getZzzEnkaData } from '../lib/ekapi/query.js' +import { _enka_data_to_mys_data } from '../lib/ekapi/enka_to_mys.js' export class Panel extends ZZZPlugin { constructor() { @@ -50,57 +52,330 @@ export class Panel extends ZZZPlugin { if (queryPanelReg.test(this.e.msg)) return await this.getCharPanel(); return false; } +// async refreshPanel() { +// const uid = await this.getUID(); +// // ... 省略获取 lastQueryTime 和冷卻时间判断的代码 ... +// this.result = null; // 先清空结果 +// +// const useEnka = _.get(settings.getConfig('config'), 'useEnka', false); +// logger.mark(`[panel.js] useEnka 设置值: ${useEnka}`); // 1. 确认是否启用 Enka 逻辑 +// +// if (useEnka) { // 检查这个 if 是否进入 +// logger.mark('[panel.js] 进入 useEnka 逻辑块'); +// let enkaData = null; // 初始化 enkaData +// try { +// logger.mark(`[panel.js] 准备调用 getZzzEnkaData for UID: ${uid}`); +// enkaData = await getZzzEnkaData(uid); +// logger.mark('[panel.js] getZzzEnkaData 调用完成'); +// +// // 2. 详细检查 enkaData 的状态 +// if (enkaData === null || enkaData === undefined) { +// logger.error('[panel.js] getZzzEnkaData 返回了 null 或 undefined'); +// await this.reply('获取Enka数据失败 (返回null/undefined),请稍后再试'); +// return false; +// } else if (enkaData === -1) { +// logger.warn('[panel.js] getZzzEnkaData 返回了 -1 (表示获取失败)'); +// await this.reply('获取Enka数据失败 (返回-1),请稍后再试'); +// return false; +// } else if (!enkaData.PlayerInfo || !enkaData.PlayerInfo.ShowcaseDetail || !enkaData.PlayerInfo.ShowcaseDetail.AvatarList) { +// logger.error('[panel.js] 获取到的 enkaData 结构不完整:', enkaData); +// await this.reply('获取到的Enka数据结构不完整,无法处理'); +// return false; +// } else { +// logger.mark('[panel.js] 成功获取到有效的 enkaData 结构'); +// console.log('[panel.js] enkaData.PlayerInfo.ShowcaseDetail.AvatarList:', enkaData.PlayerInfo.ShowcaseDetail.AvatarList); // 打印角色列表确认 +// } +// +// // 3. 检查 _enka_data_to_mys_data 函数是否已正确导入 +// if (typeof _enka_data_to_mys_data !== 'function') { +// logger.error('[panel.js] _enka_data_to_mys_data 不是一个函数! 请检查导入语句和文件是否存在。'); +// await this.reply('内部错误:数据转换函数加载失败'); +// return false; +// } +// +// logger.mark('[panel.js] _enka_data_to_mys_data 函数已找到,类型:', typeof _enka_data_to_mys_data); +// logger.mark('[panel.js] 即将调用 _enka_data_to_mys_data...'); +// +// // 4. 使用 try...catch 包裹调用,捕获可能的内部错误 +// try { +// this.result = await _enka_data_to_mys_data(enkaData); +// logger.mark('[panel.js] _enka_data_to_mys_data 调用完成。'); +// // 在这里可以检查 this.result 是否是你期望的格式 +// console.log('[panel.js] 转换后的 result (部分示例):', this.result ? JSON.stringify(this.result[0], null, 2).substring(0, 500) + '...' : 'null or empty'); +// } catch (conversionError) { +// logger.error('[panel.js] 调用 _enka_data_to_mys_data 时发生严重错误:', conversionError); +// // 打印详细错误堆栈 +// console.error(conversionError); +// await this.reply(`处理Enka数据时出错: ${conversionError.message}`); +// return false; // 出错则不再继续 +// } +// +// } catch (fetchError) { +// // 这个 catch 捕获 getZzzEnkaData 自身的 await 可能抛出的错误 +// logger.error('[panel.js] 调用 getZzzEnkaData 时发生错误:', fetchError); +// console.error(fetchError); // 打印错误堆栈 +// await this.reply(`获取Enka数据时发生网络或API错误: ${fetchError.message}`); +// return false; +// } +// +// } else { // 如果 useEnka 是 false +// logger.mark('[panel.js] 未启用 useEnka,跳过 Enka 面板逻辑'); +// // 这里可以继续执行原有的 mysapi 逻辑(如果需要的话) +// // logger.mark('mysapi执行'); +// // ... (原有的 mysapi 刷新逻辑) ... +// // 注意:如果 useEnka 为 false,this.result 可能需要从 mysapi 获取 +// } +// +// // ----- 后续处理 this.result 的代码 ----- +// // (确保无论走 Enka 逻辑还是 mysapi 逻辑,this.result 都有合适的值) +// +// // 例如,原有的 newChar 计算和渲染逻辑: +// if (this.result && Array.isArray(this.result)) { // 检查 this.result 是否有效 +// const newChar = this.result.filter(item => item.isNew); // 假设 MYS 数据结构中有 isNew +// const finalData = { +// newChar: newChar.length, +// list: this.result, // 使用转换后的或 mysapi 的结果 +// }; +// await this.render('panel/refresh.html', finalData); +// } else if (!useEnka) { +// // 如果没用 Enka 且没有执行 mysapi 逻辑(或 mysapi 逻辑没产生 result),可能需要提示 +// logger.warn('[panel.js] 没有可用的面板数据用于渲染 (useEnka=false, result无效)'); +// // 可能需要添加回复告诉用户没有数据? +// } else if (useEnka && (!this.result || this.result.length === 0)) { +// logger.warn('[panel.js] Enka 数据转换后结果为空或无效'); +// // 根据需要决定是否回复用户 +// } +// +// } - async refreshPanel() { + // async refreshPanel() { + // const uid = await this.getUID(); + // const lastQueryTime = await redis.get(`ZZZ:PANEL:${uid}:LASTTIME`); + // const panelSettings = settings.getConfig('panel'); + // const coldTime = _.get(panelSettings, 'interval', 300); + // this.result = null; // Initialize instance result + // + // // --- Enka Path --- + // if(_.get(settings.getConfig('config'), 'useEnka', false)){ // Default is false if setting missing + // logger.mark('enka面板执行') + // const enkaData = await getZzzEnkaData(uid); // Fetch Enka data + // + // // --- Issue 1: Check for failure *before* processing --- + // if (enkaData === -1) { // Check for specific failure code + // await this.reply('获取enka数据失败,请稍后再试'); + // return false; // Exit early + // } + // // Add more checks for null/undefined/incomplete data if needed here + // + // console.log('[panel.js] 获取到 enkaData,准备转换...'); + // console.log('Enka AvatarList:', enkaData?.PlayerInfo?.ShowcaseDetail?.AvatarList); + // console.log('[panel.js] 即将调用 _enka_data_to_mys_data...'); + // + // // --- This is where previous errors occurred due to name_convert.js load failure --- + // // Assuming name_convert.js path fix is applied, this should now work + // this.result = await _enka_data_to_mys_data(enkaData); // Assign to instance variable + // logger.mark('[panel.js] _enka_data_to_mys_data 调用完成.'); // Added log for confirmation + // + // // --- MYS API Path --- + // } else { + // logger.mark('mysapi执行') + // if (lastQueryTime && Date.now() - lastQueryTime < 1000 * coldTime) { + // await this.reply(`${coldTime}秒内只能刷新一次,请稍后再试`); + // return false; + // } + // const { api } = await this.getAPI(); + // await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now()); + // await this.reply('正在刷新面板列表,请稍候...'); + // await this.getPlayerInfo(); // Assumed necessary for MYS panel + // + // // --- Issue 2: Assign result correctly --- + // let mysResult = null; // Use a temporary variable for MYS result + // try { + // mysResult = await refreshPanelFunction(api); // Call the imported MYS refresh + // console.dir('MYS API res:', mysResult); // Log MYS result + // if (!mysResult) { + // await this.reply('面板列表刷新失败 (MYS API),请稍后再试'); + // return false; + // } + // // Assign the successful MYS result to the instance variable + // this.result = mysResult; // <<<< IMPORTANT ASSIGNMENT + // } catch (e) { + // this.reply(e.message); + // // Consider logging the full error for debugging + // logger.error("MYS API refresh failed:", e); + // // Depending on desired behavior, you might return false or re-throw + // return false; // Exit on MYS API error + // } + // } // End if/else + // + // // --- Post-processing: Use this.result consistently --- + // // Ensure this.result is an array before proceeding + // if (!this.result || !Array.isArray(this.result)) { + // logger.error('[panel.js] 最终结果无效或不是数组:', this.result); + // // Maybe reply to the user that no data could be processed + // await this.reply('未能获取或处理有效的面板数据。'); + // return false; + // } + // + // // --- Issue 3: Use this.result for calculations --- + // // Filter based on the unified this.result + // const newChar = this.result.filter(item => item && item.isNew); // Added check for item existence + // + // // Prepare final data using the unified this.result + // const finalData = { + // newChar: newChar.length, + // list: this.result, // Use the instance variable + // }; + // + // // Render the result + // await this.render('panel/refresh.html', finalData); + // } + async refreshPanel() { const uid = await this.getUID(); - const lastQueryTime = await redis.get(`ZZZ:PANEL:${uid}:LASTTIME`); - const panelSettings = settings.getConfig('panel'); - const coldTime = _.get(panelSettings, 'interval', 300); - if (lastQueryTime && Date.now() - lastQueryTime < 1000 * coldTime) { - await this.reply(`${coldTime}秒内只能刷新一次,请稍后再试`); - return false; - } - const { api } = await this.getAPI(); - await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now()); - await this.reply('正在刷新面板列表,请稍候...'); - await this.getPlayerInfo(); - const result = await refreshPanelFunction(api).catch(e => { - this.reply(e.message); - throw e; - }); - if (!result) { - await this.reply('面板列表刷新失败,请稍后再试'); - return false; - } - const newChar = result.filter(item => item.isNew); - const finalData = { - newChar: newChar.length, - list: result, - }; - await this.render('panel/refresh.html', finalData); - } - async getCharPanelList() { - const uid = await this.getUID(); - const result = getPanelList(uid); - if (!result) { - await this.reply('未找到面板数据,请先%刷新面板'); - return false; - } - await this.getPlayerInfo(); - const timer = setTimeout(() => { - if (this?.reply) { - this.reply('查询成功,正在下载图片资源,请稍候。'); + // --- 获取玩家信息 (带容错处理) --- + // ... (这部分代码保持不变,使用带有占位符逻辑的版本) ... + logger.mark('[panel.js] 准备调用 getPlayerInfo...'); + let playerInfo = null; + try { + playerInfo = await this.getPlayerInfo(); + if (!playerInfo) playerInfo = this.e.player; + if (!playerInfo) { + logger.warn(`[panel.js] getPlayerInfo 未返回有效信息,使用默认占位。UID: ${uid}`); + playerInfo = { uid: uid, nickname: `用户${uid}`, level: '??', region_name: '未知服务器' }; } - }, 5000); - for (const item of result) { - await item.get_basic_assets(); + logger.mark('[panel.js] getPlayerInfo 尝试完成.'); + } catch (playerInfoError) { + logger.error('[panel.js] 调用 getPlayerInfo 时出错 (使用占位):', playerInfoError.message); + playerInfo = { uid: uid, nickname: `用户${uid}`, level: '??', region_name: '错误', error: playerInfoError.message }; } - clearTimeout(timer); + // --- End 获取玩家信息 --- + + this.result = null; // 初始化结果 + + // ----- 选择 Enka 或 MYS API ----- + const useEnka = _.get(settings.getConfig('config'), 'useEnka', true); // 读取配置,Enka 优先 + logger.mark(`[panel.js] useEnka 设置值: ${useEnka}`); + // ----- End ----- + + // --- 数据获取和处理逻辑 --- + if (useEnka) { + logger.mark('[panel.js] 进入 Enka 逻辑块'); + try { + const enkaData = await getZzzEnkaData(uid); + + logger.mark('[panel.js] getZzzEnkaData 调用完成.'); // <-- 日志:调用后 + + // ----- !!! 在这里添加打印原始 Enka 数据的日志 !!! ----- + console.log('===== ZZZ Enka Raw Data Start ====='); + // 使用 JSON.stringify 完整打印,null, 2 用于格式化输出 + console.log(JSON.stringify(enkaData, null, 2)); + console.log('===== ZZZ Enka Raw Data End ====='); + // ----- !!! 日志添加结束 !!! ----- + + if (!enkaData || enkaData === -1 || !enkaData.PlayerInfo) { throw new Error('获取或验证 Enka 数据失败'); } + logger.mark('[panel.js] 即将调用 _enka_data_to_mys_data...'); + this.result = await _enka_data_to_mys_data(enkaData); // <<< Enka 结果赋给 this.result + logger.mark('[panel.js] _enka_data_to_mys_data 调用完成.'); + + // ----- !!! 在这里添加打印转换后数据的日志 !!! ----- + console.log('===== Enka Converted Data (First Avatar) Start ====='); + if (this.result && Array.isArray(this.result) && this.result.length > 0) { + console.log(JSON.stringify(this.result[0], null, 2)); // 打印第一个转换后的角色 + } else { + console.log('Converted result is empty or invalid.'); + } + console.log('===== Enka Converted Data (First Avatar) End ====='); + // ----- !!! 日志添加结束 !!! ----- + logger.mark('[panel.js] _enka_data_to_mys_data 调用完成.'); + } catch (enkaError) { + logger.error('[panel.js] 处理 Enka 逻辑时出错:', enkaError); + await this.reply(`处理Enka数据时出错: ${enkaError.message}`); + return false; + } + } else { + logger.mark('[panel.js] 进入 mysapi 逻辑块'); + try { + const { api } = await this.getAPI(); // MYS 需要 api 对象 + // MYS 逻辑需要冷却判断 + const lastQueryTime = await redis.get(`ZZZ:PANEL:${uid}:LASTTIME`); + const panelSettings = settings.getConfig('panel'); + const coldTime = _.get(panelSettings, 'interval', 300); + if (lastQueryTime && Date.now() - lastQueryTime < 1000 * coldTime) { + await this.reply(`${coldTime}秒内只能刷新一次,请稍后再试`); + return false; + } + await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now()); + await this.reply('正在刷新面板列表 (MYS API),请稍候...'); + + const mysResult = await refreshPanelFunction(api); // 调用 MYS 刷新函数 + if (!mysResult) { throw new Error('MYS API 返回空结果'); } + this.result = mysResult; // <<< MYS 结果赋给 this.result + logger.mark('[panel.js] MYS API refreshPanelFunction 调用完成.'); + } catch (mysError) { + logger.error('[panel.js] MYS API 刷新出错:', mysError); + this.reply(`MYS API 刷新出错: ${mysError.message}`); + return false; + } + } + // --- End 数据获取和处理逻辑 --- + + + // --- !!! 关键步骤:更新面板数据缓存 !!! --- + if (this.result && Array.isArray(this.result)) { // 确保有有效数据 (非 null, 是数组) + // 并且至少包含一个角色数据才存,避免存空数组?(可选) + if (this.result.length > 0) { + try { + logger.mark(`[panel.js] 准备调用 updatePanelData 更新缓存,包含 ${this.result.length} 个角色数据...`); + // 调用导入的 updatePanelData 函数 + await updatePanelData(uid, this.result); // <<< 核心:写入缓存 + logger.mark('[panel.js] updatePanelData 调用完成,缓存已更新。'); + } catch (cacheError) { + logger.error('[panel.js] 调用 updatePanelData 更新缓存时出错:', cacheError); + // 记录错误,但可能继续 + } + } else { + logger.warn('[panel.js] 获取到的角色列表为空数组,不执行缓存更新。'); + // 如果是 Enka 路径且展示柜为空,这是正常情况 + } + } else { + logger.warn('[panel.js] 没有有效的角色列表数据 (this.result),跳过缓存更新。'); + // 如果之前的步骤没有 return false,这里可能需要提示用户 + if (!useEnka) { // MYS 失败的情况 + await this.reply('未能获取或处理有效的面板列表数据。'); + return false; // 如果 MYS 失败且结果无效,应该退出 + } + // 如果是 Enka 路径且结果无效/非数组,也提示并退出 + await this.reply('处理后的面板数据格式无效。'); + return false; + } + // --- !!! End 更新缓存 !!! --- + + + // --- 后续处理:构建 finalData 用于渲染刷新摘要 --- + const currentResult = this.result || []; // 保证 currentResult 是数组 + + // newChar 的计算可能依赖于 MYS 的特定字段,Enka 结果可能没有 + // 保持之前的兼容逻辑,如果 Enka 结果没有 isNew,newCharCount 为 0 + const newCharCount = (currentResult.length > 0 && currentResult[0]?.isNew !== undefined) + ? currentResult.filter(item => item && item.isNew).length + : 0; + const finalData = { - count: result?.length || 0, - list: result, + newChar: newCharCount, + list: currentResult, + player: playerInfo, + uid: uid }; - await this.render('panel/list.html', finalData); + + logger.mark('[panel.js] 准备渲染 refresh.html 模板...'); + + // 渲染刷新摘要页面 + try { + await this.render('panel/refresh.html', finalData); + } catch (renderError) { + logger.error('[panel.js] 渲染 refresh.html 模板失败:', renderError); + await this.reply(`生成刷新结果图片时出错: ${renderError.message}`); + } } async getCharPanelListTool(uid, origin = false) { diff --git a/defSet/config.yaml b/defSet/config.yaml index 00dbba9..1d6b4b6 100644 --- a/defSet/config.yaml +++ b/defSet/config.yaml @@ -1,5 +1,6 @@ render: scale: 100 # 渲染精度 +useEnka: true # 使用enka接口查询 query: others: true # 允许查询他人信息 update: @@ -10,4 +11,4 @@ mysCode: - 1034 - 10035 # 自定义绑定设备下载url -url: https://ghproxy.mihomo.me/https://raw.githubusercontent.com/forchannot/get_device_info/main/app/build/outputs/apk/debug/app-debug.apk \ No newline at end of file +url: https://ghproxy.mihomo.me/https://raw.githubusercontent.com/forchannot/get_device_info/main/app/build/outputs/apk/debug/app-debug.apk diff --git a/lib/ekapi/PartnerScore.json b/lib/ekapi/PartnerScore.json new file mode 100644 index 0000000..ec0babd --- /dev/null +++ b/lib/ekapi/PartnerScore.json @@ -0,0 +1,512 @@ +{ + "1011": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1021": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1031": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0.75, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 1, + "异常掌控": 0 + }, + "1041": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1061": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1081": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1091": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.25, + "穿透率": 0, + "异常精通": 0.25, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1101": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 1, + "异常掌控": 0 + }, + "1111": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1121": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0, + "攻击力": 0, + "防御力百分比": 1, + "防御力": 1, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 1, + "异常掌控": 0 + }, + "1131": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 1, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1141": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 1, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1151": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.5, + "穿透率": 1, + "异常精通": 0.5, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 1, + "异常掌控": 0 + }, + "1161": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1181": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 1 + }, + "1191": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1201": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1211": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 1, + "穿透率": 1, + "异常精通": 0.5, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 1, + "异常掌控": 0 + }, + "1221": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0, + "暴击伤害": 0, + "穿透值": 0.25, + "穿透率": 0.25, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1241": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 1, + "暴击伤害": 1, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1251": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 1, + "攻击力": 1, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1271": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0, + "攻击力": 0, + "防御力百分比": 1, + "防御力": 1, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 0, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 1, + "异常掌控": 0 + }, + "1171": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0, + "暴击伤害": 0, + "穿透值": 0.25, + "穿透率": 0.25, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1261": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0, + "暴击伤害": 0, + "穿透值": 0.25, + "穿透率": 0.25, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 0 + }, + "1281": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 1 + }, + "1311": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 1 + }, + "1321": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 1 + }, + "1381": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 1 + }, + "1291": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 1 + }, + "1331": { + "生命值百分比": 0, + "生命值": 0, + "攻击力百分比": 0.75, + "攻击力": 0.75, + "防御力百分比": 0, + "防御力": 0, + "暴击率": 0.75, + "暴击伤害": 0.75, + "穿透值": 0.75, + "穿透率": 1, + "异常精通": 1, + "冲击力": 0, + "伤害加成": 1, + "能量自动回复": 0, + "异常掌控": 1 + } +} \ No newline at end of file diff --git a/lib/ekapi/alias/char_alias.json b/lib/ekapi/alias/char_alias.json new file mode 100644 index 0000000..fb692b6 --- /dev/null +++ b/lib/ekapi/alias/char_alias.json @@ -0,0 +1,173 @@ +{ + "「11号」": [ + "十一号", + "十一", + "11号", + "11", + "「11号」" + ], + "艾莲": [ + "爱莲", + "爱怜", + "艾莲·乔", + "鲨鱼妹", + "鲨鱼" + ], + "安东": [ + "安东", + "安东·伊万诺夫" + ], + "本": [ + "熊本", + "熊本熊", + "ben", + "比格", + "本·比格" + ], + "比利": [ + "比例", + "bili", + "比利·奇德", + "Billy" + ], + "苍角": [ + "苍绝", + "仓角", + "仓脚" + ], + "格莉丝": [ + "格里斯", + "格利斯", + "格莉斯" + ], + "珂蕾妲": [ + "柯雷妲", + "珂雷哒", + "柯蕾妲" + ], + "猫又": [ + "猫猫", + "猫妖", + "猫怪", + "猫鼬", + "猫宫 又奈", + "猫宫又奈", + "又奈", + "Neko", + "neko" + ], + "妮可": [ + "尼克", + "妮克", + "妮寇", + "妮可·德玛拉", + "Nicole" + ], + "朱鸢": [ + "朱渊", + "朱元" + ], + "丽娜": [ + "莉娜", + "李娜", + "亚历山德丽娜·莎芭丝缇安", + "rina" + ], + "莱卡恩": [ + "冯·莱卡恩" + ], + "安比": [ + "安比", + "安笔" + ], + "可琳": [ + "克林", + "柯林", + "可林" + ], + "雅": [ + "星见雅", + "星见" + ], + "露西": [ + "路西", + "露西亚娜·德·蒙特夫", + "露西亚娜", + "鲁西" + ], + "莱特": [ + "赖特", + "Lighter" + ], + "悠真": [ + "悠", + "浅羽悠真", + "浅羽" + ], + "柳": [ + "月城", + "月城柳" + ], + "青衣": [ + "青医", + "轻易" + ], + "赛斯": [ + "赛斯·洛威尔", + "塞斯", + "洛威尔" + ], + "派派": [ + "拍拍", + "韦尔", + "派派·韦尔" + ], + "凯撒": [ + "恺撒", + "凯萨", + "凯撒·金", + "恺撒" + ], + "简": [ + "简", + "见", + "简·杜" + ], + "柏妮思": [ + "柏妮丝", + "妮丝", + "妮思", + "伯尼斯", + "博妮丝" + ], + "哲": [ + "哲" + ], + "铃": [ + "铃" + ], + "波可娜": [ + "波可娜·费雷尼", + "PulchraFeliny" + ], + "耀嘉音": [ + "耀佳音", + "佳音", + "耀" + ], + "雨果": [ + "雨果" + ], + "薇薇安": [ + "薇薇安", + "反舌鸟" + ], + "零号·安比": [ + "安比零", + "银心锡兵", + "零号", + "大安比", + "sp安比", + "s安比" + ] +} \ No newline at end of file diff --git a/lib/ekapi/name_convert.js b/lib/ekapi/name_convert.js new file mode 100644 index 0000000..239f73c --- /dev/null +++ b/lib/ekapi/name_convert.js @@ -0,0 +1,313 @@ +// name_convert.js +console.log('[name_convert.js] 文件开始执行'); // <--- 加入这行 +import fs from 'node:fs'; +import path from 'node:path'; + +import { fileURLToPath } from 'node:url'; // <--- 1. 导入 fileURLToPath + +// Use import.meta.url WITH fileURLToPath +const __filename = fileURLToPath(import.meta.url); // <--- 2. 获取当前文件的绝对路径 +const __dirname = path.dirname(__filename); // <--- 3. 获取当前文件所在的目录路径 + +const MAP_PATH = __dirname; // Data files are in the same directory +const ALIAS_LIST_DIR = path.join(__dirname, 'alias'); // Alias directory path +const CHAR_ALIAS_FILE = path.join(ALIAS_LIST_DIR, 'char_alias.json'); // Alias file path + +const PartnerId2DataFile = 'PartnerId2Data.json'; +const WeaponId2DataFile = 'WeaponId2Data.json'; +const EquipId2DataFile = 'EquipId2Data.json'; + +let char_alias_data = {}; +try { + // Ensure the alias directory exists before trying to read the file + if (fs.existsSync(ALIAS_LIST_DIR) && fs.existsSync(CHAR_ALIAS_FILE)) { + const charAliasContent = fs.readFileSync(CHAR_ALIAS_FILE, { encoding: 'utf-8' }); + char_alias_data = JSON.parse(charAliasContent); + } else { + console.warn(`Alias file not found at: ${CHAR_ALIAS_FILE}. Aliases will not be loaded.`); + } +} catch (error) { + console.error('Error reading or parsing char_alias.json:', error); +} + +let partner_data = {}; +try { + const partnerDataPath = path.join(MAP_PATH, PartnerId2DataFile); + if (fs.existsSync(partnerDataPath)) { + const partnerDataContent = fs.readFileSync(partnerDataPath, { encoding: 'utf-8' }); + partner_data = JSON.parse(partnerDataContent); + console.log(`[name_convert.js] 成功解析 JSON。`); +console.log(`[name_convert.js] 加载了 ${Object.keys(partner_data).length} 个伙伴条目。`); +console.log(`[name_convert.js] 加载后是否存在 "1191"? ${partner_data.hasOwnProperty('1191')}`); +console.log(`[name_convert.js] 加载后是否存在 "1021"? ${partner_data.hasOwnProperty('1021')}`); + // Optional: Keep debug logs if needed during development + // console.log('--- partner_data loaded successfully ---'); + // console.log(`Loaded ${Object.keys(partner_data).length} partner entries.`); + // console.log('--- partner_data sample entry ---'); + // const sampleKey = Object.keys(partner_data)[0]; + // if (sampleKey) console.log({ [sampleKey]: partner_data[sampleKey] }); + // console.log('--- partner_data loading end ---'); + } else { + console.error(`Partner data file not found at: ${partnerDataPath}`); + } +} catch (error) { + console.error(`Error reading or parsing ${PartnerId2DataFile}:`, error); +} + +let weapon_data = {}; +try { + const weaponDataPath = path.join(MAP_PATH, WeaponId2DataFile); + if (fs.existsSync(weaponDataPath)) { + const weaponDataContent = fs.readFileSync(weaponDataPath, { encoding: 'utf-8' }); + weapon_data = JSON.parse(weaponDataContent); + } else { + console.error(`Weapon data file not found at: ${weaponDataPath}`); + } +} catch (error) { + console.error(`Error reading or parsing ${WeaponId2DataFile}:`, error); +} + +let equip_data = {}; +try { + const equipDataPath = path.join(MAP_PATH, EquipId2DataFile); + if (fs.existsSync(equipDataPath)) { + const equipDataContent = fs.readFileSync(equipDataPath, { encoding: 'utf-8' }); + equip_data = JSON.parse(equipDataContent); + } else { + console.error(`Equipment data file not found at: ${equipDataPath}`); + } +} catch (error) { + console.error(`Error reading or parsing ${EquipId2DataFile}:`, error); +} + +// --- Helper Functions --- + +/** + * Converts a character name alias to the canonical character name. + * @param {string} char_name - The alias or canonical name entered by the user. + * @returns {string} The canonical character name, or the original input if no alias is found. + */ +export function alias_to_char_name(char_name) { + if (!char_name) return char_name; // Handle null/empty input + const lowerCaseName = char_name.toLowerCase().trim(); // Normalize input + + for (const canonical_name in char_alias_data) { + // Check if the input matches the canonical name (case-insensitive) + if (lowerCaseName === canonical_name.toLowerCase()) { + return canonical_name; + } + // Check if the input matches any alias in the array (case-insensitive) + if (Array.isArray(char_alias_data[canonical_name])) { + for (const alias of char_alias_data[canonical_name]) { + if (lowerCaseName === String(alias).toLowerCase()) { + return canonical_name; + } + } + } + } + // No alias found, return the original (trimmed) name + return char_name.trim(); +} + + +/** + * Gets the sprite ID for a given character ID. + * @param {string|number} char_id - The character ID. + * @returns {string} The sprite ID, or a default ('28') if not found. + */ +export function char_id_to_sprite(char_id) { + const charIdStr = String(char_id); + // Corrected variable name: partner_data + if (partner_data[charIdStr] && partner_data[charIdStr].sprite_id) { + return partner_data[charIdStr].sprite_id; + } else { + // console.warn(`Sprite ID not found for character ID: ${charIdStr}. Using default.`); + return '28'; // Default sprite ID (Rope Master?) + } +} + +/** + * Gets the full name for a given character ID. + * @param {string|number} char_id - The character ID. + * @returns {string} The character's full name, or a default ('绳匠') if not found. + */ +export function char_id_to_full_name(char_id) { + const charIdStr = String(char_id); + // Corrected variable name: partner_data + if (partner_data[charIdStr] && partner_data[charIdStr].full_name) { + return partner_data[charIdStr].full_name; + } else { + // console.warn(`Full name not found for character ID: ${charIdStr}. Using default.`); + return '绳匠'; // Default name (Rope Master) + } +} + +/** + * Gets the sprite file name for a given equipment ID (relic). + * @param {string|number} equip_id - The equipment ID. + * @returns {string|undefined} The sprite file name, or undefined if not found. + */ +export function equip_id_to_sprite(equip_id) { + const equipIdStr = String(equip_id); + if (equipIdStr.length === 5) { // Assuming 5-digit IDs for relics + const suit_id = equipIdStr.slice(0, 3) + '00'; // Derive suit ID + if (equip_data[suit_id] && equip_data[suit_id].sprite_file) { + return equip_data[suit_id].sprite_file; + } + } + // console.warn(`Sprite file not found for equipment ID: ${equipIdStr}.`); + return undefined; // Return undefined if not found +} + + +/** + * Gets the short name for a given character ID. + * @param {string|number} char_id - The character ID. + * @returns {string|undefined} The character's short name, or undefined if not found. + */ +export function char_id_to_char_name(char_id) { + const charIdStr = String(char_id); + // Corrected variable name: partner_data + if (partner_data[charIdStr] && partner_data[charIdStr].name) { + return partner_data[charIdStr].name; + } else { + // console.warn(`Short name not found for character ID: ${charIdStr}.`); + return undefined; + } +} + +/** + * Gets the character ID for a given character name (handles aliases). + * @param {string} char_name - The character name or alias. + * @returns {string|undefined} The character ID (as a string), or undefined if not found. + */ +export function char_name_to_char_id(char_name) { + if (!char_name) return undefined; // Handle null/empty input + const canonicalName = alias_to_char_name(char_name); // Resolve alias first + + // Corrected variable name: partner_data + for (const char_id in partner_data) { + const charData = partner_data[char_id]; + // Compare against the canonical name from alias resolution + if (charData && charData.name && charData.name === canonicalName) { + return char_id; // Return the ID (key) + } + } + // console.warn(`Character ID not found for name: "${char_name}" (resolved to: "${canonicalName}").`); + return undefined; // Not found +} + +// Export the loaded data objects as well +export { + equip_data, + weapon_data, + partner_data, + char_alias_data // Optionally export alias data if needed elsewhere +}; +// import fs from 'node:fs'; +// import path from 'node:path'; +// +// const MAP_PATH = path.dirname(new URL(import.meta.url).pathname); +// const ALIAS_LIST = path.join(path.dirname(new URL(import.meta.url).pathname), 'alias'); +// const CHAR_ALIAS = path.join(ALIAS_LIST, 'char_alias.json'); +// const PartnerId2DataFile = 'PartnerId2Data.json'; +// const WeaponId2DataFile = 'WeaponId2Data.json'; +// const EquipId2DataFile = 'EquipId2Data.json'; +// +// let char_alias_data = {}; +// try { +// const charAliasContent = fs.readFileSync(CHAR_ALIAS, { encoding: 'utf-8' }); +// char_alias_data = JSON.parse(charAliasContent); +// } catch (error) { +// console.error('Error reading char_alias.json:', error); +// } +// +// let partner_data = {}; +// try { +// const partnerDataContent = fs.readFileSync(path.join(MAP_PATH, PartnerId2DataFile), { encoding: 'utf-8' }); +// partner_data = JSON.parse(partnerDataContent); +// console.log('--- partner_data 内容开始 ---'); +// console.log(partner_data); +// console.log('--- partner_data 内容结束 ---'); +// } catch (error) { +// console.error('Error reading PartnerId2Data.json:', error); +// } +// +// let weapon_data = {}; +// try { +// const weaponDataContent = fs.readFileSync(path.join(MAP_PATH, WeaponId2DataFile), { encoding: 'utf-8' }); +// weapon_data = JSON.parse(weaponDataContent); +// } catch (error) { +// console.error('Error reading WeaponId2Data.json:', error); +// } +// +// let equip_data = {}; +// try { +// const equipDataContent = fs.readFileSync(path.join(MAP_PATH, EquipId2DataFile), { encoding: 'utf-8' }); +// equip_data = JSON.parse(equipDataContent); +// } catch (error) { +// console.error('Error reading EquipId2Data.json:', error); +// } +// +// export function char_id_to_sprite(char_id) { +// const charIdStr = String(char_id); +// if (partener_data[charIdStr]) { +// return partener_data[charIdStr].sprite_id; +// } else { +// return '28'; +// } +// } +// +// export function char_id_to_full_name(char_id) { +// const charIdStr = String(char_id); +// if (partener_data[charIdStr]) { +// return partener_data[charIdStr].full_name; +// } else { +// return '绳匠'; +// } +// } +// +// export function equip_id_to_sprite(equip_id) { +// const equipIdStr = String(equip_id); +// if (equipIdStr.length === 5) { +// const suit_id = equipIdStr.slice(0, 3) + '00'; +// if (equip_data[suit_id]) { +// return equip_data[suit_id].sprite_file; +// } +// } +// return undefined; +// } +// +// export function alias_to_char_name(char_name) { +// for (const i in char_alias_data) { +// if (char_name === i || (Array.isArray(char_alias_data[i]) && char_alias_data[i].includes(char_name))) { +// return i; +// } +// } +// return char_name; +// } +// +// export function char_id_to_char_name(char_id) { +// if (partener_data[char_id]) { +// return partener_data[char_id].name; +// } else { +// return undefined; +// } +// } +// +// export function char_name_to_char_id(char_name) { +// const aliasConvertedName = alias_to_char_name(char_name); +// for (const i in partener_data) { +// const chars = partener_data[i]; +// if (aliasConvertedName === chars.name) { +// return i; +// } +// } +// return undefined; +// } +// +// export { +// equip_data, +// weapon_data, +// partner_data, +// }; diff --git a/lib/ekapi/query.js b/lib/ekapi/query.js new file mode 100644 index 0000000..3913321 --- /dev/null +++ b/lib/ekapi/query.js @@ -0,0 +1,39 @@ +import settings from '../settings.js' +import { ENKA_API } from './api.js' +import _ from 'request' + +const useEnka = _.get(settings.getConfig('config'), 'useEnka', true); +export async function getZzzEnkaData(uid) { + if (useEnka) { + try { + const response = await fetch(ENKA_API.replace('{uid}', uid), { + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept': 'application/json, text/plain, */*', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', + 'Referer': 'https://enka.network/', + 'Sec-Fetch-Dest': 'empty', + 'Sec-Fetch-Mode': 'cors', + 'Sec-Fetch-Site': 'same-origin', + }, + }); + + if (!response.ok) { + console.error(`HTTP 错误! 状态码: ${response.status}`); + return response.status; + } + + const data = await response.json(); // 解析 JSON 响应 + if (typeof data === 'number') { + return data; + } + return data; + + } catch (error) { + console.error("Error fetching ZZZ Enka data:", error); + return -1; + } + } + // 如果 useEnka 为 false,你可能需要返回一个默认值或者执行其他逻辑 + return null; // 或者其他你希望返回的值 +}