fix(desktop-client): stop false manual-verification states and auto-revive probes after sleep/offline
Challenge detection previously regex-matched the entire page innerText, so chat transcripts or console copy containing words like 验证码/安全验证/verify flagged healthy accounts as challenge_required. Detection is now scoped to real evidence only (visible captcha vendor widgets, short modal copy, sparse interstitial pages) via a shared challenge-signals module used by the generic AI probe, doubao/kimi/qwen auth rules, and the deepseek runtime snapshot. Platform API error codes (e.g. baijiahao_challenge_required) keep working. Account health also could not recover after lid-close sleep or a network drop: offline probes misread cached console pages as expired, and expired records null out nextProbeAt so they were never re-probed. Probing is now skipped while offline (network_error backoff instead), and powerMonitor resume/unlock plus an offline-to-online transition watch schedule jittered recovery probes for all tracked accounts, including expired ones. Challenge rechecks wait 8s so transient widgets during page load no longer stick. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2379,20 +2379,72 @@ async function readDeepSeekPageSnapshot(
|
||||
}
|
||||
|
||||
const bodyText = normalize(document.body?.innerText || '')
|
||||
// 登录/验证判定不能直接对整页 innerText 匹配:聊天问题与回答里出现
|
||||
// "登录/验证码/安全验证" 这类词会把正常会话误判成需要人工处理。
|
||||
// 只认三类证据:sign_in 路由、可见弹层内的短文案、稀疏拦截页。
|
||||
const loginPhrasePattern =
|
||||
/(log in|send code|scan with wechat to login|phone number|continue with deepseek|和 deepseek 继续聊|登录|发送验证码|手机号|验证码)/i
|
||||
const challengePhrasePattern =
|
||||
/(人机验证|安全验证|请完成验证|完成下方验证|拖动滑块|滑动验证|向右滑动|安全检测|访问异常|环境异常|verification required|security check|unusual traffic|not a robot|请输入(?:图形)?验证码)/i
|
||||
const findVisibleSurfaceText = (
|
||||
selectors: string,
|
||||
pattern: RegExp,
|
||||
maxTextLength: number,
|
||||
): boolean => {
|
||||
for (const node of Array.from(document.querySelectorAll(selectors)).slice(0, 50)) {
|
||||
if (!isVisible(node)) {
|
||||
continue
|
||||
}
|
||||
const text = normalize(node.innerText || node.textContent)
|
||||
if (text && text.length <= maxTextLength && pattern.test(text)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const hasChallengeVendorWidget = (() => {
|
||||
const vendorSelectors = [
|
||||
'[class*="geetest_"]',
|
||||
'[id^="nc_"][id$="_wrapper"]',
|
||||
'.nc-container',
|
||||
'[class*="nocaptcha"]',
|
||||
'[class*="yidun_modal"]',
|
||||
'[class*="yidun_popup"]',
|
||||
'[class*="vaptcha"]',
|
||||
'[class*="tcaptcha"]',
|
||||
'#tcaptcha_iframe',
|
||||
'[class*="captcha_verify"]',
|
||||
'[class*="secsdk-captcha"]',
|
||||
'iframe[src*="captcha"]',
|
||||
'iframe[src*="geetest"]',
|
||||
'iframe[src*="turnstile"]',
|
||||
'iframe[src*="hcaptcha"]',
|
||||
'iframe[src*="recaptcha"]',
|
||||
'iframe[title*="验证"]',
|
||||
].join(',')
|
||||
for (const node of Array.from(document.querySelectorAll(vendorSelectors)).slice(0, 50)) {
|
||||
if (isVisible(node)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})()
|
||||
const loginRequired =
|
||||
window.location.pathname.includes('/sign_in') ||
|
||||
Boolean(
|
||||
bodyText &&
|
||||
/(log in|send code|scan with wechat to login|phone number|continue with deepseek|和 deepseek 继续聊|登录|发送验证码|手机号|验证码)/i.test(
|
||||
bodyText,
|
||||
),
|
||||
)
|
||||
const challengeRequired = Boolean(
|
||||
bodyText &&
|
||||
/(verification required|captcha|security check|verify|challenge|人机验证|安全验证|请完成验证|验证码|风控|环境异常)/i.test(
|
||||
bodyText,
|
||||
),
|
||||
)
|
||||
findVisibleSurfaceText(
|
||||
'[role="dialog"], [aria-modal="true"], [class*="modal"], [class*="login"], [class*="sign"], [class*="auth"]',
|
||||
loginPhrasePattern,
|
||||
300,
|
||||
) ||
|
||||
Boolean(bodyText && bodyText.length <= 600 && loginPhrasePattern.test(bodyText))
|
||||
const challengeRequired =
|
||||
hasChallengeVendorWidget ||
|
||||
findVisibleSurfaceText(
|
||||
'[role="dialog"], [aria-modal="true"], [class*="captcha"], [id*="captcha"], [class*="verify"], [class*="Verify"]',
|
||||
challengePhrasePattern,
|
||||
240,
|
||||
) ||
|
||||
Boolean(bodyText && bodyText.length <= 400 && challengePhrasePattern.test(bodyText))
|
||||
|
||||
const busyDescriptors: string[] = []
|
||||
for (const node of Array.from(
|
||||
|
||||
Reference in New Issue
Block a user