JS: 切换账号解决循环跳出问题 (#1211)

* Update README.md

* Update main.js

解决循环无法跳出

* Update README.md

增加更新记录

* Update README.md

* Update manifest.json

---------

Co-authored-by: 起个名字好难 <25520958+MisakaAldrich@users.noreply.github.com>
This commit is contained in:
彩虹QQ人
2025-06-27 15:40:32 +08:00
committed by GitHub
parent 5eb10ad994
commit b3e1affaa6
3 changed files with 101 additions and 85 deletions

View File

@@ -31,7 +31,12 @@
--- ---
# 脚本信息 # 脚本信息
@name 原神自动化登录脚本 @name 原神自动化登录脚本
@version 1.1 @version 1.2
@description 原神自动登录工具(仅供学习交流,请勿商业用途) @description 原神自动登录工具(仅供学习交流,请勿商业用途)
@author 彩虹QQ人 @author 彩虹QQ人
@match 原神版本≥5.5BGI版本≥0.44.6 @match 原神版本≥5.5BGI版本≥0.44.6
# 更新记录
2025年6月26日 version 1.1 整合代码
2025年6月27日 version 1.2 解决循环无法跳出的问题

View File

@@ -39,61 +39,66 @@ const login_verification = {
name: "verification.png" name: "verification.png"
}; };
async function matchImgAndClick(obj, desc,timeout = 8000) { async function clickCenter(x, y, width, height) {
let centerX = Math.round(x + width / 2);
let centerY = Math.round(y + height / 2);
await click(centerX, centerY);
await sleep(500); // 确保点击后有足够的时间等待
return { success: true, x: centerX, y: centerY };
}
async function matchImgAndClick(obj, desc, timeout = 8000) {
const start = Date.now(); const start = Date.now();
let x = 1; // 识别次数计数 let retryCount = 0; // 识别次数计数
while (Date.now() - start < timeout) { let status = false; // 用于记录是否匹配成功
try { try {
await sleep(500); // 短暂延迟,避免过快循环 while (Date.now() - start < timeout && !status) {
let result = captureGameRegion().Find(obj.template); let result = captureGameRegion().Find(obj.template);
await sleep(500); // 短暂延迟,避免过快循环 await sleep(500); // 短暂延迟,避免过快循环
if (result.isExist()) { if (result.isExist()) {
let centerX = Math.round(result.x + result.width / 2); let clickResult = await clickCenter(result.x, result.y, result.width, result.height);
let centerY = Math.round(result.y + result.height / 2); log.info(`【IMG】成功识别并点击 ${desc}| 耗时: ${Date.now() - start}ms`);
result.click(); status = true; // 设置匹配成功状态
log.info(`成功识别并点击 ${desc}| 耗时: ${Date.now() - start}ms`); return {success: true, x: clickResult.x, y: clickResult.y};
return { success: true, x: centerX, y: centerY };
} }
} catch (error) { await sleep(200); // 短暂延迟,避免过快循环
log.error(`识别图像时发生异常: ${error.message}`); log.info(`【IMG】第${retryCount++}次识别并点击 ${desc} 失败 | 耗时: ${Date.now() - start}ms`);
} }
log.info(`${x++}次识别并点击 ${desc} 失败 | 耗时: ${Date.now() - start}ms`); } catch (error) {
log.error(`【IMG】${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`);
//如果有配置通知……
notification.error(`【IMG】${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`);
throw new Error(`【IMG】识别图像时发生异常: ${error.message}`);
} }
log.warn(`${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`); return {success: false};
//这里配置通知方法
notification.error(`${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`);
return { success: false };
} }
async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
let startTime = Date.now(); let start = Date.now();
let retryCount = 0; // 重试计数 let retryCount = 0; // 重试计数
while (Date.now() - startTime < timeout) { let status = false; // 用于记录是否匹配成功
try { try {
// 尝试 OCR 识别 while (Date.now() - start < timeout && !status) {
let resList = captureGameRegion().findMulti(ocrRegion); // 指定识别区域 let resultList = captureGameRegion().findMulti(ocrRegion);
// 遍历识别结果,检查是否找到目标文本 await sleep(500); // 短暂延迟,避免过快循环
for (let res of resList) { for (let result of resultList) {
if (res.text.includes(targetText)) { if (result.text.includes(targetText)) {
// 如果找到目标文本,计算并点击文字的中心坐标 let clickResult = await clickCenter(result.x, result.y, result.width, result.height);
let centerX = Math.round(res.x + res.width / 2); log.info(`【OCR】成功识别并点击 ${targetText}| 耗时: ${Date.now() - start}ms`);
let centerY = Math.round(res.y + res.height / 2); status = true; // 设置匹配成功状态
await click(centerX, centerY); return {success: true, x: clickResult.x, y: clickResult.y};
await sleep(500); // 确保点击后有足够的时间等待
return { success: true, x: centerX, y: centerY };
} }
} }
} catch (error) { // await sleep(200); // 短暂延迟,避免过快循环
retryCount++; // 增加重试计数 log.info(`【OCR】${targetText}失败,正在进行第 ${retryCount++} 次重试...`);
log.warn(`页面标志识别失败,正在进行第 ${retryCount} 次重试...`);
} }
await sleep(1000); // 短暂延迟,避免过快循环 }catch (error) {
log.warn(`【OCR】经过多次尝试仍然无法识别文字: ”${targetText}“,尝试点击默认中心位置`);
await clickCenter(result.x, result.y, result.width, result.height);
//如果有配置通知……
notification.error(`【OCR】识别文字: “${targetText}”,发生异常`);
throw new Error(`【OCR】识别文字时发生异常: ${error.message}`);
} }
log.warn(`经过多次尝试,仍然无法识别文字: ${targetText},尝试点击默认中心位置`); return {success: false };
let centerX = Math.round(ocrRegion.x + ocrRegion.width / 2);
let centerY = Math.round(ocrRegion.y + ocrRegion.height / 2);
await click(centerX, centerY);
await sleep(1000);
return { success: false };
} }
/** /**
* main流程开始 * main流程开始
@@ -110,50 +115,56 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
await keyPress("VK_ESCAPE"); await keyPress("VK_ESCAPE");
await sleep(500); await sleep(500);
try {
await matchImgAndClick(pm_out,"左下角退出门"); await matchImgAndClick(pm_out,"左下角退出门");
await matchImgAndClick(out_to_login,"退出至登陆页面"); await matchImgAndClick(out_to_login,"退出至登陆页面");
//这一步根据 电脑配置和当前网络情况不同休眠时间不同,建议实际运行之后,如果有日志 第x次 识别失败,就适当增加休眠时间 //这一步根据 电脑配置和当前网络情况不同休眠时间不同,建议实际运行之后,如果有日志 第x次 识别失败,就适当增加休眠时间
await sleep(9000); await sleep(9000);
await matchImgAndClick(login_out_account,"登录页的右下角退出按钮"); await matchImgAndClick(login_out_account,"登录页的右下角退出按钮");
await matchImgAndClick(out_account,"退出当前账号"); await matchImgAndClick(out_account,"退出当前账号");
await matchImgAndClick(login_other_account,"登录其他账号"); await matchImgAndClick(login_other_account,"登录其他账号");
await sleep(1000);
await matchImgAndClick(input_phone_or_email,"填写邮箱/手机号");
await inputText(settings.username);
await sleep(1000);
await matchImgAndClick(input_password,"填写密码");
await inputText(settings.password);
await sleep(1000);
//按下回车登录账号,弹出用户协议对话框
await keyPress("VK_RETURN");
//点击回车后,等待特瓦特大门加载
await matchImgAndClick(agree,"同意用户协议");
//如果当天上下线次数过于频繁
for(let i = 1;i<=2;i++){
let verify = captureGameRegion().Find(login_verification.template);
//等待1s避免循环速度过快
await sleep(1000); await sleep(1000);
if (verify.isExist()) { await matchImgAndClick(input_phone_or_email,"填写邮箱/手机号");
//这里可配置通知方法 await inputText(settings.username);
notification.error(`${script_name}触发人机验证,请手动登录。===待切换UID${settings.UID}`); await sleep(1000);
log.error(`${script_name}触发人机验证,请手动登录。===待切换UID${settings.UID}`); await matchImgAndClick(input_password,"填写密码");
await inputText(settings.password);
await sleep(1000);
//按下回车登录账号,弹出用户协议对话框
await keyPress("VK_RETURN");
//点击回车后,等待特瓦特大门加载
await matchImgAndClick(agree,"同意用户协议");
//如果当天上下线次数过于频繁
for(let i = 1;i<=2;i++){
let verify = captureGameRegion().Find(login_verification.template);
//等待1s避免循环速度过快
await sleep(1000);
if (verify.isExist()) {
//这里可配置通知方法
notification.error(`${script_name}触发人机验证,请手动登录。===待切换UID${settings.UID}`);
log.error(`${script_name}触发人机验证,请手动登录。===待切换UID${settings.UID}`);
}
} }
} /**
/** * 根据不同网络环境和电脑配置,此操作可能会将领取月卡操作取代,但是不影响使用
* 根据不同网络环境和电脑配置,此操作可能会将领取月卡操作取代,但是不影响使用 * 如果发现卡在这一步请适当延长sleep时间
* 如果发现卡在这一步请适当延长sleep时间 */
*/ await sleep(8000);
await sleep(8000); await recognizeTextAndClick("点击进入", RecognitionObject.Ocr(862, 966, 206, 104), 960, 540, 5000);
await recognizeTextAndClick("点击进入", RecognitionObject.Ocr(862, 966, 206, 104), 960, 540, 5000); await sleep(12000);
await sleep(12000);
//可能登录账号的时候出现月卡提醒,则先点击一次月卡。 //可能登录账号的时候出现月卡提醒,则先点击一次月卡。
await genshin.blessingOfTheWelkinMoon(); await genshin.blessingOfTheWelkinMoon();
await sleep(1000); await sleep(1000);
await genshin.blessingOfTheWelkinMoon(); await genshin.blessingOfTheWelkinMoon();
await sleep(1000); await sleep(1000);
//如果配置了通知 // 如果配置了通知
notification.send("账号切换成功【UID" + settings.UID + "】"); notification.send("【UID" + settings.UID + "】切换成功");
}catch (error) {
log.error(`${script_name}脚本执行过程中发生错误:${error.message}`);
//如果发生错误,则发送通知
notification.error(`${script_name}脚本执行过程中发生错误:${error.message}`);
throw new Error(`${script_name}脚本执行过程中发生错误:${error.message}`);
}
})(); })();

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 1, "manifest_version": 1,
"name": "切换账号(OCR)版本", "name": "切换账号(OCR)版本",
"version": "1.1", "version": "1.2",
"description": "使用OCR实现从主页面退出登录实现切换选定账号。脚本维护/脚本问题请联系作者。\n免责申明所有的账号密码均保存在本地请使用者妥善保管账号密码请勿外泄账号密码。若因使用此脚本导致的账号泄露、封禁问题与脚本作者无关。", "description": "使用OCR实现从主页面退出登录实现切换选定账号。脚本维护/脚本问题请联系作者。\n免责申明所有的账号密码均保存在本地请使用者妥善保管账号密码请勿外泄账号密码。若因使用此脚本导致的账号泄露、封禁问题与脚本作者无关。",
"authors": [ "authors": [
{ {