One-Click Faucet Launcher — Anti-CAPTCHA settings (UA UI)

Панель кранів: автоматичний реферал, затримки, послідовне відкривання, виключення доменів, виявлення CAPTCHA. Український інтерфейс. Не вирішує reCAPTCHA/hCaptcha.

// ==UserScript==
// @name         One-Click Faucet Launcher — Anti-CAPTCHA settings (UA UI)
// @namespace    https://example.com
// @version      1.2.1
// @description  Панель кранів: автоматичний реферал, затримки, послідовне відкривання, виключення доменів, виявлення CAPTCHA. Український інтерфейс. Не вирішує reCAPTCHA/hCaptcha.
// @author       You
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_openInTab
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @run-at       document-end
// ==/UserScript==

(async function () {
  'use strict';

  // ---------- Настройки и ключи хранения ----------
  const PANEL_ID = 'oc-panel';
  const STORAGE = {
    faucets: 'oc_faucets_v1',
    wallet: 'oc_wallet_v1',
    options: 'oc_options_v121',
    layoutPos: 'oc_panel_pos_v1',
    layoutSize: 'oc_panel_size_v1',
    layoutCollapsed: 'oc_panel_collapsed_v1'
  };

  const DEFAULT_FAUCETS_TEXT = `ShortlinksFaucet|https://shortlinksfaucet.xyz/?p=instantpayingfaucets
ClaimFreeCoins BTC|https://claimfreecoins.io
beefaucet (основний)|https://beefaucet.org
kiddypay TRX|https://kiddypay.xyz/trx
is2btc BTC|https://is2btc.xyz`;

  const DEFAULT_OPTIONS = {
    referral: '[email protected]',
    delayMs: 800,
    openInSameTab: false,
    excludedDomains: [],
    oneClickMode: true
  };

  // ---------- Утилиты хранения ----------
  async function saveItem(key, value) { try { await GM_setValue(key, value); } catch(e){} }
  async function getItem(key, fallback = null) { try { const v = await GM_getValue(key); return v === undefined ? fallback : v; } catch (e) { return fallback; } }

  // ---------- Стили UI ----------
  GM_addStyle(`
    #${PANEL_ID} { position: fixed; right: 10px; bottom: 10px; width: 520px; max-width: calc(100% - 20px); background: #0b1220; color:#e6eef8; border-radius:10px;
      z-index:2147483647; box-shadow:0 10px 30px rgba(2,6,23,0.6); font-family: Arial, sans-serif; font-size:13px; resize: both; overflow:auto; min-width:220px; min-height:54px; }
    #${PANEL_ID} .oc-header { padding:8px 10px; cursor: move; display:flex; align-items:center; justify-content:space-between; user-select:none; gap:8px; }
    #${PANEL_ID} .oc-body { padding:10px; }
    #${PANEL_ID} input, #${PANEL_ID} textarea, #${PANEL_ID} select { width:100%; padding:6px; border-radius:6px; border:1px solid rgba(255,255,255,0.06); background: rgba(255,255,255,0.02); color:inherit; box-sizing:border-box; }
    #${PANEL_ID} button { padding:6px 8px; border-radius:6px; border:none; cursor:pointer; background: rgba(255,255,255,0.06); color:inherit; font-weight:700; }
    #${PANEL_ID} .faucet-item { padding:8px; border-radius:6px; margin-bottom:8px; background: rgba(255,255,255,0.02); display:flex; justify-content:space-between; align-items:center; gap:8px; }
    #${PANEL_ID} .faucet-item .meta { max-width: 68%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    #${PANEL_ID} .ua-badge { position: absolute; left: 12px; top: 10px; background: #0057b7; color: #ffd700; padding:4px 8px; border-radius:6px; font-weight:700; font-size:12px; }
    #oc-notif { position: fixed; right: 12px; bottom: 70px; z-index:2147483648; background:#bbf7d0; color:#000; padding:8px 10px; border-radius:6px; font-weight:700; box-shadow:0 6px 18px rgba(2,6,23,0.3); }
    #${PANEL_ID} .controls { display:flex; gap:6px; align-items:center; }
    #${PANEL_ID} .small { padding:4px 6px; font-size:12px; }
    #${PANEL_ID} label.small { font-size:12px; }
  `);

  // ---------- Защита от дублей панели ----------
  if (document.getElementById(PANEL_ID)) {
    const existing = document.getElementById(PANEL_ID);
    existing.style.boxShadow = '0 0 0 3px rgba(96,165,250,0.6)';
    setTimeout(()=> existing.style.boxShadow = '', 1200);
    notify('Панель вже відкрита');
    return;
  }

  // ---------- Создание DOM панели ----------
  const panel = document.createElement('div');
  panel.id = PANEL_ID;
  panel.innerHTML = `
    <div class="oc-header">
      <div style="display:flex;gap:8px;align-items:center;">
        <strong>One-Click Faucet Launcher</strong>
        <div class="ua-badge">🇺🇦 Зроблено в Україні</div>
      </div>
      <div class="controls">
        <button id="oc-collapse" class="small">—</button>
        <button id="oc-pin" class="small" title="Зберегти позицію">📌</button>
        <button id="oc-close" class="small">✕</button>
      </div>
    </div>
    <div class="oc-body">
      <div style="margin-bottom:8px;">
        <label style="font-weight:700;">Реферальний параметр (email або код):</label>
        <input id="oc-referral" placeholder="[email protected]" />
      </div>

      <div style="display:flex;gap:8px;align-items:center;margin-bottom:8px;">
        <label class="small"><input type="checkbox" id="oc-open-same" /> Відкривати в тій же вкладці (послідовно)</label>
        <label style="margin-left:auto" class="small">Затримка між відкриттями (мс): <input id="oc-delay" type="number" value="800" style="width:80px; margin-left:6px;" /></label>
      </div>

      <div style="margin-bottom:8px;">
        <label style="font-weight:700;">Виключити домени (не додавати реферальний параметр):</label>
        <input id="oc-exclude" placeholder="example.com, sub.example.com" />
        <div style="font-size:12px;color:#9aa4b2;margin-top:4px;">Введіть домени через кому (наприклад: faucetpay.io)</div>
      </div>

      <div style="margin-top:6px;">
        <label style="font-weight:700;">Адреса гаманця (для автопідстановки):</label>
        <input id="oc-wallet" placeholder="Вставте адресу гаманця" />
        <div style="display:flex;gap:8px;margin-top:6px;">
          <button id="oc-save-wallet" class="small">Зберегти адресу</button>
          <button id="oc-paste-wallet" class="small">Вставити з буфера</button>
        </div>
      </div>

      <div style="margin-top:8px;">
        <label style="font-weight:700;">Список кранів (Назва|URL, по одному на рядок):</label>
        <textarea id="oc-list" rows="6" placeholder="Назва|https://..."></textarea>
        <div style="display:flex;gap:8px;margin-top:6px;">
          <button id="oc-import" class="small">Імпортувати/Оновити список</button>
          <button id="oc-reset" class="small">Скинути</button>
          <button id="oc-open-all" class="small">Відкрити всі</button>
        </div>
      </div>

      <div style="margin-top:12px;">
        <label style="font-weight:700;">Знайдені крани:</label>
        <div id="oc-list-container" style="margin-top:8px;"></div>
      </div>

      <div style="margin-top:10px; font-size:12px; color:#9aa4b2;">
        <div>Примітка: скрипт не обходить reCAPTCHA/hCaptcha і не автоматизує їх рішення. Якщо CAPTCHA виявлена — автоматичні дії зупиняться.</div>
      </div>
    </div>
  `;
  document.body.appendChild(panel);

  // ---------- UI: notifications и layout ----------
  function notify(msg, ms = 2600) {
    try { const old = document.getElementById('oc-notif'); if (old) old.remove(); } catch(e){}
    const n = document.createElement('div'); n.id = 'oc-notif'; n.textContent = msg;
    Object.assign(n.style, { position:'fixed', right:'12px', bottom:'70px', zIndex:2147483648, background:'#bbf7d0', color:'#000', padding:'8px 10px', borderRadius:'6px', fontWeight:'700', boxShadow:'0 6px 18px rgba(2,6,23,0.3)' });
    document.body.appendChild(n);
    setTimeout(()=>{ try { n.remove(); } catch(e){} }, ms);
  }

  function saveLayoutPos(left, top) { try { localStorage.setItem(STORAGE.layoutPos, JSON.stringify({ left, top })); } catch(e){} }
  function loadLayoutPos() { try { const s = localStorage.getItem(STORAGE.layoutPos); return s ? JSON.parse(s) : null; } catch(e){ return null; } }
  function saveLayoutSize(w,h) { try { localStorage.setItem(STORAGE.layoutSize, JSON.stringify({ width:w, height:h })); } catch(e){} }
  function loadLayoutSize() { try { const s = localStorage.getItem(STORAGE.layoutSize); return s ? JSON.parse(s) : null; } catch(e){ return null; } }
  function saveCollapsed(v) { try { localStorage.setItem(STORAGE.layoutCollapsed, v ? '1' : '0'); } catch(e){} }
  function loadCollapsed() { try { return localStorage.getItem(STORAGE.layoutCollapsed) === '1'; } catch(e){ return false; } }

  // close / collapse / pin buttons
  document.getElementById('oc-close').addEventListener('click', ()=>{ panel.remove(); notify('Панель закрита'); });
  document.getElementById('oc-collapse').addEventListener('click', ()=> {
    const body = panel.querySelector('.oc-body');
    if (body.style.display === 'none') { body.style.display = ''; document.getElementById('oc-collapse').textContent = '—'; saveCollapsed(false); }
    else { body.style.display = 'none'; document.getElementById('oc-collapse').textContent = '+'; saveCollapsed(true); }
  });
  document.getElementById('oc-pin').addEventListener('click', () => {
    const r = panel.getBoundingClientRect(); saveLayoutPos(r.left, r.top); saveLayoutSize(Math.round(r.width), Math.round(r.height)); notify('Збережено позицію та розмір');
  });

  // drag support
  (function(){
    const header = panel.querySelector('.oc-header');
    let isDown=false, sx=0, sy=0, sl=0, st=0;
    header.addEventListener('mousedown', e=>{ if(e.button!==0) return; isDown=true; sx=e.clientX; sy=e.clientY; const r=panel.getBoundingClientRect(); sl=r.left; st=r.top; panel.style.right='auto'; panel.style.bottom='auto'; panel.style.position='fixed'; document.body.style.userSelect='none'; });
    window.addEventListener('mouseup', ()=>{ if(!isDown) return; isDown=false; document.body.style.userSelect=''; const r=panel.getBoundingClientRect(); saveLayoutPos(r.left, r.top); });
    window.addEventListener('mousemove', e=>{ if(!isDown) return; const dx=e.clientX-sx, dy=e.clientY-sy; panel.style.left = (sl+dx) + 'px'; panel.style.top = (st+dy) + 'px'; });
  })();

  // restore layout
  (function(){
    const sz = loadLayoutSize(); if(sz && sz.width) { panel.style.width = sz.width + 'px'; if(sz.height) panel.style.height = sz.height + 'px'; }
    const pos = loadLayoutPos(); if(pos && typeof pos.left === 'number') { panel.style.left = pos.left + 'px'; panel.style.top = pos.top + 'px'; panel.style.right='auto'; panel.style.bottom='auto'; panel.style.position='fixed'; }
    if(loadCollapsed()){ panel.querySelector('.oc-body').style.display = 'none'; panel.querySelector('#oc-collapse').textContent = '+'; }
  })();

  // ---------- Функции по списку кранов и реферальному URL ----------
  function parseFaucetsFromText(text) {
    return (text||'').split(/\r?\n/).map(l=>l.trim()).filter(l=>l && !l.startsWith('#')).map(line=>{
      const parts = line.split('|').map(p=>p.trim());
      return { name: parts[0] || parts[1] || 'Без назви', url: parts[1] || parts[0] || '' };
    }).filter(x=>x.url);
  }
  function escapeHtml(s){ return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }

  function renderList(faucets){
    const container = panel.querySelector('#oc-list-container'); container.innerHTML = '';
    faucets.forEach(f=>{
      const item = document.createElement('div'); item.className='faucet-item';
      const meta = document.createElement('div'); meta.className='meta'; meta.title=f.url;
      meta.innerHTML = `<div style="font-weight:700;">${escapeHtml(f.name)}</div>
                        <div style="font-size:12px;opacity:0.85;">${escapeHtml(f.url)}</div>`;
      const controls = document.createElement('div'); controls.style.display='flex'; controls.style.gap='6px';
      const btnOpen = document.createElement('button'); btnOpen.textContent='Відкрити'; btnOpen.addEventListener('click', ()=>openFaucet(f));
      const btnOpenBg = document.createElement('button'); btnOpenBg.textContent='Відкрити(bg)'; btnOpenBg.addEventListener('click', ()=>openFaucet(f,{active:false}));
      const btnCopy = document.createElement('button'); btnCopy.textContent='Копіювати'; btnCopy.addEventListener('click', ()=>{ navigator.clipboard.writeText(withReferral(f.url)).then(()=>notify('Скопійовано URL з рефералом')); });
      controls.appendChild(btnOpen); controls.appendChild(btnOpenBg); controls.appendChild(btnCopy);
      item.appendChild(meta); item.appendChild(controls); container.appendChild(item);
    });
  }

  function shouldExcludeDomain(url, excludedList){
    try{ const h = new URL(url).hostname; return excludedList.some(d=> h === d || h.endsWith('.'+d)); } catch(e){ return false; }
  }

  function getOptionsFromUI(){
    return {
      referral: (panel.querySelector('#oc-referral').value || DEFAULT_OPTIONS.referral).trim(),
      delayMs: Math.max(100, parseInt(panel.querySelector('#oc-delay').value||DEFAULT_OPTIONS.delayMs,10)),
      openInSameTab: !!panel.querySelector('#oc-open-same').checked,
      excluded: (panel.querySelector('#oc-exclude').value||'').split(',').map(s=>s.trim()).filter(Boolean),
      oneClickMode: true
    };
  }

  function withReferral(url){
    try{
      if(!url) return url;
      const opts = getOptionsFromUI();
      if(shouldExcludeDomain(url, opts.excluded)) return url;
      const parts = url.split('#'); const before = parts[0]; const hash = parts.length>1 ? '#' + parts.slice(1).join('#') : '';
      if (/[?&]r=.*?([&]|$)/i.test(before)) return url;
      const sep = before.includes('?') ? '&' : '?';
      return before + sep + 'r=' + encodeURIComponent(opts.referral) + hash;
    } catch(e){ return url; }
  }

  // ---------- Детектор CAPTCHA ----------
  function detectCaptchaOnPage(doc = document){
    try {
      if (doc.querySelector('iframe[src*="recaptcha"]') || doc.querySelector('.g-recaptcha') || doc.querySelector('[data-sitekey]')) return true;
      if (doc.querySelector('iframe[src*="hcaptcha"]') || doc.querySelector('.h-captcha')) return true;
      const texts = Array.from(doc.querySelectorAll('div')).map(d=>d.innerText||'').join(' ');
      if (/recaptcha|I am not a robot|Я не робот|hcaptcha/i.test(texts)) return true;
      return false;
    } catch(e){ return false; }
  }

  // ---------- Open logic (single and "open all") ----------
  async function openFaucet(faucet, opts = { active:true }){
    const options = getOptionsFromUI();
    await saveItem(STORAGE.options, options);
    const url = withReferral(faucet.url);
    try {
      if (options.openInSameTab) {
        // navigate current tab
        location.href = url;
      } else {
        try { GM_openInTab ? GM_openInTab(url, opts) : window.open(url, opts.active ? '_blank' : '_blank'); }
        catch(e){ window.open(url, '_blank'); }
      }
      notify(`Відкрито: ${faucet.name}`);
    } catch(e){ console.error(e); notify('Помилка відкриття'); }
  }

  function sleep(ms){ return new Promise(r=>setTimeout(r,ms)); }

  panel.querySelector('#oc-open-all').addEventListener('click', async ()=>{
    if(!confirm('Відкрити всі крани послідовно (повільно)?')) return;
    const faucets = parseFaucetsFromText(panel.querySelector('#oc-list').value || '');
    const opts = getOptionsFromUI();
    if (opts.openInSameTab) {
      if (faucets.length) location.href = withReferral(faucets[0].url);
    } else {
      for (let i=0;i<faucets.length;i++){
        const f = faucets[i];
        openFaucet(f, { active:true });
        await sleep(opts.delayMs);
      }
    }
  });

  // ---------- Автоподстановка кошелька при заходе на страницу крана (тільки same origin) ----------
  async function tryAutofillOnSameOrigin(url){
    try {
      const u = new URL(url);
      if (location.hostname !== u.hostname) return;
      if (detectCaptchaOnPage(document)) { notify('CAPTCHA виявлено — автопідстановка призупинена', 5000); return; }
      const wallet = await getItem(STORAGE.wallet, '');
      if (!wallet) return;
      const selectors = ["input[name*='wallet']","input[id*='wallet']","input[name*='address']","input[id*='address']",
        "input[placeholder*='wallet']","input[placeholder*='address']","input[type='text']","textarea"];
      let filled = false;
      for (const s of selectors) {
        const nodes = Array.from(document.querySelectorAll(s));
        for (const n of nodes) {
          try {
            if (!n) continue;
            const val = (n.value || '').trim();
            if (!val || val.length < 8) {
              n.focus(); n.value = wallet;
              n.dispatchEvent(new Event('input',{bubbles:true})); n.dispatchEvent(new Event('change',{bubbles:true}));
              n.style.outline = '3px solid #60a5fa';
              filled = true;
            }
          } catch(e){}
        }
        if (filled) break;
      }
      if (filled) notify('Адреса гаманця автопідставлена');
    } catch(e){}
  }

  // Если пользователь попадает на страницу крана (совпадает hostname) — попытка autofill
  (async function autoFillOnLoadIfFaucet(){
    const text = await getItem(STORAGE.faucets, DEFAULT_FAUCETS_TEXT);
    const faucets = parseFaucetsFromText(text);
    const hostMatches = faucets.filter(f => { try { return new URL(f.url).hostname === location.hostname; } catch(e){ return false; } });
    if (!hostMatches.length) return;
    const opts = await getItem(STORAGE.options, DEFAULT_OPTIONS);
    if (!opts.oneClickMode) return;
    if (detectCaptchaOnPage(document)) { notify('CAPTCHA виявлено на сторінці — автопідстановка призупинена', 6000); return; }
    await tryAutofillOnSameOrigin(location.href);
  })();

  // ---------- Кнопки: save/import/reset/paste wallet ----------
  panel.querySelector('#oc-save-wallet').addEventListener('click', async ()=>{
    const v = panel.querySelector('#oc-wallet').value.trim();
    await saveItem(STORAGE.wallet, v);
    notify('Адресу збережено');
  });
  panel.querySelector('#oc-paste-wallet').addEventListener('click', async ()=> {
    try { const txt = await navigator.clipboard.readText(); panel.querySelector('#oc-wallet').value = txt; notify('Буфер вставлено'); } catch(e){ alert('Не вдалось прочитати буфер'); }
  });

  panel.querySelector('#oc-import').addEventListener('click', async ()=>{
    const t = panel.querySelector('#oc-list').value.trim();
    if(!t) return alert('Список порожній');
    await saveItem(STORAGE.faucets, t);
    renderList(parseFaucetsFromText(t));
    notify('Список імпортовано');
  });

  panel.querySelector('#oc-reset').addEventListener('click', async ()=>{
    if(!confirm('Скинути список кранів до стандартного?')) return;
    await saveItem(STORAGE.faucets, DEFAULT_FAUCETS_TEXT);
    panel.querySelector('#oc-list').value = DEFAULT_FAUCETS_TEXT;
    renderList(parseFaucetsFromText(DEFAULT_FAUCETS_TEXT));
    notify('Список скинуто');
  });

  // ---------- Инициализация UI значений ----------
  (async function initUI(){
    const faucetsText = await getItem(STORAGE.faucets, DEFAULT_FAUCETS_TEXT);
    panel.querySelector('#oc-list').value = faucetsText;
    panel.querySelector('#oc-list').dispatchEvent(new Event('input'));
    panel.querySelector('#oc-list').value = faucetsText;
    const wallet = await getItem(STORAGE.wallet, '');
    panel.querySelector('#oc-wallet').value = wallet;
    const opts = await getItem(STORAGE.options, DEFAULT_OPTIONS);
    panel.querySelector('#oc-referral').value = opts.referral || DEFAULT_OPTIONS.referral;
    panel.querySelector('#oc-delay').value = opts.delayMs || DEFAULT_OPTIONS.delayMs;
    panel.querySelector('#oc-open-same').checked = !!opts.openInSameTab;
    panel.querySelector('#oc-exclude').value = (opts.excluded || []).join(', ');
    renderList(parseFaucetsFromText(faucetsText));
  })();

  // keyboard toggle Ctrl+Shift+F
  window.addEventListener('keydown', (e)=>{ if (e.ctrlKey && e.shiftKey && e.code === 'KeyF') { const body = panel.querySelector('.oc-body'); if (body.style.display === 'none'){ body.style.display=''; notify('Панель показана'); } else { body.style.display='none'; notify('Панель схована'); } } });

  // GM menu toggle
  try { GM_registerMenuCommand && GM_registerMenuCommand('One-Click: Показати/сховати панель', ()=>{ panel.style.display = panel.style.display === 'none' ? '' : 'none'; }); } catch(e){}

  // немного самозащиты: удаление маленьких оставшихся блоков слева (heuristic)
  try {
    Array.from(document.querySelectorAll('div')).forEach(d => {
      try { const r = d.getBoundingClientRect(); if (r.width <= 220 && r.height <= 220 && r.left < 20 && r.top > 30 && d.id !== PANEL_ID && d.id !== 'oc-notif') { if (d.innerText && d.innerText.includes('Відкрити')) d.remove(); } } catch(e){}
    });
  } catch(e){}

})();