feat: add hotmail mail api account pool flow
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
(function activationUtilsModule(root, factory) {
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = factory();
|
||||
return;
|
||||
}
|
||||
|
||||
root.MultiPageActivationUtils = factory();
|
||||
})(typeof self !== 'undefined' ? self : globalThis, function createActivationUtils() {
|
||||
function normalizeTagName(tagName) {
|
||||
return String(tagName || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function normalizeType(type) {
|
||||
return String(type || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function normalizePathname(pathname) {
|
||||
return String(pathname || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function getActivationStrategy(target = {}) {
|
||||
const tagName = normalizeTagName(target.tagName);
|
||||
const type = normalizeType(target.type);
|
||||
const pathname = normalizePathname(target.pathname);
|
||||
const hasForm = Boolean(target.hasForm);
|
||||
const isEmailVerificationRoute = /\/email-verification(?:[/?#]|$)/i.test(pathname);
|
||||
const isSubmitButton = hasForm
|
||||
&& (
|
||||
(tagName === 'button' && (!type || type === 'submit'))
|
||||
|| (tagName === 'input' && type === 'submit')
|
||||
);
|
||||
|
||||
if (isSubmitButton && isEmailVerificationRoute) {
|
||||
return { method: 'requestSubmit' };
|
||||
}
|
||||
|
||||
return { method: 'click' };
|
||||
}
|
||||
|
||||
function isRecoverableStep9AuthFailure(statusText) {
|
||||
return /认证失败:\s*/i.test(String(statusText || '').trim());
|
||||
}
|
||||
|
||||
return {
|
||||
getActivationStrategy,
|
||||
isRecoverableStep9AuthFailure,
|
||||
};
|
||||
});
|
||||
@@ -215,9 +215,10 @@ async function prepareLoginCodeFlow(timeout = 15000) {
|
||||
loggedPasswordPage = false;
|
||||
log('步骤 7:检测到密码页,正在切换到一次性验证码登录...');
|
||||
await humanPause(350, 900);
|
||||
const verificationRequestedAt = Date.now();
|
||||
simulateClick(switchTrigger);
|
||||
await sleep(1200);
|
||||
continue;
|
||||
return { ready: true, mode: 'verification_switch', verificationRequestedAt };
|
||||
}
|
||||
|
||||
if (passwordInput && !loggedPasswordPage) {
|
||||
@@ -351,15 +352,16 @@ async function step3_fillEmailPassword(payload) {
|
||||
fillInput(passwordInput, payload.password);
|
||||
log('步骤 3:密码已填写');
|
||||
|
||||
// Report complete BEFORE submit, because submit causes page navigation
|
||||
// which kills the content script connection
|
||||
reportComplete(3, { email });
|
||||
|
||||
// Submit the form (page will navigate away after this)
|
||||
await sleep(500);
|
||||
const submitBtn = document.querySelector('button[type="submit"]')
|
||||
|| await waitForElementByText('button', /continue|sign\s*up|submit|注册|创建|create/i, 5000).catch(() => null);
|
||||
|
||||
// Report complete BEFORE submit, because submit causes page navigation
|
||||
// which kills the content script connection
|
||||
const signupVerificationRequestedAt = submitBtn ? Date.now() : null;
|
||||
reportComplete(3, { email, signupVerificationRequestedAt });
|
||||
|
||||
// Submit the form (page will navigate away after this)
|
||||
await sleep(500);
|
||||
if (submitBtn) {
|
||||
await humanPause(500, 1300);
|
||||
simulateClick(submitBtn);
|
||||
|
||||
+30
-3
@@ -1,5 +1,7 @@
|
||||
// content/utils.js — Shared utilities for all content scripts
|
||||
|
||||
const getActivationStrategy = self.MultiPageActivationUtils?.getActivationStrategy;
|
||||
|
||||
const SCRIPT_SOURCE = (() => {
|
||||
if (window.__MULTIPAGE_SOURCE) return window.__MULTIPAGE_SOURCE;
|
||||
const url = location.href;
|
||||
@@ -292,9 +294,34 @@ function reportError(step, errorMessage) {
|
||||
*/
|
||||
function simulateClick(el) {
|
||||
throwIfStopped();
|
||||
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
||||
console.log(LOG_PREFIX, `已点击: ${el.tagName} ${el.textContent?.slice(0, 30) || ''}`);
|
||||
log(`已点击 [${el.tagName}] "${el.textContent?.trim().slice(0, 30) || ''}"`);
|
||||
if (!el) {
|
||||
throw new Error('无法点击空元素。');
|
||||
}
|
||||
|
||||
const form = el.form || el.closest?.('form') || null;
|
||||
const strategy = typeof getActivationStrategy === 'function'
|
||||
? getActivationStrategy({
|
||||
tagName: el.tagName,
|
||||
type: el.getAttribute?.('type') || el.type || '',
|
||||
hasForm: Boolean(form),
|
||||
pathname: location.pathname || '',
|
||||
})
|
||||
: { method: 'click' };
|
||||
|
||||
let method = strategy.method || 'click';
|
||||
|
||||
if (method === 'requestSubmit' && form && typeof form.requestSubmit === 'function') {
|
||||
form.requestSubmit(el);
|
||||
} else if (typeof el.click === 'function') {
|
||||
method = 'click';
|
||||
el.click();
|
||||
} else {
|
||||
method = 'dispatchEvent';
|
||||
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
||||
}
|
||||
|
||||
console.log(LOG_PREFIX, `已点击(${method}): ${el.tagName} ${el.textContent?.slice(0, 30) || ''}`);
|
||||
log(`已点击(${method}) [${el.tagName}] "${el.textContent?.trim().slice(0, 30) || ''}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
console.log('[MultiPage:vps-panel] Content script loaded on', location.href);
|
||||
|
||||
const {
|
||||
isRecoverableStep9AuthFailure,
|
||||
} = self.MultiPageActivationUtils || {};
|
||||
|
||||
// Listen for commands from Background
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === 'EXECUTE_STEP') {
|
||||
@@ -111,6 +115,12 @@ async function waitForExactSuccessBadge(timeout = 30000) {
|
||||
if (statusText === '认证成功!') {
|
||||
return statusText;
|
||||
}
|
||||
if (isOAuthCallbackTimeoutFailure(statusText)) {
|
||||
throw new Error(`STEP9_OAUTH_TIMEOUT::${statusText}`);
|
||||
}
|
||||
if (typeof isRecoverableStep9AuthFailure === 'function' && isRecoverableStep9AuthFailure(statusText)) {
|
||||
throw new Error(`STEP9_OAUTH_RETRY::${statusText}`);
|
||||
}
|
||||
await sleep(200);
|
||||
}
|
||||
|
||||
@@ -118,6 +128,9 @@ async function waitForExactSuccessBadge(timeout = 30000) {
|
||||
if (isOAuthCallbackTimeoutFailure(finalText)) {
|
||||
throw new Error(`STEP9_OAUTH_TIMEOUT::${finalText}`);
|
||||
}
|
||||
if (typeof isRecoverableStep9AuthFailure === 'function' && isRecoverableStep9AuthFailure(finalText)) {
|
||||
throw new Error(`STEP9_OAUTH_RETRY::${finalText}`);
|
||||
}
|
||||
throw new Error(finalText
|
||||
? `CPA 面板状态不是“认证成功!”,当前为“${finalText}”。`
|
||||
: 'CPA 面板长时间未出现“认证成功!”状态徽标。');
|
||||
|
||||
Reference in New Issue
Block a user