async function sha256Hex(value) { if (!window.crypto?.subtle) { return '' } const encoded = new TextEncoder().encode(value) const digest = await window.crypto.subtle.digest('SHA-256', encoded) return Array.from(new Uint8Array(digest)) .map((part) => part.toString(16).padStart(2, '0')) .join('') } function readWebglVendor() { try { const canvas = document.createElement('canvas') const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl') if (!gl) { return 'no-webgl' } const extension = gl.getExtension('WEBGL_debug_renderer_info') if (!extension) { return 'webgl-hidden' } return [ gl.getParameter(extension.UNMASKED_VENDOR_WEBGL), gl.getParameter(extension.UNMASKED_RENDERER_WEBGL), ].join(':') } catch { return 'webgl-error' } } export async function buildBotFingerprint() { const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'unknown' const screenSize = typeof window.screen !== 'undefined' ? `${window.screen.width}x${window.screen.height}x${window.devicePixelRatio || 1}` : 'no-screen' const payload = [ navigator.userAgent || 'unknown-ua', navigator.language || 'unknown-language', navigator.platform || 'unknown-platform', timezone, screenSize, readWebglVendor(), ].join('|') return sha256Hex(payload) } export async function populateBotFingerprint(form) { if (!form) { return '' } const fingerprint = await buildBotFingerprint() const field = form.querySelector('input[name="_bot_fingerprint"]') if (field && fingerprint !== '') { field.value = fingerprint } return fingerprint }