.getItem('ak_nav_time') || '0', 10); let count = parseInt(localStorage.getItem('ak_nav_count') || '0', 10); if (!startTime || (now - startTime > TIME_WINDOW)) { startTime = now; count = 1; } else { count += 1; } localStorage.setItem('ak_nav_time', startTime.toString()); localStorage.setItem('ak_nav_count', count.toString()); return count; } catch (e) { return 1; } } async function checkActivityRate(navCount) { if (overlay.dataset.active === "true" || isChecking) return; isChecking = true; try { const res = await fetch(`${NODE_BACKEND_URL}/check-rate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', keepalive: true, body: JSON.stringify({ localNavCount: navCount }) }); const data = await res.json(); if (data.requireCaptcha && data.captchaData) { renderCaptcha(data.captchaData); updateStepUI(data.currentRound, data.totalRounds); showOverlay(); } } catch (e) { console.error('[Captcha] Rate check error:', e); } finally { isChecking = false; } } function handleUserNavAction() { if (overlay.dataset.active === "true") return; const currentCount = incrementAndGetLocalNavCount(); if (currentCount >= TRIGGER_THRESHOLD) { checkActivityRate(currentCount); } } async function fetchNewCaptcha() { try { refreshBtn.disabled = true; const res = await fetch(`${NODE_BACKEND_URL}/get-captcha`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include' }); const data = await res.json(); if (data.success && data.captchaData) { renderCaptcha(data.captchaData); updateStepUI(data.currentRound, data.totalRounds); } else { errorMsg.innerText = 'Failed to load puzzle. Please refresh again.'; errorMsg.style.display = 'block'; } } catch (e) { errorMsg.innerText = 'Network error. Please try again.'; errorMsg.style.display = 'block'; } finally { refreshBtn.disabled = false; } } async function submitVerify() { if (selectedIndices.size === 0) { errorMsg.innerText = 'Please select at least one square.'; errorMsg.style.display = 'block'; return; } submitBtn.disabled = true; submitBtn.innerText = 'Verifying...'; try { const res = await fetch(`${NODE_BACKEND_URL}/verify-grid`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ selectedIndices: Array.from(selectedIndices) }) }); const result = await res.json(); if (result.success) { if (result.isFinalRound) { hideOverlay(); } else { updateStepUI(result.currentRound, result.totalRounds); if (result.newCaptcha) { renderCaptcha(result.newCaptcha); } else { await fetchNewCaptcha(); } } } else { errorMsg.innerText = result.message || 'Incorrect selection. Please try again.'; errorMsg.style.display = 'block'; updateStepUI(result.currentRound || 1, result.totalRounds || totalSteps); if (result.newCaptcha) { renderCaptcha(result.newCaptcha); } else { await fetchNewCaptcha(); } } } catch (e) { errorMsg.innerText = 'Network error. Please try again.'; errorMsg.style.display = 'block'; } finally { submitBtn.disabled = false; submitBtn.innerText = 'Verify'; } } submitBtn.addEventListener('click', (e) => { e.stopPropagation(); submitVerify(); }); refreshBtn.addEventListener('click', (e) => { e.stopPropagation(); fetchNewCaptcha(); }); // 初始化时计步 1 次 handleUserNavAction(); // 全局捕获对特定标签的交互行为进行计数 document.addEventListener('click', function(e) { const target = e.target.closest('a, button, input[type="submit"]'); if (target && !overlay.contains(target)) { handleUserNavAction(); } }, { capture: true, passive: true }); })();