feat: 优化2925邮箱登录流程,添加登录前清理cookie和等待机制,更新相关文档和测试用例
This commit is contained in:
@@ -145,7 +145,7 @@
|
||||
2. 在 `2925 账号池` 中添加 `邮箱 / 密码`
|
||||
3. 在“2925 基邮箱”右侧打开 `号池` 开关后,基邮箱输入框会切成下拉框,只能从 2925 账号池中选择邮箱;关闭开关则回退到原来的手填基邮箱路线
|
||||
4. 可先点 `使用此账号` 让当前别名基邮箱切到这条 2925 账号,再点 `登录` 手动验证网页邮箱登录态
|
||||
5. 只有在 `号池` 开关开启时,自动流程执行到 Step 4 / Step 8 前才会自动检查 2925 登录态;如果未登录,会直接使用当前账号自动登录;点击登录后若 `20 秒`内仍未进入收件箱,则会判定当前登录失败
|
||||
5. 只有在 `号池` 开关开启时,自动流程执行到 Step 4 / Step 8 前才会自动检查 2925 登录态;如果未登录,会先清理登录 cookie、等待 `3 秒`,再打开登录页,并在页面打开后再等待 `3 秒`,然后使用当前账号自动登录;点击登录后若 `20 秒`内仍未进入收件箱,则会判定当前登录失败
|
||||
6. 当 Step 4 / Step 8 轮询邮箱时遇到“子邮箱已达上限邮箱”,扩展会记录当前时间;如果还有下一个可用账号,就禁用当前账号 24 小时并自动切换登录;如果没有下一个可用账号,或当前未启用号池模式,则会直接复用现有“手动暂停 / 停止”逻辑终止自动流程
|
||||
7. 如果你同时开启了 `Auto` 的自动重试,当前尝试结束后会按现有逻辑自动进入下一次尝试,不需要再手动介入
|
||||
8. `Mail = 2925` 仍然走 Gmail / 2925 共用的别名邮箱链路;实际注册邮箱会基于当前 2925 账号邮箱生成,例如 `name@2925.com -> name123456@2925.com`
|
||||
|
||||
@@ -5395,6 +5395,7 @@ const mail2925SessionManager = self.MultiPageBackgroundMail2925Session?.createMa
|
||||
pickMail2925AccountForRun,
|
||||
requestStop,
|
||||
reuseOrCreateTab,
|
||||
sendToContentScriptResilient,
|
||||
sendToMailContentScriptResilient,
|
||||
setPersistentSettings,
|
||||
setState,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
isAutoRunLockedState,
|
||||
requestStop,
|
||||
reuseOrCreateTab,
|
||||
sendToContentScriptResilient,
|
||||
sendToMailContentScriptResilient,
|
||||
setPersistentSettings,
|
||||
setState,
|
||||
@@ -128,6 +129,20 @@
|
||||
return findMail2925Account(state.mail2925Accounts, state.currentMail2925AccountId) || null;
|
||||
}
|
||||
|
||||
async function getMail2925CurrentTabUrl() {
|
||||
try {
|
||||
const state = await getState();
|
||||
const tabId = Number(state?.tabRegistry?.[MAIL2925_SOURCE]?.tabId || 0);
|
||||
if (!Number.isInteger(tabId) || tabId <= 0) {
|
||||
return '';
|
||||
}
|
||||
const tab = await chrome.tabs.get(tabId);
|
||||
return String(tab?.url || '').trim();
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
async function setCurrentMail2925Account(accountId, options = {}) {
|
||||
const { logMessage = '', updateLastUsedAt = false } = options;
|
||||
const state = await getState();
|
||||
@@ -376,6 +391,9 @@
|
||||
inject: MAIL2925_INJECT,
|
||||
injectSource: MAIL2925_INJECT_SOURCE,
|
||||
});
|
||||
if (forceRelogin && typeof sleepWithStop === 'function') {
|
||||
await sleepWithStop(3000);
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
@@ -398,6 +416,8 @@
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
const failedUrl = await getMail2925CurrentTabUrl();
|
||||
await addLog(`2925:ENSURE_MAIL2925_SESSION 通信失败,当前地址=${failedUrl || 'unknown'};原因=${getErrorMessage(err) || 'unknown'}`, 'warn');
|
||||
const stopped = await stopAutoRunForMail2925LoginFailure(
|
||||
`2925:${actionLabel}失败(${getErrorMessage(err) || '20 秒内未进入收件箱'}),已按手动停止逻辑暂停自动流程。`
|
||||
);
|
||||
@@ -408,6 +428,8 @@
|
||||
}
|
||||
|
||||
if (!result?.loggedIn) {
|
||||
const notLoggedInUrl = await getMail2925CurrentTabUrl();
|
||||
await addLog(`2925:20 秒登录等待结束但仍未进入收件箱,当前地址=${notLoggedInUrl || 'unknown'}`, 'warn');
|
||||
const stopped = await stopAutoRunForMail2925LoginFailure(
|
||||
`2925:${actionLabel}失败(20 秒内未进入收件箱),已按手动停止逻辑暂停自动流程。`
|
||||
);
|
||||
@@ -418,6 +440,8 @@
|
||||
}
|
||||
|
||||
if (result?.error) {
|
||||
const resultErrorUrl = await getMail2925CurrentTabUrl();
|
||||
await addLog(`2925:登录页返回业务错误,当前地址=${resultErrorUrl || 'unknown'};错误=${result.error}`, 'warn');
|
||||
const stopped = await stopAutoRunForMail2925LoginFailure(
|
||||
`2925:${actionLabel}失败(${result.error}),已按手动停止逻辑暂停自动流程。`
|
||||
);
|
||||
@@ -450,6 +474,129 @@
|
||||
};
|
||||
}
|
||||
|
||||
// Override the earlier version with a simpler login-page-only flow.
|
||||
async function ensureMail2925MailboxSession(options = {}) {
|
||||
const {
|
||||
accountId = null,
|
||||
forceRelogin = false,
|
||||
actionLabel = '确保 2925 邮箱登录态',
|
||||
} = options;
|
||||
const account = await ensureMail2925AccountForFlow({
|
||||
allowAllocate: true,
|
||||
preferredAccountId: accountId,
|
||||
});
|
||||
|
||||
if (forceRelogin) {
|
||||
const removedCount = await clearMail2925SessionCookies();
|
||||
await addLog(`2925:已清理 ${removedCount} 个登录相关 cookie,准备使用 ${account.email} 重新登录。`, 'info');
|
||||
if (typeof sleepWithStop === 'function') {
|
||||
await addLog('2925:清理 cookie 后等待 3 秒,再打开登录页...', 'info');
|
||||
await sleepWithStop(3000);
|
||||
}
|
||||
}
|
||||
|
||||
throwIfStopped();
|
||||
await addLog(`2925:准备打开登录页 ${MAIL2925_LOGIN_URL}(forceRelogin=${forceRelogin ? 'true' : 'false'})`, 'info');
|
||||
await reuseOrCreateTab(MAIL2925_SOURCE, forceRelogin ? MAIL2925_LOGIN_URL : MAIL2925_URL, {
|
||||
inject: MAIL2925_INJECT,
|
||||
injectSource: MAIL2925_INJECT_SOURCE,
|
||||
});
|
||||
const openedUrl = await getMail2925CurrentTabUrl();
|
||||
await addLog(`2925:打开页后当前标签地址:${openedUrl || 'unknown'}`, 'info');
|
||||
|
||||
if (forceRelogin && typeof sleepWithStop === 'function') {
|
||||
await addLog('2925:登录页已打开,等待 3 秒后开始检查输入框并执行登录...', 'info');
|
||||
await sleepWithStop(3000);
|
||||
}
|
||||
|
||||
const sendLoginMessage = typeof sendToContentScriptResilient === 'function'
|
||||
? sendToContentScriptResilient
|
||||
: async (source, message, runtimeOptions = {}) => sendToMailContentScriptResilient(
|
||||
getMail2925MailConfig(),
|
||||
message,
|
||||
{
|
||||
timeoutMs: runtimeOptions.timeoutMs,
|
||||
responseTimeoutMs: runtimeOptions.responseTimeoutMs,
|
||||
maxRecoveryAttempts: 0,
|
||||
}
|
||||
);
|
||||
|
||||
let result;
|
||||
try {
|
||||
const beforeSendUrl = await getMail2925CurrentTabUrl();
|
||||
await addLog(`2925:发送 ENSURE_MAIL2925_SESSION 前当前地址:${beforeSendUrl || 'unknown'}`, 'info');
|
||||
result = await sendLoginMessage(
|
||||
MAIL2925_SOURCE,
|
||||
{
|
||||
type: 'ENSURE_MAIL2925_SESSION',
|
||||
step: 0,
|
||||
source: 'background',
|
||||
payload: {
|
||||
email: account.email,
|
||||
password: account.password,
|
||||
forceLogin: forceRelogin,
|
||||
},
|
||||
},
|
||||
{
|
||||
timeoutMs: forceRelogin ? 30000 : 25000,
|
||||
retryDelayMs: 800,
|
||||
responseTimeoutMs: forceRelogin ? 30000 : 25000,
|
||||
logMessage: `步骤 0:2925 登录页通信异常,正在等待当前页面重新就绪后继续确认登录态...`,
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
const stopped = await stopAutoRunForMail2925LoginFailure(
|
||||
`2925:${actionLabel}失败(${getErrorMessage(err) || '20 秒内未进入收件箱'}),已按手动停止逻辑暂停自动流程。`
|
||||
);
|
||||
if (stopped) {
|
||||
throw new Error('流程已被用户停止。');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (result?.error) {
|
||||
const stopped = await stopAutoRunForMail2925LoginFailure(
|
||||
`2925:${actionLabel}失败(${result.error}),已按手动停止逻辑暂停自动流程。`
|
||||
);
|
||||
if (stopped) {
|
||||
throw new Error('流程已被用户停止。');
|
||||
}
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
if (result?.limitReached) {
|
||||
throw new Error(`${MAIL2925_LIMIT_ERROR_PREFIX}${result.limitMessage || '2925 子邮箱已达上限邮箱'}`);
|
||||
}
|
||||
|
||||
if (!result?.loggedIn) {
|
||||
const stopped = await stopAutoRunForMail2925LoginFailure(
|
||||
`2925:${actionLabel}失败(20 秒内未进入收件箱),已按手动停止逻辑暂停自动流程。`
|
||||
);
|
||||
if (stopped) {
|
||||
throw new Error('流程已被用户停止。');
|
||||
}
|
||||
throw new Error(`2925:${actionLabel}失败,当前页面仍未进入收件箱。`);
|
||||
}
|
||||
|
||||
await patchMail2925Account(account.id, {
|
||||
lastLoginAt: Date.now(),
|
||||
lastError: '',
|
||||
});
|
||||
await setState({ currentMail2925AccountId: account.id });
|
||||
broadcastDataUpdate({ currentMail2925AccountId: account.id });
|
||||
const finalUrl = await getMail2925CurrentTabUrl();
|
||||
await addLog(`2925:登录态确认成功,当前地址=${finalUrl || 'unknown'}`, 'ok');
|
||||
|
||||
return {
|
||||
account: await ensureMail2925AccountForFlow({
|
||||
allowAllocate: false,
|
||||
preferredAccountId: account.id,
|
||||
}),
|
||||
mail: getMail2925MailConfig(),
|
||||
result,
|
||||
};
|
||||
}
|
||||
|
||||
async function handleMail2925LimitReachedError(step, error) {
|
||||
const reason = getErrorMessage(error).replace(MAIL2925_LIMIT_ERROR_PREFIX, '').trim()
|
||||
|| '子邮箱已达上限邮箱';
|
||||
|
||||
@@ -833,10 +833,12 @@ async function ensureMail2925Session(payload = {}) {
|
||||
const email = String(payload?.email || '').trim();
|
||||
const password = String(payload?.password || '');
|
||||
const forceLogin = Boolean(payload?.forceLogin);
|
||||
log(`步骤 0:2925 登录态检查开始,当前地址 ${location.href},forceLogin=${forceLogin ? 'true' : 'false'}`, 'info');
|
||||
|
||||
for (let attempt = 0; attempt < 10; attempt += 1) {
|
||||
throwIfStopped();
|
||||
const currentState = detectMail2925ViewState();
|
||||
log(`步骤 0:2925 登录页状态探测,第 ${attempt + 1}/10 次,状态=${currentState.view},地址=${location.href}`, 'info');
|
||||
if (currentState.view === 'limit') {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -860,6 +862,7 @@ async function ensureMail2925Session(payload = {}) {
|
||||
}
|
||||
|
||||
const loginState = detectMail2925ViewState();
|
||||
log(`步骤 0:2925 准备执行登录,当前状态=${loginState.view},地址=${location.href}`, 'info');
|
||||
if (loginState.view === 'mailbox') {
|
||||
return {
|
||||
ok: true,
|
||||
@@ -892,9 +895,12 @@ async function ensureMail2925Session(payload = {}) {
|
||||
await sleep(150);
|
||||
fillInput(passwordInput, password);
|
||||
await sleep(200);
|
||||
log(`步骤 0:2925 已定位到登录表单,准备点击“登录”,当前地址 ${location.href}`, 'info');
|
||||
simulateClick(loginButton);
|
||||
log(`步骤 0:2925 已点击“登录”,点击后地址 ${location.href}`, 'info');
|
||||
|
||||
const finalState = await waitForMail2925View('mailbox', 20000);
|
||||
log(`步骤 0:2925 登录等待结束,状态=${finalState.view},地址=${location.href}`, 'info');
|
||||
if (finalState.view !== 'mailbox') {
|
||||
throw new Error('2925:提交账号密码后未进入收件箱。');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const mail2925Utils = require('../mail2925-utils.js');
|
||||
|
||||
test('ensureMail2925MailboxSession waits 3 seconds before and after opening login page on force relogin', async () => {
|
||||
const source = fs.readFileSync('background/mail-2925-session.js', 'utf8');
|
||||
const globalScope = {};
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundMail2925Session;`)(globalScope);
|
||||
|
||||
let currentState = {
|
||||
autoRunning: false,
|
||||
mail2925Accounts: mail2925Utils.normalizeMail2925Accounts([
|
||||
{ id: 'acc-1', email: 'acc1@2925.com', password: 'p1', enabled: true, lastUsedAt: 10 },
|
||||
]),
|
||||
currentMail2925AccountId: 'acc-1',
|
||||
};
|
||||
const events = {
|
||||
sleeps: [],
|
||||
};
|
||||
|
||||
const manager = api.createMail2925SessionManager({
|
||||
addLog: async () => {},
|
||||
broadcastDataUpdate: () => {},
|
||||
chrome: {
|
||||
cookies: {
|
||||
getAll: async () => [],
|
||||
remove: async () => ({ ok: true }),
|
||||
},
|
||||
browsingData: {
|
||||
removeCookies: async () => {},
|
||||
},
|
||||
},
|
||||
findMail2925Account: mail2925Utils.findMail2925Account,
|
||||
getMail2925AccountStatus: mail2925Utils.getMail2925AccountStatus,
|
||||
getState: async () => currentState,
|
||||
isAutoRunLockedState: () => false,
|
||||
isMail2925AccountAvailable: mail2925Utils.isMail2925AccountAvailable,
|
||||
MAIL2925_LIMIT_COOLDOWN_MS: mail2925Utils.MAIL2925_LIMIT_COOLDOWN_MS,
|
||||
normalizeMail2925Account: mail2925Utils.normalizeMail2925Account,
|
||||
normalizeMail2925Accounts: mail2925Utils.normalizeMail2925Accounts,
|
||||
pickMail2925AccountForRun: mail2925Utils.pickMail2925AccountForRun,
|
||||
requestStop: async () => {},
|
||||
reuseOrCreateTab: async () => 1,
|
||||
sendToMailContentScriptResilient: async () => ({ loggedIn: true }),
|
||||
setPersistentSettings: async (payload) => {
|
||||
currentState = { ...currentState, ...payload };
|
||||
},
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
sleepWithStop: async (ms) => {
|
||||
events.sleeps.push(ms);
|
||||
},
|
||||
throwIfStopped: () => {},
|
||||
upsertMail2925AccountInList: mail2925Utils.upsertMail2925AccountInList,
|
||||
});
|
||||
|
||||
await manager.ensureMail2925MailboxSession({
|
||||
accountId: 'acc-1',
|
||||
forceRelogin: true,
|
||||
actionLabel: '步骤 4:确认 2925 邮箱登录态',
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(events.sleeps, [3000, 3000]);
|
||||
});
|
||||
+1
-1
@@ -308,7 +308,7 @@
|
||||
补充行为:
|
||||
|
||||
- `2925` provider 会关闭 Step 4 / 8 的自动重发间隔 25 秒节流;每次“重新发送验证码”之间,会在邮箱页内部执行一轮固定 15 次的刷新轮询,不再因 OAuth 剩余时间预算而缩短。
|
||||
- 当 provider 为 `2925` 时,后台会在进入 Step 4 / Step 8 之前先确保当前 2925 账号已登录网页邮箱;如果当前标签页仍未登录,会自动补一次“清理 2925 cookie -> 使用当前账号密码重新登录”。
|
||||
- 当 provider 为 `2925` 时,后台会在进入 Step 4 / Step 8 之前先确保当前 2925 账号已登录网页邮箱;如果当前标签页仍未登录,会自动补一次“清理 2925 cookie -> 等待 3 秒 -> 打开登录页 -> 页面打开后再等待 3 秒 -> 使用当前账号密码重新登录”。
|
||||
- 普通邮箱仍会携带 `filterAfterTimestamp` 做时间窗筛选;`2925` 当前既不依赖时间窗,也不再做“新旧邮件快照差集”比较,而是每次刷新后直接遍历当前列表中的匹配邮件。
|
||||
- 自动重新发送验证码次数现在使用 sidepanel 里的单一“验证码重发”配置;普通邮箱仍按 25 秒间隔节流,Hotmail / 2925 不走这个 25 秒间隔。Step 4 若启用先请求新验证码,会先消耗一次当前步骤的自动重发次数。
|
||||
- 验证码提交重试上限当前为 15 次;页面明确拒绝验证码时,会在上限内继续拉取新验证码并重提。
|
||||
|
||||
Reference in New Issue
Block a user