ELEPHANT STL
<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>