统计圣遗物经验 (#2872)

* 增加延迟

* 圣遗物经验统计
This commit is contained in:
skyflag2022
2026-02-14 10:08:06 +08:00
committed by GitHub
parent 1f5659d639
commit ba1095f4ba
49 changed files with 644 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
统计背包中的圣遗物经验和狗粮数量建议搭配JS通知使用。

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 693 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 668 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

View File

@@ -0,0 +1,610 @@
let notify = settings.notify
let account = settings.account || "默认账户";
(async function () {
// 设置分辨率和缩放
setGameMetrics(1920, 1080, 1);
await genshin.returnMainUi();
keyPress("B");//打开背包
await sleep(1000);
// 关闭弹窗
await close_expired_stuff_popup_window();
let enterAttempts = 0;
while (enterAttempts < 10) {
await click(642,36);
const clicked = await clickPNG("分解",false);
if (clicked) break; // 找到并点击成功就退出循环
await sleep(750);
enterAttempts++;
await genshin.returnMainUi();
await sleep(100);
keyPress("B");
await sleep(1000);
}
await clickPNG("时间顺序",true,1);
await sleep(200);
await clickPNG("筛选");
await sleep(200);
click(30, 30);
await sleep(100);
await clickPNG("重置");
await sleep(200);
await clickPNG("祝圣之霜定义");
await sleep(200);
await clickPNG("未装备");
await sleep(200);
await clickPNG("未锁定");
await sleep(200);
await clickPNG("确认");
await sleep(200);
click(30, 30);
await sleep(100);
const smallBottle = await getBottleCount('背包小瓶', 'assets/RecognitionObject/三星.png');
const bigBottle = await getBottleCount('背包大瓶', 'assets/RecognitionObject/四星.png');
await clickPNG("筛选");
await sleep(200);
click(30, 30);
await sleep(100);
await clickPNG("重置");
await sleep(200);
await clickPNG("确认");
click(30, 30);
await sleep(100);
//点击分解
await clickPNG("分解");
await sleep(1000);
// 识别已储存经验1570-880-1650-930
const digits = await numberTemplateMatch("assets/已储存经验数字", 1573, 885, 74, 36);
let initialValue = 0;
if (digits >= 0) {
initialValue = digits;
log.info(`已储存经验识别成功: ${initialValue}`);
} else {
log.warn(`已储存经验值识别失败使用默认值0`);
}
await clickPNG("快速选择");
await sleep(500);
// 识别不同星级狗粮数量
const starPositions = [
{ star: 1, y: 130 },
{ star: 2, y: 200 },
{ star: 3, y: 270 },
{ star: 4, y: 340 }
];
const starCounts = {};
for (const { star, y } of starPositions) {
const count = await numberTemplateMatch("assets/选中狗粮数字", 570, y, 60, 50);
if (count < 0) {
log.warn(`${star}星狗粮位置未识别到有效数字`);
starCounts[`star${star}`] = 0; // 设置默认值为0
}else{
starCounts[`star${star}`] = count;
log.info(`${star}星狗粮识别到${count}`);
}
}
// 计算狗粮经验值
const expStar1 = starCounts.star1 * 420;
const expStar2 = starCounts.star2 * 840;
const expStar3 = starCounts.star3 * 1260;
const expStar4 = starCounts.star4 * 2520;
const expStars = expStar1 + expStar2 + expStar3 + expStar4;
// 库存经验值
const expSmall = parseInt(smallBottle || 0) * 2500;
const expBig = parseInt(bigBottle || 0) * 10000;
const expStock = expSmall + expBig + initialValue;
// 合计
const totalExp = expStars + expStock;
const totalCount = starCounts.star1 + starCounts.star2 + starCounts.star3 + starCounts.star4;
// 预计算所有需要格式化的值
const formattedExpStar1 = await formatExp(expStar1);
const formattedExpStar2 = await formatExp(expStar2);
const formattedExpStar3 = await formatExp(expStar3);
const formattedExpStar4 = await formatExp(expStar4);
const formattedExpStars = await formatExp(expStars);
const formattedExpSmall = await formatExp(expSmall);
const formattedExpBig = await formatExp(expBig);
const formattedExpStock = await formatExp(expStock);
const formattedTotalExp = await formatExp(totalExp);
// 构建通知消息
let message = `📦 圣遗物经验统计\n`;
message += `\n`;
message += `💾 已储存经验:${initialValue}\n`;
message += `\n`;
message += `📊 [狗粮数量统计]\n`;
message += `📦 总数量:${totalCount}\n`;
message += `⭐ 1星${starCounts.star1}${expStar1 > 0 ? `${formattedExpStar1}` : ''}\n`;
message += `⭐ 2星${starCounts.star2}${expStar2 > 0 ? `${formattedExpStar2}` : ''}\n`;
message += `⭐ 3星${starCounts.star3}${expStar3 > 0 ? `${formattedExpStar3}` : ''}\n`;
message += `⭐ 4星${starCounts.star4}${expStar4 > 0 ? `${formattedExpStar4}` : ''}\n`;
message += `💰 狗粮经验合计:${formattedExpStars}\n`;
message += `\n`;
message += `🧪 [经验瓶数量]\n`;
message += `🧪 小经验瓶:${smallBottle || 0}${expSmall > 0 ? `${formattedExpSmall}` : ''}\n`;
message += `🧪 大经验瓶:${bigBottle || 0}${expBig > 0 ? `${formattedExpBig}` : ''}\n`;
message += `💰 库存经验合计:${formattedExpStock}(含储存${initialValue}点)\n`;
message += `\n`;
message += `✨ 总经验:${formattedTotalExp}\n`;
// 记录保存功能
const userName = await getUserName();
const recordPath = `assets/${userName}.txt`;
// 获取本地保存的数据
const localData = await getLocalData(recordPath);
// 更新记录
await updateRecord(recordPath, initialValue, parseInt(smallBottle || 0), parseInt(bigBottle || 0), starCounts, totalExp);
// 构建基础日志信息
const formattedExpStarsLog = await formatExp(expStars);
const formattedExpSmallLog = await formatExp(expSmall);
const formattedExpBigLog = await formatExp(expBig);
const formattedExpStockLog = await formatExp(expStock);
const formattedTotalExpLog = await formatExp(totalExp);
const baseLog = `狗粮:${totalCount}个(1★${starCounts.star1} 2★${starCounts.star2} 3★${starCounts.star3} 4★${starCounts.star4}) | 狗粮经验:${formattedExpStarsLog} | 小瓶:${formattedExpSmallLog} 大瓶:${formattedExpBigLog} 储存:${initialValue} | 库存经验:${formattedExpStockLog} | 总计:${formattedTotalExpLog}`;
// 计算变化量并添加到通知消息
let logMessage = baseLog;
if (localData.initialized.initialValue && localData.initialized.smallBottle && localData.initialized.bigBottle &&
localData.initialized.star1 && localData.initialized.star2 && localData.initialized.star3 && localData.initialized.star4 &&
localData.initialized.totalExp) {
// 计算本地记录中的狗粮经验合计
const localExpStar1 = localData.starCounts.star1 * 420;
const localExpStar2 = localData.starCounts.star2 * 840;
const localExpStar3 = localData.starCounts.star3 * 1260;
const localExpStar4 = localData.starCounts.star4 * 2520;
const localExpStars = localExpStar1 + localExpStar2 + localExpStar3 + localExpStar4;
// 计算总经验和狗粮经验合计的变化
const diffTotalExp = totalExp - localData.totalExp;
const diffExpStars = expStars - localExpStars;
// 添加变化量到通知消息
message += `\n`;
if (diffTotalExp !== 0 || diffExpStars !== 0) {
if (diffTotalExp !== 0) {
const totalChangeDesc = diffTotalExp > 0 ? '增加' : '减少';
const formattedDiffTotalExp = await formatExp(Math.abs(diffTotalExp));
message += `📈 总经验${totalChangeDesc}${formattedDiffTotalExp}\n`;
}
if (diffExpStars !== 0) {
const expStarsChangeDesc = diffExpStars > 0 ? '增加' : '减少';
const formattedDiffExpStars = await formatExp(Math.abs(diffExpStars));
message += `🐶 未分解的狗粮经验${expStarsChangeDesc}${formattedDiffExpStars}`;
}
} else {
// 如果没有变化,输出不变
message += `📊 经验数据无变化`;
}
// 构建完整日志(包含变化信息)
if (diffTotalExp !== 0 || diffExpStars !== 0) {
const totalChangeDesc = diffTotalExp > 0 ? '增加' : '减少';
const expStarsChangeDesc = diffExpStars > 0 ? '增加' : '减少';
const formattedDiffTotalExpLog = await formatExp(Math.abs(diffTotalExp));
const formattedDiffExpStarsLog = await formatExp(Math.abs(diffExpStars));
logMessage = `总经验${totalChangeDesc}${formattedDiffTotalExpLog} | 狗粮经验${expStarsChangeDesc}${formattedDiffExpStarsLog} | ${baseLog}`;
} else {
// 如果没有变化,日志也显示不变
logMessage = `经验数据无变化 | ${baseLog}`;
}
}
if (settings.notify) {
notification.send(message);
}
log.info(logMessage);
await genshin.returnMainUi();
// 格式化经验值显示
async function formatExp(num) {
if (num >= 10000) {
// 直接除法并转换为字符串,保留所有有效小数
return `${(num / 10000).toString()}`;
} else {
return `${num}`;
}
}
/**
* 在指定区域内,用 0-9 的 PNG 模板做「多阈值 + 非极大抑制」数字识别,
* 最终把检测到的数字按左右顺序拼成一个整数返回。
*
* @param {string} numberPngFilePath - 存放 0.png ~ 9.png 的文件夹路径(不含文件名)
* @param {number} x - 待识别区域的左上角 x 坐标,默认 0
* @param {number} y - 待识别区域的左上角 y 坐标,默认 0
* @param {number} w - 待识别区域的宽度,默认 1920
* @param {number} h - 待识别区域的高度,默认 1080
* @param {number} maxThreshold - 模板匹配起始阈值,默认 0.95(最高可信度)
* @param {number} minThreshold - 模板匹配最低阈值,默认 0.8(最低可信度)
* @param {number} splitCount - 在 maxThreshold 与 minThreshold 之间做几次等间隔阈值递减,默认 3
* @param {number} maxOverlap - 非极大抑制时允许的最大重叠像素,默认 2只要 x 或 y 方向重叠大于该值即视为重复框
*
* @returns {number} 识别出的整数;若没有任何有效数字框则返回 -1
*
* @example
* const mora = await numberTemplateMatch('摩拉数字', 860, 70, 200, 40);
* if (mora >= 0) console.log(`当前摩拉:${mora}`);
*/
async function numberTemplateMatch(
numberPngFilePath,
x = 0, y = 0, w = 1920, h = 1080,
maxThreshold = 0.95,
minThreshold = 0.8,
splitCount = 3,
maxOverlap = 2
) {
let ros = [];
for (let i = 0; i <= 9; i++) {
ros[i] = RecognitionObject.TemplateMatch(
file.ReadImageMatSync(`${numberPngFilePath}/${i}.png`), x, y, w, h);
}
function setThreshold(roArr, newThreshold) {
for (let i = 0; i < roArr.length; i++) {
roArr[i].Threshold = newThreshold;
roArr[i].InitTemplate();
}
}
const gameRegion = captureGameRegion();
const allCandidates = [];
/* 1. splitCount 次等间隔阈值递减 */
for (let k = 0; k < splitCount; k++) {
const curThr = maxThreshold - (maxThreshold - minThreshold) * k / Math.max(splitCount - 1, 1);
setThreshold(ros, curThr);
/* 2. 0-9 每个模板跑一遍,所有框都收 */
for (let digit = 0; digit <= 9; digit++) {
const res = gameRegion.findMulti(ros[digit]);
if (res.count === 0) continue;
for (let i = 0; i < res.count; i++) {
const box = res[i];
allCandidates.push({
digit: digit,
x: box.x,
y: box.y,
w: box.width,
h: box.height,
thr: curThr
});
}
}
}
gameRegion.dispose();
/* 3. 无结果提前返回 -1 */
if (allCandidates.length === 0) {
return -1;
}
/* 4. 非极大抑制(必须 x、y 两个方向重叠都 > maxOverlap 才视为重复) */
const adopted = [];
for (const c of allCandidates) {
let overlap = false;
for (const a of adopted) {
const xOverlap = Math.max(0, Math.min(c.x + c.w, a.x + a.w) - Math.max(c.x, a.x));
const yOverlap = Math.max(0, Math.min(c.y + c.h, a.y + a.h) - Math.max(c.y, a.y));
if (xOverlap > maxOverlap && yOverlap > maxOverlap) {
overlap = true;
break;
}
}
if (!overlap) {
adopted.push(c);
//log.info(`在 [${c.x},${c.y},${c.w},${c.h}] 找到数字 ${c.digit},匹配阈值=${c.thr}`);
}
}
/* 5. 按 x 排序,拼整数;仍无有效框时返回 -1 */
if (adopted.length === 0) return -1;
adopted.sort((a, b) => a.x - b.x);
return adopted.reduce((num, item) => num * 10 + item.digit, 0);
}
async function clickPNG(png, doClick = true, maxAttempts = 40, Threshold = 0.9) {
const pngRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync(`assets/RecognitionObject/${png}.png`));
pngRo.Threshold = Threshold;
pngRo.InitTemplate();
return await findAndClick(pngRo, maxAttempts, doClick);
}
async function findAndClick(target, maxAttempts = 20, doClick) {
//log.info("调试-开始检查");
for (let i = 0; i < maxAttempts; i++) {
//log.info("调试-检查一次");
const rg = captureGameRegion();
try {
const res = rg.find(target);
if (res.isExist()) { if (doClick) await sleep(16), res.click(), await sleep(50); return true; }
} finally { rg.dispose(); }
if (i < maxAttempts - 1) await sleep(50);
}
return false;
}
// 检验账户名
async function getUserName() {
userName = userName.trim();
// 账户名规则数字、中英文长度1-20字符
if (!userName || !/^[\u4e00-\u9fa5A-Za-z0-9]{1,20}$/.test(userName)) {
log.error(`账户名${userName}违规暂时使用默认账户名请查看readme后修改`)
userName = "默认账户";
}
return userName;
}
async function close_expired_stuff_popup_window() {
const game_region = captureGameRegion();
const text_x = 850;
const text_y = 273;
const text_w = 225;
const text_h = 51;
const ocr_res = game_region.find(RecognitionObject.ocr(text_x, text_y, text_w, text_h));
if (ocr_res) {
if (ocr_res.text.includes("物品过期")) {
log.info("检测到物品过期");
click(1000, 750);
await sleep(1000);
}
}
game_region.dispose();
}
/**
* 识别背包中指定物品的数量
* @param {string} itemName - 物品名称(仅用于日志)
* @param {string} templatePath - 模板图片路径
* @returns {Promise<string>} 识别到的数字字符串(可能为空)
*/
async function getBottleCount(itemName, templatePath) {
const ro = RecognitionObject.TemplateMatch(file.ReadImageMatSync(templatePath));
ro.InitTemplate();
for (let i = 0; i < 5; i++) {
const rg = captureGameRegion();
try {
const res = rg.find(ro);
if (res.isExist()) {
const regionToCheck = { x: res.x, y: res.y + 20, width: 70, height: 20 };
// 使用numberTemplateMatch函数识别数字
const count = await numberTemplateMatch(
'assets/背包数字', // 数字模板文件夹路径
regionToCheck.x, regionToCheck.y, regionToCheck.width, regionToCheck.height
);
const digits = count === -1 ? '' : count.toString();
log.info(`识别到${itemName}数量为${digits}`);
//log.info(`识别到${itemName}识别区域为${regionToCheck.x}, ${regionToCheck.y}, ${regionToCheck.width}, ${regionToCheck.height}`)
return digits; // 成功识别即返回
}
} finally {
rg.dispose();
}
if (i < 5 - 1) await sleep(50);
}
return ''; // 未找到时返回空字符串
}
// 检验账户名
async function getUserName() {
account = account.trim();
// 账户名规则数字、中英文长度1-20字符
if (!account || !/^[\u4e00-\u9fa5A-Za-z0-9]{1,20}$/.test(account)) {
log.error(`账户名${account}违规暂时使用默认账户名请查看readme后修改`)
account = "默认账户";
}
return account;
}
/**
* 获取本地记录中最新的一组数据
* @param {string} filePath - 记录文件路径
* @returns {Promise<object>} 包含经验数据的对象
*/
async function getLocalData(filePath) {
// 初始化返回结果
const result = {
initialValue: null,
smallBottle: null,
bigBottle: null,
starCounts: {
star1: null,
star2: null,
star3: null,
star4: null
},
totalExp: null,
initialized: {
initialValue: false,
smallBottle: false,
bigBottle: false,
star1: false,
star2: false,
star3: false,
star4: false,
totalExp: false
}
};
try {
// 尝试读取文件,不存在则直接返回空结果
const content = await file.readText(filePath);
const lines = content.split('\n').filter(line => line.trim());
if (lines.length === 0) return result;
// 数据匹配正则
const initialValueRegex = /已储存经验-(\d+)/;
const smallBottleRegex = /小经验瓶-(\d+)/;
const bigBottleRegex = /大经验瓶-(\d+)/;
const star1Regex = /1星狗粮-(\d+)/;
const star2Regex = /2星狗粮-(\d+)/;
const star3Regex = /3星狗粮-(\d+)/;
const star4Regex = /4星狗粮-(\d+)/;
const totalExpRegex = /总经验-(\d+)/;
// 遍历前几条记录,寻找最新的一组完整数据
for (const line of lines) {
// 匹配已储存经验
if (!result.initialized.initialValue) {
const match = line.match(initialValueRegex);
if (match) {
result.initialValue = parseInt(match[1]);
result.initialized.initialValue = true;
}
}
// 匹配小经验瓶
if (!result.initialized.smallBottle) {
const match = line.match(smallBottleRegex);
if (match) {
result.smallBottle = parseInt(match[1]);
result.initialized.smallBottle = true;
}
}
// 匹配大经验瓶
if (!result.initialized.bigBottle) {
const match = line.match(bigBottleRegex);
if (match) {
result.bigBottle = parseInt(match[1]);
result.initialized.bigBottle = true;
}
}
// 匹配1星狗粮
if (!result.initialized.star1) {
const match = line.match(star1Regex);
if (match) {
result.starCounts.star1 = parseInt(match[1]);
result.initialized.star1 = true;
}
}
// 匹配2星狗粮
if (!result.initialized.star2) {
const match = line.match(star2Regex);
if (match) {
result.starCounts.star2 = parseInt(match[1]);
result.initialized.star2 = true;
}
}
// 匹配3星狗粮
if (!result.initialized.star3) {
const match = line.match(star3Regex);
if (match) {
result.starCounts.star3 = parseInt(match[1]);
result.initialized.star3 = true;
}
}
// 匹配4星狗粮
if (!result.initialized.star4) {
const match = line.match(star4Regex);
if (match) {
result.starCounts.star4 = parseInt(match[1]);
result.initialized.star4 = true;
}
}
// 匹配总经验
if (!result.initialized.totalExp) {
const match = line.match(totalExpRegex);
if (match) {
result.totalExp = parseInt(match[1]);
result.initialized.totalExp = true;
}
}
// 所有数据都找到,提前终止遍历
if (result.initialized.initialValue &&
result.initialized.smallBottle &&
result.initialized.bigBottle &&
result.initialized.star1 &&
result.initialized.star2 &&
result.initialized.star3 &&
result.initialized.star4 &&
result.initialized.totalExp) {
break;
}
}
return result;
} catch (error) {
// 文件不存在或读取错误时返回空结果
return result;
}
}
/**
* 更新记录文件
* @param {string} filePath - 记录文件路径
* @param {number} initialValue - 已储存经验
* @param {number} smallBottle - 小经验瓶数量
* @param {number} bigBottle - 大经验瓶数量
* @param {object} starCounts - 狗粮数量
* @param {number} totalExp - 总经验值
*/
async function updateRecord(filePath, initialValue, smallBottle, bigBottle, starCounts, totalExp) {
// 生成当前时间字符串
const now = new Date();
const timeStr = `${now.getFullYear()}/${
String(now.getMonth() + 1).padStart(2, '0')
}/${
String(now.getDate()).padStart(2, '0')
} ${
String(now.getHours()).padStart(2, '0')
}:${
String(now.getMinutes()).padStart(2, '0')
}:${
String(now.getSeconds()).padStart(2, '0')
}`;
// 生成记录
const records = [
`时间:${timeStr}-已储存经验-${initialValue}`,
`时间:${timeStr}-小经验瓶-${smallBottle}`,
`时间:${timeStr}-大经验瓶-${bigBottle}`,
`时间:${timeStr}-1星狗粮-${starCounts.star1}`,
`时间:${timeStr}-2星狗粮-${starCounts.star2}`,
`时间:${timeStr}-3星狗粮-${starCounts.star3}`,
`时间:${timeStr}-4星狗粮-${starCounts.star4}`,
`时间:${timeStr}-总经验-${totalExp}`
];
try {
let content = await file.readText(filePath);
let lines = content.split('\n').filter(line => line.trim());
if (lines.length === 0) {
// 文件为空,直接写入新记录
await file.writeText(filePath, records.join('\n'));
return true;
}
// 添加新记录到最前面
lines.unshift(...records);
// 只保留1年内的记录
const oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
const recentLines = lines.filter(line => {
const timeMatch = line.match(/时间:(\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2})/);
if (!timeMatch) return false;
const lineTime = new Date(timeMatch[1]);
return lineTime >= oneYearAgo;
});
// 写入文件
await file.writeText(filePath, recentLines.join('\n'));
return true;
} catch (error) {
// 文件不存在时创建新文件
await file.writeText(filePath, records.join('\n'));
return true;
}
}
})();

View File

@@ -0,0 +1,18 @@
{
"manifest_version": 1,
"name": "圣遗物经验统计",
"version": "1.0",
"bgi_version": "0.55",
"description": "识别可分解的1-4星圣遗物和经验瓶数量",
"authors": [
{
"name": "勺子",
"links": "https://github.com/skyflag2022"
}
],
"settings_ui": "settings.json",
"main": "main.js",
"saved_files": [
"assets/*.txt"
]
}

View File

@@ -0,0 +1,14 @@
[
{
"name": "userName",
"type": "input-text",
"label": "账户名称\n用于多账户运行时区分不同账户",
"default": "默认账户"
},
{
"name": "notify",
"type": "checkbox",
"label": "是否通知信息",
"default": true
}
]