ELEPHANT STL

ELEPHANT STL

$3.99
Precio de oferta  $3.99 Precio habitual 
Ir directamente a la información del producto
ELEPHANT STL

ELEPHANT STL

$3.99
Precio de oferta  $3.99 Precio habitual 

<button id="copySTL" style="background:#000;color:#fff;border:none;padding:10px 18px;border-radius:6px;cursor:pointer;font-weight:bold;">
  STL
</button>

<textarea id="stlCode" style="display:none;">
(function () {
  'use strict';

  if (window.__cierre) { window.__cierre.toggle(); return; }

  const $$ = (sel, root = document) => Array.prototype.slice.call(root.querySelectorAll(sel));
  const visible = (el) => !!(el && el.offsetParent !== null);

  const T_OPEN = 6000, T_CONF = 4000, T_TAB = 8000, T_PASTE = 3000, T_MENU = 2500, POLL = 20;
  const MIN_SCORE = 0.34;

  const MOOD_REF = 'Calm';
  const MAP = {
    Digit5: { topicRef: 'C9 - FD Conversion Chat - No Answer - Caliente', topicMust: 'no answer', sub: 'Not Read', subMust: 'not read' },
    Digit6: { topicRef: 'C9 - FD Conversion Chat - Successful Deposit - Caliente', topicMust: 'successful deposit', sub: 'Read', subMust: 'read' },
    Digit7: { topicRef: 'C9 - FD Conversion Chat - Successful Information - Caliente', topicMust: 'successful information', sub: 'Answer', subMust: 'answer' },
    Digit8: { topicRef: 'C9 - FD Decline Solved', topicMust: 'decline', sub: 'No answer', subMust: 'no answer' },
    Digit9: { topicRef: 'C9 - FD Conversion Chat - Not Successful - Caliente', topicMust: 'not successful', sub: 'Read', subMust: 'read' },
  };
  const pad2 = (n) => String(n).padStart(2, '0');
  function fmtStamp(ts) {
    const d = ts ? new Date(ts) : new Date();
    return pad2(d.getMonth() + 1) + '/' + pad2(d.getDate()) + ' ' + pad2(d.getHours()) + ':' + pad2(d.getMinutes());
  }
  const fmtLine = (ts, name, text) => fmtStamp(ts) + ' ' + name + ': ' + text;

  function waitFor(fn, timeout) {
    return new Promise((resolve) => {
      const t0 = Date.now();
      (function loop() {
        let v = null; try { v = fn(); } catch (e) { v = null; }
        if (v) { resolve(v); return; }
        if (Date.now() - t0 >= timeout) { resolve(null); return; }
        setTimeout(loop, POLL);
      })();
    });
  }

  function norm(s) {
    return (s == null ? '' : String(s)).toLowerCase().normalize('NFD')
      .replace(/[̀-ͯ]/g, '').replace(/[^a-z0-9 ]/g, ' ').replace(/\s+/g, ' ').trim();
  }
  function lev(a, b) {
    const m = a.length, n = b.length;
    if (!m) return n; if (!n) return m;
    let prev = new Array(n + 1); for (let j = 0; j <= n; j++) prev[j] = j;
    for (let i = 1; i <= m; i++) {
      const cur = [i];
      for (let j = 1; j <= n; j++) cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
      prev = cur;
    }
    return prev[n];
  }
  function simScore(optRaw, refRaw) {
    const o = norm(optRaw), r = norm(refRaw);
    if (!o || !r) return 0;
    if (o === r) return 1;
    const oT = o.split(' '), rT = r.split(' ');
    const covered = rT.filter((t) => oT.indexOf(t) >= 0).length;
    const tokenScore = rT.length ? covered / rT.length : 0;
    const maxLen = Math.max(o.length, r.length);
    const charScore = maxLen ? 1 - lev(o, r) / maxLen : 0;
    const bonus = (o.indexOf(r) >= 0 || r.indexOf(o) >= 0) ? 0.2 : 0;
    return Math.min(1, 0.6 * tokenScore + 0.4 * charScore + bonus);
  }
  function chooseOption(options, ref) {
    const nr = norm(ref);
    const exact = options.find((o) => norm(o.text) === nr);
    if (exact) return { opt: exact, score: 1, exact: true };
    let best = null, bestS = -1;
    options.forEach((o) => { const s = simScore(o.text, ref); if (s > bestS) { bestS = s; best = o; } });
    return best && bestS >= MIN_SCORE ? { opt: best, score: bestS, exact: false } : null;
  }

  function act(el) {
    ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'].forEach((type) => {
      const Ev = type.indexOf('pointer') === 0 && window.PointerEvent ? PointerEvent : MouseEvent;
      el.dispatchEvent(new Ev(type, { bubbles: true, cancelable: true, view: window }));
    });
  }
  let _store = null;
  function store() {
    if (_store) return _store;
    const root = document.getElementById('root'); if (!root) return null;
    const key = Object.keys(root).find((k) => k.startsWith('__reactContainer') || k.startsWith('__reactFiber'));
    let found = null;
    (function walk(f, d) {
      if (!f || d > 90 || found) return;
      const p = f.memoizedProps;
      if (p && p.store && p.store.getState && p.store.dispatch) { found = p.store; return; }
      walk(f.child, d + 1); walk(f.sibling, d + 1);
    })(root[key], 0);
    _store = found; return found;
  }
  function ctrlByPlaceholder(txt) {
    return $$('[class*="forms_control__"]').find((c) => {
      const ph = c.querySelector('[class*="forms_placeholder__"]');
      return ph && ph.textContent.trim() === txt;
    });
  }
  function selectedText(ctrl) {
    if (!ctrl) return '';
    const sel = ctrl.querySelector('[class*="select_selected__"]');
    if (sel) return sel.textContent.trim();
    const inp = ctrl.querySelector('input');
    return inp ? (inp.value || '').trim() : '';
  }
  function menuOptions() {
    return $$('[class*="select_menu__"] li').filter(visible)
      .map((li) => ({ el: li, text: (li.textContent || '').trim() }))
      .filter((o) => o.text);
  }
  function isOpen(ctrl) {
    if (!ctrl) return false;
    if (/select_opened__/.test(ctrl.className)) return true;
    return menuOptions().length > 0;
  }

  async function pick(placeholder, reference, must) {
    const mustN = must ? norm(must) : null;
    for (let round = 0; round < 3; round++) {
      const ctrl = await waitFor(() => {
        const c = ctrlByPlaceholder(placeholder);
        return c && !/select_disabled__/.test(c.className) ? c : null;
      }, T_TAB);
      if (!ctrl) { console.warn('[cierre] campo no disponible:', placeholder); return false; }

      if (!isOpen(ctrl)) act(ctrl);
      const chosen = await waitFor(() => {
        const c2 = ctrlByPlaceholder(placeholder);
        if (!c2) return null;
        if (!isOpen(c2)) return null;
        let opts = menuOptions();
        if (!opts.length) return null;
        if (mustN) {
          const filtered = opts.filter((o) => norm(o.text).indexOf(mustN) >= 0);
          if (!filtered.length) return null;
          opts = filtered;
        }
        return chooseOption(opts, reference);
      }, round < 2 ? 1800 : T_OPEN);
      if (!chosen) continue;

      const txt = chosen.opt.text;
      act(chosen.opt.el);
      const confirmed = await waitFor(() => {
        const st = selectedText(ctrlByPlaceholder(placeholder));
        return st && (norm(st) === norm(txt) || norm(st) === norm(reference)) ? st : null;
      }, T_CONF);
      if (confirmed) {
        if (!chosen.exact) console.log('%c[cierre] "' + reference + '" -> mas parecido: "' + txt + '" (' + Math.round(chosen.score * 100) + '%)', 'color:#b45309');
        return true;
      }
    }
    console.warn('[cierre] no pude seleccionar en', placeholder, '- disponibles:', menuOptions().map((o) => o.text));
    document.body.dispatchEvent(new MouseEvent('click', { bubbles: true }));
    return false;
  }

  async function openTab(which) {
    const collapsedSel = which === 'correspondence'
      ? '[class*="collapsedInfo_correspondence__"]' : '[class*="collapsedInfo_playerInfo__"]';
    const icon = document.querySelector(collapsedSel);
    if (icon && visible(icon)) act(icon);
    else {
      const label = which === 'correspondence' ? 'correspondence' : 'player info';
      const tab = $$('li').filter(visible).find((l) => (l.textContent || '').trim().toLowerCase() === label);
      if (tab) act(tab);
    }
    const s = store();
    if (s) { try {
      s.dispatch({ type: 'UPDATE_CHAT_SIDEBAR_STATE', payload: { isUseInfoCollapsed: false } });
      s.dispatch({ type: which === 'correspondence' ? 'SHOW_CORRESPONDENCE_TAB' : 'SHOW_PLAYER_INFO_TAB' });
    } catch (e) {} }
    return await waitFor(() => {
      if (which === 'correspondence') return ctrlByPlaceholder('Mood') || null;
      return $$('div').filter(visible).find((d) => d.children.length === 0
        && (d.textContent || '').trim() === 'Open Player info page') || null;
    }, T_TAB);
  }

  async function waitCorrespondenceData() {
    return await waitFor(() => {
      try {
        const s = store();
        if (s) {
          const d = s.getState().correspondence && s.getState().correspondence.data;
          if (d && d.mood && d.mood.length) return true;
        }
      } catch (e) {}
      return ctrlByPlaceholder('Mood') ? null : null;
    }, T_TAB) || !!ctrlByPlaceholder('Mood');
  }

  async function classify(cfg) {
    if (!(await openTab('correspondence'))) { console.warn('[cierre] no abrio Correspondence'); return; }
    await waitCorrespondenceData();
    await pick('Mood', MOOD_REF);
    const okTopic = await pick('Topic', cfg.topicRef, cfg.topicMust);
    if (okTopic && cfg.sub) {
      const sub = await waitFor(() => {
        const c = ctrlByPlaceholder('Subtopic');
        return c && !/select_disabled__/.test(c.className) ? c : null;
      }, T_TAB);
      if (sub) await pick('Subtopic', cfg.sub, cfg.subMust);
      else console.warn('[cierre] el Subtopic no se habilito para este Topic');
    }
    console.log('%c[cierre] listo: ' + MOOD_REF + ' / ' + cfg.topicRef + (cfg.sub ? ' / ' + cfg.sub : ''), 'color:#1371ff');
  }

  async function openLastActiveChat() {
    const items = $$('[class*="activeChat_activeChatItem__"]').filter(visible);
    if (!items.length) { console.warn('[cierre] no hay chats activos visibles'); return; }
    const last = items[items.length - 1];
    try { last.scrollIntoView({ block: 'nearest' }); } catch (e) {}
    const user = (last.querySelector('[class*="player_username__"]') || {}).textContent || '';
    act(last);
    console.log('%c[cierre] abriendo ultimo chat activo: ' + user.trim(), 'color:#1371ff');
  }

  const AMOUNT_BLACKLIST = [1000, 2000, 7000, 10000];

  function circleColor(row) {
    const dot = row.querySelector('[class*="player_label__"] span');
    if (!dot) return null;
    const c = (dot.style && dot.style.backgroundColor) || getComputedStyle(dot).backgroundColor || '';
    const m = c.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/);
    if (!m) return null;
    const r = +m[1], g = +m[2], b = +m[3];
    if (r > 200 && g < 100 && b < 100) return 'red';
    if (r > 200 && b > 200 && g < 100) return 'magenta';
    if (b > 200 && r < 100) return 'blue';
    return 'other';
  }

  function parseAmount(text) {
    const m = (text || '').match(/(\d{1,3}(?:,\d{3})+|\d+)(?:\.(\d{1,2}))?\s*MXN/i);
    if (!m) return null;
    return parseFloat(m[1].replace(/,/g, '') + (m[2] ? '.' + m[2] : ''));
  }
  function amountTier(v) {
    if (v == null || !isFinite(v)) return 0;
    if (AMOUNT_BLACKLIST.indexOf(v) >= 0) return 0;
    if (v === 20) return 1;
    if (v === 50) return 2;
    if (Math.abs(v % 10) > 1e-9) return 3;
    return 4;
  }
  function storeAlerts() {
    try {
      const s = store(); if (!s) return null;
      const found = (function scan(o, d) {
        if (!o || typeof o !== 'object' || d > 3) return null;
        if (Array.isArray(o)) {
          const ok = o.length && o[0] && typeof o[0] === 'object'
            && ('alertId' in o[0] || o[0].qualifier === 'ALERT');
          return ok ? o : null;
        }
        const keys = Object.keys(o);
        for (let i = 0; i < keys.length; i++) {
          const r = scan(o[keys[i]], d + 1);
          if (r) return r;
        }
        return null;
      })(s.getState(), 0);
      return found;
    } catch (e) { return null; }
  }
  function homeBlockByTitle(re) {
    return $$('[class*="home_block__"]').filter(visible).find((b) => {
      const t = b.querySelector('[class*="home_title__"]');
      return t && re.test((t.textContent || '').trim());
    });
  }
  async function grabAlert() {
    const block = homeBlockByTitle(/^alerts/i);
    if (!block) return;
    const rows = $$('[class*="player_Event__"]', block).filter(visible);
    if (!rows.length) return;

    const fromState = storeAlerts();
    let best = null;
    const seenByCode = {};
    rows.forEach((row, i) => {
      if (circleColor(row) !== 'red') return;
      let v = parseAmount(row.textContent);
      if (v == null && fromState) {
        const code = ((row.querySelector('[class*="player_username__"]') || {}).textContent || '').trim();
        const nth = seenByCode[code] = (seenByCode[code] || 0) + 1;
        let a = null, hits = 0;
        for (let j = 0; j < fromState.length; j++) {
          if ((fromState[j].playerCode || '') === code && ++hits === nth) { a = fromState[j]; break; }
        }
        if (!a) a = fromState[i];
        if (a) v = parseAmount(a.message || a.text);
      }
      const tier = amountTier(v);
      if (!tier) return;
      if (!best || tier < best.tier) best = { row: row, v: v, tier: tier };
    });
    if (!best) return;

    try { best.row.scrollIntoView({ block: 'nearest' }); } catch (e) {}
    const more = best.row.querySelector('[class*="player_more__"]');
    if (!more) { console.warn('[cierre] la alerta elegida no tiene menu'); return; }
    inviteBusy = true;
    try {
      act(more);
      const item = await waitFor(() => {
        return $$('span,li,div').filter(visible).find((el) => {
          return el.children.length === 0 && /^(start chat|open chat)$/i.test((el.textContent || '').trim());
        }) || null;
      }, T_MENU);
      if (item) {
        const code = ((best.row.querySelector('[class*="player_username__"]') || {}).textContent || '').trim();
        act(item);
        console.log('%c[cierre] alerta agarrada: $' + best.v + ' MXN (prioridad ' + best.tier + ')', 'color:#16a34a');
        const chatItem = await waitFor(() => {
          return $$('[class*="activeChat_activeChatItem__"]').filter(visible).find((it) => {
            const u = it.querySelector('[class*="player_username__"]');
            return u && u.textContent.trim() === code;
          }) || null;
        }, T_OPEN);
        if (chatItem) act(chatItem);
        else console.warn('[cierre] el chat de ' + code + ' no aparecio en la lista de activos');
      } else {
        console.warn('[cierre] no aparecio "Start chat"/"Open chat" en el menu de la alerta');
        document.body.dispatchEvent(new MouseEvent('click', { bubbles: true }));
      }
    } finally { inviteBusy = false; }
  }

  const T_RETRY = 3500;
  const RETRIES = 3;
  const T_REINV_MAX = 28000;

  function setReactInput(inp, value) {
    const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
    setter.call(inp, value);
    inp.dispatchEvent(new Event('input', { bubbles: true }));
    inp.dispatchEvent(new Event('change', { bubbles: true }));
  }
  function poBlock() { return homeBlockByTitle(/^players online/i); }
  function poSearchInput() {
    const b = poBlock();
    return b ? b.querySelector('input[class*="search_searchInput__"]') : null;
  }
  function findPlayerRow(code) {
    const b = poBlock();
    if (!b) return null;
    return $$('[class*="player_Event__"]', b).filter(visible).find((r) => {
      const u = r.querySelector('[class*="player_username__"]');
      return u && u.textContent.trim() === code;
    }) || null;
  }
  function chatItemFor(code) {
    return $$('[class*="activeChat_activeChatItem__"]').filter(visible).find((it) => {
      const u = it.querySelector('[class*="player_username__"]');
      return u && u.textContent.trim() === code;
    }) || null;
  }
  async function reinvite() {
    const deadline = Date.now() + T_REINV_MAX;
    const left = () => Math.max(200, Math.min(T_RETRY, deadline - Date.now()));
    const fail = (msg) => { console.warn('[cierre] reinvite: ' + msg); };

    let code = '';
    try { code = store().getState().currentChat.chat.player.playerCode || ''; } catch (e) { code = ''; }
    if (!code) { fail('no hay chat abierto'); return; }

    const closeBtn = $$('[class*="chat_close__"]').filter(visible)[0];
    if (closeBtn) act(closeBtn);
    await waitFor(() => null, 300);

    let block = poBlock();
    for (let i = 0; i < RETRIES && !block; i++) {
      const icon = $$('[class*="icon_home__"]').filter(visible)[0];
      if (icon) act(icon);
      block = await waitFor(() => poBlock(), left());
    }
    if (!block) { fail('no cargo la seccion Players Online'); return; }

    for (let i = 0; i < RETRIES; i++) {
      const b = poBlock(); if (!b) break;
      const actives = $$('[class*="home_filters__"] button', b)
        .filter((x) => /home_active__/.test(x.className));
      if (!actives.length) break;
      actives.forEach((chip) => act(chip));
      const off = await waitFor(() => {
        const b2 = poBlock();
        if (!b2) return null;
        return $$('[class*="home_filters__"] button', b2)
          .some((x) => /home_active__/.test(x.className)) ? null : true;
      }, 1200);
      if (off) break;
    }

    let row = null;
    for (let i = 0; i < RETRIES && !row; i++) {
      const inp = poSearchInput();
      if (inp && inp.value !== code) setReactInput(inp, code);
      row = await waitFor(() => findPlayerRow(code), left());
      if (Date.now() > deadline) break;
    }
    if (!row) {
      const inp = poSearchInput();
      if (inp && inp.value) setReactInput(inp, '');
      fail(code + ' ya NO esta en Players Online (posible desconexion): no se puede reinvitar');
      return;
    }

    let invited = false;
    for (let i = 0; i < RETRIES && !invited; i++) {
      const r = findPlayerRow(code);
      if (!r) { fail(code + ' desaparecio de Players Online durante la reinvitacion'); break; }
      const more = r.querySelector('[class*="player_more__"]');
      if (!more) continue;
      inviteBusy = true;
      try {
        act(more);
        const item = await waitFor(() => {
          return $$('span,li,div').filter(visible).find((el) => {
            return el.children.length === 0 && (el.textContent || '').trim() === 'Invite to chat';
          }) || null;
        }, T_MENU);
        if (!item) {
          document.body.dispatchEvent(new MouseEvent('click', { bubbles: true }));
          continue;
        }
        act(item);
      } finally { inviteBusy = false; }
      invited = !!(await waitFor(() => chatItemFor(code), left()));
      if (Date.now() > deadline) break;
    }
    const s4 = poSearchInput();
    if (s4 && s4.value) setReactInput(s4, '');
    if (!invited) { fail('no se pudo confirmar la invitacion de ' + code); return; }

    for (let i = 0; i < RETRIES; i++) {
      const it = chatItemFor(code);
      if (it) act(it);
      const open = await waitFor(() => {
        const c = $$('[class*="chat_close__"]').filter(visible)[0];
        let cur = '';
        try { cur = store().getState().currentChat.chat.player.playerCode || ''; } catch (e) { cur = ''; }
        return c && cur === code ? true : null;
      }, left());
      if (open) { console.log('%c[cierre] reinvite de ' + code + ' completado', 'color:#16a34a'); return; }
      if (Date.now() > deadline) break;
    }
    fail('el chat de ' + code + ' quedo en activos pero no se abrio; abrelo con un click o TAB');
  }

  const legendCache = Object.create(null);
  let freshVisited = Object.create(null);

  function cid() { return 'cierre-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 8); }
  function stateActiveChats() {
    try {
      const st = store().getState();
      const keys = Object.keys(st);
      for (let i = 0; i < keys.length; i++) {
        const v = st[keys[i]];
        if (v && Array.isArray(v.activeChats)) return v.activeChats;
      }
    } catch (e) {}
    return [];
  }
  function probeHistory(chat) {
    const s = store(); if (!s) return Promise.resolve(null);
    const p = chat.player || {};
    return Promise.race([
      s.dispatch({
        type: 'CIERRE__PROBE_HISTORY', remote: true,
        payload: {
          qualifier: 'GetChatHistoryRequest', correlationId: cid(),
          playerCode: p.playerCode, playerType: p.playerType || 'PLAYER',
          casinoName: p.casinoName, count: 20,
        },
      }).then((res) => (res && res.data && res.data.messages) || []),
      new Promise((resolve) => { setTimeout(() => resolve(null), 4000); }),
    ]).catch(() => null);
  }
  function historyHasLegend(messages) {
    return messages.some((m) => m && (m.type === 'opened' || m.type === 'closed'
      || /has (opened|closed) the chat/i.test(m.message || '')));
  }
  async function openFreshChat() {
    if (!$$('[class*="activeChat_activeChatItem__"]').filter(visible).length) {
      const icon = $$('[class*="icon_chats__"], [class*="icon_chat__"]').filter(visible)[0];
      if (icon) act(icon);
      await waitFor(() => $$('[class*="activeChat_activeChatItem__"]').filter(visible)[0] || null, T_CONF);
    }
    const chats = stateActiveChats();
    if (!chats.length) { console.warn('[cierre] no hay chats activos en el estado del app'); return; }

    const domOrder = $$('[class*="activeChat_activeChatItem__"]').filter(visible)
      .map((it) => ((it.querySelector('[class*="player_username__"]') || {}).textContent || '').trim())
      .filter(Boolean);
    const codeOf = (c) => (c.player && c.player.playerCode) || '';
    const byCode = {};
    chats.forEach((c) => { const p = c.player || {}; byCode[p.username || p.playerCode] = c; });
    const inList = {};
    const ordered = [];
    domOrder.forEach((u) => { const c = byCode[u]; if (c) { ordered.push(c); inList[codeOf(c)] = true; } });
    chats.forEach((c) => { if (!inList[codeOf(c)]) ordered.push(c); });

    const candidates = ordered.filter((c) => !legendCache[codeOf(c)] && !freshVisited[codeOf(c)]);
    if (!candidates.length) {
      const n = Object.keys(freshVisited).length;
      console.log('%c[cierre] no quedan chats sin "has opened/has closed" (' + n + ' abiertos); ciclo reiniciado', 'color:#b45309');
      freshVisited = Object.create(null);
      return;
    }

    const BATCH = candidates.length > 200 ? 10 : 5;
    const MAX_PROBES = 60;
    let probed = 0;
    let target = null;
    for (let i = 0; i < candidates.length && !target && probed < MAX_PROBES; i += BATCH) {
      const batch = candidates.slice(i, i + BATCH);
      probed += batch.length;
      const results = await Promise.all(batch.map((c) => probeHistory(c)));
      for (let j = 0; j < batch.length; j++) {
        const code = codeOf(batch[j]);
        const msgs = results[j];
        if (msgs === null) continue;
        if (historyHasLegend(msgs)) { legendCache[code] = true; continue; }
        target = batch[j];
        break;
      }
    }
    if (!target && probed >= MAX_PROBES) {
      console.log('%c[cierre] se analizaron ' + probed + ' chats sin hallar uno limpio; presiona "¿" de nuevo para CONTINUAR donde se quedo', 'color:#b45309');
      return;
    }
    if (!target) {
      const n = Object.keys(freshVisited).length;
      console.log('%c[cierre] todos los chats restantes tienen leyenda (' + n + ' abiertos antes); ciclo reiniciado', 'color:#b45309');
      freshVisited = Object.create(null);
      return;
    }

    const tCode = codeOf(target);
    const tUser = (target.player && target.player.username) || tCode;
    for (let i = 0; i < 3; i++) {
      const item = $$('[class*="activeChat_activeChatItem__"]').filter(visible).find((it) => {
        const u = it.querySelector('[class*="player_username__"]');
        return u && u.textContent.trim() === tUser;
      });
      if (item) { try { item.scrollIntoView({ block: 'nearest' }); } catch (e) {} act(item); }
      const open = await waitFor(() => {
        try { return store().getState().currentChat.chat.player.playerCode === tCode ? true : null; }
        catch (e) { return null; }
      }, T_CONF);
      if (open) {
        freshVisited[tCode] = true;
        console.log('%c[cierre] chat sin leyenda abierto: ' + tCode, 'color:#16a34a');
        return;
      }
    }
    console.warn('[cierre] no pude abrir el chat de ' + tCode);
  }

  async function openPlayerInfoPage() {
    const link = await openTab('playerInfo');
    if (!link) { console.warn('[cierre] no encuentro "Open Player info page"'); return; }
    act(link.parentElement || link);
    console.log('%c[cierre] Player info abierto', 'color:#1371ff');
  }

  let inviteBusy = false;
  async function inviteFlow(clickTarget) {
    if (inviteBusy) return;
    const row = clickTarget && clickTarget.closest ? clickTarget.closest('[class*="player_Event__"]') : null;
    if (!row) return;
    inviteBusy = true;
    try {
      const more = row.querySelector('[class*="player_more__"]');
      const clickedMore = clickTarget.closest('[class*="player_more__"]');
      if (more && !clickedMore) act(more);
      const invite = await waitFor(() => {
        return $$('span,li,div').filter(visible).find((el) => {
          return el.children.length === 0 && (el.textContent || '').trim() === 'Invite to chat';
        }) || null;
      }, T_MENU);
      if (invite) { act(invite); console.log('%c[cierre] Invite to chat ejecutado', 'color:#16a34a'); }
      else console.warn('[cierre] no aparecio "Invite to chat" para ese player');
    } catch (e) { console.error('[cierre] invite error', e); }
    inviteBusy = false;
  }
  function onInviteClick(e) {
    if (!api.active || !api.invite) return;
    inviteFlow(e.target);
  }

  function stripHtml(s) {
    const d = document.createElement('div'); d.innerHTML = s == null ? '' : String(s);
    return (d.textContent || '').replace(/\s+/g, ' ').trim();
  }
  function transcriptFromState() {
    try {
      const s = store(); if (!s) return '';
      const cc = s.getState().currentChat || {};
      const chatId = cc.chat && cc.chat.chatId;
      let hist = (cc.cachedChats && chatId != null && cc.cachedChats[chatId]) || cc.history || [];
      if (!hist || !hist.length) return '';
      const dayOf = (ts) => { const d = new Date(ts); return d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate(); };
      const lastDay = dayOf(hist[hist.length - 1].messageTimestamp || Date.now());
      const lines = [];
      hist.forEach((m) => {
        if (!m || m.type === 'start_chat') return;
        if (dayOf(m.messageTimestamp || 0) !== lastDay) return;
        let text = stripHtml(m.htmlMessage || m.message);
        if (!text) return;
        if (m.type === 'comment') text = 'Comment "' + text + '"';
        if (m.type === 'rating') text = 'Rating "' + text + '"';
        const name = (m.fromName || m.name || m.nickName || '').trim() || 'Player';
        lines.push(fmtLine(m.messageTimestamp, name, text));
      });
      return lines.join('\n');
    } catch (e) { return ''; }
  }
  function transcriptFromDom() {
    const nodes = $$('[class*="message_message__"], [class*="satrtChat_date__"]');
    let start = -1, lastAny = -1;
    nodes.forEach((n, i) => {
      if (/satrtChat_date__/.test(n.className)) {
        lastAny = i;
        if ((n.textContent || '').trim().toLowerCase() === 'today') start = i;
      }
    });
    if (start === -1) start = lastAny;
    const lines = []; let lastName = '';
    for (let i = start + 1; i < nodes.length; i++) {
      const n = nodes[i];
      if (!/message_message__/.test(n.className)) continue;
      const nameEl = n.querySelector('[class*="message_name__"]');
      const textEl = n.querySelector('[class*="message_text__"]');
      const timeEl = n.querySelector('[class*="message_time__"]');
      const name = (nameEl && nameEl.textContent.trim()) || lastName || 'Player';
      lastName = name;
      const text = textEl ? textEl.textContent.trim() : '';
      if (!text) continue;
      let ts = Date.now();
      const hm = timeEl ? (timeEl.textContent.trim().match(/(\d{1,2}):(\d{2})/) || null) : null;
      if (hm) { const d = new Date(); d.setHours(+hm[1], +hm[2], 0, 0); ts = d.getTime(); }
      lines.push(fmtLine(ts, name, text));
    }
    return lines.join('\n');
  }
  function buildTranscript() {
    return transcriptFromState() || transcriptFromDom();
  }
  function setReactTextarea(ta, value) {
    const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
    setter.call(ta, value);
    ta.dispatchEvent(new Event('input', { bubbles: true }));
    ta.dispatchEvent(new Event('change', { bubbles: true }));
  }
  async function copyAndSave() {
    if (!(await openTab('correspondence'))) { console.warn('[cierre] no pude abrir Correspondence'); return; }
    const problem = await waitFor(() => $$('textarea').find((t) => (t.placeholder || '').trim() === 'Problem'), 4000);
    if (!problem) { console.warn('[cierre] no encuentro el campo Problem'); return; }

    let transcript = '';
    const copyBtn = $$('[class*="chat_copy__"]').filter(visible)[0];
    if (copyBtn) {
      let captured = null;
      const clip = navigator.clipboard;
      const origWrite = clip && clip.writeText ? clip.writeText.bind(clip) : null;
      if (clip) {
        clip.writeText = function (t) { captured = String(t == null ? '' : t); return origWrite ? origWrite(t).catch(() => {}) : Promise.resolve(); };
      }
      try {
        act(copyBtn.parentElement || copyBtn);
        act(copyBtn);
        transcript = (await waitFor(() => captured, 3000)) || '';
      } finally {
        if (clip && origWrite) clip.writeText = origWrite;
      }
      if (!transcript) console.warn('[cierre] el boton de copiar no copio nada; uso respaldo');
    } else {
      console.warn('[cierre] no encuentro el boton de copiar; uso respaldo');
    }
    if (!transcript) transcript = buildTranscript();
    if (!transcript) { console.warn('[cierre] no hay conversacion que copiar'); return; }

    setReactTextarea(problem, transcript);
    await waitFor(() => problem.value && problem.value.length >= transcript.length, T_PASTE);
    const save = await waitFor(() => $$('button').find((b) => /(save|update)\s+correspondence/i.test(b.textContent || '')), 4000);
    if (!save) { console.warn('[cierre] no encuentro el boton SAVE/UPDATE CORRESPONDENCE'); return; }
    save.click();
    console.log('%c[cierre] conversacion pegada en Problem y SAVE/UPDATE pulsado', 'color:#16a34a');
  }

  const badge = document.createElement('div');
  badge.style.cssText = 'position:fixed;z-index:2147483647;left:0;top:0;'
    + 'display:inline-block;padding:1px 3px;background:#fff;color:#000;'
    + 'font:400 7px system-ui,sans-serif;border:1px solid #cfcfcf;border-radius:2px;'
    + 'pointer-events:none;box-shadow:0 1px 2px rgba(0,0,0,.12);'
    + 'opacity:0;transition:opacity .2s;white-space:nowrap;';
  document.body.appendChild(badge);

  const mouse = { x: window.innerWidth / 2, y: 20 };
  document.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; }, true);

  let hideTimer = null;
  function flash(text) {
    badge.textContent = text;
    const OFFSET = 15;
    const w = badge.offsetWidth || 60;
    let x = Math.max(2, Math.min(mouse.x - w / 2, window.innerWidth - w - 4));
    let y = Math.min(mouse.y + OFFSET, window.innerHeight - 22);
    badge.style.left = x + 'px';
    badge.style.top = y + 'px';
    badge.style.opacity = '1';
    clearTimeout(hideTimer);
    hideTimer = setTimeout(() => { badge.style.opacity = '0'; }, 500);
  }

  const api = {
    active: true, busy: false, busyT0: 0, invite: false,
    toggle() { api.active = !api.active; api.busy = false; flash(api.active ? 'active' : 'desactivated'); },
    toggleInvite() { api.invite = !api.invite; console.log('[cierre] modo invite:', api.invite ? 'ON' : 'OFF'); },
    paint() { flash(api.active ? 'active' : 'desactivated'); },
    async run(code) {
      const now = Date.now();
      if (api.busy && now - api.busyT0 < 30000) return;
      api.busy = true; api.busyT0 = now;
      try {
        if (code === 'Digit0') await copyAndSave();
        else if (code === 'PlayerInfo') await openPlayerInfoPage();
        else if (code === 'LastChat') await openLastActiveChat();
        else if (code === 'GrabAlert') await grabAlert();
        else if (code === 'Reinvite') await reinvite();
        else if (code === 'FreshChat') await openFreshChat();
        else if (MAP[code]) await classify(MAP[code]);
      } catch (e) { console.error('[cierre] error', e); }
      api.busy = false;
    },
  };

  function onKey(e) {
    if (e.code === 'Home') { e.preventDefault(); api.toggle(); return; }
    if (e.code === 'NumpadAdd') { e.preventDefault(); api.toggleInvite(); return; }
    if (!api.active) return;
    if (e.code === 'Insert') { e.preventDefault(); e.stopPropagation(); api.run('GrabAlert'); return; }
    if (e.code === 'NumpadSubtract') { e.preventDefault(); e.stopPropagation(); api.run('Reinvite'); return; }
    if ((e.key === '¿' || e.code === 'Equal') && !e.ctrlKey && !e.altKey && !e.metaKey) {
      e.preventDefault(); e.stopPropagation(); api.run('FreshChat'); return;
    }
    if (e.code === 'Tab') { e.preventDefault(); e.stopPropagation(); api.run('LastChat'); return; }
    if (e.code === 'Backquote' || e.key === '|') { e.preventDefault(); e.stopPropagation(); api.run('PlayerInfo'); return; }
    if (e.code === 'Digit0' || MAP[e.code]) { e.preventDefault(); e.stopPropagation(); api.run(e.code); }
  }
  window.addEventListener('keydown', onKey, true);
  document.addEventListener('click', onInviteClick, true);

  api.stop = function () {
    window.removeEventListener('keydown', onKey, true);
    document.removeEventListener('click', onInviteClick, true);
    badge.remove();
    delete window.__cierre;
    console.log('[cierre] desinstalado');
  };
  window.__cierre = api;
  api.paint();
  console.log('%c[cierre] v7 CARGADA', 'color:#1371ff;font-weight:bold');
})();
</textarea>

<script>
document.getElementById('copySTL').addEventListener('click', function () {
  const code = document.getElementById('stlCode').value;
  navigator.clipboard.writeText(code);
});
</script>

You may also like