|
1 | 1 | const author = "彩虹QQ人"; |
2 | 2 | const script_name = "切换账号(OCR)版本"; |
3 | | -const pm_menu = { |
4 | | - template: RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/pm_menu.png")), |
5 | | - name: "pm_menu.png" |
6 | | -}; |
| 3 | + |
7 | 4 | const pm_out = { |
8 | 5 | template: RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/pm_out.png")), |
9 | 6 | name: "pm_out.png" |
@@ -41,113 +38,122 @@ const login_verification = { |
41 | 38 | template:RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/verification.png")), |
42 | 39 | name: "verification.png" |
43 | 40 | }; |
44 | | -const click_into = { |
45 | | - template:RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/click_into.png")), |
46 | | - name: "click_into.png" |
47 | | -}; |
48 | 41 |
|
49 | | - |
50 | | -(async function () { |
51 | | - |
52 | | - /** |
53 | | - * 普通的识别点击方法 |
54 | | - * @param obj 识别对象 |
55 | | - * @param desc 识别对象的描述,方便日志查看 |
56 | | - */ |
57 | | - async function identificationAndClick (obj,desc) { |
58 | | - const start = Date.now(); |
59 | | - // 在截图中寻找 |
| 42 | +async function matchImgAndClick(obj, desc,timeout = 8000) { |
| 43 | + const start = Date.now(); |
| 44 | + let x = 1; // 识别次数计数 |
| 45 | + while (Date.now() - start < timeout) { |
| 46 | + try { |
| 47 | + await sleep(500); // 短暂延迟,避免过快循环 |
60 | 48 | let result = captureGameRegion().Find(obj.template); |
| 49 | + await sleep(500); // 短暂延迟,避免过快循环 |
61 | 50 | if (result.isExist()) { |
| 51 | + let centerX = Math.round(result.x + result.width / 2); |
| 52 | + let centerY = Math.round(result.y + result.height / 2); |
62 | 53 | result.click(); |
63 | | - log.info(`成功识别并点击 ${desc} | 耗时: ${Date.now() - start}ms`); |
64 | | - }else { |
65 | | - //这里可配置通知方法 |
66 | | - // notification.error(`${script_name}识别超时===待切换账号:${settings.username}===原因:未找到目标 [${desc}] | 文件:${obj.name}`); |
67 | | - throw new Error(`${script_name}识别超时:未找到目标 [${desc}] | 文件:${obj.name}`); |
| 54 | + log.info(`成功识别并点击 ${desc}| 耗时: ${Date.now() - start}ms`); |
| 55 | + return { success: true, x: centerX, y: centerY }; |
68 | 56 | } |
| 57 | + } catch (error) { |
| 58 | + log.error(`识别图像时发生异常: ${error.message}`); |
| 59 | + } |
| 60 | + log.info(`第${x++}次识别并点击 ${desc} 失败 | 耗时: ${Date.now() - start}ms`); |
69 | 61 | } |
70 | | - |
71 | | - /** |
72 | | - * 等待正确页面的识别点击方法 |
73 | | - * @param obj 识别对象 |
74 | | - * @param desc 识别对象的描述,方便日志查看 |
75 | | - * @param timeout 设置超时时间(默认值为15000即不传此参数情况下) |
76 | | - */ |
77 | | - async function waitForToClick(obj, desc,timeout = 15000) { |
78 | | - await sleep(1000); |
79 | | - const start = Date.now(); |
80 | | - let x = 1; |
81 | | - while (Date.now() - start < timeout) { |
82 | | - let result = captureGameRegion().Find(obj.template); |
83 | | - //等待1s确保识别结果 |
84 | | - await sleep(800); |
85 | | - if (result.isExist()) { |
86 | | - result.click(); |
87 | | - log.info(`成功识别并点击 ${desc}| 耗时: ${Date.now() - start}ms`); |
88 | | - return true; |
| 62 | + log.warn(`${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`); |
| 63 | + //这里配置通知方法 |
| 64 | + notification.error(`${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`); |
| 65 | + return { success: false }; |
| 66 | +} |
| 67 | +async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { |
| 68 | + let startTime = Date.now(); |
| 69 | + let retryCount = 0; // 重试计数 |
| 70 | + while (Date.now() - startTime < timeout) { |
| 71 | + try { |
| 72 | + // 尝试 OCR 识别 |
| 73 | + let resList = captureGameRegion().findMulti(ocrRegion); // 指定识别区域 |
| 74 | + // 遍历识别结果,检查是否找到目标文本 |
| 75 | + for (let res of resList) { |
| 76 | + if (res.text.includes(targetText)) { |
| 77 | + // 如果找到目标文本,计算并点击文字的中心坐标 |
| 78 | + let centerX = Math.round(res.x + res.width / 2); |
| 79 | + let centerY = Math.round(res.y + res.height / 2); |
| 80 | + await click(centerX, centerY); |
| 81 | + await sleep(500); // 确保点击后有足够的时间等待 |
| 82 | + return { success: true, x: centerX, y: centerY }; |
| 83 | + } |
89 | 84 | } |
90 | | - log.info(`第${x}识别并点击 ${desc} 失败 | 耗时: ${Date.now() - start}ms`); |
91 | | - x++; |
92 | | - await sleep(1000); |
| 85 | + } catch (error) { |
| 86 | + retryCount++; // 增加重试计数 |
| 87 | + log.warn(`页面标志识别失败,正在进行第 ${retryCount} 次重试...`); |
93 | 88 | } |
94 | | - //这里可配置通知方法 |
95 | | - //notification.error(`${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`); |
96 | | - throw new Error(`${script_name}等待超时:未找到目标 [${desc}] | 文件:${obj.name}`); |
| 89 | + await sleep(1000); // 短暂延迟,避免过快循环 |
97 | 90 | } |
98 | | - |
| 91 | + log.warn(`经过多次尝试,仍然无法识别文字: ${targetText},尝试点击默认中心位置`); |
| 92 | + let centerX = Math.round(ocrRegion.x + ocrRegion.width / 2); |
| 93 | + let centerY = Math.round(ocrRegion.y + ocrRegion.height / 2); |
| 94 | + await click(centerX, centerY); |
| 95 | + await sleep(1000); |
| 96 | + return { success: false }; |
| 97 | +} |
| 98 | +/** |
| 99 | + * main流程开始 |
| 100 | + */ |
| 101 | +(async function () { |
| 102 | + |
| 103 | + setGameMetrics(1920, 1080, 1); |
| 104 | + // 如果切换账号是第一个脚本,则有可能出现月卡选项 |
| 105 | + await genshin.blessingOfTheWelkinMoon(); |
| 106 | + await sleep(1000); |
| 107 | + await genshin.blessingOfTheWelkinMoon(); |
| 108 | + await sleep(1000); |
99 | 109 | await genshin.returnMainUi(); |
100 | | - //按下alt键(确保释放) |
101 | | - await keyDown("VK_MENU"); |
| 110 | + |
| 111 | + await keyPress("VK_ESCAPE"); |
102 | 112 | await sleep(500); |
103 | | - try { |
104 | | - await identificationAndClick(pm_menu,"左上角派蒙脑袋"); |
105 | | - } finally { |
106 | | - await keyUp("VK_MENU"); |
107 | | - } |
108 | | - await waitForToClick(pm_out,"左下角退出门"); |
109 | | - await waitForToClick(out_to_login,"退出至登陆页面"); |
| 113 | + |
| 114 | + await matchImgAndClick(pm_out,"左下角退出门"); |
| 115 | + await matchImgAndClick(out_to_login,"退出至登陆页面"); |
110 | 116 | //这一步根据 电脑配置和当前网络情况不同休眠时间不同,建议实际运行之后,如果有日志 : 第x次 识别失败,就适当增加休眠时间 |
111 | 117 | await sleep(9000); |
112 | | - await waitForToClick(login_out_account,"登录页的右下角退出按钮"); |
113 | | - await waitForToClick(out_account,"退出当前账号"); |
114 | | - await waitForToClick(login_other_account,"登录其他账号"); |
| 118 | + await matchImgAndClick(login_out_account,"登录页的右下角退出按钮"); |
| 119 | + await matchImgAndClick(out_account,"退出当前账号"); |
| 120 | + await matchImgAndClick(login_other_account,"登录其他账号"); |
115 | 121 | await sleep(1000); |
116 | | - await waitForToClick(input_phone_or_email,"填写邮箱/手机号"); |
| 122 | + await matchImgAndClick(input_phone_or_email,"填写邮箱/手机号"); |
117 | 123 | await inputText(settings.username); |
118 | 124 | await sleep(1000); |
119 | | - await waitForToClick(input_password,"填写密码"); |
| 125 | + await matchImgAndClick(input_password,"填写密码"); |
120 | 126 | await inputText(settings.password); |
121 | 127 | await sleep(1000); |
122 | 128 | //按下回车登录账号,弹出用户协议对话框 |
123 | 129 | await keyPress("VK_RETURN"); |
124 | 130 | //点击回车后,等待特瓦特大门加载 |
125 | | - await waitForToClick(agree,"同意用户协议"); |
| 131 | + await matchImgAndClick(agree,"同意用户协议"); |
126 | 132 | //如果当天上下线次数过于频繁 |
127 | 133 | for(let i = 1;i<=2;i++){ |
128 | | - await sleep(1000); |
129 | 134 | let verify = captureGameRegion().Find(login_verification.template); |
130 | | - //等待1s确保识别结果 |
131 | | - await sleep(800); |
| 135 | + //等待1s避免循环速度过快 |
| 136 | + await sleep(1000); |
132 | 137 | if (verify.isExist()) { |
133 | 138 | //这里可配置通知方法 |
134 | | - //notification.error(`${script_name}触发人机验证,请手动登录。===待切换账号:${settings.username}`); |
135 | | - throw new Error(`${script_name}触发人机验证,请手动登录`); |
| 139 | + notification.error(`${script_name}触发人机验证,请手动登录。===待切换UID:${settings.UID}`); |
| 140 | + log.error(`${script_name}触发人机验证,请手动登录。===待切换UID:${settings.UID}`); |
136 | 141 | } |
137 | 142 | } |
138 | 143 | /** |
139 | 144 | * 根据不同网络环境和电脑配置,此操作可能会将领取月卡操作取代,但是不影响使用 |
140 | 145 | * 如果发现卡在这一步,请适当延长sleep时间 |
141 | 146 | */ |
142 | | - log.info("点击中心屏幕,等待提瓦特开门"); |
143 | | - await sleep(5000); |
144 | | - for(let i = 1;i<=8;i++){ |
145 | | - click(genshin.width/2.0,genshin.height/2.0); |
146 | | - await sleep(1000); |
147 | | - } |
148 | | - await sleep(5000); |
149 | | - log.info("执行开门自动领取月卡"); |
| 147 | + await sleep(8000); |
| 148 | + await recognizeTextAndClick("点击进入", RecognitionObject.Ocr(862, 966, 206, 104), 960, 540, 5000); |
| 149 | + await sleep(12000); |
| 150 | + |
| 151 | + //可能登录账号的时候出现月卡提醒,则先点击一次月卡。 |
| 152 | + await genshin.blessingOfTheWelkinMoon(); |
| 153 | + await sleep(1000); |
150 | 154 | await genshin.blessingOfTheWelkinMoon(); |
151 | 155 | await sleep(1000); |
152 | | - log.info(`账号切换成功,当前帐号:${settings.username}`); |
| 156 | + //如果配置了通知 |
| 157 | + notification.send("账号切换成功【UID:" + settings.UID + "】"); |
| 158 | + |
153 | 159 | })(); |
0 commit comments