|
|
@@ -1,9 +1,30 @@
|
|
|
// background.js — Service Worker: orchestration, state, tab management, message routing
|
|
|
|
|
|
-importScripts('data/names.js');
|
|
|
+importScripts('data/names.js', 'hotmail-utils.js', 'content/activation-utils.js');
|
|
|
+
|
|
|
+const {
|
|
|
+ buildHotmailMailApiLatestUrl,
|
|
|
+ extractVerificationCodeFromMessage,
|
|
|
+ filterHotmailAccountsByUsage,
|
|
|
+ getLatestHotmailMessage,
|
|
|
+ getHotmailMailApiRequestConfig,
|
|
|
+ getHotmailVerificationPollConfig,
|
|
|
+ getHotmailVerificationRequestTimestamp,
|
|
|
+ normalizeHotmailMailApiMessages,
|
|
|
+ pickHotmailAccountForRun,
|
|
|
+ pickVerificationMessage,
|
|
|
+ pickVerificationMessageWithFallback,
|
|
|
+ pickVerificationMessageWithTimeFallback,
|
|
|
+ shouldClearHotmailCurrentSelection,
|
|
|
+} = self.HotmailUtils;
|
|
|
+const {
|
|
|
+ isRecoverableStep9AuthFailure,
|
|
|
+} = self.MultiPageActivationUtils;
|
|
|
|
|
|
const LOG_PREFIX = '[MultiPage:bg]';
|
|
|
const DUCK_AUTOFILL_URL = 'https://duckduckgo.com/email/settings/autofill';
|
|
|
+const HOTMAIL_PROVIDER = 'hotmail-api';
|
|
|
+const HOTMAIL_MAILBOXES = ['INBOX', 'Junk'];
|
|
|
const STOP_ERROR_MESSAGE = '流程已被用户停止。';
|
|
|
const HUMAN_STEP_DELAY_MIN = 700;
|
|
|
const HUMAN_STEP_DELAY_MAX = 2200;
|
|
|
@@ -23,6 +44,7 @@ const PERSISTED_SETTING_DEFAULTS = {
|
|
|
mailProvider: '163',
|
|
|
inbucketHost: '',
|
|
|
inbucketMailbox: '',
|
|
|
+ hotmailAccounts: [],
|
|
|
};
|
|
|
|
|
|
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
|
|
@@ -38,10 +60,13 @@ const DEFAULT_STATE = {
|
|
|
password: null,
|
|
|
accounts: [], // { email, password, createdAt }
|
|
|
lastEmailTimestamp: null,
|
|
|
+ signupVerificationRequestedAt: null,
|
|
|
+ loginVerificationRequestedAt: null,
|
|
|
lastSignupCode: null,
|
|
|
lastLoginCode: null,
|
|
|
localhostUrl: null,
|
|
|
flowStartTime: null,
|
|
|
+ currentHotmailAccountId: null,
|
|
|
tabRegistry: {},
|
|
|
sourceLastUrls: {},
|
|
|
logs: [],
|
|
|
@@ -59,6 +84,7 @@ async function getPersistedSettings() {
|
|
|
...PERSISTED_SETTING_DEFAULTS,
|
|
|
...stored,
|
|
|
autoRunSkipFailures: Boolean(stored.autoRunSkipFailures ?? PERSISTED_SETTING_DEFAULTS.autoRunSkipFailures),
|
|
|
+ hotmailAccounts: normalizeHotmailAccounts(stored.hotmailAccounts),
|
|
|
};
|
|
|
}
|
|
|
|
|
|
@@ -92,9 +118,13 @@ async function setPersistentSettings(updates) {
|
|
|
const persistedUpdates = {};
|
|
|
for (const key of PERSISTED_SETTING_KEYS) {
|
|
|
if (updates[key] !== undefined) {
|
|
|
- persistedUpdates[key] = key === 'autoRunSkipFailures'
|
|
|
- ? Boolean(updates[key])
|
|
|
- : updates[key];
|
|
|
+ if (key === 'autoRunSkipFailures') {
|
|
|
+ persistedUpdates[key] = Boolean(updates[key]);
|
|
|
+ } else if (key === 'hotmailAccounts') {
|
|
|
+ persistedUpdates[key] = normalizeHotmailAccounts(updates[key]);
|
|
|
+ } else {
|
|
|
+ persistedUpdates[key] = updates[key];
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -174,6 +204,408 @@ function generatePassword() {
|
|
|
return pw.split('').sort(() => Math.random() - 0.5).join('');
|
|
|
}
|
|
|
|
|
|
+function normalizeHotmailAccount(account = {}) {
|
|
|
+ return {
|
|
|
+ id: String(account.id || crypto.randomUUID()),
|
|
|
+ email: String(account.email || '').trim(),
|
|
|
+ password: String(account.password || ''),
|
|
|
+ clientId: String(account.clientId || '').trim(),
|
|
|
+ accessToken: String(account.accessToken || ''),
|
|
|
+ refreshToken: String(account.refreshToken || ''),
|
|
|
+ expiresAt: Number.isFinite(Number(account.expiresAt)) ? Number(account.expiresAt) : 0,
|
|
|
+ status: String(account.status || (account.refreshToken ? 'authorized' : 'pending')),
|
|
|
+ enabled: account.enabled !== undefined ? Boolean(account.enabled) : true,
|
|
|
+ used: Boolean(account.used),
|
|
|
+ lastUsedAt: Number.isFinite(Number(account.lastUsedAt)) ? Number(account.lastUsedAt) : 0,
|
|
|
+ lastAuthAt: Number.isFinite(Number(account.lastAuthAt)) ? Number(account.lastAuthAt) : 0,
|
|
|
+ lastError: String(account.lastError || ''),
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function normalizeHotmailAccounts(accounts) {
|
|
|
+ if (!Array.isArray(accounts)) return [];
|
|
|
+
|
|
|
+ const deduped = new Map();
|
|
|
+ for (const account of accounts) {
|
|
|
+ const normalized = normalizeHotmailAccount(account);
|
|
|
+ if (!normalized.email && !normalized.id) continue;
|
|
|
+ deduped.set(normalized.id, normalized);
|
|
|
+ }
|
|
|
+ return [...deduped.values()];
|
|
|
+}
|
|
|
+
|
|
|
+function findHotmailAccount(accounts, accountId) {
|
|
|
+ return normalizeHotmailAccounts(accounts).find((account) => account.id === accountId) || null;
|
|
|
+}
|
|
|
+
|
|
|
+function isHotmailProvider(stateOrProvider) {
|
|
|
+ const provider = typeof stateOrProvider === 'string'
|
|
|
+ ? stateOrProvider
|
|
|
+ : stateOrProvider?.mailProvider;
|
|
|
+ return provider === HOTMAIL_PROVIDER;
|
|
|
+}
|
|
|
+
|
|
|
+async function syncHotmailAccounts(accounts) {
|
|
|
+ const normalized = normalizeHotmailAccounts(accounts);
|
|
|
+ await setPersistentSettings({ hotmailAccounts: normalized });
|
|
|
+ await setState({ hotmailAccounts: normalized });
|
|
|
+ broadcastDataUpdate({ hotmailAccounts: normalized });
|
|
|
+ return normalized;
|
|
|
+}
|
|
|
+
|
|
|
+async function upsertHotmailAccount(input) {
|
|
|
+ const state = await getState();
|
|
|
+ const accounts = normalizeHotmailAccounts(state.hotmailAccounts);
|
|
|
+ const normalizedEmail = String(input?.email || '').trim().toLowerCase();
|
|
|
+ const existing = input?.id
|
|
|
+ ? findHotmailAccount(accounts, input.id)
|
|
|
+ : accounts.find((account) => account.email.toLowerCase() === normalizedEmail) || null;
|
|
|
+ const normalized = normalizeHotmailAccount({
|
|
|
+ ...(existing || {}),
|
|
|
+ ...input,
|
|
|
+ id: input?.id || existing?.id || crypto.randomUUID(),
|
|
|
+ });
|
|
|
+
|
|
|
+ const nextAccounts = existing
|
|
|
+ ? accounts.map((account) => (account.id === normalized.id ? normalized : account))
|
|
|
+ : [...accounts, normalized];
|
|
|
+
|
|
|
+ await syncHotmailAccounts(nextAccounts);
|
|
|
+ return normalized;
|
|
|
+}
|
|
|
+
|
|
|
+async function deleteHotmailAccount(accountId) {
|
|
|
+ const state = await getState();
|
|
|
+ const accounts = normalizeHotmailAccounts(state.hotmailAccounts);
|
|
|
+ const nextAccounts = accounts.filter((account) => account.id !== accountId);
|
|
|
+ await syncHotmailAccounts(nextAccounts);
|
|
|
+
|
|
|
+ if (state.currentHotmailAccountId === accountId) {
|
|
|
+ await setState({ currentHotmailAccountId: null });
|
|
|
+ if (isHotmailProvider(state)) {
|
|
|
+ await setEmailState(null);
|
|
|
+ }
|
|
|
+ broadcastDataUpdate({ currentHotmailAccountId: null });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+async function deleteHotmailAccounts(mode = 'all') {
|
|
|
+ const state = await getState();
|
|
|
+ const accounts = normalizeHotmailAccounts(state.hotmailAccounts);
|
|
|
+ const targets = filterHotmailAccountsByUsage(accounts, mode);
|
|
|
+ const targetIds = new Set(targets.map((account) => account.id));
|
|
|
+ const nextAccounts = mode === 'used'
|
|
|
+ ? accounts.filter((account) => !targetIds.has(account.id))
|
|
|
+ : [];
|
|
|
+
|
|
|
+ await syncHotmailAccounts(nextAccounts);
|
|
|
+
|
|
|
+ if (state.currentHotmailAccountId && targetIds.has(state.currentHotmailAccountId)) {
|
|
|
+ await setState({ currentHotmailAccountId: null });
|
|
|
+ if (isHotmailProvider(state)) {
|
|
|
+ await setEmailState(null);
|
|
|
+ }
|
|
|
+ broadcastDataUpdate({ currentHotmailAccountId: null });
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ deletedCount: targets.length,
|
|
|
+ remainingCount: nextAccounts.length,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function patchHotmailAccount(accountId, updates = {}) {
|
|
|
+ const state = await getState();
|
|
|
+ const accounts = normalizeHotmailAccounts(state.hotmailAccounts);
|
|
|
+ const account = findHotmailAccount(accounts, accountId);
|
|
|
+ if (!account) {
|
|
|
+ throw new Error('未找到对应的 Hotmail 账号。');
|
|
|
+ }
|
|
|
+
|
|
|
+ const nextAccount = normalizeHotmailAccount({
|
|
|
+ ...account,
|
|
|
+ ...updates,
|
|
|
+ id: account.id,
|
|
|
+ });
|
|
|
+
|
|
|
+ await syncHotmailAccounts(accounts.map((item) => (item.id === account.id ? nextAccount : item)));
|
|
|
+
|
|
|
+ if (state.currentHotmailAccountId === account.id && shouldClearHotmailCurrentSelection(nextAccount)) {
|
|
|
+ await setState({ currentHotmailAccountId: null });
|
|
|
+ broadcastDataUpdate({ currentHotmailAccountId: null });
|
|
|
+ if (isHotmailProvider(state)) {
|
|
|
+ await setEmailState(null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nextAccount;
|
|
|
+}
|
|
|
+
|
|
|
+async function setCurrentHotmailAccount(accountId, options = {}) {
|
|
|
+ const { markUsed = false, syncEmail = true } = options;
|
|
|
+ const state = await getState();
|
|
|
+ const accounts = normalizeHotmailAccounts(state.hotmailAccounts);
|
|
|
+ const account = findHotmailAccount(accounts, accountId);
|
|
|
+ if (!account) {
|
|
|
+ throw new Error('未找到对应的 Hotmail 账号。');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (markUsed) {
|
|
|
+ account.lastUsedAt = Date.now();
|
|
|
+ await syncHotmailAccounts(accounts.map((item) => (item.id === account.id ? account : item)));
|
|
|
+ }
|
|
|
+
|
|
|
+ await setState({ currentHotmailAccountId: account.id });
|
|
|
+ broadcastDataUpdate({ currentHotmailAccountId: account.id });
|
|
|
+ if (syncEmail) {
|
|
|
+ await setEmailState(account.email || null);
|
|
|
+ }
|
|
|
+ return account;
|
|
|
+}
|
|
|
+
|
|
|
+async function ensureHotmailAccountForFlow(options = {}) {
|
|
|
+ const { allowAllocate = true, markUsed = false, preferredAccountId = null } = options;
|
|
|
+ const state = await getState();
|
|
|
+ const accounts = normalizeHotmailAccounts(state.hotmailAccounts);
|
|
|
+ const isAccountAllocatable = (candidate) => Boolean(candidate)
|
|
|
+ && candidate.status === 'authorized'
|
|
|
+ && !candidate.used
|
|
|
+ && Boolean(candidate.refreshToken);
|
|
|
+
|
|
|
+ let account = null;
|
|
|
+ if (preferredAccountId) {
|
|
|
+ account = findHotmailAccount(accounts, preferredAccountId);
|
|
|
+ }
|
|
|
+ if (!account && state.currentHotmailAccountId) {
|
|
|
+ account = findHotmailAccount(accounts, state.currentHotmailAccountId);
|
|
|
+ }
|
|
|
+ if ((!account || !isAccountAllocatable(account)) && allowAllocate) {
|
|
|
+ account = pickHotmailAccountForRun(accounts, {});
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!account) {
|
|
|
+ throw new Error('没有可用的 Hotmail 账号。请先在侧边栏添加至少一个带 refresh token 的账号。');
|
|
|
+ }
|
|
|
+ if (!isAccountAllocatable(account)) {
|
|
|
+ throw new Error(`Hotmail 账号 ${account.email || account.id} 尚未就绪,无法读取邮件。`);
|
|
|
+ }
|
|
|
+
|
|
|
+ return setCurrentHotmailAccount(account.id, { markUsed, syncEmail: true });
|
|
|
+}
|
|
|
+
|
|
|
+async function requestHotmailMailApi(account, mailbox = 'INBOX') {
|
|
|
+ if (!account?.email) {
|
|
|
+ throw new Error('Hotmail 账号缺少邮箱地址。');
|
|
|
+ }
|
|
|
+ if (!account?.clientId) {
|
|
|
+ throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少 client ID。`);
|
|
|
+ }
|
|
|
+ if (!account?.refreshToken) {
|
|
|
+ throw new Error(`Hotmail 账号 ${account.email || account.id} 缺少 refresh token。`);
|
|
|
+ }
|
|
|
+
|
|
|
+ const url = buildHotmailMailApiLatestUrl({
|
|
|
+ clientId: account.clientId,
|
|
|
+ email: account.email,
|
|
|
+ refreshToken: account.refreshToken,
|
|
|
+ mailbox,
|
|
|
+ responseType: 'json',
|
|
|
+ });
|
|
|
+ const { timeoutMs } = getHotmailMailApiRequestConfig();
|
|
|
+ const controller = new AbortController();
|
|
|
+ const timeoutId = setTimeout(() => controller.abort(new Error('timeout')), timeoutMs);
|
|
|
+
|
|
|
+ let response;
|
|
|
+ try {
|
|
|
+ response = await fetch(url, { method: 'GET', signal: controller.signal });
|
|
|
+ } catch (err) {
|
|
|
+ if (err?.name === 'AbortError') {
|
|
|
+ throw new Error(`Hotmail API 请求超时(>${Math.round(timeoutMs / 1000)} 秒):${mailbox}`);
|
|
|
+ }
|
|
|
+ throw new Error(`Hotmail API 请求失败:${err.message}`);
|
|
|
+ } finally {
|
|
|
+ clearTimeout(timeoutId);
|
|
|
+ }
|
|
|
+
|
|
|
+ const text = await response.text();
|
|
|
+ let payload = {};
|
|
|
+ try {
|
|
|
+ payload = text ? JSON.parse(text) : {};
|
|
|
+ } catch {
|
|
|
+ payload = { raw: text };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ const errorText = payload?.message || payload?.error || payload?.msg || text || `HTTP ${response.status}`;
|
|
|
+ throw new Error(`Hotmail API 请求失败:${errorText}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (payload && payload.success === false) {
|
|
|
+ const errorText = payload?.message || payload?.msg || payload?.error || '未知错误';
|
|
|
+ throw new Error(`Hotmail API 返回失败:${errorText}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ mailbox,
|
|
|
+ payload,
|
|
|
+ messages: normalizeHotmailMailApiMessages(payload?.data),
|
|
|
+ nextRefreshToken: String(payload?.new_refresh_token || payload?.newRefreshToken || '').trim(),
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function applyHotmailApiResultToAccount(account, apiResult) {
|
|
|
+ const nextRefreshToken = String(apiResult?.nextRefreshToken || '').trim();
|
|
|
+ return {
|
|
|
+ ...account,
|
|
|
+ accessToken: '',
|
|
|
+ expiresAt: 0,
|
|
|
+ refreshToken: nextRefreshToken || account.refreshToken,
|
|
|
+ status: 'authorized',
|
|
|
+ lastAuthAt: Date.now(),
|
|
|
+ lastError: '',
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function fetchHotmailMailboxMessages(account, mailboxes = HOTMAIL_MAILBOXES) {
|
|
|
+ let workingAccount = normalizeHotmailAccount(account);
|
|
|
+ const mailboxResults = [];
|
|
|
+
|
|
|
+ for (const mailbox of mailboxes) {
|
|
|
+ const result = await requestHotmailMailApi(workingAccount, mailbox);
|
|
|
+ workingAccount = applyHotmailApiResultToAccount(workingAccount, result);
|
|
|
+ mailboxResults.push({
|
|
|
+ mailbox,
|
|
|
+ count: result.messages.length,
|
|
|
+ messages: result.messages.map((message) => ({ ...message, mailbox })),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ const savedAccount = await upsertHotmailAccount(workingAccount);
|
|
|
+ return {
|
|
|
+ account: savedAccount,
|
|
|
+ mailboxResults,
|
|
|
+ messages: mailboxResults.flatMap((item) => item.messages),
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function verifyHotmailAccount(accountId) {
|
|
|
+ const state = await getState();
|
|
|
+ const account = findHotmailAccount(state.hotmailAccounts, accountId);
|
|
|
+ if (!account) {
|
|
|
+ throw new Error('未找到需要校验的 Hotmail 账号。');
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await fetchHotmailMailboxMessages(account, ['INBOX']);
|
|
|
+ return {
|
|
|
+ account: result.account,
|
|
|
+ messageCount: result.mailboxResults[0]?.count || 0,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function testHotmailAccountMailAccess(accountId) {
|
|
|
+ const state = await getState();
|
|
|
+ const account = findHotmailAccount(state.hotmailAccounts, accountId);
|
|
|
+ if (!account) {
|
|
|
+ throw new Error('未找到需要测试的 Hotmail 账号。');
|
|
|
+ }
|
|
|
+
|
|
|
+ const result = await fetchHotmailMailboxMessages(account, HOTMAIL_MAILBOXES);
|
|
|
+ const latestMessage = getLatestHotmailMessage(result.messages);
|
|
|
+ const latestCode = latestMessage ? extractVerificationCodeFromMessage(latestMessage) : null;
|
|
|
+
|
|
|
+ return {
|
|
|
+ account: result.account,
|
|
|
+ accountId: result.account.id,
|
|
|
+ email: result.account.email,
|
|
|
+ messageCount: result.messages.length,
|
|
|
+ latestSubject: latestMessage?.subject || '',
|
|
|
+ latestMailbox: latestMessage?.mailbox || '',
|
|
|
+ latestCode: latestCode || '',
|
|
|
+ inboxCount: result.mailboxResults.find((item) => item.mailbox === 'INBOX')?.count || 0,
|
|
|
+ junkCount: result.mailboxResults.find((item) => item.mailbox === 'Junk')?.count || 0,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function pollHotmailVerificationCode(step, state, pollPayload = {}) {
|
|
|
+ await addLog(`步骤 ${step}:正在确定 Hotmail 收信账号...`, 'info');
|
|
|
+ let account = await ensureHotmailAccountForFlow({
|
|
|
+ allowAllocate: true,
|
|
|
+ markUsed: false,
|
|
|
+ preferredAccountId: state.currentHotmailAccountId || null,
|
|
|
+ });
|
|
|
+ await addLog(`步骤 ${step}:当前使用 Hotmail 账号 ${account.email} 轮询收件箱。`, 'info');
|
|
|
+
|
|
|
+ const maxAttempts = Number(pollPayload.maxAttempts) || 5;
|
|
|
+ const intervalMs = Number(pollPayload.intervalMs) || 3000;
|
|
|
+ let lastError = null;
|
|
|
+
|
|
|
+ function summarizeMessagesForLog(messages) {
|
|
|
+ return (messages || [])
|
|
|
+ .slice()
|
|
|
+ .sort((left, right) => {
|
|
|
+ const leftTime = Date.parse(left.receivedDateTime || '') || 0;
|
|
|
+ const rightTime = Date.parse(right.receivedDateTime || '') || 0;
|
|
|
+ return rightTime - leftTime;
|
|
|
+ })
|
|
|
+ .slice(0, 3)
|
|
|
+ .map((message) => {
|
|
|
+ const receivedAt = message?.receivedDateTime || 'unknown-time';
|
|
|
+ const sender = message?.from?.emailAddress?.address || 'unknown';
|
|
|
+ const subject = message?.subject || '(no subject)';
|
|
|
+ const preview = String(message?.bodyPreview || '').replace(/\s+/g, ' ').trim().slice(0, 80);
|
|
|
+ return `[${message.mailbox || 'INBOX'}] ${receivedAt} | ${sender} | ${subject} | ${preview}`;
|
|
|
+ })
|
|
|
+ .join(' || ');
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
|
+ throwIfStopped();
|
|
|
+ try {
|
|
|
+ await addLog(`步骤 ${step}:正在轮询 Hotmail 邮件(${attempt}/${maxAttempts})...`, 'info');
|
|
|
+ const fetchResult = await fetchHotmailMailboxMessages(account, HOTMAIL_MAILBOXES);
|
|
|
+ account = fetchResult.account;
|
|
|
+ const matchResult = pickVerificationMessageWithTimeFallback(fetchResult.messages, {
|
|
|
+ afterTimestamp: pollPayload.filterAfterTimestamp || 0,
|
|
|
+ senderFilters: pollPayload.senderFilters || [],
|
|
|
+ subjectFilters: pollPayload.subjectFilters || [],
|
|
|
+ excludeCodes: pollPayload.excludeCodes || [],
|
|
|
+ });
|
|
|
+ const match = matchResult.match;
|
|
|
+
|
|
|
+ if (match?.code) {
|
|
|
+ const mailboxLabel = match.message?.mailbox || 'INBOX';
|
|
|
+ if (matchResult.usedRelaxedFilters) {
|
|
|
+ const fallbackLabel = matchResult.usedTimeFallback ? '宽松匹配 + 时间回退' : '宽松匹配';
|
|
|
+ await addLog(`步骤 ${step}:严格规则未命中,已改用 ${fallbackLabel} 并命中 Hotmail ${mailboxLabel} 验证码。`, 'warn');
|
|
|
+ }
|
|
|
+ await addLog(`步骤 ${step}:已在 Hotmail ${mailboxLabel} 中找到验证码:${match.code}`, 'ok');
|
|
|
+ return {
|
|
|
+ ok: true,
|
|
|
+ code: match.code,
|
|
|
+ emailTimestamp: match.receivedAt || Date.now(),
|
|
|
+ mailId: match.message?.id || '',
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ lastError = new Error(`步骤 ${step}:暂未在 Hotmail 收件箱中找到匹配验证码(${attempt}/${maxAttempts})。`);
|
|
|
+ await addLog(lastError.message, attempt === maxAttempts ? 'warn' : 'info');
|
|
|
+ const mailSummary = summarizeMessagesForLog(fetchResult.messages);
|
|
|
+ if (mailSummary) {
|
|
|
+ await addLog(`步骤 ${step}:最近邮件样本:${mailSummary}`, 'info');
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ lastError = err;
|
|
|
+ await addLog(`步骤 ${step}:Hotmail 收件箱轮询失败:${err.message}`, 'warn');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (attempt < maxAttempts) {
|
|
|
+ await sleepWithStop(intervalMs);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ throw lastError || new Error(`步骤 ${step}:未在 Hotmail 收件箱中找到新的匹配验证码。`);
|
|
|
+}
|
|
|
+
|
|
|
// ============================================================
|
|
|
// Tab Registry
|
|
|
// ============================================================
|
|
|
@@ -614,6 +1046,7 @@ function getSourceLabel(source) {
|
|
|
'mail-163': '163 邮箱',
|
|
|
'inbucket-mail': 'Inbucket 邮箱',
|
|
|
'duck-mail': 'Duck 邮箱',
|
|
|
+ 'hotmail-api': 'Hotmail API',
|
|
|
};
|
|
|
return labels[source] || source || '未知来源';
|
|
|
}
|
|
|
@@ -650,7 +1083,7 @@ function getErrorMessage(error) {
|
|
|
|
|
|
function isVerificationMailPollingError(error) {
|
|
|
const message = getErrorMessage(error);
|
|
|
- return /未在 .*邮箱中找到新的匹配邮件|邮箱轮询结束,但未获取到验证码|无法获取新的(?:注册|登录)验证码|页面未能重新就绪|页面通信异常|did not respond in \d+s/i.test(message);
|
|
|
+ return /未在 .*邮箱中找到新的匹配邮件|未在 Hotmail 收件箱中找到新的匹配验证码|邮箱轮询结束,但未获取到验证码|无法获取新的(?:注册|登录)验证码|页面未能重新就绪|页面通信异常|did not respond in \d+s/i.test(message);
|
|
|
}
|
|
|
|
|
|
function isRestartCurrentAttemptError(error) {
|
|
|
@@ -658,7 +1091,13 @@ function isRestartCurrentAttemptError(error) {
|
|
|
return /当前邮箱已存在,需要重新开始新一轮/.test(message);
|
|
|
}
|
|
|
|
|
|
-function isStep9OAuthTimeoutError(error) {
|
|
|
+function isStep9RecoverableAuthError(error) {
|
|
|
+ const message = String(typeof error === 'string' ? error : error?.message || '');
|
|
|
+ return /STEP9_OAUTH_RETRY::/i.test(message)
|
|
|
+ || isRecoverableStep9AuthFailure(message);
|
|
|
+}
|
|
|
+
|
|
|
+function isLegacyStep9RecoverableAuthError(error) {
|
|
|
const message = String(typeof error === 'string' ? error : error?.message || '');
|
|
|
return /STEP9_OAUTH_TIMEOUT::|认证失败:\s*Timeout waiting for OAuth callback/i.test(message);
|
|
|
}
|
|
|
@@ -687,6 +1126,8 @@ function getDownstreamStateResets(step) {
|
|
|
flowStartTime: null,
|
|
|
password: null,
|
|
|
lastEmailTimestamp: null,
|
|
|
+ signupVerificationRequestedAt: null,
|
|
|
+ loginVerificationRequestedAt: null,
|
|
|
lastSignupCode: null,
|
|
|
lastLoginCode: null,
|
|
|
localhostUrl: null,
|
|
|
@@ -696,6 +1137,8 @@ function getDownstreamStateResets(step) {
|
|
|
return {
|
|
|
password: null,
|
|
|
lastEmailTimestamp: null,
|
|
|
+ signupVerificationRequestedAt: null,
|
|
|
+ loginVerificationRequestedAt: null,
|
|
|
lastSignupCode: null,
|
|
|
lastLoginCode: null,
|
|
|
localhostUrl: null,
|
|
|
@@ -704,6 +1147,8 @@ function getDownstreamStateResets(step) {
|
|
|
if (step === 3 || step === 4) {
|
|
|
return {
|
|
|
lastEmailTimestamp: null,
|
|
|
+ signupVerificationRequestedAt: null,
|
|
|
+ loginVerificationRequestedAt: null,
|
|
|
lastSignupCode: null,
|
|
|
lastLoginCode: null,
|
|
|
localhostUrl: null,
|
|
|
@@ -712,6 +1157,7 @@ function getDownstreamStateResets(step) {
|
|
|
if (step === 5 || step === 6 || step === 7) {
|
|
|
return {
|
|
|
lastLoginCode: null,
|
|
|
+ loginVerificationRequestedAt: null,
|
|
|
localhostUrl: null,
|
|
|
};
|
|
|
}
|
|
|
@@ -1067,6 +1513,63 @@ async function handleMessage(message, sender) {
|
|
|
return { ok: true };
|
|
|
}
|
|
|
|
|
|
+ case 'UPSERT_HOTMAIL_ACCOUNT': {
|
|
|
+ const account = await upsertHotmailAccount(message.payload || {});
|
|
|
+ return { ok: true, account };
|
|
|
+ }
|
|
|
+
|
|
|
+ case 'DELETE_HOTMAIL_ACCOUNT': {
|
|
|
+ await deleteHotmailAccount(String(message.payload?.accountId || ''));
|
|
|
+ return { ok: true };
|
|
|
+ }
|
|
|
+
|
|
|
+ case 'DELETE_HOTMAIL_ACCOUNTS': {
|
|
|
+ const result = await deleteHotmailAccounts(String(message.payload?.mode || 'all'));
|
|
|
+ return { ok: true, ...result };
|
|
|
+ }
|
|
|
+
|
|
|
+ case 'SELECT_HOTMAIL_ACCOUNT': {
|
|
|
+ const account = await setCurrentHotmailAccount(String(message.payload?.accountId || ''), {
|
|
|
+ markUsed: false,
|
|
|
+ syncEmail: true,
|
|
|
+ });
|
|
|
+ return { ok: true, account };
|
|
|
+ }
|
|
|
+
|
|
|
+ case 'PATCH_HOTMAIL_ACCOUNT': {
|
|
|
+ const account = await patchHotmailAccount(
|
|
|
+ String(message.payload?.accountId || ''),
|
|
|
+ message.payload?.updates || {}
|
|
|
+ );
|
|
|
+ return { ok: true, account };
|
|
|
+ }
|
|
|
+
|
|
|
+ case 'VERIFY_HOTMAIL_ACCOUNT':
|
|
|
+ case 'AUTHORIZE_HOTMAIL_ACCOUNT': {
|
|
|
+ const accountId = String(message.payload?.accountId || '');
|
|
|
+ try {
|
|
|
+ const result = await verifyHotmailAccount(accountId);
|
|
|
+ await setCurrentHotmailAccount(result.account.id, { markUsed: false, syncEmail: true });
|
|
|
+ await addLog(`Hotmail 账号 ${result.account.email} 校验通过,可直接用于收信。`, 'ok');
|
|
|
+ return { ok: true, account: result.account, messageCount: result.messageCount };
|
|
|
+ } catch (err) {
|
|
|
+ const state = await getState();
|
|
|
+ const accounts = normalizeHotmailAccounts(state.hotmailAccounts);
|
|
|
+ const target = findHotmailAccount(accounts, accountId);
|
|
|
+ if (target) {
|
|
|
+ target.status = 'error';
|
|
|
+ target.lastError = err.message;
|
|
|
+ await syncHotmailAccounts(accounts.map((item) => (item.id === target.id ? target : item)));
|
|
|
+ }
|
|
|
+ throw err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ case 'TEST_HOTMAIL_ACCOUNT': {
|
|
|
+ const result = await testHotmailAccountMailAccess(String(message.payload?.accountId || ''));
|
|
|
+ return { ok: true, ...result };
|
|
|
+ }
|
|
|
+
|
|
|
// Side panel data updates
|
|
|
case 'SAVE_EMAIL': {
|
|
|
const state = await getState();
|
|
|
@@ -1114,9 +1617,29 @@ async function handleStepData(step, payload) {
|
|
|
break;
|
|
|
case 3:
|
|
|
if (payload.email) await setEmailState(payload.email);
|
|
|
+ if (payload.signupVerificationRequestedAt) {
|
|
|
+ await setState({ signupVerificationRequestedAt: payload.signupVerificationRequestedAt });
|
|
|
+ }
|
|
|
+ if (payload.loginVerificationRequestedAt) {
|
|
|
+ await setState({ loginVerificationRequestedAt: payload.loginVerificationRequestedAt });
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 6:
|
|
|
+ if (payload.loginVerificationRequestedAt) {
|
|
|
+ await setState({ loginVerificationRequestedAt: payload.loginVerificationRequestedAt });
|
|
|
+ }
|
|
|
break;
|
|
|
case 4:
|
|
|
- if (payload.emailTimestamp) await setState({ lastEmailTimestamp: payload.emailTimestamp });
|
|
|
+ await setState({
|
|
|
+ lastEmailTimestamp: payload.emailTimestamp || null,
|
|
|
+ signupVerificationRequestedAt: null,
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 7:
|
|
|
+ await setState({
|
|
|
+ lastEmailTimestamp: payload.emailTimestamp || null,
|
|
|
+ loginVerificationRequestedAt: null,
|
|
|
+ });
|
|
|
break;
|
|
|
case 8:
|
|
|
if (payload.localhostUrl) {
|
|
|
@@ -1125,6 +1648,14 @@ async function handleStepData(step, payload) {
|
|
|
}
|
|
|
break;
|
|
|
case 9: {
|
|
|
+ const latestState = await getState();
|
|
|
+ if (latestState.currentHotmailAccountId && isHotmailProvider(latestState)) {
|
|
|
+ await patchHotmailAccount(latestState.currentHotmailAccountId, {
|
|
|
+ used: true,
|
|
|
+ lastUsedAt: Date.now(),
|
|
|
+ });
|
|
|
+ await addLog('当前 Hotmail 账号已自动标记为已用。', 'ok');
|
|
|
+ }
|
|
|
const localhostPrefix = buildLocalhostCleanupPrefix(payload.localhostUrl);
|
|
|
if (localhostPrefix) {
|
|
|
await closeTabsByUrlPrefix(localhostPrefix);
|
|
|
@@ -1352,6 +1883,16 @@ async function resumeAutoRunIfWaitingForEmail(options = {}) {
|
|
|
|
|
|
async function ensureAutoEmailReady(targetRun, totalRuns, attemptRuns) {
|
|
|
const currentState = await getState();
|
|
|
+ if (isHotmailProvider(currentState)) {
|
|
|
+ const account = await ensureHotmailAccountForFlow({
|
|
|
+ allowAllocate: true,
|
|
|
+ markUsed: true,
|
|
|
+ preferredAccountId: null,
|
|
|
+ });
|
|
|
+ await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:已分配 Hotmail 账号 ${account.email}(第 ${attemptRuns} 次尝试)===`, 'ok');
|
|
|
+ return account.email;
|
|
|
+ }
|
|
|
+
|
|
|
if (currentState.email) {
|
|
|
return currentState.email;
|
|
|
}
|
|
|
@@ -1430,14 +1971,23 @@ async function runAutoSequenceFromStep(startStep, context = {}) {
|
|
|
await executeStepAndWait(step, AUTO_STEP_DELAYS[step]);
|
|
|
step += 1;
|
|
|
} catch (err) {
|
|
|
- if (step === 9 && isStep9OAuthTimeoutError(err) && step9RestartAttempts < maxStep9RestartAttempts) {
|
|
|
+ const latestState = await getState();
|
|
|
+ const currentMail = getMailConfig(latestState);
|
|
|
+ const shouldRetryStep9 = step === 9
|
|
|
+ && (
|
|
|
+ isLegacyStep9RecoverableAuthError(err)
|
|
|
+ || (currentMail.provider === HOTMAIL_PROVIDER && isStep9RecoverableAuthError(err))
|
|
|
+ )
|
|
|
+ && step9RestartAttempts < maxStep9RestartAttempts;
|
|
|
+
|
|
|
+ if (shouldRetryStep9) {
|
|
|
step9RestartAttempts += 1;
|
|
|
await addLog(
|
|
|
- `步骤 9:检测到 OAuth callback 超时,正在回到步骤 6 重新开始授权流程(${step9RestartAttempts}/${maxStep9RestartAttempts})...`,
|
|
|
+ `步骤 9:检测到 CPA 认证失败,正在回到步骤 6 重新开始授权流程(${step9RestartAttempts}/${maxStep9RestartAttempts})...`,
|
|
|
'warn'
|
|
|
);
|
|
|
await invalidateDownstreamAfterStepRestart(6, {
|
|
|
- logLabel: `步骤 9 超时后准备回到步骤 6 重试(${step9RestartAttempts}/${maxStep9RestartAttempts})`,
|
|
|
+ logLabel: `步骤 9 认证失败后准备回到步骤 6 重试(${step9RestartAttempts}/${maxStep9RestartAttempts})`,
|
|
|
});
|
|
|
step = 6;
|
|
|
continue;
|
|
|
@@ -1696,7 +2246,7 @@ async function executeStep1(state) {
|
|
|
}
|
|
|
await addLog('步骤 1:正在打开 CPA 面板...');
|
|
|
await reuseOrCreateTab('vps-panel', state.vpsUrl, {
|
|
|
- inject: ['content/utils.js', 'content/vps-panel.js'],
|
|
|
+ inject: ['content/activation-utils.js', 'content/utils.js', 'content/vps-panel.js'],
|
|
|
reloadIfSameUrl: true,
|
|
|
});
|
|
|
|
|
|
@@ -1732,26 +2282,39 @@ async function executeStep2(state) {
|
|
|
// ============================================================
|
|
|
|
|
|
async function executeStep3(state) {
|
|
|
- if (!state.email) {
|
|
|
+ let resolvedEmail = state.email;
|
|
|
+ if (isHotmailProvider(state)) {
|
|
|
+ const account = await ensureHotmailAccountForFlow({
|
|
|
+ allowAllocate: true,
|
|
|
+ markUsed: true,
|
|
|
+ preferredAccountId: state.currentHotmailAccountId || null,
|
|
|
+ });
|
|
|
+ resolvedEmail = account.email;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!resolvedEmail) {
|
|
|
throw new Error('缺少邮箱地址,请先在侧边栏粘贴邮箱。');
|
|
|
}
|
|
|
|
|
|
const password = state.customPassword || generatePassword();
|
|
|
+ if (resolvedEmail !== state.email) {
|
|
|
+ await setEmailState(resolvedEmail);
|
|
|
+ }
|
|
|
await setPasswordState(password);
|
|
|
|
|
|
// Save account record
|
|
|
const accounts = state.accounts || [];
|
|
|
- accounts.push({ email: state.email, password, createdAt: new Date().toISOString() });
|
|
|
+ accounts.push({ email: resolvedEmail, password, createdAt: new Date().toISOString() });
|
|
|
await setState({ accounts });
|
|
|
|
|
|
await addLog(
|
|
|
- `步骤 3:正在填写邮箱 ${state.email},密码为${state.customPassword ? '自定义' : '自动生成'}(${password.length} 位)`
|
|
|
+ `步骤 3:正在填写邮箱 ${resolvedEmail},密码为${state.customPassword ? '自定义' : '自动生成'}(${password.length} 位)`
|
|
|
);
|
|
|
await sendToContentScript('signup-page', {
|
|
|
type: 'EXECUTE_STEP',
|
|
|
step: 3,
|
|
|
source: 'background',
|
|
|
- payload: { email: state.email, password },
|
|
|
+ payload: { email: resolvedEmail, password },
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -1761,6 +2324,9 @@ async function executeStep3(state) {
|
|
|
|
|
|
function getMailConfig(state) {
|
|
|
const provider = state.mailProvider || 'qq';
|
|
|
+ if (provider === HOTMAIL_PROVIDER) {
|
|
|
+ return { provider: HOTMAIL_PROVIDER, label: 'Hotmail API' };
|
|
|
+ }
|
|
|
if (provider === '163') {
|
|
|
return { source: 'mail-163', url: 'https://mail.163.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '163 邮箱' };
|
|
|
}
|
|
|
@@ -1778,7 +2344,7 @@ function getMailConfig(state) {
|
|
|
url: `${host}/m/${encodeURIComponent(mailbox)}/`,
|
|
|
label: `Inbucket 邮箱(${mailbox})`,
|
|
|
navigateOnReuse: true,
|
|
|
- inject: ['content/utils.js', 'content/inbucket-mail.js'],
|
|
|
+ inject: ['content/activation-utils.js', 'content/utils.js', 'content/inbucket-mail.js'],
|
|
|
injectSource: 'inbucket-mail',
|
|
|
};
|
|
|
}
|
|
|
@@ -1810,7 +2376,7 @@ function getVerificationCodeLabel(step) {
|
|
|
function getVerificationPollPayload(step, state, overrides = {}) {
|
|
|
if (step === 4) {
|
|
|
return {
|
|
|
- filterAfterTimestamp: state.flowStartTime || 0,
|
|
|
+ filterAfterTimestamp: getHotmailVerificationRequestTimestamp(4, state),
|
|
|
senderFilters: ['openai', 'noreply', 'verify', 'auth', 'duckduckgo', 'forward'],
|
|
|
subjectFilters: ['verify', 'verification', 'code', '楠岃瘉', 'confirm'],
|
|
|
targetEmail: state.email,
|
|
|
@@ -1821,7 +2387,7 @@ function getVerificationPollPayload(step, state, overrides = {}) {
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
- filterAfterTimestamp: state.lastEmailTimestamp || state.flowStartTime || 0,
|
|
|
+ filterAfterTimestamp: getHotmailVerificationRequestTimestamp(7, state),
|
|
|
senderFilters: ['openai', 'noreply', 'verify', 'auth', 'chatgpt', 'duckduckgo', 'forward'],
|
|
|
subjectFilters: ['verify', 'verification', 'code', '楠岃瘉', 'confirm', 'login'],
|
|
|
targetEmail: state.email,
|
|
|
@@ -1855,6 +2421,15 @@ async function requestVerificationCodeResend(step) {
|
|
|
}
|
|
|
|
|
|
async function pollFreshVerificationCode(step, state, mail, pollOverrides = {}) {
|
|
|
+ if (mail.provider === HOTMAIL_PROVIDER) {
|
|
|
+ const hotmailPollConfig = getHotmailVerificationPollConfig(step);
|
|
|
+ return pollHotmailVerificationCode(step, state, {
|
|
|
+ ...getVerificationPollPayload(step, state),
|
|
|
+ ...hotmailPollConfig,
|
|
|
+ ...pollOverrides,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
const stateKey = getVerificationCodeStateKey(step);
|
|
|
const rejectedCodes = new Set();
|
|
|
if (state[stateKey]) {
|
|
|
@@ -1943,12 +2518,18 @@ async function submitVerificationCode(step, code) {
|
|
|
async function resolveVerificationStep(step, state, mail, options = {}) {
|
|
|
const stateKey = getVerificationCodeStateKey(step);
|
|
|
const rejectedCodes = new Set();
|
|
|
- if (state[stateKey]) {
|
|
|
+ const hotmailPollConfig = mail.provider === HOTMAIL_PROVIDER
|
|
|
+ ? getHotmailVerificationPollConfig(step)
|
|
|
+ : null;
|
|
|
+ const ignorePersistedLastCode = Boolean(hotmailPollConfig?.ignorePersistedLastCode);
|
|
|
+ if (state[stateKey] && !ignorePersistedLastCode) {
|
|
|
rejectedCodes.add(state[stateKey]);
|
|
|
}
|
|
|
|
|
|
const nextFilterAfterTimestamp = options.filterAfterTimestamp ?? null;
|
|
|
- const requestFreshCodeFirst = Boolean(options.requestFreshCodeFirst);
|
|
|
+ const requestFreshCodeFirst = options.requestFreshCodeFirst !== undefined
|
|
|
+ ? Boolean(options.requestFreshCodeFirst)
|
|
|
+ : (hotmailPollConfig?.requestFreshCodeFirst ?? false);
|
|
|
const maxSubmitAttempts = 3;
|
|
|
|
|
|
if (requestFreshCodeFirst) {
|
|
|
@@ -1960,6 +2541,14 @@ async function resolveVerificationStep(step, state, mail, options = {}) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (mail.provider === HOTMAIL_PROVIDER) {
|
|
|
+ const initialDelayMs = Number(options.initialDelayMs ?? hotmailPollConfig.initialDelayMs) || 0;
|
|
|
+ if (initialDelayMs > 0) {
|
|
|
+ await addLog(`步骤 ${step}:等待 ${Math.round(initialDelayMs / 1000)} 秒,让 Hotmail 验证码邮件先到达...`, 'info');
|
|
|
+ await sleepWithStop(initialDelayMs);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
for (let attempt = 1; attempt <= maxSubmitAttempts; attempt++) {
|
|
|
const result = await pollFreshVerificationCode(step, state, mail, {
|
|
|
excludeCodes: [...rejectedCodes],
|
|
|
@@ -2024,35 +2613,42 @@ async function executeStep4(state) {
|
|
|
if (prepareResult && prepareResult.error) {
|
|
|
throw new Error(prepareResult.error);
|
|
|
}
|
|
|
+ if (prepareResult?.verificationRequestedAt) {
|
|
|
+ await setState({ loginVerificationRequestedAt: prepareResult.verificationRequestedAt });
|
|
|
+ }
|
|
|
if (prepareResult?.alreadyVerified) {
|
|
|
await completeStepFromBackground(4, {});
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- await addLog(`步骤 4:正在打开${mail.label}...`);
|
|
|
-
|
|
|
- // For mail tabs, only create if not alive — don't navigate (preserves login session)
|
|
|
- const alive = await isTabAlive(mail.source);
|
|
|
- if (alive) {
|
|
|
- if (mail.navigateOnReuse) {
|
|
|
+ if (mail.provider === HOTMAIL_PROVIDER) {
|
|
|
+ await addLog(`步骤 4:正在通过 ${mail.label} 轮询验证码...`);
|
|
|
+ } else {
|
|
|
+ await addLog(`步骤 4:正在打开${mail.label}...`);
|
|
|
+
|
|
|
+ // For mail tabs, only create if not alive — don't navigate (preserves login session)
|
|
|
+ const alive = await isTabAlive(mail.source);
|
|
|
+ if (alive) {
|
|
|
+ if (mail.navigateOnReuse) {
|
|
|
+ await reuseOrCreateTab(mail.source, mail.url, {
|
|
|
+ inject: mail.inject,
|
|
|
+ injectSource: mail.injectSource,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ const tabId = await getTabId(mail.source);
|
|
|
+ await chrome.tabs.update(tabId, { active: true });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
await reuseOrCreateTab(mail.source, mail.url, {
|
|
|
inject: mail.inject,
|
|
|
injectSource: mail.injectSource,
|
|
|
});
|
|
|
- } else {
|
|
|
- const tabId = await getTabId(mail.source);
|
|
|
- await chrome.tabs.update(tabId, { active: true });
|
|
|
}
|
|
|
- } else {
|
|
|
- await reuseOrCreateTab(mail.source, mail.url, {
|
|
|
- inject: mail.inject,
|
|
|
- injectSource: mail.injectSource,
|
|
|
- });
|
|
|
}
|
|
|
|
|
|
await resolveVerificationStep(4, state, mail, {
|
|
|
- filterAfterTimestamp: stepStartedAt,
|
|
|
- requestFreshCodeFirst: true,
|
|
|
+ filterAfterTimestamp: mail.provider === HOTMAIL_PROVIDER ? undefined : stepStartedAt,
|
|
|
+ requestFreshCodeFirst: mail.provider === HOTMAIL_PROVIDER ? false : true,
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
@@ -2148,29 +2744,33 @@ async function runStep7Attempt(state) {
|
|
|
throw new Error(prepareResult.error);
|
|
|
}
|
|
|
|
|
|
- await addLog(`步骤 7:正在打开${mail.label}...`);
|
|
|
-
|
|
|
- const alive = await isTabAlive(mail.source);
|
|
|
- if (alive) {
|
|
|
- if (mail.navigateOnReuse) {
|
|
|
+ if (mail.provider === HOTMAIL_PROVIDER) {
|
|
|
+ await addLog(`步骤 7:正在通过 ${mail.label} 轮询验证码...`);
|
|
|
+ } else {
|
|
|
+ await addLog(`步骤 7:正在打开${mail.label}...`);
|
|
|
+
|
|
|
+ const alive = await isTabAlive(mail.source);
|
|
|
+ if (alive) {
|
|
|
+ if (mail.navigateOnReuse) {
|
|
|
+ await reuseOrCreateTab(mail.source, mail.url, {
|
|
|
+ inject: mail.inject,
|
|
|
+ injectSource: mail.injectSource,
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ const tabId = await getTabId(mail.source);
|
|
|
+ await chrome.tabs.update(tabId, { active: true });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
await reuseOrCreateTab(mail.source, mail.url, {
|
|
|
inject: mail.inject,
|
|
|
injectSource: mail.injectSource,
|
|
|
});
|
|
|
- } else {
|
|
|
- const tabId = await getTabId(mail.source);
|
|
|
- await chrome.tabs.update(tabId, { active: true });
|
|
|
}
|
|
|
- } else {
|
|
|
- await reuseOrCreateTab(mail.source, mail.url, {
|
|
|
- inject: mail.inject,
|
|
|
- injectSource: mail.injectSource,
|
|
|
- });
|
|
|
}
|
|
|
|
|
|
await resolveVerificationStep(7, state, mail, {
|
|
|
- filterAfterTimestamp: stepStartedAt,
|
|
|
- requestFreshCodeFirst: true,
|
|
|
+ filterAfterTimestamp: mail.provider === HOTMAIL_PROVIDER ? undefined : stepStartedAt,
|
|
|
+ requestFreshCodeFirst: mail.provider === HOTMAIL_PROVIDER ? false : true,
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -2345,7 +2945,7 @@ async function executeStep9(state) {
|
|
|
// Inject scripts directly and wait for them to be ready
|
|
|
await chrome.scripting.executeScript({
|
|
|
target: { tabId },
|
|
|
- files: ['content/utils.js', 'content/vps-panel.js'],
|
|
|
+ files: ['content/activation-utils.js', 'content/utils.js', 'content/vps-panel.js'],
|
|
|
});
|
|
|
await new Promise(r => setTimeout(r, 1000));
|
|
|
|