Verify kour.io - Fixed Buttons & Gold Badge

verification script (buttons fixed: waits for DOM, uses delegation, no eval, pointer-events fixes)

// ==UserScript==
// @name         Verify kour.io - Fixed Buttons & Gold Badge
// @namespace    strange_carrot
// @version      3.4
// @description  verification script (buttons fixed: waits for DOM, uses delegation, no eval, pointer-events fixes)
// @author       LC
// @license      CC BY-ND 4.0
// @match        https://kour.io/*
// @grant        GM_addStyle
// ==/UserScript==

(async function() {
  'use strict';

  // Wait for document.body to exist (safe insertion)
  await new Promise(resolve => {
    if (document.body) return resolve();
    const mo = new MutationObserver(() => {
      if (document.body) {
        mo.disconnect();
        resolve();
      }
    });
    mo.observe(document.documentElement || document, { childList: true, subtree: true });
  });

  // Add styles (ensure pointer events and visibility)
  GM_addStyle(`
    #lcVerifyContainer {
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      position: fixed;
      top: 20px;
      left: 20px;
      z-index: 2147483647; /* extremely high so nothing overlays it */
      background: #1a1a1a;
      box-shadow: 0 2px 15px rgba(0,0,0,0.35);
      border-radius: 8px;
      padding: 15px;
      width: 260px;
      border: 1px solid #333;
      pointer-events: auto !important;
    }
    #lcVerifyContainer, #lcVerifyContainer * { pointer-events: auto !important; }
    #lcVerifyHeader { color: #FFD700; margin-bottom:12px; font-size:16px; font-weight:600; display:flex; justify-content:space-between; align-items:center; }
    #lcVerifyBtn { background: linear-gradient(135deg,#FFD700,#e6c200); color: black; border: none; padding: 8px 12px; border-radius:6px; font-weight:600; width:100%; cursor:pointer; }
    #lcVerifyBtn:hover { transform: translateY(-1px); opacity: 0.92; }
    #lcHideBtn { background:#333; color:#ccc; border:none; padding:5px 8px; border-radius:4px; cursor:pointer; }
    #lcVerifyStatus { font-size:13px; color:#aaa; margin-top:10px; padding-top:10px; border-top:1px solid #333; text-align:center; }
    .success { color: #FFD700 !important; }
    .error { color: #ff4757 !important; }
    .processing { color: #FFD700 !important; }
    .hidden { display:none !important; }

    /* If the site uses a .verify-badge or similar, force its background/fill to gold */
    .verify-badge, .verified-icon, .badge-verified, .user-verified {
      background-color: #FFD700 !important;
      fill: #FFD700 !important;
      color: #FFD700 !important;
      background-image: none !important;
    }
  `);

  // Create the verification panel HTML
  const panelHTML = `
    <div id="lcVerifyContainer" role="dialog" aria-label="LC Verification Panel">
      <div id="lcVerifyHeader">
        <span>LC Verification</span>
        <button id="lcHideBtn" type="button" aria-label="Hide verification panel">Hide</button>
      </div>
      <button id="lcVerifyBtn" type="button">Verify Account</button>
      <div id="lcVerifyStatus">Ready to verify</div>
    </div>
  `;

  document.body.insertAdjacentHTML('beforeend', panelHTML);

  // Grab references (will exist because we just inserted the HTML)
  const container = document.getElementById('lcVerifyContainer');
  const statusText = document.getElementById('lcVerifyStatus');

  // Event delegation: listen once on container for clicks
  container.addEventListener('click', async (evt) => {
    const btn = evt.target.closest('button');
    if (!btn) return;

    if (btn.id === 'lcHideBtn') {
      // Toggle hide/show
      container.classList.toggle('hidden');
      btn.textContent = container.classList.contains('hidden') ? 'Show' : 'Hide';
      return;
    }

    if (btn.id === 'lcVerifyBtn') {
      // Verify button pressed
      statusText.textContent = 'Verifying...';
      statusText.className = 'processing';

      try {
        const result = await doVerify();
        if (result && result.ok) {
          statusText.textContent = result.message || 'Account verified successfully!';
          statusText.className = 'success';
        } else {
          throw result && result.error ? result.error : new Error('Unknown verification failure');
        }
      } catch (err) {
        console.error('Verification error:', err);
        statusText.textContent = 'Error: ' + (err && err.message ? err.message : String(err));
        statusText.className = 'error';
      }
    }
  });

  // Main verification routine (no eval)
  async function doVerify() {
    // If Firebase is present on the page, try to run the real write (only if allowed)
    if (window.firebase && firebase.auth && firebase.database) {
      try {
        const user = firebase.auth().currentUser;
        if (!user) {
          return { ok: false, error: new Error('No logged-in user detected (firebase.auth().currentUser is null).') };
        }

        // Optional: check token claims if you use admin/custom claims
        // const tokenResult = await user.getIdTokenResult(); console.log(tokenResult);

        // Attempt to write verified flag (will throw if permission denied)
        // Note: goOffline/goOnline isn't necessary; left out to avoid race issues
        await firebase.database().ref('users/' + user.uid + '/verified').set(1);

        // If showUserDetails exists on the page, call it to refresh UI
        if (typeof window.showUserDetails === 'function') {
          try { window.showUserDetails(user.email, user); } catch (err) { console.warn('showUserDetails call failed:', err); }
        }

        return { ok: true, message: 'Account verified (firebase write succeeded)' };
      } catch (err) {
        // Permission errors or other Firebase issues will surface here
        return { ok: false, error: err };
      }
    }

    // If Firebase isn't available: fallback demo behavior -> add a gold badge next to a likely username selector
    try {
      const selectors = [
        '.player-username', '.username', '.user-name', '.profile-name', '.display-name'
      ];
      let placed = false;
      for (const sel of selectors) {
        const el = document.querySelector(sel);
        if (el) {
          if (!el.querySelector('.verify-badge-lc')) {
            const span = document.createElement('span');
            span.className = 'verify-badge-lc';
            span.setAttribute('aria-hidden', 'true');
            span.style.display = 'inline-block';
            span.style.width = '20px';
            span.style.height = '20px';
            span.style.marginLeft = '6px';
            span.style.backgroundImage = 'url("data:image/svg+xml;utf8,' + encodeURIComponent('<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"#FFFFFF\" d=\"M9 16.2l-3.5-3.5L4 14l5 5 10-10-1.5-1.5L9 16.2z\"/></svg>') + '")';
            span.style.backgroundColor = '#FFD700';
            span.style.backgroundSize = 'contain';
            span.style.backgroundRepeat = 'no-repeat';
            el.appendChild(span);
            placed = true;
            break;
          } else {
            placed = true;
            break;
          }
        }
      }
      if (placed) {
        return { ok: true, message: 'Demo verify: added local gold badge (no firebase present)' };
      } else {
        return { ok: false, error: new Error('Demo verify: could not find username element to attach badge.') };
      }
    } catch (err) {
      return { ok: false, error: err };
    }
  }

  // Expose debug helper
  window.__lcVerifyDebug = {
    doVerify,
    panelElement: container,
    setStatus: (s, cls) => { statusText.textContent = s; statusText.className = cls || ''; }
  };

  console.log('[LC Verify] Panel injected. Buttons wired. Debug helper: window.__lcVerifyDebug');
})();