mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-04-06 07:05:13 +08:00
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时设置兑换不足标志 - 集成通知发送功能用于兑换异常提醒 ```
This commit is contained in:
BIN
repo/js/每月自动兑换抽卡资源/assets/paimon_menu.png
Normal file
BIN
repo/js/每月自动兑换抽卡资源/assets/paimon_menu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
@@ -1,5 +1,18 @@
|
||||
(async function () {
|
||||
|
||||
import {ocrUid} from "./utils/uid.js";
|
||||
import {toMainUi, throwError} from "./utils/tool.js";
|
||||
|
||||
const config = {
|
||||
tryRe: {
|
||||
max: 3,
|
||||
count: 0
|
||||
},
|
||||
user: {
|
||||
uid: undefined,
|
||||
insufficient_exchange: false,//兑换不足
|
||||
},
|
||||
send_notification: false
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断任务是否已刷新
|
||||
* @param {string} filePath - 存储最后完成时间的文件路径
|
||||
@@ -23,26 +36,52 @@ async function isTaskRefreshed(filePath, options = {}) {
|
||||
monthlyDay = 1, // 每月刷新默认第1天
|
||||
monthlyHour = 4 // 每月刷新默认凌晨4点
|
||||
} = options;
|
||||
|
||||
if (config.user.insufficient_exchange){
|
||||
throwError("兑换不足,请手动兑换", config.send_notification)
|
||||
}
|
||||
config.tryRe.count++
|
||||
const retry = config.tryRe;
|
||||
const try_count_max = retry.max
|
||||
const try_count = retry.count
|
||||
if (try_count_max < try_count) {
|
||||
throw new Error("已重试" + (try_count - 1) + "次数,超出最大重试" + try_count_max + "次数");
|
||||
}
|
||||
if (!config.user.uid) {
|
||||
const resolvedUid = await ocrUid();
|
||||
if (!Number.isInteger(resolvedUid) || resolvedUid <= 0) {
|
||||
throw new Error(`UID 识别失败: ${resolvedUid}`);
|
||||
}
|
||||
config.user.uid = resolvedUid;
|
||||
}
|
||||
const uid = config.user.uid;
|
||||
const current = {uid: uid, time: undefined}
|
||||
// 读取文件内容
|
||||
let contentList = [];
|
||||
try {
|
||||
contentList = JSON.parse(file.readTextSync(filePath))
|
||||
} catch (e) {
|
||||
log.debug("warn:" + e.message)
|
||||
}
|
||||
const last = contentList.find(item => item.uid === uid)
|
||||
const lastTimeNumber = last?.time
|
||||
const lastTime = lastTimeNumber ? new Date(lastTimeNumber) : new Date(0);
|
||||
try {
|
||||
// 读取文件内容
|
||||
let content = await file.readText(filePath);
|
||||
const lastTime = new Date(content);
|
||||
const nowTime = new Date();
|
||||
|
||||
current.time = nowTime.getTime();
|
||||
|
||||
let shouldRefresh = false;
|
||||
|
||||
|
||||
|
||||
switch (refreshType) {
|
||||
case 'hourly': // 每小时刷新
|
||||
shouldRefresh = (nowTime - lastTime) >= 3600 * 1000;
|
||||
break;
|
||||
|
||||
|
||||
case 'daily': // 每天固定时间刷新
|
||||
// 检查是否已经过了当天的刷新时间
|
||||
const todayRefresh = new Date(nowTime);
|
||||
todayRefresh.setHours(dailyHour, 0, 0, 0);
|
||||
|
||||
|
||||
// 如果当前时间已经过了今天的刷新时间,检查上次完成时间是否在今天刷新之前
|
||||
if (nowTime >= todayRefresh) {
|
||||
shouldRefresh = lastTime < todayRefresh;
|
||||
@@ -53,7 +92,7 @@ async function isTaskRefreshed(filePath, options = {}) {
|
||||
shouldRefresh = lastTime < yesterdayRefresh;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'weekly': // 每周固定时间刷新
|
||||
// 获取本周的刷新时间
|
||||
const thisWeekRefresh = new Date(nowTime);
|
||||
@@ -61,7 +100,7 @@ async function isTaskRefreshed(filePath, options = {}) {
|
||||
const dayDiff = (thisWeekRefresh.getDay() - weeklyDay + 7) % 7;
|
||||
thisWeekRefresh.setDate(thisWeekRefresh.getDate() - dayDiff);
|
||||
thisWeekRefresh.setHours(weeklyHour, 0, 0, 0);
|
||||
|
||||
|
||||
// 如果当前时间已经过了本周的刷新时间
|
||||
if (nowTime >= thisWeekRefresh) {
|
||||
shouldRefresh = lastTime < thisWeekRefresh;
|
||||
@@ -72,14 +111,14 @@ async function isTaskRefreshed(filePath, options = {}) {
|
||||
shouldRefresh = lastTime < lastWeekRefresh;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'monthly': // 每月固定时间刷新
|
||||
// 获取本月的刷新时间
|
||||
const thisMonthRefresh = new Date(nowTime);
|
||||
// 设置为本月指定日期的凌晨
|
||||
thisMonthRefresh.setDate(monthlyDay);
|
||||
thisMonthRefresh.setHours(monthlyHour, 0, 0, 0);
|
||||
|
||||
|
||||
// 如果当前时间已经过了本月的刷新时间
|
||||
if (nowTime >= thisMonthRefresh) {
|
||||
shouldRefresh = lastTime < thisMonthRefresh;
|
||||
@@ -94,39 +133,60 @@ async function isTaskRefreshed(filePath, options = {}) {
|
||||
case 'custom': // 自定义小时数刷新
|
||||
shouldRefresh = (nowTime - lastTime) >= customHours * 3600 * 1000;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new Error(`未知的刷新类型: ${refreshType}`);
|
||||
}
|
||||
|
||||
// 如果文件内容无效或不存在,视为需要刷新
|
||||
if (!content || isNaN(lastTime.getTime())) {
|
||||
await file.writeText(filePath, nowTime.toISOString());
|
||||
shouldRefresh = true;
|
||||
|
||||
// // 如果文件内容无效或不存在,视为需要刷新
|
||||
// if (!contentList || isNaN(lastTime.getTime())) {
|
||||
// //todo:写入也要改 contentList.put(uid, nowTime.toISOString())
|
||||
// // await file.writeText(filePath, JSON.stringify(contentList));
|
||||
// shouldRefresh = true;
|
||||
// }
|
||||
try {
|
||||
if (shouldRefresh) {
|
||||
const message = `任务已刷新,执行每月兑换抽卡资源`;
|
||||
log.info(message)
|
||||
if (config.send_notification) {
|
||||
notification.send(message);
|
||||
}
|
||||
await exchangeGoods();
|
||||
|
||||
if (contentList.some(item => item.uid === current.uid)) {
|
||||
contentList.forEach(item => {
|
||||
if (item.uid === current.uid) {
|
||||
item.time = current.time;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
contentList.push(current);
|
||||
}
|
||||
|
||||
// 更新最后完成时间
|
||||
await file.writeText(filePath, JSON.stringify(contentList));
|
||||
return true;
|
||||
} else {
|
||||
const message = `任务未刷新,跳过每月兑换抽卡资源`;
|
||||
log.info(message)
|
||||
if (config.send_notification) {
|
||||
notification.send(message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
log.debug("contentList:", JSON.stringify(contentList))
|
||||
}
|
||||
|
||||
if (shouldRefresh) {
|
||||
notification.send(`任务已刷新,执行每月兑换抽卡资源`);
|
||||
await exchangeGoods();
|
||||
// 更新最后完成时间
|
||||
await file.writeText(filePath, nowTime.toISOString());
|
||||
return true;
|
||||
} else {
|
||||
notification.send(`任务未刷新,跳过每月兑换抽卡资源`);
|
||||
return false;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// 如果文件不存在,创建新文件并返回true(视为需要刷新)
|
||||
const createResult = await file.writeText(filePath, '');
|
||||
log.error(`刷新任务失败: ${error}`);
|
||||
const createResult = await file.writeText(filePath, JSON.stringify(contentList));
|
||||
if (createResult) {
|
||||
log.info("创建新文件成功");
|
||||
await isTaskRefreshed(filePath, options = {});
|
||||
log.debug("创建新文件成功");
|
||||
await isTaskRefreshed(filePath, options = {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//检查是否为正整数
|
||||
function positiveIntegerJudgment(testNumber) {
|
||||
// 如果输入是字符串,尝试转换为数字
|
||||
@@ -135,76 +195,149 @@ function positiveIntegerJudgment(testNumber) {
|
||||
const cleaned = testNumber.replace(/[^\d]/g, '');
|
||||
testNumber = parseInt(cleaned, 10);
|
||||
}
|
||||
|
||||
|
||||
// 检查是否为有效的数字
|
||||
if (typeof testNumber !== 'number' || isNaN(testNumber)) {
|
||||
throw new Error(`无效的值: ${testNumber} (必须为数字)`);
|
||||
}
|
||||
|
||||
|
||||
// 检查是否为整数
|
||||
if (!Number.isInteger(testNumber)) {
|
||||
throw new Error(`必须为整数: ${testNumber}`);
|
||||
}
|
||||
|
||||
|
||||
return testNumber;
|
||||
}
|
||||
|
||||
|
||||
async function exchangeGoods() {
|
||||
|
||||
await genshin.returnMainUi();await sleep(1000);
|
||||
keyPress("ESCAPE"); await sleep(2000);//呼叫派蒙
|
||||
click(198,416);await sleep(2000);//点击商城
|
||||
click(127,434);await sleep(1000);//尘辉兑换
|
||||
click(998,125);await sleep(1000);//星辰兑换
|
||||
await toMainUi();
|
||||
await sleep(1000);
|
||||
keyPress("ESCAPE");
|
||||
await sleep(2000);//呼叫派蒙
|
||||
click(198, 416);
|
||||
await sleep(2000);//点击商城
|
||||
click(127, 434);
|
||||
await sleep(1000);//尘辉兑换
|
||||
click(998, 125);
|
||||
await sleep(1000);//星辰兑换
|
||||
let materialQuantity = "";
|
||||
//检查星辰的数量
|
||||
const region = RecognitionObject.ocr(1400, 31, 150, 50); // 星辰数量区域
|
||||
let capture = captureGameRegion();
|
||||
let res = capture.find(region);
|
||||
capture.dispose();
|
||||
let materialQuantity = res.text;
|
||||
let validatedMaterialQuantity = positiveIntegerJudgment(materialQuantity);
|
||||
if(validatedMaterialQuantity < 750){
|
||||
notification.send(`星尘数量为:${validatedMaterialQuantity},无法全部兑换`);
|
||||
throw new Error(`星尘数量为:${validatedMaterialQuantity},不能完全兑换`);
|
||||
try {
|
||||
let res = capture.find(region);
|
||||
materialQuantity = res.text;
|
||||
} finally {
|
||||
if (capture) {
|
||||
capture.dispose();
|
||||
}
|
||||
}
|
||||
log.info(`星尘数量为:${validatedMaterialQuantity},数量充足,可以全部兑换`);
|
||||
|
||||
const pinkBallRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/pinkBall.png"));
|
||||
let ro1 = captureGameRegion();
|
||||
let pinkBall = ro1.find(pinkBallRo);
|
||||
ro1.dispose();
|
||||
if (pinkBall.isExist()) {
|
||||
pinkBall.click();await sleep(1000);
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1164,782);await sleep(500);//确认兑换
|
||||
click(960,754);await sleep(1000);//点击空白处继续
|
||||
let pinkBallExist = false
|
||||
let pinkBall
|
||||
try {
|
||||
let pinkBallFind = ro1.find(pinkBallRo);
|
||||
pinkBallExist = pinkBallFind.isExist();
|
||||
pinkBall = {
|
||||
x: pinkBallFind.x,
|
||||
y: pinkBallFind.y
|
||||
}
|
||||
} finally {
|
||||
if (ro1) {
|
||||
ro1.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const blueBallRo = RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/blueBall.png"));
|
||||
let ro2 = captureGameRegion();
|
||||
let blueBall = ro2.find(blueBallRo);
|
||||
ro2.dispose();
|
||||
if (blueBall.isExist()) {
|
||||
blueBall.click();await sleep(1000);
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1290,604);await sleep(500);//增加
|
||||
click(1164,782);await sleep(500);//确认兑换
|
||||
click(960,754);await sleep(1000);//点击空白处继续
|
||||
let blueBallExist = false
|
||||
let blueBall
|
||||
try {
|
||||
let blueBallFind = ro2.find(blueBallRo);
|
||||
blueBallExist = blueBallFind.isExist();
|
||||
blueBall = {
|
||||
x: blueBallFind.x,
|
||||
y: blueBallFind.y
|
||||
}
|
||||
} finally {
|
||||
if (ro2) {
|
||||
ro2.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (!pinkBallExist && !blueBallExist) {
|
||||
log.info(`没有粉球和蓝球,跳过兑换`);
|
||||
return
|
||||
}
|
||||
|
||||
let validatedMaterialQuantity = positiveIntegerJudgment(materialQuantity);
|
||||
if (validatedMaterialQuantity < 750) {
|
||||
config.user.insufficient_exchange=true
|
||||
throwError(`星尘数量为:${validatedMaterialQuantity},数量不足,无法全部兑换`, config.send_notification)
|
||||
// notification.send(`星尘数量为:${validatedMaterialQuantity},无法全部兑换`);
|
||||
// throw new Error(`星尘数量为:${validatedMaterialQuantity},不能完全兑换`);
|
||||
}
|
||||
log.info(`星尘数量为:${validatedMaterialQuantity},数量充足,可以全部兑换`);
|
||||
|
||||
if (pinkBallExist && pinkBall) {
|
||||
// pinkBall.click();
|
||||
click(pinkBall.x, pinkBall.y)
|
||||
await sleep(1000);
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1164, 782);
|
||||
await sleep(500);//确认兑换
|
||||
click(960, 754);
|
||||
await sleep(1000);//点击空白处继续
|
||||
}
|
||||
|
||||
if (blueBallExist && blueBall) {
|
||||
// blueBall.click();
|
||||
click(blueBall.x, blueBall.y)
|
||||
await sleep(1000);
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1290, 604);
|
||||
await sleep(500);//增加
|
||||
click(1164, 782);
|
||||
await sleep(500);//确认兑换
|
||||
click(960, 754);
|
||||
await sleep(1000);//点击空白处继续
|
||||
}
|
||||
const message = `商城抽卡资源兑换完成`;
|
||||
log.info(message)
|
||||
if (config.send_notification) {
|
||||
notification.send(message);
|
||||
}
|
||||
notification.send(`商城抽卡资源兑换完成`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
config.tryRe.max = parseInt(settings.try_count_max + "") || config.tryRe.max
|
||||
config.send_notification = settings.send_notification
|
||||
} catch (e) {
|
||||
}
|
||||
try {
|
||||
await isTaskRefreshed("assets/monthly.txt", {
|
||||
refreshType: 'monthly',
|
||||
monthlyDay: 1, // 每月第1天(默认值,可省略)
|
||||
monthlyHour: 4 // 凌晨4点(默认值,可省略)
|
||||
});
|
||||
} finally {
|
||||
await toMainUi()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await isTaskRefreshed("assets/monthly.txt", {
|
||||
refreshType: 'monthly',
|
||||
monthlyDay: 1, // 每月第1天(默认值,可省略)
|
||||
monthlyHour: 4 // 凌晨4点(默认值,可省略)
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
await main();
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"name": "每月自动兑换抽卡资源",
|
||||
"version": "1.1",
|
||||
"version": "1.2",
|
||||
"description": "每个月自动兑换蓝球和粉球,兑换资源不够会提醒(需要打开 js 通知),本月兑换过会自动跳过,想要重置 CD可以把monthly.txt中的时间删掉",
|
||||
"authors": [
|
||||
{
|
||||
"name": "柒叶子",
|
||||
"links": "https://github.com/5117600049"
|
||||
},
|
||||
{
|
||||
"name": "云端客",
|
||||
"links": "https://github.com/Kirito520Asuna"
|
||||
}
|
||||
],
|
||||
"settings_ui": "settings.json",
|
||||
|
||||
18
repo/js/每月自动兑换抽卡资源/settings.json
Normal file
18
repo/js/每月自动兑换抽卡资源/settings.json
Normal file
@@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"name": "send_notification",
|
||||
"type": "checkbox",
|
||||
"label": "发送通知"
|
||||
},
|
||||
{
|
||||
"name": "try_count_max",
|
||||
"type": "select",
|
||||
"label": "最大重试次数",
|
||||
"options": [
|
||||
"1","2","3",
|
||||
"4","5","6",
|
||||
"7","8","9"
|
||||
] ,
|
||||
"default": "3"
|
||||
}
|
||||
]
|
||||
82
repo/js/每月自动兑换抽卡资源/utils/tool.js
Normal file
82
repo/js/每月自动兑换抽卡资源/utils/tool.js
Normal file
@@ -0,0 +1,82 @@
|
||||
const commonPath = 'assets/'
|
||||
const commonMap = new Map([
|
||||
['main_ui', {
|
||||
path: `${commonPath}`,
|
||||
name: 'paimon_menu',
|
||||
type: '.png',
|
||||
}],
|
||||
])
|
||||
const genshinJson = {
|
||||
width: 1920,//genshin.width,
|
||||
height: 1080,//genshin.height,
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据键值获取JSON路径
|
||||
* @param {string} key - 要查找的键值
|
||||
* @returns {any} 返回与键值对应的JSON路径值
|
||||
*/
|
||||
function getJsonPath(key) {
|
||||
return commonMap.get(key); // 通过commonMap的get方法获取指定键对应的值
|
||||
}
|
||||
|
||||
// 判断是否在主界面的函数
|
||||
const isInMainUI = () => {
|
||||
// let name = '主界面'
|
||||
let main_ui = getJsonPath('main_ui');
|
||||
// 定义识别对象
|
||||
let paimonMenuRo = RecognitionObject.TemplateMatch(
|
||||
file.ReadImageMatSync(`${main_ui.path}${main_ui.name}${main_ui.type}`),
|
||||
0,
|
||||
0,
|
||||
genshinJson.width / 3.0,
|
||||
genshinJson.width / 5.0
|
||||
);
|
||||
let captureRegion = captureGameRegion();
|
||||
try {
|
||||
let res = captureRegion.find(paimonMenuRo);
|
||||
return !res.isEmpty();
|
||||
} finally {
|
||||
captureRegion.dispose()
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
async function toMainUi() {
|
||||
let ms = 300
|
||||
let index = 1
|
||||
await sleep(ms);
|
||||
while (!isInMainUI()) {
|
||||
await sleep(ms);
|
||||
await genshin.returnMainUi(); // 如果未启用,则返回游戏主界面
|
||||
await sleep(ms);
|
||||
if (index > 3) {
|
||||
throwError(`多次尝试返回主界面失败`);
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 抛出错误函数
|
||||
* 该函数用于显示错误通知并抛出错误对象
|
||||
* @param {string} msg - 错误信息,将用于通知和错误对象
|
||||
*/
|
||||
function throwError(msg, isNotification = false) {
|
||||
// 使用notification组件显示错误通知
|
||||
// notification.error(`${msg}`);
|
||||
if (isNotification) {
|
||||
notification.error(`${msg}`);
|
||||
}
|
||||
// 抛出一个包含错误信息的Error对象
|
||||
throw new Error(`${msg}`);
|
||||
}
|
||||
|
||||
export {
|
||||
getJsonPath,
|
||||
isInMainUI,
|
||||
toMainUi,
|
||||
throwError,
|
||||
}
|
||||
64
repo/js/每月自动兑换抽卡资源/utils/uid.js
Normal file
64
repo/js/每月自动兑换抽卡资源/utils/uid.js
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
async function saveOnlyNumber(str) {
|
||||
str = str ? str : '';
|
||||
// 使用正则表达式匹配字符串中的所有数字
|
||||
// \d匹配一个或多个数字
|
||||
// .join('') 将匹配到的数字数组连接成一个字符串
|
||||
// parseInt 将连接后的字符串转换为整数
|
||||
// return parseInt(str.match(/\d+/g).join(''));
|
||||
const matches = str.match(/\d+/g);
|
||||
if (!matches) {
|
||||
return 0; // 或抛出错误
|
||||
}
|
||||
return parseInt(matches.join(''), 10);
|
||||
}
|
||||
/**
|
||||
* 对指定区域进行OCR文字识别
|
||||
* @param {number} x - 区域左上角x坐标,默认为0
|
||||
* @param {number} y - 区域左上角y坐标,默认为0
|
||||
* @param {number} w - 区域宽度,默认为1920
|
||||
* @param {number} h - 区域高度,默认为1080
|
||||
* @returns {Promise<string|null>} 返回识别到的文本内容,如果识别失败则返回null
|
||||
*/
|
||||
async function ocrRegion(x = 0,
|
||||
y = 0,
|
||||
w = 1920,
|
||||
h = 1080) {
|
||||
// 创建OCR识别对象,使用指定的坐标和尺寸
|
||||
let recognitionObjectOcr = RecognitionObject.Ocr(x, y, w, h);
|
||||
// 捕获游戏区域图像
|
||||
let region3 = captureGameRegion()
|
||||
try {
|
||||
// 在捕获的区域中查找OCR识别对象
|
||||
let res = region3.find(recognitionObjectOcr);
|
||||
// 返回识别到的文本内容,如果不存在则返回undefined
|
||||
return res?.text
|
||||
} catch (e) {
|
||||
// 捕获并记录错误信息
|
||||
log.error("识别异常:{1}", e.message)
|
||||
return null
|
||||
} finally {
|
||||
// 确保释放区域资源
|
||||
region3.dispose()
|
||||
}
|
||||
}
|
||||
/**
|
||||
* OCR识别UID的异步函数
|
||||
* 该函数用于通过OCR技术识别屏幕上特定位置的UID文本
|
||||
* @returns {Promise<number>} - 异步函数,没有明确的返回值
|
||||
*/
|
||||
async function ocrUid() {
|
||||
// 定义OCR识别的坐标和尺寸参数
|
||||
let uid_json = {
|
||||
x: 1683, // OCR识别区域的左上角x坐标
|
||||
y: 1051, // OCR识别区域的左上角y坐标
|
||||
width: 234, // OCR识别区域的宽度
|
||||
height: 28, // OCR识别区域的高度
|
||||
}
|
||||
let text = await ocrRegion(uid_json.x, uid_json.y, uid_json.width, uid_json.height);
|
||||
return await saveOnlyNumber(text);
|
||||
}
|
||||
|
||||
export {
|
||||
ocrUid,
|
||||
}
|
||||
Reference in New Issue
Block a user