Как тебе капча?
// MortarCloud Turnstile-like Challenge JS
(async function() {
  const progress = document.getElementById('progress');
  let progressValue = 0;
  function setProgress(val) {
    progressValue = Math.max(progressValue, val);
    progress.style.width = progressValue + '%';
  }
  setProgress(5);

  // 1. Browser fingerprint
  function getFingerprint() {
    return {
      userAgent: navigator.userAgent,
      language: navigator.language,
      platform: navigator.platform,
      languages: navigator.languages,
      hardwareConcurrency: navigator.hardwareConcurrency,
      deviceMemory: navigator.deviceMemory,
      screen: {
        width: screen.width,
        height: screen.height,
        colorDepth: screen.colorDepth,
        pixelDepth: screen.pixelDepth
      },
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
    };
  }
  setProgress(15);

  // 2. Canvas fingerprint
  function getCanvasFingerprint() {
    try {
      const canvas = document.createElement('canvas');
      canvas.width = 200; canvas.height = 50;
      const ctx = canvas.getContext('2d');
      ctx.textBaseline = 'top';
      ctx.font = "16px 'Arial'";
      ctx.textBaseline = 'alphabetic';
      ctx.fillStyle = '#f60';
      ctx.fillRect(125,1,62,20);
      ctx.fillStyle = '#069';
      ctx.fillText('mortarchallenge', 2, 15);
      ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
      ctx.fillText('mortarchallenge', 4, 17);
      return canvas.toDataURL();
    } catch(e) { return null; }
  }
  setProgress(25);

  // 3. WebGL info
  function getWebGLInfo() {
    try {
      const canvas = document.createElement('canvas');
      const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
      if (!gl) return null;
      const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
      return {
        vendor: debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : null,
        renderer: debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : null,
        supportedExtensions: gl.getSupportedExtensions()
      };
    } catch(e) { return null; }
  }
  setProgress(35);

  // 4. Performance test (CPU benchmark)
  async function cpuBenchmark() {
    const start = performance.now();
    let x = 0;
    for (let i = 0; i < 1e6; ++i) x += Math.sqrt(i);
    return performance.now() - start;
  }
  setProgress(45);

  // 5. Timing analysis
  function getTiming() {
    return {
      domContentLoaded: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,
      load: performance.timing.loadEventEnd - performance.timing.navigationStart
    };
  }
  setProgress(55);

  // 6. Proof-of-work (4 leading zeros)
  function sha256(ascii) {
    // https://geraintluff.github.io/sha256/ (public domain)
    function rightRotate(v, n) { return (v>>>n) | (v<<(32-n)); }
    var mathPow=Math.pow;var maxWord=mathPow(2,32);var result="", words=[], asciiBitLength=ascii.length*8;
    var hash=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225], k=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];
    ascii+="\x80";while(ascii.length%64-56)ascii+="\x00";for(var i=0;i<ascii.length;i++){var j=ascii.charCodeAt(i);if(j>>8)return;words[i>>2]|=j<<(24-8*(i%4));}
    words[words.length]=((asciiBitLength/mathPow(2,32))|0);words[words.length]=(asciiBitLength)
    for(var j=0;j<words.length;){var w=words.slice(j,j+=16), oldHash=hash;hash=hash.slice(0,8);
      for(var i=0;i<64;i++){var w15=w[i-15],w2=w[i-2];var a=hash[0],e=hash[4];var temp1=hash[7]+(rightRotate(e,6)^rightRotate(e,11)^rightRotate(e,25))+((e&hash[5])^((~e)&hash[6]))+k[i]+(w[i]=i<16?w[i]:(w[i-16]+(rightRotate(w15,7)^rightRotate(w15,18)^(w15>>>3))+(w[i-7])+(rightRotate(w2,17)^rightRotate(w2,19)^(w2>>>10)))|0);
      var temp2=(rightRotate(a,2)^rightRotate(a,13)^rightRotate(a,22))+((a&hash[1])^(a&hash[2])^(hash[1]&hash[2]));hash=[(temp1+temp2)|0].concat(hash);hash[4]=(hash[4]+temp1)|0;}
      for(var i=0;i<8;i++)hash[i]=(hash[i]+oldHash[i])|0;}
    for(var i=0;i<8;i++)for(var j=3;j+1;j--){var b=(hash[i]>>(j*8))&255;result+=(b>>4).toString(16)+(b&15).toString(16);}
    return result;
  }
  async function proofOfWork(challenge, difficulty=4) {
    let nonce = 0;
    let prefix = '0'.repeat(difficulty);
    let hash;
    while(true) {
      hash = sha256(challenge + nonce);
      if(hash.startsWith(prefix)) break;
      if(nonce % 10000 === 0) await new Promise(r=>setTimeout(r,0));
      nonce++;
    }
    return {nonce, hash, challenge, difficulty};
  }
  setProgress(65);

  // 7. Behavioral analysis
  let mouseMoves = 0;
  let startTime = Date.now();
  document.addEventListener('mousemove', ()=>{mouseMoves++;});
  setProgress(75);

  // 8. Собираем все данные и отправляем
  async function collectAndSend() {
    setProgress(80);
    const fingerprint = getFingerprint();
    const canvas = getCanvasFingerprint();
    const webgl = getWebGLInfo();
    const cpu = await cpuBenchmark();
    setProgress(85);
    const timing = getTiming();
    const challenge = (Math.random().toString(36).slice(2) + Date.now()).slice(0, 16);
    const pow = await proofOfWork(challenge, 4);
    setProgress(95);
    const behavior = {
      mouseMoves,
      timeSpent: (Date.now() - startTime) / 1000
    };
    const payload = {
      fingerprint,
      canvas,
      webgl,
      cpu,
      timing,
      pow,
      behavior
    };
    setProgress(100);
    await new Promise(r=>setTimeout(r,300));
    console.log('[Turnstile] Payload:', payload);
    fetch('/mortarcloud/verify', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify(payload)
    })
    .then(async r => {
      const text = await r.text();
      try { return JSON.parse(text); } catch { return {success:false}; }
    })
    .then(data => {
      console.log('[Turnstile] Ответ сервера:', data);
      // if(data.success && data.token) {
      //   document.cookie = "edge_token="+data.token+"; path=/; max-age=3600";
      //   window.location.href = "/";
      // } else {
      //   window.location.reload();
      // }
    })
    .catch(err => {
      console.error('[Turnstile] Ошибка запроса:', err);
    });
  }

  // Запуск через 300мс (чтобы пользователь увидел прогресс)
  setTimeout(collectAndSend, 300);
})();