const form = document.querySelector('#access-form'); const input = document.querySelector('#access-number'); const button = document.querySelector('#enter-button'); const label = document.querySelector('#button-label'); const status = document.querySelector('#form-status'); const codeLength = Number(form.dataset.codeLength); let waiting = false; let cooldown; function announce(message, success = false) { status.textContent = message; status.classList.toggle('success', success); status.hidden = false; } input.addEventListener('input', () => { input.removeAttribute('aria-invalid'); if (!waiting) status.hidden = true; }); form.addEventListener('submit', async event => { event.preventDefault(); if (waiting) return; const code = input.value.replace(/\s/g, ''); if (!new RegExp(`^\\d{${codeLength}}$`).test(code)) { input.setAttribute('aria-invalid', 'true'); announce(`Enter the ${codeLength}-digit number from your bot DM.`); input.focus(); return; } waiting = true; button.disabled = true; form.setAttribute('aria-busy', 'true'); label.textContent = 'CHECKING YOUR NUMBER…'; status.hidden = true; let retryAfter = 0; let approved = false; try { const params = new URLSearchParams(location.search); const next = location.pathname.startsWith('/_access') ? params.get('next') || '/' : location.pathname + location.search + location.hash; const response = await fetch('/_access/login', { method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ code, next }), signal: AbortSignal.timeout(15000), }); const data = await response.json(); if (!response.ok) { retryAfter = response.status === 429 ? Number(data.retryAfter) || 60 : 0; if (response.status === 401) input.setAttribute('aria-invalid', 'true'); throw new Error(data.error || 'We could not check your number. Please try again.'); } approved = true; input.value = ''; announce('Access granted. Your next chapter is opening…', true); label.textContent = 'OPENING THE GROUND…'; location.assign(data.next); } catch (error) { announce(error.name === 'TimeoutError' || error instanceof TypeError ? 'Connection interrupted. Check your connection and try again.' : error.message); status.focus(); } finally { form.removeAttribute('aria-busy'); if (retryAfter > 0) { const deadline = Date.now() + retryAfter * 1000; const tick = () => { const seconds = Math.max(0, Math.ceil((deadline - Date.now()) / 1000)); label.textContent = `TRY AGAIN IN ${seconds}s`; if (seconds === 0) { clearInterval(cooldown); waiting = false; button.disabled = false; label.textContent = 'ENTER THE ALPHA'; } }; tick(); cooldown = setInterval(tick, 1000); } else if (!approved) { waiting = false; button.disabled = false; label.textContent = 'ENTER THE ALPHA'; } } });