From b662ef9db73f6b3a8d8182e7444e52a774c742e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B7=E4=B8=AA=E5=90=8D=E5=AD=97=E5=A5=BD=E9=9A=BE?= =?UTF-8?q?=E7=9A=84=E5=96=B5?= <25520958+MisakaAldrich@users.noreply.github.com> Date: Tue, 31 Mar 2026 00:48:07 +0800 Subject: [PATCH] feat: Global Account Dropdown Mode (#3056) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: SwitchGlobalAccountDropDownMode * docs: 更新readme和版本号 --- .../Assets/RecognitionObject/out_account.png | Bin 1886 -> 1904 bytes .../Assets/RecognitionObject/quit.png | Bin 1904 -> 0 bytes repo/js/SwitchAccountMultipleMode/README.md | 4 +- repo/js/SwitchAccountMultipleMode/main.js | 122 +++++++++++++----- .../SwitchAccountMultipleMode/manifest.json | 2 +- .../SwitchAccountMultipleMode/settings.json | 7 +- 6 files changed, 100 insertions(+), 35 deletions(-) delete mode 100644 repo/js/SwitchAccountMultipleMode/Assets/RecognitionObject/quit.png diff --git a/repo/js/SwitchAccountMultipleMode/Assets/RecognitionObject/out_account.png b/repo/js/SwitchAccountMultipleMode/Assets/RecognitionObject/out_account.png index 4e25371ca4cd65c007c90a05c89eacea08d022bb..1d11fa5a31a5539e4b4b47985417e0c029c2d95a 100644 GIT binary patch literal 1904 zcmZ8ic|6ox8y{k9@sX`k!r(P-7-NiO7+YgEbHfPN)~pO>Ff)uLAr)i2UED&-QdzQG zQIwQ@LP=R7OpzAbTT{vnHScfk+vmQ^ALpFsdCqgb&-Z-K`JBT&igS^dRhI>UK=N*` z7;j+o0-Av|80fLz2TFlKjOFd(2x=NVxC}TFVGdXa5U8aX~tgK)rrZ7`eBOt?wmB?Td z5{wuuogV-g3X8;~#jt773@s5LffyafMnmG_Xk-+XKt&SCWTX+1fFK*e;bejlfsB9~ zkr4>81rly)N`(_4KgzRd)c@3Huztt@2m%w$z~IIvBFZ8g^)r)30a%Di+QM!An*X1e z4NOGjzlhoXI|A$#u>Mv?B-7vOQW$`w96&q6q6=~$kR;O$Gd70J8 z2=0t;NG2(~NjPVvD2_Rsre3|fM$0+Ri!0$^CZVJF#X%da?3Ei^a-kxeIxW>|xYbi@ zDBJH;WS^CfJ@diT$vo&*9BDo>^x6DsCOvp|vS+H5;(B>WxNwQZVEuD^JUMj}Zx|>@ zb1up{>Uf|k_fjoJ2BYV$^^=#l7gs6Ey~rM4C2_?AeuaGfWSVq^fn6-D#wrxwE zQ#9@SGPfUW-i*E0);l^gVw}3&W`GsHY)qd(=|%u3w{Z>IU(Nt7>Pcy_4($Q z_N!B7+#FW!+6iNM?FwGqjqO6`=cp7=Ltt_LiYm9VCilTL&Wa!*qvo2A#P6poukxSF zXR4Wyb-9k+Dy^fbbL&S1pWSY=6Ybu<5V8e+8B%Ey)xG&)-rOhLF@jn9XJ+N7#;v3j zolv~9`)(=On(Yyy`fp4$@iXMz+**WK)mu7W+B=8TyPvg zJdHKI@@`p95K-<6^ZW0e;>*A_c{f(awbx2^Qtxt4Bf8UXoBbDto7Z%NK*Mb;}p zL_*2}MX!HVQG70>_|bL~cwjI1YSbs@Ykvfuj~!JpEd_`Uf%bJsXfeNU6IT z*T3yC^uE%7!Je5?&}`JWw>ntV;h#QJD;MaPHr~iww2*4qS`a{0oR%NwXCR@!rU&ht z-V6P}L(D&wfJEwnZ@e9-TO#j&X?JYVk7r%mwL;)~?&4if9uq&7Qzd;UB!ci-39)x& z?rtcxZ`OJu+6-z~s{Gzg;q|>UG7m3LzDTX(um3EQ(P5*GrFWSHugXUhs2CiMHIo)Q z@XSAPjgJXQvf1w>9Tl%xQrEJ^{}p%g(thpk@#_Rxu1!4{dDA!|B6x?%9~!bd=%5Pa zQ8(pR?Ka!x+Wqu~s&ouquIqXKBdDs~@70c~MJbrWXS|$mY!?g;)~Pgk^hIzNG|SFc z-B8OOj%wpbX1-4eG=b|IVjB?~ZElZFdrTOd3Mh$vSVrhx9sjfL4o`?UvDKp+;$NQo z#AIPSTY9-wbvGI^(2b6nYO_q8{UmvepJbI0_NnlJ=cG^Y>e9^ubt`s99?nJcYa%r} zVl^eBN+9BmU&H z{nwUwyy`8Rb4qMJ8Mw{^R?fF4ZJp*n7DJ6 zekbX!x~jhQ%)R^RT~{AXcP>$G3o!1uAQS6Ly>Ybf-}d}U|Tr`_f1_@24wEMHg8cLnB+>$dT0 z(b6lLhrM-;Lyr?z3+trCCeL3~Ip2QV3>p_E*0p9k{mxFIXrDpmPtR+2 zke0axss>78Qw^?5-<@Rc20T4|__z+?OPi(IC|7^Jo~P2Rs994HB+OGt^9j>9C_gPT z{1`#XemT@Arf`0EW`q$HpBQcQRM+%FyL|y-=S{D$q$RbH(4i^`30!P+2VSr$B|XJr eX!`#@Zr%5D0#G{fye%owx98@B!!$V_KlLx!Oj;WN literal 1886 zcmbW2`8V4M7skJFY)wO{C6;+(87f0VFsfp&mZH|iR${4YL)BJmi$M*h@nTSv znTRH$G__TAkXUMuwP+O4ASB+*pYXmveC~73bMCqKo^#K+H`CDpDFT)Q0{|dmYlCp& z=?M=@VLo1jnim`KGiD!!O2o(rZLh@{b}mfG;3>}Va;!mq;sy1F-Pw6*Sq!FEo%%ceBB>HSsm*4B4X~_ z4xA->L+@*!s-E|28K-ri^~#pKl`MDO;5t5e;I)nobxOEwb~Ac2mNx2!CL*i$0093NA8;ctzni+#Z!K{_scKzSv6b1AG+ji|`;XrY0!vE#4#rs#Y8 zd|bptisS9_LuT{n%KpeYF&p9j#IZwwkIP~zxZnnVQP;H()UScrLjOnHop7g1Ce^|5 z=57VG=m1X<5kNG&E9!z+15CxYg!@&>-c=6fFgxM{pQVcpTw>O17TS>I|cWi5Qlf zpA4t^2R{p*?Dld@M=wnM`U#=T@AM*Mr)_XZNGQDBh^S!etz^RBl}f$Rf-i&_?H0 zxR=auZ`cVFjK^L>XEJ;<}(!~9)`)fDek(7{_~cg3akm!dyLE z--81}tVe|gbq*W1ySundxtPA#>7!O99}9HVzo8g)`mp;xtU)(eUqrcdjeeB(n}hGA zLm5klCLfQ_1^A(_RlVPv=YuwKv&PX);ONEMSaj_bKRnXTNha|Kc0-3V;@Cg%!X)&- z=2uc}pE%@l#rig6{yWwuS&#~E@?lrKa&t;cccLJTu=h>OIA}Y3!*A7y(4P8WV4M8_ zyFd*4*jUyoAk$H-oFam&PW`s{Hs#2LKaFVpXiGjOefUpa2eEjuzbf93su<}?Md9+P zl)fW!qI1?QEfqS48!0IOJeN4jXOMZ$EOB&}R_|g8R4P zK`*Dx05M^3^1^VCW%zHF_E^HzN!p@l&S1b(6so|$Kv?J4)DXke;po!O2nU_897r(@2CC?UCwNN|Q-hr(P;k^`-P{H8>V@sKmvs zD){61|HPcBsV_SCJr8;?2UmN=RRNvnuL-*Jp{I5b?^n>f1yMYv;2%{o=9F120hg+o z1#8eh;cy>#?9?N&32KJ7RYIh6!9&BXl`MnH3W_^_NCWW}I)nt#N=*5^{fydO*w1t} zK2`Jbc%e2eG#<12)}eAXlk<_EvWKiwzu`r`rxz2Wtp-Pic$Bhx@>mz`@JRb6YyCBu z@tmE*)U=vTPG31yZ&`-3lk}5kVgwFd62H1!Z* zmYsf4_FxVs;&y#R5(GG-Pm19`>Wd0@=(IzDY$pq;mAZ()G>3+hGL>99wRJ;0H?s(A zS103}ct;{Ozn7m1%ei|wJS@s-z>HOO)tw(SqsXPOD`tKQqL`YW4%RK)fEFnQJhlQ2cm~yRa?#6MSC8Qc|8nt#awcT|* V&g$wFf)uLAr)i2UED&-QdzQG zQIwQ@LP=R7OpzAbTT{vnHScfk+vmQ^ALpFsdCqgb&-Z-K`JBT&igS^dRhI>UK=N*` z7;j+o0-Av|80fLz2TFlKjOFd(2x=NVxC}TFVGdXa5U8aX~tgK)rrZ7`eBOt?wmB?Td z5{wuuogV-g3X8;~#jt773@s5LffyafMnmG_Xk-+XKt&SCWTX+1fFK*e;bejlfsB9~ zkr4>81rly)N`(_4KgzRd)c@3Huztt@2m%w$z~IIvBFZ8g^)r)30a%Di+QM!An*X1e z4NOGjzlhoXI|A$#u>Mv?B-7vOQW$`w96&q6q6=~$kR;O$Gd70J8 z2=0t;NG2(~NjPVvD2_Rsre3|fM$0+Ri!0$^CZVJF#X%da?3Ei^a-kxeIxW>|xYbi@ zDBJH;WS^CfJ@diT$vo&*9BDo>^x6DsCOvp|vS+H5;(B>WxNwQZVEuD^JUMj}Zx|>@ zb1up{>Uf|k_fjoJ2BYV$^^=#l7gs6Ey~rM4C2_?AeuaGfWSVq^fn6-D#wrxwE zQ#9@SGPfUW-i*E0);l^gVw}3&W`GsHY)qd(=|%u3w{Z>IU(Nt7>Pcy_4($Q z_N!B7+#FW!+6iNM?FwGqjqO6`=cp7=Ltt_LiYm9VCilTL&Wa!*qvo2A#P6poukxSF zXR4Wyb-9k+Dy^fbbL&S1pWSY=6Ybu<5V8e+8B%Ey)xG&)-rOhLF@jn9XJ+N7#;v3j zolv~9`)(=On(Yyy`fp4$@iXMz+**WK)mu7W+B=8TyPvg zJdHKI@@`p95K-<6^ZW0e;>*A_c{f(awbx2^Qtxt4Bf8UXoBbDto7Z%NK*Mb;}p zL_*2}MX!HVQG70>_|bL~cwjI1YSbs@Ykvfuj~!JpEd_`Uf%bJsXfeNU6IT z*T3yC^uE%7!Je5?&}`JWw>ntV;h#QJD;MaPHr~iww2*4qS`a{0oR%NwXCR@!rU&ht z-V6P}L(D&wfJEwnZ@e9-TO#j&X?JYVk7r%mwL;)~?&4if9uq&7Qzd;UB!ci-39)x& z?rtcxZ`OJu+6-z~s{Gzg;q|>UG7m3LzDTX(um3EQ(P5*GrFWSHugXUhs2CiMHIo)Q z@XSAPjgJXQvf1w>9Tl%xQrEJ^{}p%g(thpk@#_Rxu1!4{dDA!|B6x?%9~!bd=%5Pa zQ8(pR?Ka!x+Wqu~s&ouquIqXKBdDs~@70c~MJbrWXS|$mY!?g;)~Pgk^hIzNG|SFc z-B8OOj%wpbX1-4eG=b|IVjB?~ZElZFdrTOd3Mh$vSVrhx9sjfL4o`?UvDKp+;$NQo z#AIPSTY9-wbvGI^(2b6nYO_q8{UmvepJbI0_NnlJ=cG^Y>e9^ubt`s99?nJcYa%r} zVl^eBN+9BmU&H z{nwUwyy`8Rb4qMJ8Mw{^R?fF4ZJp*n7DJ6 zekbX!x~jhQ%)R^RT~{AXcP>$G3o!1uAQS6Ly>Ybf-}d}U|Tr`_f1_@24wEMHg8cLnB+>$dT0 z(b6lLhrM-;Lyr?z3+trCCeL3~Ip2QV3>p_E*0p9k{mxFIXrDpmPtR+2 zke0axss>78Qw^?5-<@Rc20T4|__z+?OPi(IC|7^Jo~P2Rs994HB+OGt^9j>9C_gPT z{1`#XemT@Arf`0EW`q$HpBQcQRM+%FyL|y-=S{D$q$RbH(4i^`30!P+2VSr$B|XJr eX!`#@Zr%5D0#G{fye%owx98@B!!$V_KlLx!Oj;WN diff --git a/repo/js/SwitchAccountMultipleMode/README.md b/repo/js/SwitchAccountMultipleMode/README.md index 9f3e77556..8096d3b62 100644 --- a/repo/js/SwitchAccountMultipleMode/README.md +++ b/repo/js/SwitchAccountMultipleMode/README.md @@ -21,7 +21,9 @@ ## 3.国际服 -目前已实现国际服账号+密码登陆,并能切换服务器(亚、欧、美、台港澳),部分未尽问题尚未处理,大家多测测,更诚邀大佬们来改良 +目前已实现国际服账号+密码和下拉列表登陆,并能切换服务器(亚、欧、美、台港澳),部分未尽问题尚未处理,大家多测测,更诚邀大佬们来改良 + +国际服有个坑点是账号密码登完后,是否保存记录的弹框是在退出的时候才弹的,本脚本没做处理,用下拉列表的望周知 ## 4.账号+密码 diff --git a/repo/js/SwitchAccountMultipleMode/main.js b/repo/js/SwitchAccountMultipleMode/main.js index 4e7e72e44..c13a6f7a2 100644 --- a/repo/js/SwitchAccountMultipleMode/main.js +++ b/repo/js/SwitchAccountMultipleMode/main.js @@ -152,8 +152,8 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { // Library functions var u = {}; // utilities 工具函数集合 - u.logi = function (message, args) { log.info("[切换账号]" + message, args) }; - u.logw = function (message, args) { log.warn("[切换账号]" + message, args) }; + u.logi = function (message, args) { log.info("[下拉列表切换账号]" + message, args) }; + u.logw = function (message, args) { log.warn("[下拉列表切换账号]" + message, args) }; u.loadTemplate = function (filePath, x /* 0 if omit */, y /* 0 if omit */, w /* maxWidth if omit */, h /* maxHeight if omit */) { return RecognitionObject.TemplateMatch(file.ReadImageMatSync(filePath), x, y, w, h); }; @@ -179,21 +179,32 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { }; u.matchUserRelaxed = function (text, username) { if (typeof text !== "string" || typeof username !== "string") return false; - // Check the head - for (let i = 0; i < text.length; i++) { - const a = text[i]; - const b = username[i]; - if (a === '*') break; // Stop checking when a '*' is found in text. - if (a !== b) return false; + + // 1. 规范化:将连续的 '*' 替换为单个 '*' + const pattern = text.replace(/\*+/g, '*'); + const target = username.replace(/\*+/g, '*'); + + // 2. 如果规范化后没有通配符,则进行全等匹配 + if (!pattern.includes('*')) { + return pattern === target; } - // Check the tail - for (let i = 0; i < text.length; i++) { - const a = text[text.length - 1 - i]; - const b = username[username.length - 1 - i]; - if (a === '*') break; // Stop checking when a '*' is found in text. - if (a !== b) return false; - } - return true; + + // 3. 获取前缀和后缀 + // split('*') 会将 "abc*def" 拆分为 ["abc", "def"] + // 如果是 "*abc",拆分为 ["", "abc"];如果是 "abc*",拆分为 ["abc", ""] + const parts = pattern.split('*'); + const prefix = parts[0]; + const suffix = parts[parts.length - 1]; + + // 4. 匹配逻辑: + // - 目标字符串必须以 prefix 开头 + // - 目标字符串必须以 suffix 结尾 + // - 目标字符串的长度必须足以容纳 prefix 和 suffix (避免重叠部分的逻辑错误) + return ( + target.startsWith(prefix) && + target.endsWith(suffix) && + target.length >= (prefix.length + suffix.length) + ); }; u.waitAndFindImage = async function (asset, internal = 500, timeout = 60000) { const start = Date.now(); @@ -370,7 +381,7 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { btnLogout.DrawSelf("LogoutBtn"); btnLogout.Click(); - const assetQuitTextButton = u.loadTemplate("Assets/RecognitionObject/quit.png", 680, 380, 1220, 700); + const assetQuitTextButton = u.loadTemplate("Assets/RecognitionObject/out_account.png", 680, 380, 1220, 700); let btnQuit = await u.waitAndFindImage(assetQuitTextButton, 200); // u.logi("识别到退出按钮,点击"); // btnQuit.DrawSelf("QuitBtn"); @@ -399,8 +410,9 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { { const start = Date.now(); let lastLog = start; - while (selectedUser == null) { - await sleep(200); + // 超时8秒直接跳出让下一步报错 + while (selectedUser == null && Date.now() - start <= 8000) { + await sleep(500); let captureRegion = captureGameRegion(); let resList = captureRegion.findMulti(RecognitionObject.ocr(680, 540, 540, 500)); @@ -411,16 +423,9 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { if (user) { selectedUser = res; break; - } - } - - if (Date.now() - lastLog >= 10000) { - let elapsed = ((Date.now() - start) / 1000).toFixed(1); - u.logw("等待匹配图像已持续 {0} 秒,仍在尝试寻找账号文本:{1}", elapsed, targetUser); - lastLog = Date.now(); - for (let i = 0; i < resList.count; i++) { - let res = resList[i]; - u.logw("账户文本:{0}", res.text); + } else { + u.logw("当前匹配文本:{0}", res.text); + lastLog = Date.now(); } } } @@ -467,8 +472,10 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { await BilibiliKeyboardMouseMode(); } else if (settings.Modes == "B服切换另一个账号匹配+键鼠") { await BilibiliMatchAndKeyboardMouseMode(); - } else if (settings.Modes == "国际服账号+密码+OCR") { + } else if (settings.Modes == "国际服+账号+密码+OCR") { await GlobalOcrMode(); + } else if (settings.Modes == "国际服+下拉列表") { + await GlobalDropDownMode(); } else { log.info("尖尖哇嘎乃") } @@ -490,6 +497,60 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { } + // 国际服下拉列表模式 + async function GlobalDropDownMode() { + const script_mode = "国际服下拉列表模式"; + const page = new BvPage(); + setGameMetrics(1920, 1080, 1); + const isInGame = await waitAndDetermineCurrentView(); + if (isInGame) { + await stateReturnToGenshinGate(); + await sleep(1000); + } + await page.WaitForOcrMatch("开始游戏"); + await matchImgAndClick(login_out_account, "登录页的右下角退出按钮"); + await page.WaitForOcrMatch("切换账号"); + await matchImgAndClick(confirm_switch_account, "确认切换账号"); + await sleep(500); + await stateChangeUser(); + await sleep(500); + // 换服务器操作 + if (settings.Servers) { + await page.WaitForOcrMatch("开始游戏"); + log.info("正在更换服务器") + await matchImgAndClick(switch_server, "更换服务器"); + let serversMatched = true; + if (settings.Servers == "Asia") { + await matchImgAndClick(asia_server, "亚服"); + } else if (settings.Servers == "Europe") { + await matchImgAndClick(europe_server, "欧服"); + } else if (settings.Servers == "America") { + await matchImgAndClick(america_server, "美服"); + } else if (settings.Servers == "TW,HK,MO") { + await matchImgAndClick(twhkmo_server, "台港澳"); + } else { + log.info("尖尖哇嘎乃") + serversMatched = false; + } + if (serversMatched) { + await matchImgAndClick(confirm_button, "确认换服"); + } + } + await keyPress("VK_ESCAPE"); + await page.WaitForOcrMatch("开始游戏"); + await click(960, 640); + await page.Wait(5000); + log.info('等待提瓦特大门加载'); + await page.WaitForOcrMatch("点击进入"); + await click(960, 640); + // 可能登录账号的时候出现月卡提醒,则先点击一次月卡。 + await genshin.blessingOfTheWelkinMoon(); + await page.Wait(1000); + await genshin.returnMainUi(); + // 如果配置了通知 + notification.send("账号【" + settings.username + "】切换成功"); + } + // 纯键鼠模式 对应:账号+密码+键鼠(根据分辨率确定鼠标位置) async function KeyboardMouseMode() { setGameMetrics(1920, 1080, 2.0); @@ -871,6 +932,7 @@ async function recognizeTextAndClick(targetText, ocrRegion, timeout = 8000) { await click(960, 640); // 可能登录账号的时候出现月卡提醒,则先点击一次月卡。 await genshin.blessingOfTheWelkinMoon(); + await page.Wait(1000); await genshin.returnMainUi(); // 如果配置了通知 notification.send("账号【" + settings.username + "】切换成功"); diff --git a/repo/js/SwitchAccountMultipleMode/manifest.json b/repo/js/SwitchAccountMultipleMode/manifest.json index 52f510934..b2fa7353c 100644 --- a/repo/js/SwitchAccountMultipleMode/manifest.json +++ b/repo/js/SwitchAccountMultipleMode/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 1, "name": "切换账号多模式", - "version": "1.8", + "version": "1.9", "bgi_version": "0.57.0", "description": "多种模式的切换账号,有下拉列表、填写账号密码OCR操作或键鼠操作,目前支持B服/国际服但不完整\n免责申明:所有的账号密码均保存在本地,请使用者妥善保管账号密码,请勿外泄账号密码。若因使用此脚本导致的账号泄露、封禁问题与脚本作者无关。", "tags": [ diff --git a/repo/js/SwitchAccountMultipleMode/settings.json b/repo/js/SwitchAccountMultipleMode/settings.json index 9b3b51592..939557509 100644 --- a/repo/js/SwitchAccountMultipleMode/settings.json +++ b/repo/js/SwitchAccountMultipleMode/settings.json @@ -7,16 +7,17 @@ "下拉列表", "账号+密码+键鼠", "账号+密码+OCR", + "国际服+下拉列表", + "国际服+账号+密码+OCR", "B服切换另一个账号纯键鼠", - "B服切换另一个账号匹配+键鼠", - "国际服账号+密码+OCR" + "B服切换另一个账号匹配+键鼠" ], "default": "下拉列表" }, { "name": "username", "type": "input-text", - "label": "账号(只保存在本地,请妥善保管)\n下拉列表手机号格式:114******98\n下拉列表邮箱格式:11****1@919.com\n账号密码模式请输入完整的手机号或邮箱" + "label": "账号(只保存在本地,请妥善保管)\n下拉列表手机号格式:114******98\n下拉列表邮箱格式:11****1@919.com\n下拉列表第三方登陆格式:apple\n账号+密码模式请输入完整的手机号或邮箱" }, { "name": "password",