Skip to content

Commit 4f49fbb

Browse files
refactor(auto-exchange): 优化抽卡资源兑换脚本 (#3053)
* refactor(auto-exchange): 优化抽卡资源兑换脚本的变量初始化 - 移除不必要的注释内容,保持代码简洁性 - 确保 contentList 变量正确初始化为空数组 - 提升代码可读性和维护性 refactor(auto-exchange): 优化抽卡资源自动兑换的时间记录逻辑 - 将存储结构从 Map 改为数组对象列表,每个对象包含 uid 和时间戳 - 使用 find 方法替代 get 方法查找用户对应的完成时间记录 - 实现了更准确的时间戳记录和更新机制 - 修改文件读写操作以适配新的数据结构 - 修复了时间记录的数据持久化逻辑 - 更新了调试日志的内容显示方式 feat(auto-exchange): 添加兑换抽卡资源任务的通知配置选项 - 实现通知发送条件控制,仅当 config.send_notification 为 true 时发送 - 将固定的通知消息改为变量存储并记录到日志 - 调整文件创建成功的日志级别从 info 降至 debug - 优化代码结构以支持更灵活的通知策略 feat(auto-exchange): 添加通知配置和错误处理优化 - 引入 throwError 工具函数替代原有错误抛出方式 - 新增 send_notification 配置选项控制通知发送 - 优化星尘数量不足时的错误提示信息 - 实现条件通知发送机制,避免重复通知 - 更新设置界面添加通知配置复选框 - 重构错误处理逻辑提高代码健壮性 feat(exchange): 实现每月自动兑换抽卡资源功能 - 添加了OCR识别UID功能用于用户标识 - 实现了重试机制和错误处理逻辑 - 集成了定时刷新任务系统,默认每月1号凌晨4点执行 - 添加了商城抽卡资源自动兑换流程 - 集成了返回主界面的安全导航功能 - 添加了配置文件存储兑换记录和状态管理 - 实现了图像识别定位和点击操作自动化 - 添加了设置界面支持最大重试次数配置 feat(exchange): 实现基于UID的任务状态管理与重试机制 - 添加OCR识别UID功能,支持多用户任务状态区分 - 修改任务刷新逻辑,将单一时间戳改为UID映射的时间存储 - 实现重试机制,支持最大重试次数限制 - 优化资源兑换流程,添加空球检测和安全释放机制 - 改进错误处理,添加详细的日志记录和异常处理 - 升级版本号至1.2并添加新的开发者信息 * refactor(auto-exchange): 优化抽卡资源自动兑换的时间记录逻辑 - 移除重复的时间记录代码,将逻辑集中到兑换成功后执行 - 调整代码结构,先获取当前时间再进行兑换操作 - 简化最后一次执行时间的查找逻辑 - 删除无用的注释代码 - 统一时间戳更新流程,确保数据一致性 fix(auto-exchange): 解决UID识别验证问题 - 添加UID类型检查确保为正整数 - 验证OCR识别结果的有效性 - 在UID识别失败时抛出具体错误信息 - 防止无效UID导致后续兑换流程异常 ``` refactor(exchange): 优化每月兑换抽卡资源任务逻辑 - 将代码块包裹在 try-finally 结构中确保调试日志始终输出 - 移除多余的注释代码以提高代码整洁性 - 保持原有的业务逻辑不变,仅调整代码结构 - 确保 contentList 调试信息在所有情况下都能正确记录 ``` fix(auto-exchange): 修复抽卡资源自动兑换的时间处理逻辑 - 修正了查找用户最后兑换时间的变量名错误 - 更新了时间数据的获取方式,从字符串改为数字格式 - 修复了时间对象创建的逻辑以正确处理时间戳 - 确保当前时间戳能正确更新到用户记录中 * ``` feat(exchange): 添加兑换不足检测功能 - 在用户配置中增加insufficient_exchange状态标识 - 实现兑换不足时的错误抛出机制 - 添加星尘数量验证逻辑 - 当星尘数量少于750时设置兑换不足标志 - 集成通知发送功能用于兑换异常提醒 ```
1 parent 193a8e0 commit 4f49fbb

6 files changed

Lines changed: 387 additions & 86 deletions

File tree

2.32 KB
Loading
Lines changed: 218 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
1-
(async function () {
2-
1+
import {ocrUid} from "./utils/uid.js";
2+
import {toMainUi, throwError} from "./utils/tool.js";
3+
4+
const config = {
5+
tryRe: {
6+
max: 3,
7+
count: 0
8+
},
9+
user: {
10+
uid: undefined,
11+
insufficient_exchange: false,//兑换不足
12+
},
13+
send_notification: false
14+
}
15+
316
/**
417
* 判断任务是否已刷新
518
* @param {string} filePath - 存储最后完成时间的文件路径
@@ -23,26 +36,52 @@ async function isTaskRefreshed(filePath, options = {}) {
2336
monthlyDay = 1, // 每月刷新默认第1天
2437
monthlyHour = 4 // 每月刷新默认凌晨4点
2538
} = options;
26-
39+
if (config.user.insufficient_exchange){
40+
throwError("兑换不足,请手动兑换", config.send_notification)
41+
}
42+
config.tryRe.count++
43+
const retry = config.tryRe;
44+
const try_count_max = retry.max
45+
const try_count = retry.count
46+
if (try_count_max < try_count) {
47+
throw new Error("已重试" + (try_count - 1) + "次数,超出最大重试" + try_count_max + "次数");
48+
}
49+
if (!config.user.uid) {
50+
const resolvedUid = await ocrUid();
51+
if (!Number.isInteger(resolvedUid) || resolvedUid <= 0) {
52+
throw new Error(`UID 识别失败: ${resolvedUid}`);
53+
}
54+
config.user.uid = resolvedUid;
55+
}
56+
const uid = config.user.uid;
57+
const current = {uid: uid, time: undefined}
58+
// 读取文件内容
59+
let contentList = [];
60+
try {
61+
contentList = JSON.parse(file.readTextSync(filePath))
62+
} catch (e) {
63+
log.debug("warn:" + e.message)
64+
}
65+
const last = contentList.find(item => item.uid === uid)
66+
const lastTimeNumber = last?.time
67+
const lastTime = lastTimeNumber ? new Date(lastTimeNumber) : new Date(0);
2768
try {
28-
// 读取文件内容
29-
let content = await file.readText(filePath);
30-
const lastTime = new Date(content);
3169
const nowTime = new Date();
32-
70+
current.time = nowTime.getTime();
71+
3372
let shouldRefresh = false;
34-
73+
3574

3675
switch (refreshType) {
3776
case 'hourly': // 每小时刷新
3877
shouldRefresh = (nowTime - lastTime) >= 3600 * 1000;
3978
break;
40-
79+
4180
case 'daily': // 每天固定时间刷新
4281
// 检查是否已经过了当天的刷新时间
4382
const todayRefresh = new Date(nowTime);
4483
todayRefresh.setHours(dailyHour, 0, 0, 0);
45-
84+
4685
// 如果当前时间已经过了今天的刷新时间,检查上次完成时间是否在今天刷新之前
4786
if (nowTime >= todayRefresh) {
4887
shouldRefresh = lastTime < todayRefresh;
@@ -53,15 +92,15 @@ async function isTaskRefreshed(filePath, options = {}) {
5392
shouldRefresh = lastTime < yesterdayRefresh;
5493
}
5594
break;
56-
95+
5796
case 'weekly': // 每周固定时间刷新
5897
// 获取本周的刷新时间
5998
const thisWeekRefresh = new Date(nowTime);
6099
// 计算与本周指定星期几的差值
61100
const dayDiff = (thisWeekRefresh.getDay() - weeklyDay + 7) % 7;
62101
thisWeekRefresh.setDate(thisWeekRefresh.getDate() - dayDiff);
63102
thisWeekRefresh.setHours(weeklyHour, 0, 0, 0);
64-
103+
65104
// 如果当前时间已经过了本周的刷新时间
66105
if (nowTime >= thisWeekRefresh) {
67106
shouldRefresh = lastTime < thisWeekRefresh;
@@ -72,14 +111,14 @@ async function isTaskRefreshed(filePath, options = {}) {
72111
shouldRefresh = lastTime < lastWeekRefresh;
73112
}
74113
break;
75-
114+
76115
case 'monthly': // 每月固定时间刷新
77116
// 获取本月的刷新时间
78117
const thisMonthRefresh = new Date(nowTime);
79118
// 设置为本月指定日期的凌晨
80119
thisMonthRefresh.setDate(monthlyDay);
81120
thisMonthRefresh.setHours(monthlyHour, 0, 0, 0);
82-
121+
83122
// 如果当前时间已经过了本月的刷新时间
84123
if (nowTime >= thisMonthRefresh) {
85124
shouldRefresh = lastTime < thisMonthRefresh;
@@ -94,39 +133,60 @@ async function isTaskRefreshed(filePath, options = {}) {
94133
case 'custom': // 自定义小时数刷新
95134
shouldRefresh = (nowTime - lastTime) >= customHours * 3600 * 1000;
96135
break;
97-
136+
98137
default:
99138
throw new Error(`未知的刷新类型: ${refreshType}`);
100139
}
101-
102-
// 如果文件内容无效或不存在,视为需要刷新
103-
if (!content || isNaN(lastTime.getTime())) {
104-
await file.writeText(filePath, nowTime.toISOString());
105-
shouldRefresh = true;
106-
}
107-
108-
if (shouldRefresh) {
109-
notification.send(`任务已刷新,执行每月兑换抽卡资源`);
110-
await exchangeGoods();
111-
// 更新最后完成时间
112-
await file.writeText(filePath, nowTime.toISOString());
113-
return true;
114-
} else {
115-
notification.send(`任务未刷新,跳过每月兑换抽卡资源`);
116-
return false;
140+
141+
// // 如果文件内容无效或不存在,视为需要刷新
142+
// if (!contentList || isNaN(lastTime.getTime())) {
143+
// //todo:写入也要改 contentList.put(uid, nowTime.toISOString())
144+
// // await file.writeText(filePath, JSON.stringify(contentList));
145+
// shouldRefresh = true;
146+
// }
147+
try {
148+
if (shouldRefresh) {
149+
const message = `任务已刷新,执行每月兑换抽卡资源`;
150+
log.info(message)
151+
if (config.send_notification) {
152+
notification.send(message);
153+
}
154+
await exchangeGoods();
155+
156+
if (contentList.some(item => item.uid === current.uid)) {
157+
contentList.forEach(item => {
158+
if (item.uid === current.uid) {
159+
item.time = current.time;
160+
}
161+
})
162+
} else {
163+
contentList.push(current);
164+
}
165+
166+
// 更新最后完成时间
167+
await file.writeText(filePath, JSON.stringify(contentList));
168+
return true;
169+
} else {
170+
const message = `任务未刷新,跳过每月兑换抽卡资源`;
171+
log.info(message)
172+
if (config.send_notification) {
173+
notification.send(message);
174+
}
175+
return false;
176+
}
177+
} finally {
178+
log.debug("contentList:", JSON.stringify(contentList))
117179
}
118-
119180
} catch (error) {
120-
// 如果文件不存在,创建新文件并返回true(视为需要刷新)
121-
const createResult = await file.writeText(filePath, '');
181+
log.error(`刷新任务失败: ${error}`);
182+
const createResult = await file.writeText(filePath, JSON.stringify(contentList));
122183
if (createResult) {
123-
log.info("创建新文件成功");
124-
await isTaskRefreshed(filePath, options = {});
184+
log.debug("创建新文件成功");
185+
await isTaskRefreshed(filePath, options = {});
125186
}
126187
}
127188
}
128189

129-
130190
//检查是否为正整数
131191
function positiveIntegerJudgment(testNumber) {
132192
// 如果输入是字符串,尝试转换为数字
@@ -135,76 +195,149 @@ function positiveIntegerJudgment(testNumber) {
135195
const cleaned = testNumber.replace(/[^\d]/g, '');
136196
testNumber = parseInt(cleaned, 10);
137197
}
138-
198+
139199
// 检查是否为有效的数字
140200
if (typeof testNumber !== 'number' || isNaN(testNumber)) {
141201
throw new Error(`无效的值: ${testNumber} (必须为数字)`);
142202
}
143-
203+
144204
// 检查是否为整数
145205
if (!Number.isInteger(testNumber)) {
146206
throw new Error(`必须为整数: ${testNumber}`);
147207
}
148-
208+
149209
return testNumber;
150210
}
151211

152-
153212
async function exchangeGoods() {
154-
155-
await genshin.returnMainUi();await sleep(1000);
156-
keyPress("ESCAPE"); await sleep(2000);//呼叫派蒙
157-
click(198,416);await sleep(2000);//点击商城
158-
click(127,434);await sleep(1000);//尘辉兑换
159-
click(998,125);await sleep(1000);//星辰兑换
213+
await toMainUi();
214+
await sleep(1000);
215+
keyPress("ESCAPE");
216+
await sleep(2000);//呼叫派蒙
217+
click(198, 416);
218+
await sleep(2000);//点击商城
219+
click(127, 434);
220+
await sleep(1000);//尘辉兑换
221+
click(998, 125);
222+
await sleep(1000);//星辰兑换
223+
let materialQuantity = "";
160224
//检查星辰的数量
161225
const region = RecognitionObject.ocr(1400, 31, 150, 50); // 星辰数量区域
162226
let capture = captureGameRegion();
163-
let res = capture.find(region);
164-
capture.dispose();
165-
let materialQuantity = res.text;
166-
let validatedMaterialQuantity = positiveIntegerJudgment(materialQuantity);
167-
if(validatedMaterialQuantity < 750){
168-
notification.send(`星尘数量为:${validatedMaterialQuantity},无法全部兑换`);
169-
throw new Error(`星尘数量为:${validatedMaterialQuantity},不能完全兑换`);
227+
try {
228+
let res = capture.find(region);
229+
materialQuantity = res.text;
230+
} finally {
231+
if (capture) {
232+
capture.dispose();
233+
}
170234
}
171-
log.info(`星尘数量为:${validatedMaterialQuantity},数量充足,可以全部兑换`);
235+
172236
const pinkBallRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/pinkBall.png"));
173237
let ro1 = captureGameRegion();
174-
let pinkBall = ro1.find(pinkBallRo);
175-
ro1.dispose();
176-
if (pinkBall.isExist()) {
177-
pinkBall.click();await sleep(1000);
178-
click(1290,604);await sleep(500);//增加
179-
click(1290,604);await sleep(500);//增加
180-
click(1290,604);await sleep(500);//增加
181-
click(1290,604);await sleep(500);//增加
182-
click(1164,782);await sleep(500);//确认兑换
183-
click(960,754);await sleep(1000);//点击空白处继续
238+
let pinkBallExist = false
239+
let pinkBall
240+
try {
241+
let pinkBallFind = ro1.find(pinkBallRo);
242+
pinkBallExist = pinkBallFind.isExist();
243+
pinkBall = {
244+
x: pinkBallFind.x,
245+
y: pinkBallFind.y
246+
}
247+
} finally {
248+
if (ro1) {
249+
ro1.dispose();
250+
}
184251
}
252+
185253
const blueBallRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/blueBall.png"));
186254
let ro2 = captureGameRegion();
187-
let blueBall = ro2.find(blueBallRo);
188-
ro2.dispose();
189-
if (blueBall.isExist()) {
190-
blueBall.click();await sleep(1000);
191-
click(1290,604);await sleep(500);//增加
192-
click(1290,604);await sleep(500);//增加
193-
click(1290,604);await sleep(500);//增加
194-
click(1290,604);await sleep(500);//增加
195-
click(1164,782);await sleep(500);//确认兑换
196-
click(960,754);await sleep(1000);//点击空白处继续
197-
}
198-
notification.send(`商城抽卡资源兑换完成`);
199-
}
255+
let blueBallExist = false
256+
let blueBall
257+
try {
258+
let blueBallFind = ro2.find(blueBallRo);
259+
blueBallExist = blueBallFind.isExist();
260+
blueBall = {
261+
x: blueBallFind.x,
262+
y: blueBallFind.y
263+
}
264+
} finally {
265+
if (ro2) {
266+
ro2.dispose();
267+
}
268+
}
200269

270+
if (!pinkBallExist && !blueBallExist) {
271+
log.info(`没有粉球和蓝球,跳过兑换`);
272+
return
273+
}
274+
275+
let validatedMaterialQuantity = positiveIntegerJudgment(materialQuantity);
276+
if (validatedMaterialQuantity < 750) {
277+
config.user.insufficient_exchange=true
278+
throwError(`星尘数量为:${validatedMaterialQuantity},数量不足,无法全部兑换`, config.send_notification)
279+
// notification.send(`星尘数量为:${validatedMaterialQuantity},无法全部兑换`);
280+
// throw new Error(`星尘数量为:${validatedMaterialQuantity},不能完全兑换`);
281+
}
282+
log.info(`星尘数量为:${validatedMaterialQuantity},数量充足,可以全部兑换`);
201283

202-
203-
await isTaskRefreshed("assets/monthly.txt", {
204-
refreshType: 'monthly',
205-
monthlyDay: 1, // 每月第1天(默认值,可省略)
206-
monthlyHour: 4 // 凌晨4点(默认值,可省略)
207-
});
284+
if (pinkBallExist && pinkBall) {
285+
// pinkBall.click();
286+
click(pinkBall.x, pinkBall.y)
287+
await sleep(1000);
288+
click(1290, 604);
289+
await sleep(500);//增加
290+
click(1290, 604);
291+
await sleep(500);//增加
292+
click(1290, 604);
293+
await sleep(500);//增加
294+
click(1290, 604);
295+
await sleep(500);//增加
296+
click(1164, 782);
297+
await sleep(500);//确认兑换
298+
click(960, 754);
299+
await sleep(1000);//点击空白处继续
300+
}
208301

302+
if (blueBallExist && blueBall) {
303+
// blueBall.click();
304+
click(blueBall.x, blueBall.y)
305+
await sleep(1000);
306+
click(1290, 604);
307+
await sleep(500);//增加
308+
click(1290, 604);
309+
await sleep(500);//增加
310+
click(1290, 604);
311+
await sleep(500);//增加
312+
click(1290, 604);
313+
await sleep(500);//增加
314+
click(1164, 782);
315+
await sleep(500);//确认兑换
316+
click(960, 754);
317+
await sleep(1000);//点击空白处继续
318+
}
319+
const message = `商城抽卡资源兑换完成`;
320+
log.info(message)
321+
if (config.send_notification) {
322+
notification.send(message);
323+
}
324+
}
325+
326+
async function main() {
327+
try {
328+
config.tryRe.max = parseInt(settings.try_count_max + "") || config.tryRe.max
329+
config.send_notification = settings.send_notification
330+
} catch (e) {
331+
}
332+
try {
333+
await isTaskRefreshed("assets/monthly.txt", {
334+
refreshType: 'monthly',
335+
monthlyDay: 1, // 每月第1天(默认值,可省略)
336+
monthlyHour: 4 // 凌晨4点(默认值,可省略)
337+
});
338+
} finally {
339+
await toMainUi()
340+
}
341+
}
209342

210-
})();
343+
await main();

0 commit comments

Comments
 (0)