修复2925号池不能自动登陆问题

This commit is contained in:
QLHazyCoder
2026-04-21 13:31:50 +08:00
parent d85f7feaef
commit 3718575bff
12 changed files with 263 additions and 25 deletions
+17 -1
View File
@@ -3,6 +3,11 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
const mail2925Utils = require('../mail2925-utils.js');
test('background mail2925 session uses /login/ as relogin entry url', () => {
const source = fs.readFileSync('background/mail-2925-session.js', 'utf8');
assert.match(source, /const MAIL2925_LOGIN_URL = 'https:\/\/2925\.com\/login\/';/);
});
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 = {};
@@ -17,6 +22,8 @@ test('ensureMail2925MailboxSession waits 3 seconds before and after opening logi
};
const events = {
sleeps: [],
openedUrls: [],
readyCalls: 0,
};
const manager = api.createMail2925SessionManager({
@@ -41,7 +48,13 @@ test('ensureMail2925MailboxSession waits 3 seconds before and after opening logi
normalizeMail2925Accounts: mail2925Utils.normalizeMail2925Accounts,
pickMail2925AccountForRun: mail2925Utils.pickMail2925AccountForRun,
requestStop: async () => {},
reuseOrCreateTab: async () => 1,
ensureContentScriptReadyOnTab: async () => {
events.readyCalls += 1;
},
reuseOrCreateTab: async (_source, url) => {
events.openedUrls.push(url);
return 1;
},
sendToMailContentScriptResilient: async () => ({ loggedIn: true }),
setPersistentSettings: async (payload) => {
currentState = { ...currentState, ...payload };
@@ -54,6 +67,7 @@ test('ensureMail2925MailboxSession waits 3 seconds before and after opening logi
},
throwIfStopped: () => {},
upsertMail2925AccountInList: mail2925Utils.upsertMail2925AccountInList,
waitForTabUrlMatch: async () => ({ url: 'https://2925.com/login/' }),
});
await manager.ensureMail2925MailboxSession({
@@ -62,5 +76,7 @@ test('ensureMail2925MailboxSession waits 3 seconds before and after opening logi
actionLabel: '步骤 4:确认 2925 邮箱登录态',
});
assert.deepStrictEqual(events.openedUrls, ['https://2925.com/login/']);
assert.deepStrictEqual(events.sleeps, [3000, 3000]);
assert.equal(events.readyCalls, 1);
});
@@ -0,0 +1,57 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('background/steps/fetch-signup-code.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep4;`)(globalScope);
test('step 4 passes a fixed 10-minute lookback window to 2925 mailbox polling', async () => {
let capturedOptions = null;
const realDateNow = Date.now;
Date.now = () => 700000;
const executor = api.createStep4Executor({
addLog: async () => {},
chrome: {
tabs: {
update: async () => {},
},
},
completeStepFromBackground: async () => {},
confirmCustomVerificationStepBypass: async () => {},
ensureMail2925MailboxSession: async () => {},
getMailConfig: () => ({
provider: '2925',
label: '2925 邮箱',
source: 'mail-2925',
url: 'https://2925.com',
}),
getTabId: async () => 1,
HOTMAIL_PROVIDER: 'hotmail-api',
isTabAlive: async () => true,
LUCKMAIL_PROVIDER: 'luckmail-api',
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
resolveVerificationStep: async (_step, _state, _mail, options) => {
capturedOptions = options;
},
reuseOrCreateTab: async () => {},
sendToContentScriptResilient: async () => ({}),
shouldUseCustomRegistrationEmail: () => false,
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
throwIfStopped: () => {},
});
try {
await executor.executeStep4({
email: 'user@example.com',
password: 'secret',
mail2925UseAccountPool: false,
});
} finally {
Date.now = realDateNow;
}
assert.equal(capturedOptions.filterAfterTimestamp, 100000);
assert.equal(capturedOptions.resendIntervalMs, 0);
});
+13 -6
View File
@@ -89,8 +89,10 @@ test('step 8 submits login verification directly without replaying step 7', asyn
]);
});
test('step 8 disables resend interval for 2925 mailbox polling', async () => {
test('step 8 uses a fixed 10-minute lookback window and disables resend interval for 2925 mailbox polling', async () => {
let capturedOptions = null;
const realDateNow = Date.now;
Date.now = () => 900000;
const executor = api.createStep8Executor({
addLog: async () => {},
@@ -130,12 +132,17 @@ test('step 8 disables resend interval for 2925 mailbox polling', async () => {
throwIfStopped: () => {},
});
await executor.executeStep8({
email: 'user@example.com',
password: 'secret',
oauthUrl: 'https://oauth.example/latest',
});
try {
await executor.executeStep8({
email: 'user@example.com',
password: 'secret',
oauthUrl: 'https://oauth.example/latest',
});
} finally {
Date.now = realDateNow;
}
assert.equal(capturedOptions.filterAfterTimestamp, 300000);
assert.equal(capturedOptions.resendIntervalMs, 0);
assert.equal(capturedOptions.targetEmail, '');
assert.equal(capturedOptions.beforeSubmit, undefined);
+96 -2
View File
@@ -56,7 +56,10 @@ function extractFunction(name) {
}
test('handlePollEmail establishes a baseline after opening from detail view and only picks mail from a later refresh', async () => {
const bundle = extractFunction('handlePollEmail');
const bundle = [
extractFunction('normalizeMinuteTimestamp'),
extractFunction('handlePollEmail'),
].join('\n');
const api = new Function(`
let state = 'detail';
@@ -162,7 +165,10 @@ return {
});
test('handlePollEmail ignores targetEmail and still tests any matching ChatGPT mail', async () => {
const bundle = extractFunction('handlePollEmail');
const bundle = [
extractFunction('normalizeMinuteTimestamp'),
extractFunction('handlePollEmail'),
].join('\n');
const api = new Function(`
let state = 'empty';
@@ -242,6 +248,94 @@ return {
assert.deepEqual(api.getReadAndDeleteCalls(), ['mail-1']);
});
test('handlePollEmail only accepts 2925 mails inside the fixed lookback window', async () => {
const bundle = [
extractFunction('normalizeMinuteTimestamp'),
extractFunction('handlePollEmail'),
].join('\n');
const api = new Function(`
let state = 'ready';
const seenCodes = new Set();
const readAndDeleteCalls = [];
const oldMail = {
id: 'mail-old',
text: 'OpenAI verification code 111111',
timestamp: 1000,
};
const windowMail = {
id: 'mail-window',
text: 'OpenAI verification code 222222',
timestamp: 301000,
};
function findMailItems() {
return state === 'ready' ? [oldMail, windowMail] : [];
}
function getMailItemId(item) {
return item.id;
}
function getCurrentMailIds(items = []) {
return new Set(items.map((item) => item.id));
}
function parseMailItemTimestamp(item) {
return item.timestamp;
}
function matchesMailFilters(text) {
return /openai|verification/i.test(String(text || ''));
}
function getMailItemText(item) {
return item.text;
}
function extractVerificationCode(text) {
const match = String(text || '').match(/(\\d{6})/);
return match ? match[1] : null;
}
async function sleep() {}
async function sleepRandom() {}
async function returnToInbox() {
return true;
}
async function refreshInbox() {}
async function openMailAndDeleteAfterRead(item) {
readAndDeleteCalls.push(item.id);
return item.text;
}
async function ensureSeenCodesSession() {}
function persistSeenCodes() {}
function log() {}
${bundle}
return {
handlePollEmail,
getReadAndDeleteCalls() {
return readAndDeleteCalls.slice();
},
};
`)();
const result = await api.handlePollEmail(4, {
senderFilters: ['openai'],
subjectFilters: ['verification'],
maxAttempts: 1,
intervalMs: 1,
filterAfterTimestamp: 120000,
});
assert.equal(result.code, '222222');
assert.deepEqual(api.getReadAndDeleteCalls(), ['mail-window']);
});
test('ensureSeenCodesSession resets tried codes only when a new verification step session starts', async () => {
const bundle = [
extractFunction('buildSeenCodeSessionKey'),
+7 -8
View File
@@ -6,7 +6,7 @@ const source = fs.readFileSync('background/verification-flow.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundVerificationFlow;`)(globalScope);
test('verification flow extends 2925 polling window', () => {
test('verification flow keeps 2925 polling cadence in the default payload', () => {
const helpers = api.createVerificationFlowHelpers({
addLog: async () => {},
chrome: { tabs: { update: async () => {} } },
@@ -37,10 +37,8 @@ test('verification flow extends 2925 polling window', () => {
const step4Payload = helpers.getVerificationPollPayload(4, { email: 'user@example.com', mailProvider: '2925' });
const step8Payload = helpers.getVerificationPollPayload(8, { email: 'user@example.com', mailProvider: '2925' });
assert.equal(step4Payload.filterAfterTimestamp, 0);
assert.equal(step4Payload.maxAttempts, 15);
assert.equal(step4Payload.intervalMs, 15000);
assert.equal(step8Payload.filterAfterTimestamp, 0);
assert.equal(step8Payload.maxAttempts, 15);
assert.equal(step8Payload.intervalMs, 15000);
});
@@ -111,7 +109,7 @@ test('verification flow runs beforeSubmit hook before filling the code', async (
]);
});
test('verification flow clears 2925 mailbox before polling and after successful login code submission', async () => {
test('verification flow skips 2925 mailbox preclear when using a fixed login mail window and still clears after success', async () => {
const mailMessages = [];
const helpers = api.createVerificationFlowHelpers({
@@ -164,15 +162,15 @@ test('verification flow clears 2925 mailbox before polling and after successful
lastLoginCode: null,
},
{ provider: '2925', label: '2925 邮箱' },
{}
{ filterAfterTimestamp: 123456 }
);
await new Promise((resolve) => setTimeout(resolve, 0));
assert.deepStrictEqual(mailMessages, ['DELETE_ALL_EMAILS', 'POLL_EMAIL', 'DELETE_ALL_EMAILS']);
assert.deepStrictEqual(mailMessages, ['POLL_EMAIL', 'DELETE_ALL_EMAILS']);
});
test('verification flow clears 2925 mailbox before polling and after successful signup code submission', async () => {
test('verification flow skips 2925 mailbox preclear when using a fixed signup mail window and still clears after success', async () => {
const mailMessages = [];
const helpers = api.createVerificationFlowHelpers({
@@ -229,13 +227,14 @@ test('verification flow clears 2925 mailbox before polling and after successful
},
{ provider: '2925', label: '2925 邮箱' },
{
filterAfterTimestamp: 123456,
requestFreshCodeFirst: false,
}
);
await new Promise((resolve) => setTimeout(resolve, 0));
assert.deepStrictEqual(mailMessages, ['DELETE_ALL_EMAILS', 'POLL_EMAIL', 'DELETE_ALL_EMAILS']);
assert.deepStrictEqual(mailMessages, ['POLL_EMAIL', 'DELETE_ALL_EMAILS']);
});
test('verification flow treats add-phone after login code submit as fatal instead of completing step 8', async () => {