Improved chat: Fixed badge overflow, fullscreen memory, Emojis, Anti-duplicate.
// ==UserScript==
// @name Autodarts Pro Chat
// @name:en Autodarts Pro Chat
// @name:de Autodarts Pro Chat
// @name:fr Autodarts Pro Chat
// @namespace http://tampermonkey.net/
// @version 4.2
// @description Chat v4.2 : Fix affichage du macaron (overflow), plein écran, Emojis, Anti-doublon.
// @description:en Improved chat: Fixed badge overflow, fullscreen memory, Emojis, Anti-duplicate.
// @description:de Verbesserter Chat: Badge-Overflow-Fix, Vollbildspeicher, Emojis, Anti-Duplikat.
// @description:fr Améliore le chat d'Autodarts avec une fenêtre redimensionnable, des messages prédéfinis, des emojis et une invisibilité totale de l'interface native.
// @author Nevro
// @license All Rights Reserved
// @copyright 2025, Nevro
// @match *://*.autodarts.io/*
// @grant GM_addStyle
// @run-at document-idle
// @icon https://autodarts.io/favicon.ico
// ==/UserScript==
(function() {
'use strict';
console.log("🚀 Autodarts Pro Chat v4.2 (Badge Fix) DÉMARRÉ !");
const UI_ID = 'ad-chat-panel-v4';
const STORAGE_KEY = 'ad_chat_settings_v4';
const PRESETS_KEY = 'ad_chat_presets_v4';
// Variables d'état
let MY_USERNAME = "Moi";
let KNOWN_PLAYERS = [];
let SENT_CACHE = [];
let WAS_FULLSCREEN = false;
let UNREAD_COUNT = 0;
const EMOJIS = ["🎯", "👏", "👍", "👎", "😂", "😎", "😭", "😡", "🍀", "🤝", "🍺", "🍻", "👋", "🔥", "👀", "🤷♂️"];
// --- CONFIG V4 ---
let STATE = {
isRetracted: false,
fontSize: 13,
normal: {
open: { top: '100px', left: '100px', width: '320px', height: '450px' },
retracted: { top: '20px', left: '20px' }
},
fullscreen: {
open: { top: '100px', left: '100px', width: '320px', height: '450px' },
retracted: { top: '20px', left: '20px' }
}
};
let PRESETS = [
"Hello! 👋",
"Good luck! 🍀",
"Nice darts! 🎯",
"Well played! 🤝",
"Thanks!",
"Non"
];
// --- CSS ---
GM_addStyle(`
/* GHOST MODE */
.ad-ghost-target { opacity: 0 !important; pointer-events: none !important; position: fixed !important; z-index: -9999 !important; width: 1px !important; height: 1px !important; bottom: 0 !important; right: 0 !important; }
button:has(path[d*="M20 2H4c"]), button:has(path[d*="L2 22l4-4"]), button[aria-label="Open chat"] { opacity: 0 !important; pointer-events: none !important; position: fixed !important; z-index: -9999 !important; width: 1px !important; height: 1px !important; visibility: hidden !important; }
.chakra-modal__content:has(button[aria-label="Send"]), .chakra-modal__content:has(button[aria-label="Insert Emoji"]) { opacity: 0 !important; pointer-events: none !important; position: fixed !important; left: -9999px !important; }
.chakra-modal__overlay { opacity: 0 !important; pointer-events: none !important; }
#chakra-toast-manager-bottom-right, .chakra-toast { opacity: 0 !important; pointer-events: none !important; }
/* PANEL */
#${UI_ID} {
position: fixed; background: #1a202c; border: 1px solid #4a5568; border-radius: 12px; z-index: 2147483647;
display: flex; flex-direction: column; color: white; font-family: 'Segoe UI', sans-serif;
box-shadow: 0 10px 30px rgba(0,0,0,0.9); resize: both;
overflow: hidden; /* C'est lui qui coupe quand c'est ouvert, normal */
min-width: 300px; min-height: 400px;
transition: opacity 0.2s, width 0.3s, height 0.3s, border-radius 0.3s;
opacity: 0; pointer-events: none; touch-action: none;
}
#${UI_ID}.visible { opacity: 1; pointer-events: all; }
#${UI_ID}.animating-pos { transition: top 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), left 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), width 0.3s, height 0.3s, border-radius 0.3s !important; }
/* RETRACTED */
#${UI_ID}.retracted {
width: 60px !important; height: 60px !important;
min-width: 0 !important; min-height: 0 !important;
border-radius: 50% !important; resize: none !important;
background: #3182ce; border: 2px solid white; cursor: pointer;
/* IMPORTANT: visible pour que le macaron sorte de la bulle ! */
overflow: visible !important;
box-shadow: 0 4px 10px rgba(0,0,0,0.5);
}
#${UI_ID} .ad-inner-content { display: flex; flex-direction: column; height: 100%; width: 100%; }
#${UI_ID}.retracted .ad-inner-content { display: none !important; }
.ad-bubble-icon { display: none; width: 100%; height: 100%; align-items: center; justify-content: center; font-size: 30px; pointer-events: none; user-select: none; position: relative; }
#${UI_ID}.retracted .ad-bubble-icon { display: flex; }
/* BADGE NOTIFICATION */
#ad-unread-badge {
display: none; position: absolute;
top: -6px; right: -6px; /* Déborde bien de la bulle */
background: #e53e3e; color: white; border-radius: 50%; font-size: 13px; font-weight: bold;
min-width: 22px; height: 22px; line-height: 20px; text-align: center; border: 2px solid white;
box-shadow: 0 2px 4px rgba(0,0,0,0.4); font-family: sans-serif; padding: 0 4px; box-sizing: border-box;
z-index: 10;
}
#ad-unread-badge.show { display: block; }
/* HEADER */
#ad-chat-header { padding: 8px 12px; background: #2d3748; display: flex; justify-content: space-between; align-items: center; font-weight: bold; border-bottom: 1px solid #4a5568; cursor: move; user-select: none; }
.ad-header-left { display: flex; align-items: center; gap: 8px; }
.ad-header-btns span { cursor: pointer; padding: 4px 6px; border-radius: 4px; font-size: 14px; margin-left: 2px; }
.ad-header-btns span:hover { background: rgba(255,255,255,0.1); color: #63b3ed; }
.ad-header-btns span.active { color: #ed8936; background: rgba(255,255,255,0.1); }
.ad-font-ctrl { font-size: 10px !important; opacity: 0.7; border: 1px solid #4a5568; margin: 0 1px; }
.ad-font-ctrl:hover { opacity: 1; border-color: #63b3ed; }
/* CONTENT */
.ad-content-area { flex: 1; overflow-y: auto; padding: 10px; background: #171923; position: relative; scroll-behavior: smooth; }
#ad-chat-view { display: flex; flex-direction: column; gap: 8px; min-height: 100%; }
#ad-presets-view { display: none; flex-direction: column; gap: 5px; }
#${UI_ID}.show-presets #ad-chat-view { display: none; }
#${UI_ID}.show-presets #ad-presets-view { display: flex; }
/* EMOJI PICKER */
#ad-emoji-picker { display: none; grid-template-columns: repeat(4, 1fr); gap: 5px; padding: 10px; background: #2d3748; position: absolute; bottom: 60px; right: 10px; border-radius: 8px; box-shadow: 0 -5px 20px rgba(0,0,0,0.5); border: 1px solid #4a5568; z-index: 10; }
#ad-emoji-picker.show { display: grid; }
.ad-emoji-item { cursor: pointer; font-size: 20px; text-align: center; padding: 5px; border-radius: 4px; }
.ad-emoji-item:hover { background: rgba(255,255,255,0.1); transform: scale(1.2); }
/* PRESETS UI */
.ad-preset-item { display: flex; align-items: center; gap: 5px; background: #2d3748; padding: 5px 10px; border-radius: 6px; border: 1px solid transparent; }
.ad-preset-item:hover { border-color: #4a5568; background: #353f52; }
.ad-preset-text { flex: 1; cursor: pointer; font-size: 13px; user-select: none; }
.ad-preset-text:active { transform: scale(0.98); color: #63b3ed; }
.ad-preset-actions { display: flex; gap: 5px; opacity: 0.5; }
.ad-preset-item:hover .ad-preset-actions { opacity: 1; }
.ad-preset-btn { cursor: pointer; font-size: 12px; padding: 2px 5px; }
.ad-preset-btn:hover { color: white; background: rgba(255,255,255,0.2); border-radius: 3px; }
.ad-preset-btn.del:hover { color: #fc8181; }
#ad-preset-add-area { margin-top: 10px; padding-top: 10px; border-top: 1px solid #4a5568; display: flex; gap: 5px; }
#ad-new-preset-input { flex: 1; background: #1a202c; border: 1px solid #4a5568; color: white; padding: 6px; border-radius: 4px; font-size: 12px; }
#ad-add-preset-btn { background: #48bb78; color: white; border: none; border-radius: 4px; cursor: pointer; padding: 0 10px; font-weight: bold; }
/* INPUT */
#ad-chat-input-area { padding: 10px; background: #2d3748; border-top: 1px solid #4a5568; display: flex; gap: 8px; align-items: center; }
#ad-chat-input { flex: 1; background: #1a202c; border: 1px solid #4a5568; color: white; padding: 10px; border-radius: 6px; font-size: 14px; outline: none; }
#ad-chat-input:focus { border-color: #63b3ed; }
#ad-emoji-btn { background: transparent; border: none; font-size: 18px; cursor: pointer; padding: 0 5px; filter: grayscale(1); transition: filter 0.2s; }
#ad-emoji-btn:hover { filter: grayscale(0); }
#ad-send-btn { background: #3182ce; color: white; border: none; padding: 0 15px; border-radius: 6px; cursor: pointer; font-weight: bold; height: 38px; }
#ad-send-btn:hover { background: #2b6cb0; }
/* MESSAGES */
.ad-msg { padding: 6px 10px; border-radius: 8px; line-height: 1.4; max-width: 85%; word-wrap: break-word; }
.ad-msg.left { align-self: flex-start; background: #2d3748; border-left: 3px solid #ed8936; }
.ad-msg.right { align-self: flex-end; background: #2b6cb0; border-right: 3px solid #63b3ed; }
.ad-msg-header { font-size: 0.85em; margin-bottom: 3px; display: block; opacity: 0.8; }
.ad-msg-time { margin-right: 6px; }
.ad-msg-user { font-weight: bold; }
.ad-msg.right .ad-msg-user { color: #bee3f8; }
`);
// --- LOGIC ---
function loadData() {
const savedState = localStorage.getItem(STORAGE_KEY) || localStorage.getItem('ad_chat_settings_v3');
if (savedState) {
try {
let parsed = JSON.parse(savedState);
if (parsed.open && !parsed.normal) {
STATE.normal = { open: parsed.open, retracted: parsed.retracted };
STATE.fullscreen = { open: parsed.open, retracted: parsed.retracted };
STATE.fontSize = parsed.fontSize || 13;
STATE.isRetracted = parsed.isRetracted || false;
} else {
STATE = { ...STATE, ...parsed };
}
} catch(e) {}
}
const savedPresets = localStorage.getItem(PRESETS_KEY) || localStorage.getItem('ad_chat_presets_v3');
if (savedPresets) { try { PRESETS = JSON.parse(savedPresets); } catch(e) {} }
WAS_FULLSCREEN = isFullscreenMode();
}
function saveState() { localStorage.setItem(STORAGE_KEY, JSON.stringify(STATE)); }
function savePresets() { localStorage.setItem(PRESETS_KEY, JSON.stringify(PRESETS)); }
function isFullscreenMode() {
return !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)
|| (window.innerWidth === screen.width && window.innerHeight === screen.height);
}
function clampPosition(el) {
if (!el) return;
const rect = el.getBoundingClientRect();
let top = parseInt(el.style.top || 0, 10);
let left = parseInt(el.style.left || 0, 10);
if (top < 0) top = 0;
if (left < 0) left = 0;
if (top + rect.height > window.innerHeight) top = Math.max(0, window.innerHeight - rect.height);
if (left + rect.width > window.innerWidth) left = Math.max(0, window.innerWidth - rect.width);
el.style.top = top + 'px';
el.style.left = left + 'px';
}
function saveCurrentPosition(element) {
if(!element) return;
const mode = isFullscreenMode() ? 'fullscreen' : 'normal';
if (STATE.isRetracted) {
STATE[mode].retracted.top = element.style.top;
STATE[mode].retracted.left = element.style.left;
} else {
STATE[mode].open.top = element.style.top;
STATE[mode].open.left = element.style.left;
STATE[mode].open.width = element.style.width;
STATE[mode].open.height = element.style.height;
}
saveState();
}
function applyState(element) {
element.classList.add('animating-pos');
const mode = isFullscreenMode() ? 'fullscreen' : 'normal';
const currentPos = STATE.isRetracted ? STATE[mode].retracted : STATE[mode].open;
if (STATE.isRetracted) {
element.classList.add('retracted');
element.style.width = ''; element.style.height = '';
} else {
element.classList.remove('retracted');
element.style.width = currentPos.width || '320px';
element.style.height = currentPos.height || '450px';
}
element.style.top = currentPos.top || '100px';
element.style.left = currentPos.left || '100px';
element.style.bottom = 'auto'; element.style.right = 'auto';
updateFontSize();
setTimeout(() => {
clampPosition(element);
element.classList.remove('animating-pos');
}, 300);
}
function updateFontSize() {
const view = document.getElementById('ad-chat-view');
if (view) view.style.fontSize = (STATE.fontSize || 13) + 'px';
}
function updateBadge() {
const badge = document.getElementById('ad-unread-badge');
if (!badge) return;
if (UNREAD_COUNT > 0) {
badge.innerText = UNREAD_COUNT > 99 ? '99+' : UNREAD_COUNT;
badge.classList.add('show');
} else {
badge.classList.remove('show');
}
}
// --- UI INIT ---
function initUI() {
if (document.getElementById(UI_ID)) return;
loadData();
const panel = document.createElement('div');
panel.id = UI_ID;
panel.innerHTML = `
<div class="ad-bubble-icon">
💬
<span id="ad-unread-badge">0</span>
</div>
<div class="ad-inner-content">
<div id="ad-chat-header">
<div class="ad-header-left">
<span>💬 Chat</span>
<span id="ad-font-minus" class="ad-font-ctrl" title="Font -">A-</span>
<span id="ad-font-plus" class="ad-font-ctrl" title="Font +">A+</span>
</div>
<div class="ad-header-btns">
<span id="ad-toggle-presets" title="Quick Messages">⚡</span>
<span id="ad-retract" title="Minimize">_</span>
<span id="ad-clear" title="Clear">🗑️</span>
</div>
</div>
<div class="ad-content-area">
<div id="ad-chat-view"></div>
<div id="ad-presets-view">
<div id="ad-presets-list"></div>
<div id="ad-preset-add-area">
<input type="text" id="ad-new-preset-input" placeholder="New message...">
<button id="ad-add-preset-btn">+</button>
</div>
</div>
</div>
<div id="ad-emoji-picker"></div>
<div id="ad-chat-input-area">
<input type="text" id="ad-chat-input" placeholder="Message..." autocomplete="off">
<button id="ad-emoji-btn" title="Emojis">😀</button>
<button id="ad-send-btn">➤</button>
</div>
</div>
`;
document.body.appendChild(panel);
applyState(panel);
renderPresets();
renderEmojis();
// LISTENERS
window.addEventListener('resize', () => {
const currentlyFullscreen = isFullscreenMode();
const el = document.getElementById(UI_ID);
if (!el) return;
if (WAS_FULLSCREEN !== currentlyFullscreen) {
WAS_FULLSCREEN = currentlyFullscreen;
applyState(el);
} else {
clampPosition(el);
saveCurrentPosition(el);
}
});
document.getElementById('ad-font-minus').onclick = (e) => { e.stopPropagation(); STATE.fontSize = Math.max(10, (STATE.fontSize||13) - 1); saveState(); updateFontSize(); };
document.getElementById('ad-font-plus').onclick = (e) => { e.stopPropagation(); STATE.fontSize = Math.min(24, (STATE.fontSize||13) + 1); saveState(); updateFontSize(); };
const btnPresets = document.getElementById('ad-toggle-presets');
btnPresets.onclick = (e) => { e.stopPropagation(); panel.classList.toggle('show-presets'); btnPresets.classList.toggle('active'); };
const addPresetBtn = document.getElementById('ad-add-preset-btn');
const newPresetInput = document.getElementById('ad-new-preset-input');
const addPreset = () => { const txt = newPresetInput.value.trim(); if (txt) { PRESETS.push(txt); savePresets(); renderPresets(); newPresetInput.value = ''; } };
addPresetBtn.onclick = addPreset; newPresetInput.addEventListener('keypress', (e) => { if(e.key === 'Enter') addPreset(); });
const toggleRetract = () => {
const rect = panel.getBoundingClientRect();
const mode = isFullscreenMode() ? 'fullscreen' : 'normal';
if (STATE.isRetracted) {
STATE[mode].retracted.top = rect.top + 'px';
STATE[mode].retracted.left = rect.left + 'px';
} else {
STATE[mode].open.top = rect.top + 'px';
STATE[mode].open.left = rect.left + 'px';
STATE[mode].open.width = panel.style.width;
STATE[mode].open.height = panel.style.height;
}
STATE.isRetracted = !STATE.isRetracted;
if (!STATE.isRetracted) {
UNREAD_COUNT = 0; // Remise à zéro du compteur quand on ouvre
updateBadge();
}
saveState();
applyState(panel);
};
document.getElementById('ad-retract').onclick = (e) => { e.stopPropagation(); toggleRetract(); };
panel.addEventListener('click', (e) => { if (panel.classList.contains('retracted')) toggleRetract(); });
panel.addEventListener('mouseup', () => { saveCurrentPosition(panel); clampPosition(panel); });
panel.addEventListener('touchend', () => { saveCurrentPosition(panel); clampPosition(panel); });
document.getElementById('ad-clear').onclick = () => document.getElementById('ad-chat-view').innerHTML = '';
const emojiBtn = document.getElementById('ad-emoji-btn');
const emojiPicker = document.getElementById('ad-emoji-picker');
emojiBtn.onclick = (e) => { e.stopPropagation(); emojiPicker.classList.toggle('show'); };
document.addEventListener('click', (e) => { if(!e.target.closest('#ad-emoji-picker') && !e.target.closest('#ad-emoji-btn')) emojiPicker.classList.remove('show'); });
const input = document.getElementById('ad-chat-input');
const btn = document.getElementById('ad-send-btn');
const triggerSend = () => { if(input.value.trim()) performSend(input.value.trim()); };
input.addEventListener('keypress', (e) => { if (e.key === 'Enter') triggerSend(); }); btn.addEventListener('click', triggerSend); btn.addEventListener('touchend', (e) => { e.preventDefault(); triggerSend(); });
makeDraggable(panel);
updateBadge();
}
function renderEmojis() {
const picker = document.getElementById('ad-emoji-picker');
picker.innerHTML = EMOJIS.map(e => `<div class="ad-emoji-item">${e}</div>`).join('');
picker.querySelectorAll('.ad-emoji-item').forEach(item => {
item.onclick = () => {
const input = document.getElementById('ad-chat-input');
input.value += item.innerText;
input.focus();
picker.classList.remove('show');
};
});
}
function renderPresets() {
const list = document.getElementById('ad-presets-list'); list.innerHTML = '';
PRESETS.forEach((txt, index) => {
const row = document.createElement('div'); row.className = 'ad-preset-item';
const textSpan = document.createElement('span'); textSpan.className = 'ad-preset-text'; textSpan.textContent = txt;
textSpan.onclick = () => {
performSend(txt);
document.getElementById(UI_ID).classList.remove('show-presets');
document.getElementById('ad-toggle-presets').classList.remove('active');
};
const actionsDiv = document.createElement('div'); actionsDiv.className = 'ad-preset-actions';
const editBtn = document.createElement('span'); editBtn.className = 'ad-preset-btn'; editBtn.textContent = '✏️';
editBtn.onclick = (e) => { e.stopPropagation(); const newTxt = prompt("Edit:", txt); if (newTxt!==null) { PRESETS[index] = newTxt.trim() || txt; savePresets(); renderPresets(); } };
const delBtn = document.createElement('span'); delBtn.className = 'ad-preset-btn del'; delBtn.textContent = '✖';
delBtn.onclick = (e) => { e.stopPropagation(); if (confirm('Delete?')) { PRESETS.splice(index, 1); savePresets(); renderPresets(); } };
actionsDiv.appendChild(editBtn); actionsDiv.appendChild(delBtn); row.appendChild(textSpan); row.appendChild(actionsDiv); list.appendChild(row);
});
}
function makeDraggable(element) {
let pos1=0,pos2=0,pos3=0,pos4=0; element.onmousedown=dragStart; element.addEventListener('touchstart',dragStart,{passive:false});
function dragStart(e) {
let target=e.target;
if(!element.classList.contains('retracted')){if(!target.closest('#ad-chat-header'))return;}
if(target.closest('.ad-header-btns')||target.closest('.ad-font-ctrl')||target.closest('#ad-chat-input-area')||target.closest('#ad-emoji-picker')||target.closest('.ad-content-area'))return;
if(e.type==='touchstart'){pos3=e.touches[0].clientX;pos4=e.touches[0].clientY;}else{e.preventDefault();pos3=e.clientX;pos4=e.clientY;}
document.onmouseup=closeDragElement;document.onmousemove=elementDrag;document.addEventListener('touchend',closeDragElement);document.addEventListener('touchmove',elementDrag,{passive:false});
}
function elementDrag(e) {
e.preventDefault();
let clientX=e.type==='touchmove'?e.touches[0].clientX:e.clientX; let clientY=e.type==='touchmove'?e.touches[0].clientY:e.clientY;
pos1=pos3-clientX;pos2=pos4-clientY;pos3=clientX;pos4=clientY;
let newTop = element.offsetTop - pos2;
let newLeft = element.offsetLeft - pos1;
if (newTop < 0) newTop = 0;
if (newLeft < 0) newLeft = 0;
if (newTop + element.offsetHeight > window.innerHeight) newTop = window.innerHeight - element.offsetHeight;
if (newLeft + element.offsetWidth > window.innerWidth) newLeft = window.innerWidth - element.offsetWidth;
element.style.top = newTop + "px";
element.style.left = newLeft + "px";
element.style.bottom = 'auto'; element.style.right = 'auto';
}
function closeDragElement() {
document.onmouseup=null;document.onmousemove=null;document.removeEventListener('touchend',closeDragElement);document.removeEventListener('touchmove',elementDrag);
saveCurrentPosition(element);
}
}
// --- SCANNER JOUEURS ---
function scanPlayers() {
const currentPlayers = [];
const selectors = [".ad-ext-player-name p", ".ad-ext-player-name", ".chakra-avatar__badge + span"];
const nameElements = document.querySelectorAll(selectors.join(", "));
nameElements.forEach(el => {
const name = el.innerText.trim();
if (name && !currentPlayers.includes(name)) currentPlayers.push(name);
});
if (JSON.stringify(currentPlayers) !== JSON.stringify(KNOWN_PLAYERS)) KNOWN_PLAYERS = currentPlayers;
const menuBtn = document.querySelector('.css-xl71ch');
if (menuBtn && menuBtn.innerText) MY_USERNAME = menuBtn.innerText.trim();
}
function addToCache(text) {
SENT_CACHE.push({ text: text, time: Date.now() });
SENT_CACHE = SENT_CACHE.filter(item => Date.now() - item.time < 3000);
}
function isMyOwnMessage(text) {
SENT_CACHE = SENT_CACHE.filter(item => Date.now() - item.time < 3000);
return SENT_CACHE.some(item => text.includes(item.text) || item.text.includes(text));
}
function setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) prototypeValueSetter.call(element, value); else valueSetter.call(element, value);
element.dispatchEvent(new Event('input', { bubbles: true })); element.dispatchEvent(new Event('change', { bubbles: true }));
}
function findChatOpenButton() {
const allButtons = Array.from(document.querySelectorAll('button'));
let target = allButtons.find(b => b.innerHTML.includes('L2 22l4-4')) || allButtons.find(b => b.innerHTML.includes('M20 2H4c')) || allButtons.find(b => (b.getAttribute('aria-label') || "").toLowerCase().includes('chat'));
if (target && !target.classList.contains('ad-ghost-target')) target.classList.add('ad-ghost-target');
return target;
}
async function performSend(text) {
const inputField = document.getElementById('ad-chat-input');
const chatBtn = findChatOpenButton();
if (!chatBtn) return;
addToCache(text);
addMessage("Moi", text, true);
chatBtn.click();
await new Promise(r => setTimeout(r, 100));
const modalInput = document.querySelector('input[placeholder="Type your message"]') || document.querySelector('.chakra-modal__body input');
if (modalInput) {
setNativeValue(modalInput, text); modalInput.focus();
await new Promise(r => setTimeout(r, 50));
let sendBtn = document.querySelector('button[aria-label="Send"]') || modalInput.closest('form')?.querySelector('button[type="submit"]');
if (sendBtn) {
if (sendBtn.disabled) { modalInput.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); setNativeValue(modalInput, text + ' '); setNativeValue(modalInput, text); }
await new Promise(r => setTimeout(r, 50));
sendBtn.click(); inputField.value = '';
setTimeout(() => document.body.dispatchEvent(new KeyboardEvent('keydown', {key: 'Escape', code: 'Escape', bubbles: true})), 50);
}
}
}
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.id && node.id.includes('1p')) return;
if (node.nodeType === 1) {
const isToast = node.classList.contains('chakra-toast') || node.querySelector('.chakra-toast');
if (isToast) {
setTimeout(() => {
const contentNode = node.querySelector('.chakra-toast__inner') || node;
const clone = contentNode.cloneNode(true);
let hiddenName = "";
const initialsEl = clone.querySelector('.chakra-avatar__initials');
const imgEl = clone.querySelector('img');
if (initialsEl && initialsEl.getAttribute('aria-label')) hiddenName = initialsEl.getAttribute('aria-label');
else if (imgEl && imgEl.alt) hiddenName = imgEl.alt;
clone.querySelectorAll('.chakra-avatar__initials, .chakra-avatar, svg, img').forEach(el => el.remove());
let rawText = clone.innerText.trim();
if (!rawText) return;
if (isMyOwnMessage(rawText)) return;
let user = "Adversaire";
let text = rawText;
const sortedPlayers = [...KNOWN_PLAYERS].sort((a, b) => b.length - a.length);
let foundInText = false;
for (let player of sortedPlayers) {
if (rawText.toLowerCase().startsWith(player.toLowerCase())) {
user = player;
text = rawText.substring(player.length).trim();
foundInText = true;
break;
}
}
if (!foundInText && hiddenName) {
for (let player of sortedPlayers) {
if (hiddenName.toLowerCase().includes(player.toLowerCase()) || player.toLowerCase().includes(hiddenName.toLowerCase())) {
user = player;
break;
}
}
}
if (user === "Adversaire") {
const titleEl = contentNode.querySelector('.chakra-alert__title');
if (titleEl) user = titleEl.innerText.trim();
}
if (user.toLowerCase() === MY_USERNAME.toLowerCase()) user = "Moi";
if (text.includes("1Password") || text.includes("menu 1Password")) return;
if (text.length > 0 && !text.includes("available")) {
addMessage(user, text, (user === "Moi"));
}
}, 50);
}
}
});
});
});
function addMessage(user, text, isMe) {
const list = document.getElementById('ad-chat-view'); if (!list) return;
const lastMsg = list.lastElementChild;
if (lastMsg && lastMsg.innerText.includes(text) && (lastMsg.innerText.includes(user) || (isMe && lastMsg.classList.contains('right')))) return;
const time = new Date().toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}).replace(':', 'h');
const div = document.createElement('div');
div.className = `ad-msg ${isMe ? 'right' : 'left'}`;
div.innerHTML = `<div class="ad-msg-header"><span class="ad-msg-time">${time}</span><span class="ad-msg-user">${user}</span></div><div>${text}</div>`;
list.appendChild(div);
requestAnimationFrame(() => {
list.scrollTop = list.scrollHeight;
const area = document.querySelector('.ad-content-area');
if(area) area.scrollTop = area.scrollHeight;
});
if (!isMe && STATE.isRetracted) {
UNREAD_COUNT++;
updateBadge();
}
}
setInterval(() => {
initUI();
scanPlayers();
const panel = document.getElementById(UI_ID);
if (window.location.href.includes('/matches/') && !window.location.href.includes('/history')) {
panel.classList.add('visible');
findChatOpenButton();
if (!document.body.getAttribute('data-ad-chat-active')) {
observer.observe(document.body, { childList: true, subtree: true });
document.body.setAttribute('data-ad-chat-active', 'true');
}
} else {
panel.classList.remove('visible');
}
}, 1000);
})();