Files

490 lines
25 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// fakeLog 函数,使用方法:将本函数放在主函数前,调用时请务必使用await否则可能出现v8白框报错
//在js开头处伪造该js结束运行的日志信息如 await fakeLog("js脚本", true, true, 0);
//在js结尾处伪造该js开始运行的日志信息如 await fakeLog("js脚本", true, false, 2333);
//duration项目仅在伪造结束信息时有效且无实际作用可以任意填写当你需要在日志中输出特定值时才需要单位为毫秒
//在调用地图追踪前伪造该地图追踪开始运行的日志信息,如 await fakeLog(`地图追踪.json`, false, true, 0);
//在调用地图追踪后伪造该地图追踪结束运行的日志信息,如 await fakeLog(`地图追踪.json`, false, false, 0);
//如此便可以在js运行过程中伪造地图追踪的日志信息可以在日志分析等中查看
async function fakeLog(name, isJs, isStart, duration) {
await sleep(10);
const currentTime = Date.now();
// 参数检查
if (typeof name !== 'string') {
log.error("参数 'name' 必须是字符串类型!");
return;
}
if (typeof isJs !== 'boolean') {
log.error("参数 'isJs' 必须是布尔型!");
return;
}
if (typeof isStart !== 'boolean') {
log.error("参数 'isStart' 必须是布尔型!");
return;
}
if (typeof currentTime !== 'number' || !Number.isInteger(currentTime)) {
log.error("参数 'currentTime' 必须是整数!");
return;
}
if (typeof duration !== 'number' || !Number.isInteger(duration)) {
log.error("参数 'duration' 必须是整数!");
return;
}
// 将 currentTime 转换为 Date 对象并格式化为 HH:mm:ss.sss
const date = new Date(currentTime);
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
const formattedTime = `${hours}:${minutes}:${seconds}.${milliseconds}`;
// 将 duration 转换为分钟和秒,并保留三位小数
const durationInSeconds = duration / 1000; // 转换为秒
const durationMinutes = Math.floor(durationInSeconds / 60);
const durationSeconds = (durationInSeconds % 60).toFixed(3); // 保留三位小数
// 使用四个独立的 if 语句处理四种情况
if (isJs && isStart) {
// 处理 isJs = true 且 isStart = true 的情况
const logMessage = `正在伪造js开始的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 开始执行JS脚本: "${name}"`;
log.debug(logMessage);
}
if (isJs && !isStart) {
// 处理 isJs = true 且 isStart = false 的情况
const logMessage = `正在伪造js结束的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}${durationSeconds}\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------`;
log.debug(logMessage);
}
if (!isJs && isStart) {
// 处理 isJs = false 且 isStart = true 的情况
const logMessage = `正在伪造地图追踪开始的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 开始执行地图追踪任务: "${name}"`;
log.debug(logMessage);
}
if (!isJs && !isStart) {
// 处理 isJs = false 且 isStart = false 的情况
const logMessage = `正在伪造地图追踪结束的日志记录\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`→ 脚本执行结束: "${name}", 耗时: ${durationMinutes}${durationSeconds}\n\n` +
`[${formattedTime}] [INF] BetterGenshinImpact.Service.ScriptService\n` +
`------------------------------`;
log.debug(logMessage);
}
}
// 定义目标文件夹路径和记录文件路径
const recordFolder = "record"; // 存储记录文件的文件夹路径
const defaultTimeStamp = "2023-10-13T00:00:00.000Z"; // 固定的时间戳
// 从 settings 中读取用户配置,并设置默认值
const userSettings = {
operationMode: settings.operationMode || "执行任务(若不存在索引文件则自动创建)",
pathGroup1CdType: settings.pathGroup1CdType || "",
pathGroup2CdType: settings.pathGroup2CdType || "",
pathGroup3CdType: settings.pathGroup3CdType || "",
otherPathGroupsCdTypes: settings.otherPathGroupsCdTypes || "",
partyNames: settings.partyNames || "",
skipTimeRanges: settings.skipTimeRanges || "",
infoFileName: settings.infoFileName || ""
};
// 将 partyNames 分割并存储到一个数组中
const partyNamesArray = userSettings.partyNames.split(";").map(name => name.trim());
// 新增一个数组 pathGroupCdType存储每个路径组的 cdtype 信息
const pathGroupCdType = [
userSettings.pathGroup1CdType,
userSettings.pathGroup2CdType,
userSettings.pathGroup3CdType
];
// 如果 otherPathGroupsCdTypes 不为空,将其分割为数组并添加到 pathGroupCdType 中
if (userSettings.otherPathGroupsCdTypes) {
pathGroupCdType.push(...userSettings.otherPathGroupsCdTypes.split(";"));
}
// 当infoFileName为空时将其改为由其他自定义配置决定的一个字符串
if (!userSettings.infoFileName) {
userSettings.infoFileName = [
userSettings.pathGroup1CdType,
userSettings.pathGroup2CdType,
userSettings.pathGroup3CdType,
userSettings.otherPathGroupsCdTypes,
].join(".");
}
// 定义自定义函数 basename用于获取文件名
function basename(filePath) {
const lastSlashIndex = filePath.lastIndexOf('\\'); // 或者使用 '/',取决于你的路径分隔符
return filePath.substring(lastSlashIndex + 1);
}
// 定义自定义函数 removeJsonSuffix用于移除文件名中的 .json 后缀
function removeJsonSuffix(fileName) {
if (fileName.endsWith('.json')) {
return fileName.substring(0, fileName.length - 5); // 移除 .json 后缀
}
return fileName;
}
async function readFolder(folderPath, onlyJson) {
log.info(`开始读取文件夹:${folderPath}`);
// 新增一个堆栈,初始时包含 folderPath
const folderStack = [folderPath];
// 新增一个数组,用于存储文件信息对象
const files = [];
// 当堆栈不为空时,继续处理
while (folderStack.length > 0) {
// 从堆栈中弹出一个路径
const currentPath = folderStack.pop();
// 读取当前路径下的所有文件和子文件夹路径
const filesInSubFolder = file.ReadPathSync(currentPath);
// 临时数组,用于存储子文件夹路径
const subFolders = [];
for (const filePath of filesInSubFolder) {
if (file.IsFolder(filePath)) {
// 如果是文件夹,先存储到临时数组中
subFolders.push(filePath);
} else {
// 如果是文件,根据 onlyJson 判断是否存储
if (onlyJson) {
if (filePath.endsWith(".json")) {
const fileName = filePath.split('\\').pop(); // 提取文件名
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
files.push({
fullPath: filePath,
fileName: fileName,
folderPathArray: folderPathArray
});
//log.info(`找到 JSON 文件:${filePath}`);
}
} else {
const fileName = filePath.split('\\').pop(); // 提取文件名
const folderPathArray = filePath.split('\\').slice(0, -1); // 提取文件夹路径数组
files.push({
fullPath: filePath,
fileName: fileName,
folderPathArray: folderPathArray
});
//log.info(`找到文件:${filePath}`);
}
}
}
// 将临时数组中的子文件夹路径按原顺序压入堆栈
folderStack.push(...subFolders.reverse()); // 反转子文件夹路径
}
return files;
}
(async function () {
try {
// 获取子文件夹路径
const subFolderName = userSettings.infoFileName; // 使用设置后的 infoFileName
const subFolderPath = `${recordFolder}/${subFolderName}`;
// 读取子文件夹中的所有文件路径
const filesInSubFolder = file.ReadPathSync(subFolderPath);
// 检查路径组1.txt文件是否存在
let indexDoExist = false;
for (const filePath of filesInSubFolder) {
const fileName = basename(filePath); // 提取文件名
if (fileName === "路径组1.txt") {
indexDoExist = true;
break;
}
}
log.debug(`路径组1.txt 是否存在: ${indexDoExist}`);
{
if (userSettings.operationMode === "重新生成索引文件用于强制刷新CD") {
log.info("重新生成索引文件模式,将覆盖现有索引文件");
}
if (!indexDoExist) {
log.info("路径组1.txt 文件不存在,将尝试生成索引文件");
}
dispatcher.addTimer(new RealtimeTimer("AutoPick"));
await fakeLog("采集cd管理", true, false, 1000);
// 循环处理多个路径组
for (let i = 1; ; i++) {
// 检查当前路径组的 cdtype 是否为空
const currentCdType = pathGroupCdType[i - 1] || "";
if (!currentCdType) {
log.info(`路径组${i} 的 cdtype 为空,停止处理`);
break;
}
const targetFolder = `pathing/路径组${i}`; // 动态生成目标文件夹路径
const files = await readFolder(targetFolder, true);
const filePaths = files.map(file => file.fullPath);
// 如果文件夹为空,退出循环
if (filePaths.length === 0) {
log.info(`路径组${i} 文件夹为空,停止处理`);
break;
}
// 用于存储符合条件的文件名的数组
const jsonFileNames = [];
const entryMap = {};
// 如果 indexDoExist 为 true则读取对应的原文件
if (indexDoExist) {
const pathGroupFilePath = `${subFolderPath}/路径组${i}.txt`; // 使用外层循环的变量 i
let pathGroupContent = await file.readText(pathGroupFilePath);
let pathGroupEntries = pathGroupContent.trim().split('\n');
// 创建一个对象来存储 entryName 和 entryTimestamp 的映射
for (let j = 0; j < pathGroupEntries.length; j++) {
const entryWithTimestamp = pathGroupEntries[j].trim();
const [entryName, entryTimestamp] = entryWithTimestamp.split('::');
entryMap[entryName] = entryTimestamp;
}
}
// 遍历文件路径数组并提取文件名
for (const filePath of filePaths) {
const fileName = basename(filePath); // 提取文件名
if (fileName.endsWith('.json')) { // 检查文件名是否以 .json 结尾
const fileNameWithoutSuffix = removeJsonSuffix(fileName); // 移除 .json 后缀
// 给 routeTimeStamp 赋值为 defaultTimeStamp
let routeTimeStamp = defaultTimeStamp;
if (indexDoExist && userSettings.operationMode !== "重新生成索引文件用于强制刷新CD" && entryMap[fileNameWithoutSuffix]) {
routeTimeStamp = entryMap[fileNameWithoutSuffix];
}
routeTimeStamp = `::${routeTimeStamp}`;
// 添加时间戳并存储
jsonFileNames.push(`${fileNameWithoutSuffix}${routeTimeStamp}`);
}
}
// 如果没有找到符合条件的文件,跳过当前路径组
if (jsonFileNames.length === 0) {
//log.info(`路径组${i} 中未找到符合条件的 .json 文件,跳过`);
continue;
}
// 将文件名数组转换为字符串,每个文件名占一行
const fileNamesContent = jsonFileNames.join("\n");
// 定义记录文件的完整路径
const recordFilePath = `${subFolderPath}/路径组${i}.txt`;
// 将文件名写入记录文件
const writeResult = file.writeTextSync(recordFilePath, fileNamesContent);
if (writeResult) {
log.info(`文件名已成功写入: ${recordFilePath}`);
} else {
log.error(`写入文件失败: ${recordFilePath}`);
}
//当选择“执行任务(若不存在索引文件则自动创建)”时,执行类似路径执行的逻辑
if (userSettings.operationMode === "执行任务(若不存在索引文件则自动创建)") {
log.info("启用自动拾取的实时任务");
let groupNumber = i;
const pathGroupFilePath = `${subFolderPath}/路径组${groupNumber}.txt`; // 动态生成路径组文件路径
genshin.returnMainUi();
//切换到指定配队
if (partyNamesArray[groupNumber - 1] !== "") {
await genshin.switchParty(partyNamesArray[groupNumber - 1])
}
genshin.returnMainUi();
try {
let pathGroupContent = await file.readText(pathGroupFilePath);
let pathGroupEntries = pathGroupContent.trim().split('\n');
for (let i = 0; i < pathGroupEntries.length; i++) {
const entryWithTimestamp = pathGroupEntries[i].trim();
const [entryName, entryTimestamp] = entryWithTimestamp.split('::');
// 查找对应的完整路径
const fileEntry = files.find(file => file.fileName === `${entryName}.json`);
const pathingFilePath = fileEntry.fullPath;
// 获取开始时间
const startTime = new Date();
// 比较当前时间戳与任务的时间戳
const entryDate = new Date(entryTimestamp);
if (startTime <= entryDate) {
log.info(`当前任务 ${entryName} 未刷新,跳过任务 ${i + 1}/${pathGroupEntries.length}`);
await sleep(10);
continue; // 跳过当前任务
}
// 新增校验:若当前时间的小时数和 skipTimeRanges 一致,则跳过任务
const currentHour = startTime.getHours(); // 获取当前时间的小时数
const skipHours = userSettings.skipTimeRanges.split(';').map(Number); // 将 skipTimeRanges 转换为数字数组
if (skipHours.includes(currentHour)) {
log.info(`当前时间的小时数为 ${currentHour},在跳过时间范围内,跳过任务 ${entryName}`);
continue; // 跳过当前任务
}
//伪造地图追踪开始的日志
await fakeLog(entryName, false, true, 0);
// 日志输出当前任务信息
log.info(`当前任务 ${entryName} 为第 ${i + 1}/${pathGroupEntries.length}`);
// 执行路径文件
try {
await pathingScript.runFile(pathingFilePath);
log.info(`执行任务: ${entryName}`);
} catch (error) {
log.error(`路径文件 ${pathingFilePath} 不存在或执行失败: ${error}`);
continue; // 跳过当前任务
}
//捕获任务取消的信息并跳出循环
try {
await sleep(10); // 假设 sleep 是一个异步函数,休眠 10 毫秒
} catch (error) {
log.error(`发生错误: ${error}`);
break; // 终止循环
}
// 获取结束时间
const endTime = new Date();
// 比较开始时间与结束时间
const timeDiff = endTime.getTime() - startTime.getTime(); // 时间差(毫秒)
//伪造地图追踪结束的日志
await fakeLog(entryName, false, false, timeDiff);
if (timeDiff > 3000) { // 时间差大于3秒
// 获取当前路径组的 cdtype
const currentCdType = pathGroupCdType[groupNumber - 1] || "未知类型";
// 初始化 newTimestamp 和 nextAvailableTime
let newTimestamp;
let nextAvailableTime;
// 根据 cdtype 执行不同的操作
switch (currentCdType) {
case "1次0点刷新":
// 将任务文件中对应的时间戳改为下一个0点
const tomorrow = new Date(startTime.getTime() + 24 * 60 * 60 * 1000);
tomorrow.setHours(0, 0, 0, 0); // 设置为下一个0点
newTimestamp = tomorrow.toISOString();
nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式
//log.info(`下一次可用时间为 ${nextAvailableTime}`);
break;
case "2次0点刷新":
// 将任务文件中对应的时间戳改为下下个0点
const dayAfterTomorrow = new Date(startTime.getTime() + 48 * 60 * 60 * 1000);
dayAfterTomorrow.setHours(0, 0, 0, 0); // 设置为下下个0点
newTimestamp = dayAfterTomorrow.toISOString();
nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式
//log.info(`下一次可用时间为 ${nextAvailableTime}`);
break;
case "3次0点刷新":
// 将任务文件中对应的时间戳改为下下下个0点
const twoDaysAfterTomorrow = new Date(startTime.getTime() + 72 * 60 * 60 * 1000);
twoDaysAfterTomorrow.setHours(0, 0, 0, 0); // 设置为下下下个0点
newTimestamp = twoDaysAfterTomorrow.toISOString();
nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式
//log.info(`下一次可用时间为 ${nextAvailableTime}`);
break;
case "4点刷新":
// 将任务文件中对应的时间戳改为下一个4点
const next4AM = new Date(startTime.getTime());
next4AM.setHours(4, 0, 0, 0); // 设置为当天的4点
if (next4AM <= startTime) {
next4AM.setDate(next4AM.getDate() + 1); // 如果当前时间已过4点则设置为下一天的4点
}
newTimestamp = next4AM.toISOString();
nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式
//log.info(`下一次可用时间为 ${nextAvailableTime}`);
break;
case "12小时刷新":
// 将任务文件中对应的时间戳改为开始时间后12小时0分0秒
newTimestamp = new Date(startTime.getTime() + 12 * 60 * 60 * 1000).toISOString();
nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式
//log.info(`下一次可用时间为 ${nextAvailableTime}`);
break;
case "24小时刷新":
// 将任务文件中对应的时间戳改为开始时间后24小时0分0秒
newTimestamp = new Date(startTime.getTime() + 24 * 60 * 60 * 1000).toISOString();
nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式
//log.info(`下一次可用时间为 ${nextAvailableTime}`);
break;
case "46小时刷新":
// 将任务文件中对应的时间戳改为开始时间后46小时0分0秒
newTimestamp = new Date(startTime.getTime() + 46 * 60 * 60 * 1000).toISOString();
nextAvailableTime = new Date(newTimestamp).toLocaleString(); // 转换为本地时间格式
//log.info(`下一次可用时间为 ${nextAvailableTime}`);
break;
default:
log.warn(`路径组${groupNumber} 的 cdtype 是 ${currentCdType},执行默认操作`);
// 默认操作:将下一个可用时间设置为开始时间
newTimestamp = startTime.toISOString();
nextAvailableTime = startTime.toLocaleString(); // 转换为本地时间格式
break;
}
// 更新任务文件中的时间戳
// 首先根据newTimestamp修改pathGroupEntries中对应项
pathGroupEntries = pathGroupEntries.map(entry => {
const [name, timestamp] = entry.split('::');
if (name === entryName) {
return `${name}::${newTimestamp}`;
}
return entry;
});
// 然后根据pathGroupEntries修改pathGroupContent
pathGroupContent = pathGroupEntries.join('\n');
// 最后将pathGroupContent写回原文件
await file.writeText(pathGroupFilePath, pathGroupContent);
log.info(`本任务执行大于3秒cd信息已更新下一次可用时间为 ${nextAvailableTime}`);
}
}
log.info(`路径组${groupNumber} 的所有任务运行完成`);
} catch (error) {
log.error(`读取路径组文件 ${pathGroupFilePath} 时出错: ${error}`);
}
}
}
}
} catch (error) {
log.error(`操作失败: ${error}`);
}
//伪造js开始的日志
await fakeLog("采集cd管路", true, true, 0);
})();