Skip to content

Commit c37f9b8

Browse files
authored
Update 更新OCR切换账号脚本 (#1202)
* Update README.md * Update manifest.json * Update main.js * Update README.md
1 parent 3902415 commit c37f9b8

File tree

3 files changed

+125
-113
lines changed

3 files changed

+125
-113
lines changed
Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
1-
// ==UserScript==
2-
// @name 原神自动化登录脚本
3-
// @version 1.0
4-
// @description 原神自动登录工具(仅供学习交流)
5-
// @author 彩虹QQ人
6-
// @match 原神版本:5.5;BGI版本:0.44.6
7-
// ==/UserScript==
8-
9-
此版本可以发送相关通知,如需要可以修改main中的notification方法。
10-
11-
/**
12-
* === 重要免责声明 ===
13-
* 1. 数据安全
14-
* - 本脚本使用的用户名、密码等敏感信息仅存储在本地设备,开发者无法获取。
15-
* - 使用者需自行承担账户信息泄露风险,请勿在公共设备或不可信环境中使用。
16-
*
17-
* 2. 使用风险
18-
* - 本脚本为开源学习项目,禁止用于商业用途或违反游戏条款的行为。
19-
* - 滥用可能导致游戏账号封禁,开发者不承担任何直接或间接责任。
20-
*
21-
* 3. 责任限制
22-
* - 本脚本按“现状”提供,不承诺兼容性、安全性或功能完整性。
23-
* - 因使用本脚本导致的账号、数据、设备损失,开发者概不负责。
24-
*
25-
* 4. 禁止条款
26-
* - 严禁逆向工程、恶意篡改或用于外挂等非法用途。
27-
* - 若游戏运营商提出要求,开发者保留随时停止维护的权利。
28-
*
29-
* 继续使用即表示您已阅读并同意上述条款。
30-
* Last Updated: 2024-04-22
31-
*/
1+
# 自动切换账号脚本
2+
3+
**重要免责声明:**
4+
5+
1. **数据安全:**
6+
1. 本脚本使用的用户名、密码等敏感信息仅存储在本地设备,开发者无法获取。
7+
2. 使用者需自行承担账户信息泄露风险,请勿在公共设备或不可信环境中使用。
8+
2. **使用风险:**
9+
1. 本脚本为开源学习项目,禁止用于商业用途或违反游戏条款的行为。
10+
2. 滥用可能导致游戏账号封禁,开发者不承担任何直接或间接责任。
11+
3. **责任限制:**
12+
1. 本脚本按“现状”提供,不承诺兼容性、安全性或功能完整性。
13+
2. 因使用本脚本导致的账号、数据、设备损失,开发者概不负责。
14+
4. **禁止条款:**
15+
1. 严禁逆向工程、恶意篡改或用于外挂等非法用途。
16+
2. 若游戏运营商提出要求,开发者保留随时停止维护的权利。
17+
18+
使用脚本即表示您已阅读并同意上述条款。
19+
20+
如果使用BGI一条龙启动,且第一个脚本就是账号切换,则需要开启BGI启动配置-同时启动原神-自动进入游戏
21+
22+
根据使用者设备配置和网络环境,脚本中的延时可以适当调整。(如果不熟悉脚本操作则不建议修改延时)
23+
24+
作者留言:
25+
毕竟都使用BGI了,时间还算个事吗?(笑)
26+
27+
需要国际服适配脚本私信我。
28+
29+
**Last Updated:** 2025-06-26
30+
31+
---
32+
# 脚本信息
33+
@name 原神自动化登录脚本
34+
@version 1.1
35+
@description 原神自动登录工具(仅供学习交流,请勿商业用途)
36+
@author 彩虹QQ人
37+
@match 原神版本:≥5.5;BGI版本:≥0.44.6
Lines changed: 85 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
const author = "彩虹QQ人";
22
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+
74
const pm_out = {
85
template: RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/pm_out.png")),
96
name: "pm_out.png"
@@ -41,113 +38,122 @@ const login_verification = {
4138
template:RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/verification.png")),
4239
name: "verification.png"
4340
};
44-
const click_into = {
45-
template:RecognitionObject.TemplateMatch(file.ReadImageMatSync("assets/click_into.png")),
46-
name: "click_into.png"
47-
};
4841

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); // 短暂延迟,避免过快循环
6048
let result = captureGameRegion().Find(obj.template);
49+
await sleep(500); // 短暂延迟,避免过快循环
6150
if (result.isExist()) {
51+
let centerX = Math.round(result.x + result.width / 2);
52+
let centerY = Math.round(result.y + result.height / 2);
6253
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 };
6856
}
57+
} catch (error) {
58+
log.error(`识别图像时发生异常: ${error.message}`);
59+
}
60+
log.info(`第${x++}次识别并点击 ${desc} 失败 | 耗时: ${Date.now() - start}ms`);
6961
}
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+
}
8984
}
90-
log.info(`第${x}识别并点击 ${desc} 失败 | 耗时: ${Date.now() - start}ms`);
91-
x++;
92-
await sleep(1000);
85+
} catch (error) {
86+
retryCount++; // 增加重试计数
87+
log.warn(`页面标志识别失败,正在进行第 ${retryCount} 次重试...`);
9388
}
94-
//这里可配置通知方法
95-
//notification.error(`${script_name}等待超时,请人工介入。===待切换账号:${settings.username}===超时原因:未找到目标 [${desc}] | 文件:${obj.name}`);
96-
throw new Error(`${script_name}等待超时:未找到目标 [${desc}] | 文件:${obj.name}`);
89+
await sleep(1000); // 短暂延迟,避免过快循环
9790
}
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);
99109
await genshin.returnMainUi();
100-
//按下alt键(确保释放)
101-
await keyDown("VK_MENU");
110+
111+
await keyPress("VK_ESCAPE");
102112
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,"退出至登陆页面");
110116
//这一步根据 电脑配置和当前网络情况不同休眠时间不同,建议实际运行之后,如果有日志 : 第x次 识别失败,就适当增加休眠时间
111117
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,"登录其他账号");
115121
await sleep(1000);
116-
await waitForToClick(input_phone_or_email,"填写邮箱/手机号");
122+
await matchImgAndClick(input_phone_or_email,"填写邮箱/手机号");
117123
await inputText(settings.username);
118124
await sleep(1000);
119-
await waitForToClick(input_password,"填写密码");
125+
await matchImgAndClick(input_password,"填写密码");
120126
await inputText(settings.password);
121127
await sleep(1000);
122128
//按下回车登录账号,弹出用户协议对话框
123129
await keyPress("VK_RETURN");
124130
//点击回车后,等待特瓦特大门加载
125-
await waitForToClick(agree,"同意用户协议");
131+
await matchImgAndClick(agree,"同意用户协议");
126132
//如果当天上下线次数过于频繁
127133
for(let i = 1;i<=2;i++){
128-
await sleep(1000);
129134
let verify = captureGameRegion().Find(login_verification.template);
130-
//等待1s确保识别结果
131-
await sleep(800);
135+
//等待1s避免循环速度过快
136+
await sleep(1000);
132137
if (verify.isExist()) {
133138
//这里可配置通知方法
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}`);
136141
}
137142
}
138143
/**
139144
* 根据不同网络环境和电脑配置,此操作可能会将领取月卡操作取代,但是不影响使用
140145
* 如果发现卡在这一步,请适当延长sleep时间
141146
*/
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);
150154
await genshin.blessingOfTheWelkinMoon();
151155
await sleep(1000);
152-
log.info(`账号切换成功,当前帐号:${settings.username}`);
156+
//如果配置了通知
157+
notification.send("账号切换成功【UID:" + settings.UID + "】");
158+
153159
})();
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"manifest_version": 1,
33
"name": "切换账号(OCR)版本",
4-
"version": "1.0",
5-
"description": "使用OCR实现:从主页面退出登录实现切换选定账号。脚本问题请联系作者。免责申明:所有的账号密码均保存在本地,请使用者妥善保管账号密码,请勿外泄账号密码。",
4+
"version": "1.1",
5+
"description": "使用OCR实现:从主页面退出登录实现切换选定账号。脚本维护/脚本问题请联系作者。\n免责申明:所有的账号密码均保存在本地,请使用者妥善保管账号密码,请勿外泄账号密码。若因使用此脚本导致的账号泄露、封禁问题与脚本作者无关",
66
"authors": [
77
{
88
"name": "彩虹QQ人",
@@ -11,4 +11,4 @@
1111
],
1212
"settings_ui": "settings.json",
1313
"main": "main.js"
14-
}
14+
}

0 commit comments

Comments
 (0)