fix: 修复切换账号的OCR方法 (#3176)

* fix: 修复切换账号的OCR方法

* 更新版本号
This commit is contained in:
ShadowLemoon
2026-05-01 19:23:52 +08:00
committed by GitHub
parent a88b9823a1
commit 624cd61f5a
2 changed files with 117 additions and 13 deletions

View File

@@ -151,6 +151,107 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
}
return { success: false };
}
function normalizeOcrText(text) {
return String(text || "").replace(/\s+/g, "");
}
function levenshteinDistance(a, b) {
const rows = a.length + 1;
const cols = b.length + 1;
const dp = Array.from({ length: rows }, () => Array(cols).fill(0));
for (let i = 0; i < rows; i++) dp[i][0] = i;
for (let j = 0; j < cols; j++) dp[0][j] = j;
for (let i = 1; i < rows; i++) {
for (let j = 1; j < cols; j++) {
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
dp[i][j] = Math.min(
dp[i - 1][j] + 1,
dp[i][j - 1] + 1,
dp[i - 1][j - 1] + cost
);
}
}
return dp[a.length][b.length];
}
function ocrSimilarity(actual, target) {
actual = normalizeOcrText(actual);
target = normalizeOcrText(target);
if (!actual || !target) return 0;
if (actual.includes(target) || target.includes(actual)) return 1;
let best = 1 - levenshteinDistance(actual, target) / Math.max(actual.length, target.length);
if (actual.length > target.length) {
for (let i = 0; i <= actual.length - target.length; i++) {
const part = actual.substring(i, i + target.length);
best = Math.max(best, 1 - levenshteinDistance(part, target) / target.length);
}
}
return best;
}
function rectToOcrObject(rect) {
if (!rect) return RecognitionObject.ocrThis;
const x = rect.X != null ? rect.X : (rect.x != null ? rect.x : 0);
const y = rect.Y != null ? rect.Y : (rect.y != null ? rect.y : 0);
const width = rect.Width != null ? rect.Width : (rect.width != null ? rect.width : 0);
const height = rect.Height != null ? rect.Height : (rect.height != null ? rect.height : 0);
if (x === 0 && y === 0 && width === 0 && height === 0) {
return RecognitionObject.ocrThis;
}
return RecognitionObject.Ocr(x, y, width, height);
}
function ocrMatch(target, rect = null, threshold = null) {
const actualThreshold = threshold != null ? threshold : 0.75;
const targetText = normalizeOcrText(target);
const captureRegion = captureGameRegion();
try {
const resultList = captureRegion.findMulti(rectToOcrObject(rect));
const texts = [];
for (let result of resultList) {
const text = normalizeOcrText(result.text);
if (!text) continue;
if (ocrSimilarity(text, targetText) >= actualThreshold) {
return true;
}
texts.push(text);
}
return ocrSimilarity(texts.join(""), targetText) >= actualThreshold;
} finally {
captureRegion.dispose();
}
}
async function waitForOcrMatch(target, rect = null, threshold = null, timeout = null) {
const actualTimeout = timeout != null ? timeout : 10000;
const interval = 1000;
const start = Date.now();
let retryCount = 0;
while (Date.now() - start < actualTimeout) {
if (ocrMatch(target, rect, threshold)) {
return true;
}
retryCount++;
await sleep(interval);
}
log.warn(`【OCR】等待文字匹配超时: ${target} | 重试次数: ${retryCount} | 耗时: ${Date.now() - start}ms`);
return false;
}
// 切换账号OCR模式
// ======================================================
@@ -510,12 +611,12 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
await stateReturnToGenshinGate();
await sleep(1000);
}
await page.WaitForOcrMatch("开始游戏");
await waitForOcrMatch("开始游戏");
await matchImgAndClick(login_out_account, "登录页的右下角退出按钮");
await page.WaitForOcrMatch("切换账号");
await waitForOcrMatch("切换账号");
await matchImgAndClick(confirm_switch_account, "确认切换账号");
// 检测是否弹出保存登陆记录弹框
if (await page.WaitForOcrMatch("退出并", new OpenCvSharp.OpenCvSharp.Rect(0, 0, 1920, 1080), 0.8, 1000)) {
if (await waitForOcrMatch("退出并", new OpenCvSharp.OpenCvSharp.Rect(0, 0, 1920, 1080), 0.8, 1000)) {
await matchImgAndClick(save_login_info, "保留登陆记录");
await sleep(500);
await matchImgAndClick(confirm_switch_account, "确认切换账号");
@@ -525,7 +626,7 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
await sleep(500);
// 换服务器操作
if (settings.Servers && (settings.Servers !== "不切换服务器" || settings.Servers == "")) {
await page.WaitForOcrMatch("开始游戏");
await waitForOcrMatch("开始游戏");
log.info("正在更换服务器")
await matchImgAndClick(switch_server, "更换服务器");
let serversMatched = true;
@@ -546,11 +647,11 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
}
}
await keyPress("VK_ESCAPE");
await page.WaitForOcrMatch("开始游戏");
await waitForOcrMatch("开始游戏");
await click(960, 640);
await page.Wait(5000);
log.info('等待提瓦特大门加载');
await page.WaitForOcrMatch("点击进入");
await waitForOcrMatch("点击进入");
await click(960, 640);
// 可能登录账号的时候出现月卡提醒,则先点击一次月卡。
await genshin.blessingOfTheWelkinMoon();
@@ -748,12 +849,12 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
try {
await matchImgAndClick(pm_out, "左下角退出门");
await matchImgAndClick(out_to_login, "退出至登陆页面");
await page.WaitForOcrMatch("开始游戏");
await waitForOcrMatch("开始游戏");
await matchImgAndClick(login_out_account, "登录页的右下角退出按钮");
await page.WaitForOcrMatch("切换账号");
await waitForOcrMatch("切换账号");
await matchImgAndClick(confirm_switch_account, "确认切换账号");
// 检测是否弹出保存登陆记录弹框
if (await page.WaitForOcrMatch("退出并", new OpenCvSharp.OpenCvSharp.Rect(0, 0, 1920, 1080), 0.8, 1000)) {
if (await waitForOcrMatch("退出并", new OpenCvSharp.OpenCvSharp.Rect(0, 0, 1920, 1080), 0.8, 1000)) {
await matchImgAndClick(save_login_info, "保留登陆记录");
await sleep(500);
await matchImgAndClick(confirm_switch_account, "确认切换账号");
@@ -783,7 +884,7 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
}
// 换服务器操作
if (settings.Servers && (settings.Servers !== "不切换服务器" || settings.Servers == "")) {
await page.WaitForOcrMatch("开始游戏");
await waitForOcrMatch("开始游戏");
log.info("正在更换服务器")
await matchImgAndClick(switch_server, "更换服务器");
let serversMatched = true;
@@ -804,11 +905,11 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) {
}
}
await keyPress("VK_ESCAPE");
await page.WaitForOcrMatch("开始游戏");
await waitForOcrMatch("开始游戏");
await click(960, 640);
await page.Wait(5000);
log.info('等待提瓦特大门加载');
await page.WaitForOcrMatch("点击进入");
await waitForOcrMatch("点击进入");
await click(960, 640);
// 可能登录账号的时候出现月卡提醒,则先点击一次月卡。
await genshin.blessingOfTheWelkinMoon();

View File

@@ -1,7 +1,7 @@
{
"manifest_version": 1,
"name": "切换账号多模式",
"version": "2.0",
"version": "2.1",
"bgi_version": "0.57.0",
"description": "多种模式的切换账号有下拉列表、填写账号密码OCR操作或键鼠操作目前支持B服/国际服但不完整\n免责申明所有的账号密码均保存在本地请使用者妥善保管账号密码请勿外泄账号密码。若因使用此脚本导致的账号泄露、封禁问题与脚本作者无关。",
"tags": [
@@ -33,6 +33,9 @@
{
"name": "空",
"links": "https://pd.qq.com/g/620449244035027412/post/B_022e6a69a93f0a001441152193488791090X60?showGuild=1&errCode=0&asideType=Full"
},
{
"name": "檬"
}
],
"settings_ui": "settings.json",