diff --git a/background.js b/background.js index d8b0884..25c8b29 100644 --- a/background.js +++ b/background.js @@ -251,7 +251,7 @@ const STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS = 8; const OAUTH_FLOW_TIMEOUT_MS = 5 * 60 * 1000; const SUB2API_STEP1_RESPONSE_TIMEOUT_MS = 90000; const SUB2API_STEP9_RESPONSE_TIMEOUT_MS = 120000; -const DEFAULT_SUB2API_URL = 'https://sub2api.hisence.fun/admin/accounts'; +const DEFAULT_SUB2API_URL = ''; const DEFAULT_CODEX2API_URL = 'http://localhost:8080/admin/accounts'; const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top'; const DEFAULT_SUB2API_GROUP_NAME = 'codex'; @@ -2364,15 +2364,23 @@ function normalizePersistentSettingValue(key, value) { ); case 'gopayHelperApiUrl': { - const legacyGpcHelperApiUrl = 'https://gpc.leftcode.xyz'; const defaultGpcHelperApiUrl = PERSISTED_SETTING_DEFAULTS.gopayHelperApiUrl || (typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined' ? DEFAULT_GPC_HELPER_API_URL : 'https://gpc.qlhazycoder.top'); const normalizedGpcHelperApiUrl = self.GoPayUtils?.normalizeGpcHelperBaseUrl ? self.GoPayUtils.normalizeGpcHelperBaseUrl(value || defaultGpcHelperApiUrl) : String(value || defaultGpcHelperApiUrl).trim().replace(/\/+$/g, ''); - return normalizedGpcHelperApiUrl === legacyGpcHelperApiUrl - ? defaultGpcHelperApiUrl - : normalizedGpcHelperApiUrl; + if (!self.GoPayUtils?.normalizeGpcHelperBaseUrl) { + try { + const parsed = new URL(normalizedGpcHelperApiUrl); + const hostname = parsed.hostname.toLowerCase(); + if (hostname !== 'gpc.qlhazycoder.top' && hostname !== 'localhost' && hostname !== '127.0.0.1') { + return defaultGpcHelperApiUrl; + } + } catch { + return defaultGpcHelperApiUrl; + } + } + return normalizedGpcHelperApiUrl; } case 'gopayHelperApiKey': case 'gopayHelperCardKey': @@ -7102,6 +7110,7 @@ function normalizeSub2ApiUrl(rawUrl) { return navigationUtils.normalizeSub2ApiUrl(rawUrl); } const input = (rawUrl || '').trim() || DEFAULT_SUB2API_URL; + if (!input) return ''; const withProtocol = /^https?:\/\//i.test(input) ? input : `https://${input}`; const parsed = new URL(withProtocol); if (!parsed.pathname || parsed.pathname === '/') { diff --git a/background/mail-2925-session.js b/background/mail-2925-session.js index e80dc4c..e0e79cd 100644 --- a/background/mail-2925-session.js +++ b/background/mail-2925-session.js @@ -37,12 +37,10 @@ const MAIL2925_COOKIE_DOMAINS = [ '2925.com', 'www.2925.com', - 'mail2.xiyouji.com', ]; const MAIL2925_COOKIE_ORIGINS = [ 'https://2925.com', 'https://www.2925.com', - 'https://mail2.xiyouji.com', ]; const MAIL2925_LIMIT_ERROR_PREFIX = 'MAIL2925_LIMIT_REACHED::'; const MAIL2925_THREAD_TERMINATED_ERROR_PREFIX = 'MAIL2925_THREAD_TERMINATED::'; diff --git a/background/navigation-utils.js b/background/navigation-utils.js index f1ceef3..9077502 100644 --- a/background/navigation-utils.js +++ b/background/navigation-utils.js @@ -19,6 +19,7 @@ function normalizeSub2ApiUrl(rawUrl) { const input = (rawUrl || '').trim() || DEFAULT_SUB2API_URL; + if (!input) return ''; const withProtocol = /^https?:\/\//i.test(input) ? input : `https://${input}`; const parsed = new URL(withProtocol); if (!parsed.pathname || parsed.pathname === '/') { diff --git a/background/panel-bridge.js b/background/panel-bridge.js index a4dff4c..8e204b5 100644 --- a/background/panel-bridge.js +++ b/background/panel-bridge.js @@ -251,6 +251,9 @@ const sub2apiUrl = normalizeSub2ApiUrl(state.sub2apiUrl); const groupName = (state.sub2apiGroupName || DEFAULT_SUB2API_GROUP_NAME).trim() || DEFAULT_SUB2API_GROUP_NAME; + if (!sub2apiUrl) { + throw new Error('SUB2API URL is not configured. Please fill it in the side panel first.'); + } if (!state.sub2apiEmail) { throw new Error('尚未配置 SUB2API 登录邮箱,请先在侧边栏填写。'); } diff --git a/background/steps/oauth-login.js b/background/steps/oauth-login.js index 07eb041..7c8e0bd 100644 --- a/background/steps/oauth-login.js +++ b/background/steps/oauth-login.js @@ -40,29 +40,64 @@ return /缺少|未配置|请输入|无效|错误|失败|401|认证失败|未授权|unauthorized|invalid/i.test(message); } + function normalizeStep7IdentifierType(value = '') { + const normalized = String(value || '').trim().toLowerCase(); + return normalized === 'phone' || normalized === 'email' ? normalized : ''; + } + + function normalizeStep7SignupMethod(value = '') { + return String(value || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email'; + } + + function canUseConfiguredPhoneSignup(state = {}) { + return normalizeStep7SignupMethod(state?.signupMethod) === 'phone' + && Boolean(state?.phoneVerificationEnabled) + && !Boolean(state?.plusModeEnabled) + && !Boolean(state?.contributionMode); + } + + function resolveStep7LoginIdentifierType(state = {}, fallbackType = '') { + const explicitIdentifierType = normalizeStep7IdentifierType(state?.accountIdentifierType); + if (explicitIdentifierType) { + return explicitIdentifierType; + } + + const frozenSignupMethod = normalizeStep7IdentifierType(state?.resolvedSignupMethod); + if (frozenSignupMethod) { + return frozenSignupMethod; + } + + if (canUseConfiguredPhoneSignup(state)) { + return 'phone'; + } + + return normalizeStep7IdentifierType(fallbackType) || 'email'; + } + async function executeStep7(state) { const visibleStep = Math.floor(Number(state?.visibleStep) || 0); const completionStep = visibleStep > 0 ? visibleStep : 7; - const resolvedIdentifierType = String( - state?.accountIdentifierType - || (state?.signupPhoneNumber ? 'phone' : '') - || '' - ).trim().toLowerCase() === 'phone' - ? 'phone' - : 'email'; - const phoneNumber = String( - state?.signupPhoneNumber - || (resolvedIdentifierType === 'phone' ? state?.accountIdentifier : '') - || state?.signupPhoneCompletedActivation?.phoneNumber - || state?.signupPhoneActivation?.phoneNumber - || '' - ).trim(); - const email = String( - state?.email - || (resolvedIdentifierType === 'email' ? state?.accountIdentifier : '') - || '' - ).trim(); - if (!email && !phoneNumber) { + const resolvedIdentifierType = resolveStep7LoginIdentifierType(state); + const phoneNumber = resolvedIdentifierType === 'phone' + ? String( + state?.signupPhoneNumber + || (normalizeStep7IdentifierType(state?.accountIdentifierType) === 'phone' ? state?.accountIdentifier : '') + || state?.signupPhoneCompletedActivation?.phoneNumber + || state?.signupPhoneActivation?.phoneNumber + || '' + ).trim() + : ''; + const email = resolvedIdentifierType === 'email' + ? String( + state?.email + || (normalizeStep7IdentifierType(state?.accountIdentifierType) === 'email' ? state?.accountIdentifier : '') + || '' + ).trim() + : ''; + if ( + (resolvedIdentifierType === 'phone' && !phoneNumber) + || (resolvedIdentifierType !== 'phone' && !email) + ) { throw new Error('缺少登录账号:请先完成步骤 2,或在侧栏“注册邮箱/注册手机号”中手动填写账号后再执行当前步骤。'); } @@ -75,25 +110,23 @@ try { const currentState = attempt === 1 ? state : await getState(); const password = currentState.password || currentState.customPassword || ''; - const currentIdentifierType = String( - currentState?.accountIdentifierType - || (currentState?.signupPhoneNumber ? 'phone' : '') - || resolvedIdentifierType - ).trim().toLowerCase() === 'phone' - ? 'phone' - : 'email'; - const currentPhoneNumber = String( - currentState?.signupPhoneNumber - || (currentIdentifierType === 'phone' ? currentState?.accountIdentifier : '') - || currentState?.signupPhoneCompletedActivation?.phoneNumber - || currentState?.signupPhoneActivation?.phoneNumber - || phoneNumber - ).trim(); - const currentEmail = String( - currentState?.email - || (currentIdentifierType === 'email' ? currentState?.accountIdentifier : '') - || email - ).trim(); + const currentIdentifierType = resolveStep7LoginIdentifierType(currentState, resolvedIdentifierType); + const currentPhoneNumber = currentIdentifierType === 'phone' + ? String( + currentState?.signupPhoneNumber + || (normalizeStep7IdentifierType(currentState?.accountIdentifierType) === 'phone' ? currentState?.accountIdentifier : '') + || currentState?.signupPhoneCompletedActivation?.phoneNumber + || currentState?.signupPhoneActivation?.phoneNumber + || phoneNumber + ).trim() + : ''; + const currentEmail = currentIdentifierType === 'email' + ? String( + currentState?.email + || (normalizeStep7IdentifierType(currentState?.accountIdentifierType) === 'email' ? currentState?.accountIdentifier : '') + || email + ).trim() + : ''; const accountIdentifier = currentIdentifierType === 'phone' ? currentPhoneNumber : currentEmail; diff --git a/background/steps/platform-verify.js b/background/steps/platform-verify.js index e9fb06c..2d39999 100644 --- a/background/steps/platform-verify.js +++ b/background/steps/platform-verify.js @@ -343,6 +343,9 @@ } const sub2apiUrl = normalizeSub2ApiUrl(state.sub2apiUrl); + if (!sub2apiUrl) { + throw new Error('SUB2API URL is not configured. Please fill it in the side panel first.'); + } const injectFiles = ['content/utils.js', 'content/sub2api-panel.js']; await addStepLog(visibleStep, '正在打开 SUB2API 后台...'); diff --git a/content/signup-page.js b/content/signup-page.js index 797236a..d78aea6 100644 --- a/content/signup-page.js +++ b/content/signup-page.js @@ -3484,6 +3484,10 @@ function summarizePhoneInputCandidate(element, options = {}) { const normalizedName = summary.name.toLowerCase(); const normalizedId = summary.id.toLowerCase(); const combinedText = `${normalizedName} ${normalizedId} ${summary.placeholder} ${summary.ariaLabel}`; + if (isLoginEmailLikeInput(element)) { + summary.skipReason = 'email_like'; + return summary; + } if ( summary.type === 'tel' || summary.autocomplete === 'tel' @@ -3517,14 +3521,50 @@ function findUsablePhoneInput(selector, options = {}) { .find((element) => isUsablePhoneInputElement(element, options)) || null; } +function getLoginInputAttributeText(input) { + return { + type: String(input?.getAttribute?.('type') || input?.type || '').trim().toLowerCase(), + autocomplete: String(input?.getAttribute?.('autocomplete') || '').trim().toLowerCase(), + name: String(input?.getAttribute?.('name') || input?.name || '').trim(), + id: String(input?.getAttribute?.('id') || input?.id || '').trim(), + placeholder: String(input?.getAttribute?.('placeholder') || '').trim(), + ariaLabel: String(input?.getAttribute?.('aria-label') || '').trim(), + }; +} + +function isLoginEmailLikeInput(input) { + const summary = getLoginInputAttributeText(input); + const nameId = `${summary.name} ${summary.id}`; + const labelText = `${summary.placeholder} ${summary.ariaLabel}`; + return summary.type === 'email' + || summary.autocomplete === 'email' + || /email/i.test(nameId) + || /email|电子邮件|邮箱/i.test(labelText); +} + function getLoginEmailInput() { - const input = document.querySelector( - 'input[type="email"], input[name="email"], input[name="username"], input[id*="email"], input[placeholder*="email" i], input[placeholder*="Email"]' - ); - if (isLoginPhoneUsernameKind() || isLoginPhoneEntryPageText()) { + const input = Array.from(document.querySelectorAll([ + 'input[type="email"]', + 'input[autocomplete="email"]', + 'input[name="email"]', + 'input[name="username"]', + 'input[autocomplete="username"]', + 'input[id*="email" i]', + 'input[placeholder*="email" i]', + 'input[placeholder*="Email"]', + 'input[placeholder*="电子邮件"]', + 'input[placeholder*="邮箱"]', + 'input[aria-label*="email" i]', + 'input[aria-label*="电子邮件"]', + 'input[aria-label*="邮箱"]', + ].join(', '))).find((candidate) => isVisibleElement(candidate)) || null; + if (!input) { return null; } - return input && isVisibleElement(input) ? input : null; + if ((isLoginPhoneUsernameKind() || isLoginPhoneEntryPageText()) && !isLoginEmailLikeInput(input)) { + return null; + } + return input; } function getLoginPhoneInput() { @@ -5162,9 +5202,9 @@ async function step6OpenLoginEntry(payload, snapshot) { const visibleStep = Math.floor(Number(payload?.visibleStep) || 0) || 7; const currentSnapshot = normalizeStep6Snapshot(snapshot || inspectLoginAuthState()); const preferPhoneLogin = String(payload?.loginIdentifierType || '').trim() === 'phone' || (!payload?.email && payload?.phoneNumber); - const trigger = preferPhoneLogin - ? (currentSnapshot.phoneEntryTrigger || findLoginPhoneEntryTrigger()) - : (currentSnapshot.loginEntryTrigger || findLoginEntryTrigger()); + const genericEntryTrigger = currentSnapshot.loginEntryTrigger || findLoginEntryTrigger(); + const phoneEntryTrigger = currentSnapshot.phoneEntryTrigger || findLoginPhoneEntryTrigger(); + const trigger = genericEntryTrigger || (preferPhoneLogin ? phoneEntryTrigger : null); if (!trigger || !isActionEnabled(trigger)) { return createStep6RecoverableResult('missing_login_entry_trigger', currentSnapshot, { message: preferPhoneLogin diff --git a/gopay-utils.js b/gopay-utils.js index 0fe3e9b..b2c2eee 100644 --- a/gopay-utils.js +++ b/gopay-utils.js @@ -5,7 +5,7 @@ const PLUS_PAYMENT_METHOD_GOPAY = 'gopay'; const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper'; const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top'; - const LEGACY_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz'; + const ALLOWED_GPC_HELPER_REMOTE_HOST = 'gpc.qlhazycoder.top'; function normalizePlusPaymentMethod(value = '') { const normalized = String(value || '').trim().toLowerCase(); @@ -67,10 +67,17 @@ normalized = normalized.replace(/\/api\/gp\/balance(?:\?.*)?$/i, ''); normalized = normalized.replace(/\/api\/card\/balance(?:\?.*)?$/i, ''); normalized = normalized.replace(/\/api\/card\/redeem-api-key(?:\?.*)?$/i, ''); - if (normalized === LEGACY_GPC_HELPER_API_URL) { + + try { + const parsed = new URL(normalized); + const hostname = parsed.hostname.toLowerCase(); + if (hostname === ALLOWED_GPC_HELPER_REMOTE_HOST || hostname === 'localhost' || hostname === '127.0.0.1') { + return normalized || DEFAULT_GPC_HELPER_API_URL; + } + return DEFAULT_GPC_HELPER_API_URL; + } catch { return DEFAULT_GPC_HELPER_API_URL; } - return normalized || DEFAULT_GPC_HELPER_API_URL; } function buildGpcHelperApiUrl(apiUrl = '', path = '') { diff --git a/hotmail-utils.js b/hotmail-utils.js index 14103c6..3ff4778 100644 --- a/hotmail-utils.js +++ b/hotmail-utils.js @@ -6,7 +6,6 @@ root.HotmailUtils = factory(); })(typeof self !== 'undefined' ? self : globalThis, function createHotmailUtils() { - const HOTMAIL_MAIL_API_URL = 'https://apple.882263.xyz/api/mail-new'; const HOTMAIL_SERVICE_MODE_REMOTE = 'remote'; const HOTMAIL_SERVICE_MODE_LOCAL = 'local'; @@ -287,8 +286,11 @@ return list.map((message) => normalizeHotmailMailApiMessage(message)); } - function buildHotmailMailApiLatestUrl(options) { - const apiUrl = String(options?.apiUrl || '').trim() || HOTMAIL_MAIL_API_URL; + function buildHotmailMailApiLatestUrl(options = {}) { + const apiUrl = String(options?.apiUrl || '').trim(); + if (!apiUrl) { + throw new Error('Hotmail mail API URL is required.'); + } const url = new URL(apiUrl); url.searchParams.set('refresh_token', String(options?.refreshToken || '')); url.searchParams.set('client_id', String(options?.clientId || '')); diff --git a/manifest.json b/manifest.json index 0532b5f..33d7996 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 3, "name": "codex-oauth-automation-extension", - "version": "7.0", - "version_name": "Ultra7.0", + "version": "7.3", + "version_name": "Ultra7.3", "description": "用于自动执行多步骤 OAuth 注册流程", "permissions": [ "sidePanel", diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index ac485c2..4a4e5bc 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -179,7 +179,7 @@