Auto 正在真正运行或重试时,步骤按钮和手动完成按钮不会放开。

Auto 处于暂停等待邮箱时,可以继续,也可以显式接管为手动。
Step 1 和 Step 9 不提供手动完成同步。
Step 3 会要求已知邮箱和密码;Step 8 会要求已经捕获 localhostUrl。
This commit is contained in:
QLHazyCoder
2026-04-08 10:52:36 +08:00
parent 099066987d
commit cdf46cde55
5 changed files with 746 additions and 113 deletions
+62
View File
@@ -11,6 +11,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|| message.type === 'STEP8_FIND_AND_CLICK'
|| message.type === 'PREPARE_LOGIN_CODE'
|| message.type === 'RESEND_VERIFICATION_CODE'
|| message.type === 'GET_MANUAL_COMPLETION_STATE'
) {
resetStopState();
handleCommand(message).then((result) => {
@@ -22,6 +23,11 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
return;
}
if (message.type === 'GET_MANUAL_COMPLETION_STATE') {
sendResponse({ error: err.message });
return;
}
if (message.type === 'STEP8_FIND_AND_CLICK') {
log(`步骤 8${err.message}`, 'error');
sendResponse({ error: err.message });
@@ -53,6 +59,8 @@ async function handleCommand(message) {
return await prepareLoginCodeFlow();
case 'RESEND_VERIFICATION_CODE':
return await resendVerificationCode(message.step);
case 'GET_MANUAL_COMPLETION_STATE':
return getManualCompletionState();
case 'STEP8_FIND_AND_CLICK':
return await step8_findAndClick();
}
@@ -145,6 +153,25 @@ function findOneTimeCodeLoginTrigger() {
return null;
}
function findRegisterEntryTrigger() {
const candidates = document.querySelectorAll(
'a, button, [role="button"], [role="link"], input[type="button"], input[type="submit"]'
);
for (const el of candidates) {
if (!isVisibleElement(el)) continue;
if (el.disabled || el.getAttribute('aria-disabled') === 'true') continue;
const text = getActionText(el);
const href = el.getAttribute('href') || '';
if ((text && /sign\s*up|register|create\s*account|注册/i.test(text)) || /signup|register/i.test(href)) {
return el;
}
}
return null;
}
function findResendVerificationCodeTrigger({ allowDisabled = false } = {}) {
const candidates = document.querySelectorAll(
'button, a, [role="button"], [role="link"], input[type="button"], input[type="submit"]'
@@ -394,6 +421,18 @@ function isStep5Ready() {
);
}
function isCredentialsPageReady() {
const emailInput = document.querySelector(
'input[type="email"], input[name="email"], input[name="username"], input[id*="email"], input[placeholder*="email" i]'
);
if (emailInput && isVisibleElement(emailInput)) {
return true;
}
const passwordInput = document.querySelector('input[type="password"]');
return Boolean(passwordInput && isVisibleElement(passwordInput));
}
function getPageTextSnapshot() {
return (document.body?.innerText || document.body?.textContent || '')
.replace(/\s+/g, ' ')
@@ -443,6 +482,29 @@ function isStep8Ready() {
return OAUTH_CONSENT_PAGE_PATTERN.test(getPageTextSnapshot());
}
function getManualCompletionState() {
if (isStep8Ready()) {
return { stage: 'consent', summary: '已进入 OAuth 授权同意页', url: location.href };
}
if (isAddPhonePageReady()) {
return { stage: 'add_phone', summary: '已进入手机号页面', url: location.href };
}
if (isStep5Ready()) {
return { stage: 'profile', summary: '已进入姓名/生日页面', url: location.href };
}
if (isVerificationPageStillVisible()) {
return { stage: 'verification', summary: '仍停留在验证码页面', url: location.href };
}
if (isCredentialsPageReady()) {
return { stage: 'credentials', summary: '已进入邮箱或密码输入页面', url: location.href };
}
if (findRegisterEntryTrigger()) {
return { stage: 'register', summary: '仍停留在注册入口页面', url: location.href };
}
return { stage: 'unknown', summary: '无法识别当前认证页状态', url: location.href };
}
async function waitForVerificationSubmitOutcome(step, timeout) {
const resolvedTimeout = timeout ?? (step === 7 ? 30000 : 12000);
const start = Date.now();