|
@@ -222,6 +222,7 @@ const PERSISTED_SETTING_DEFAULTS = {
|
|
|
sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME,
|
|
sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME,
|
|
|
sub2apiDefaultProxyName: DEFAULT_SUB2API_PROXY_NAME,
|
|
sub2apiDefaultProxyName: DEFAULT_SUB2API_PROXY_NAME,
|
|
|
customPassword: '',
|
|
customPassword: '',
|
|
|
|
|
+ autoRunTotalRuns: 1,
|
|
|
autoRunSkipFailures: false,
|
|
autoRunSkipFailures: false,
|
|
|
autoRunFallbackThreadIntervalMinutes: 0,
|
|
autoRunFallbackThreadIntervalMinutes: 0,
|
|
|
autoRunAddPhonePauseMinutes: DEFAULT_ADD_PHONE_PAUSE_MINUTES,
|
|
autoRunAddPhonePauseMinutes: DEFAULT_ADD_PHONE_PAUSE_MINUTES,
|
|
@@ -287,8 +288,8 @@ const DEFAULT_STATE = {
|
|
|
manualAliasUsage: {},
|
|
manualAliasUsage: {},
|
|
|
preservedAliases: {},
|
|
preservedAliases: {},
|
|
|
lastEmailTimestamp: null, // 最近一次获取到邮箱数据的运行时时间戳。
|
|
lastEmailTimestamp: null, // 最近一次获取到邮箱数据的运行时时间戳。
|
|
|
- lastSignupCode: null, // 注册验证码,运行时由程序自动读取并写入。
|
|
|
|
|
- lastLoginCode: null, // 登录验证码,运行时由程序自动读取并写入。
|
|
|
|
|
|
|
+ lastSignupCode: null, // 最近一次已尝试/成功提交的注册验证码,用于避免重复提交旧验证码。
|
|
|
|
|
+ lastLoginCode: null, // 最近一次已尝试/成功提交的登录验证码,用于避免重复提交旧验证码。
|
|
|
localhostUrl: null, // 运行时捕获到的 localhost 回调地址,不要手动预填。
|
|
localhostUrl: null, // 运行时捕获到的 localhost 回调地址,不要手动预填。
|
|
|
sub2apiSessionId: null, // SUB2API OpenAI Auth 会话 ID。
|
|
sub2apiSessionId: null, // SUB2API OpenAI Auth 会话 ID。
|
|
|
sub2apiOAuthState: null, // SUB2API OpenAI Auth state。
|
|
sub2apiOAuthState: null, // SUB2API OpenAI Auth state。
|
|
@@ -861,6 +862,8 @@ function normalizePersistentSettingValue(key, value) {
|
|
|
case 'autoRunSkipFailures':
|
|
case 'autoRunSkipFailures':
|
|
|
case 'autoRunDelayEnabled':
|
|
case 'autoRunDelayEnabled':
|
|
|
return Boolean(value);
|
|
return Boolean(value);
|
|
|
|
|
+ case 'autoRunTotalRuns':
|
|
|
|
|
+ return normalizeRunCount(value);
|
|
|
case 'autoRunFallbackThreadIntervalMinutes':
|
|
case 'autoRunFallbackThreadIntervalMinutes':
|
|
|
return normalizeAutoRunFallbackThreadIntervalMinutes(value);
|
|
return normalizeAutoRunFallbackThreadIntervalMinutes(value);
|
|
|
case 'autoRunAddPhonePauseMinutes':
|
|
case 'autoRunAddPhonePauseMinutes':
|
|
@@ -5299,6 +5302,7 @@ const autoRunController = self.MultiPageBackgroundAutoRunController?.createAutoR
|
|
|
broadcastAutoRunStatus,
|
|
broadcastAutoRunStatus,
|
|
|
broadcastStopToContentScripts,
|
|
broadcastStopToContentScripts,
|
|
|
cancelPendingCommands,
|
|
cancelPendingCommands,
|
|
|
|
|
+ cleanupAfterAddPhone: (...args) => runAddPhoneCooldownCleanup(...args),
|
|
|
chooseAddPhonePauseMinutes: async () => normalizeAddPhonePauseMinutes(
|
|
chooseAddPhonePauseMinutes: async () => normalizeAddPhonePauseMinutes(
|
|
|
(await getState()).autoRunAddPhonePauseMinutes,
|
|
(await getState()).autoRunAddPhonePauseMinutes,
|
|
|
DEFAULT_ADD_PHONE_PAUSE_MINUTES
|
|
DEFAULT_ADD_PHONE_PAUSE_MINUTES
|
|
@@ -6308,6 +6312,20 @@ function shouldClearPreLoginCookie(cookie) {
|
|
|
));
|
|
));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function shouldClosePreLoginCleanupTab(url) {
|
|
|
|
|
+ if (!url) return false;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const parsed = new URL(String(url));
|
|
|
|
|
+ const domain = normalizeCookieDomainForMatch(parsed.hostname);
|
|
|
|
|
+ if (!domain) return false;
|
|
|
|
|
+ return PRE_LOGIN_COOKIE_CLEAR_DOMAINS.some((target) => (
|
|
|
|
|
+ domain === target || domain.endsWith(`.${target}`)
|
|
|
|
|
+ ));
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function buildCookieRemovalUrl(cookie) {
|
|
function buildCookieRemovalUrl(cookie) {
|
|
|
const host = normalizeCookieDomainForMatch(cookie?.domain);
|
|
const host = normalizeCookieDomainForMatch(cookie?.domain);
|
|
|
const path = String(cookie?.path || '/').startsWith('/')
|
|
const path = String(cookie?.path || '/').startsWith('/')
|
|
@@ -6348,6 +6366,37 @@ async function collectCookiesForPreLoginCleanup() {
|
|
|
return cookies;
|
|
return cookies;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+async function closeOpenAITabsForSessionCleanup() {
|
|
|
|
|
+ if (!chrome.tabs?.query || !chrome.tabs?.remove) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const tabs = await chrome.tabs.query({});
|
|
|
|
|
+ const matchedIds = tabs
|
|
|
|
|
+ .filter((tab) => Number.isInteger(tab?.id) && shouldClosePreLoginCleanupTab(tab.url))
|
|
|
|
|
+ .map((tab) => tab.id);
|
|
|
|
|
+
|
|
|
|
|
+ if (!matchedIds.length) {
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await chrome.tabs.remove(matchedIds).catch(() => { });
|
|
|
|
|
+
|
|
|
|
|
+ const registry = { ...((await getState()).tabRegistry || {}) };
|
|
|
|
|
+ let registryChanged = false;
|
|
|
|
|
+ for (const [source, entry] of Object.entries(registry)) {
|
|
|
|
|
+ if (entry?.tabId && matchedIds.includes(entry.tabId)) {
|
|
|
|
|
+ registry[source] = null;
|
|
|
|
|
+ registryChanged = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (registryChanged) {
|
|
|
|
|
+ await setState({ tabRegistry: registry });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return matchedIds.length;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function removeCookieDirectly(cookie) {
|
|
async function removeCookieDirectly(cookie) {
|
|
|
const details = {
|
|
const details = {
|
|
|
url: buildCookieRemovalUrl(cookie),
|
|
url: buildCookieRemovalUrl(cookie),
|
|
@@ -6453,6 +6502,60 @@ async function runPreStep1SessionCleanup() {
|
|
|
await addLog(`步骤 1:已清理登录态(cookies ${removedCookieCount} 个),准备以干净状态打开官网。`, 'ok');
|
|
await addLog(`步骤 1:已清理登录态(cookies ${removedCookieCount} 个),准备以干净状态打开官网。`, 'ok');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+async function runAddPhoneCooldownCleanup(context = {}) {
|
|
|
|
|
+ const currentRun = Number(context?.currentRun) || 0;
|
|
|
|
|
+ const totalRuns = Number(context?.totalRuns) || 0;
|
|
|
|
|
+ const attemptRun = Number(context?.attemptRun) || 0;
|
|
|
|
|
+ const label = currentRun > 0 && totalRuns > 0
|
|
|
|
|
+ ? `第 ${currentRun}/${totalRuns} 轮(尝试 ${attemptRun || 1})add-phone 冷却前`
|
|
|
|
|
+ : 'add-phone 冷却前';
|
|
|
|
|
+
|
|
|
|
|
+ await addLog(`${label}:正在关闭 ChatGPT / OpenAI 页面并清理登录态...`, 'info');
|
|
|
|
|
+
|
|
|
|
|
+ const closedTabCount = await closeOpenAITabsForSessionCleanup();
|
|
|
|
|
+
|
|
|
|
|
+ let removedCookieCount = 0;
|
|
|
|
|
+ if (chrome.cookies?.getAll && chrome.cookies?.remove) {
|
|
|
|
|
+ const cookies = await collectCookiesForPreLoginCleanup();
|
|
|
|
|
+ for (const cookie of cookies) {
|
|
|
|
|
+ if (await removeCookieDirectly(cookie)) {
|
|
|
|
|
+ removedCookieCount += 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (chrome.browsingData?.remove) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await chrome.browsingData.remove({
|
|
|
|
|
+ since: 0,
|
|
|
|
|
+ origins: PRE_LOGIN_COOKIE_CLEAR_ORIGINS,
|
|
|
|
|
+ }, {
|
|
|
|
|
+ cookies: true,
|
|
|
|
|
+ localStorage: true,
|
|
|
|
|
+ cacheStorage: true,
|
|
|
|
|
+ indexedDB: true,
|
|
|
|
|
+ serviceWorkers: true,
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ await addLog(`${label}:清理站点存储失败:${getErrorMessage(err)}`, 'warn');
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (chrome.browsingData?.removeCookies) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ await chrome.browsingData.removeCookies({
|
|
|
|
|
+ since: 0,
|
|
|
|
|
+ origins: PRE_LOGIN_COOKIE_CLEAR_ORIGINS,
|
|
|
|
|
+ });
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ await addLog(`${label}:browsingData 清理 cookies 失败:${getErrorMessage(err)}`, 'warn');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ await addLog(
|
|
|
|
|
+ `${label}:已关闭 ${closedTabCount} 个 ChatGPT / OpenAI 标签页,并清理登录态(cookies ${removedCookieCount} 个)。`,
|
|
|
|
|
+ 'ok'
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// ============================================================
|
|
// ============================================================
|
|
|
// Step 7: Login and ensure the auth page reaches the login verification page
|
|
// Step 7: Login and ensure the auth page reaches the login verification page
|
|
|
// ============================================================
|
|
// ============================================================
|