Blacklist & Warning for AutoDarts

Search for player names and display a warning message if found.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Blacklist & Warning for AutoDarts 
// @namespace    Owl
// @version      0.1
// @description  Search for player names and display a warning message if found.
// @match        https://play.autodarts.io/*
// @run-at       document-idle
// @license      MIT
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log("[Blacklist Script] Starting (EN) - File Import/Export...");

    let blacklistedPlayers = JSON.parse(localStorage.getItem('blacklistedPlayers')) || [];

    function savePlayerList() {
        localStorage.setItem('blacklistedPlayers', JSON.stringify(blacklistedPlayers));
    }

    // Popup variables
    let popupVisible = false;
    let popupContainer = null;

    // Menu item ID
    const MENU_ITEM_ID = 'autodarts-blacklist-menu-item';

    // --------------------------------------------------
    // A) DRAG LOGIC
    // --------------------------------------------------
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;
    let hasConvertedCenter = false;

    function onDragMouseDown(e) {
        e.preventDefault();
        if (!hasConvertedCenter) {
            convertCenterToAbsolutePosition(popupContainer);
            hasConvertedCenter = true;
        }
        isDragging = true;
        offsetX = popupContainer.offsetLeft - e.clientX;
        offsetY = popupContainer.offsetTop - e.clientY;
    }

    function onDragMouseMove(e) {
        if (!isDragging) return;
        e.preventDefault();
        const newLeft = e.clientX + offsetX;
        const newTop = e.clientY + offsetY;
        popupContainer.style.left = newLeft + 'px';
        popupContainer.style.top = newTop + 'px';
    }

    function onDragMouseUp() {
        isDragging = false;
    }

    function convertCenterToAbsolutePosition(elem) {
        const rect = elem.getBoundingClientRect();
        elem.style.left = rect.left + 'px';
        elem.style.top = rect.top + 'px';
        elem.style.transform = 'none';
    }

    // --------------------------------------------------
    // B) CREATE POPUP
    // --------------------------------------------------
    function createPopup() {
        if (popupContainer) return;

        popupContainer = document.createElement('div');
        popupContainer.id = 'autodarts-blacklist-popup';

        Object.assign(popupContainer.style, {
            position: 'fixed',
            left: '50%',
            top: '50%',
            transform: 'translate(-50%, -50%)',

            padding: '20px',
            backgroundColor: '#1A202C', // dark gray
            color: '#E2E8F0',
            border: '1px solid #2D3748',
            borderRadius: '8px',
            boxShadow: '0 0 10px rgba(0,0,0,0.5)',
            zIndex: '99999',
            fontFamily: 'sans-serif',

            width: 'auto',
            minWidth: '300px',
            maxWidth: '80vw',
            maxHeight: '80vh',
            overflowY: 'auto',
            display: 'none'
        });

        // Drag bar
        const dragBar = document.createElement('div');
        dragBar.style.height = '20px';
        dragBar.style.cursor = 'move';
        dragBar.style.marginBottom = '10px';
        popupContainer.appendChild(dragBar);

        // Close "X" button
        const closeXButton = document.createElement('button');
        closeXButton.textContent = '×';
        Object.assign(closeXButton.style, {
            position: 'absolute',
            top: '4px',
            right: '8px',
            background: 'transparent',
            border: 'none',
            color: '#E2E8F0',
            fontSize: '20px',
            lineHeight: '20px',
            cursor: 'pointer'
        });
        closeXButton.addEventListener('click', () => {
            togglePopup(false);
        });
        popupContainer.appendChild(closeXButton);

        // Title
        const title = document.createElement('h2');
        title.textContent = 'BLACKLIST';
        title.style.marginTop = '0';
        title.style.fontSize = '1.4rem';
        title.style.fontWeight = 'bold';
        popupContainer.appendChild(title);

        // Input area
        const inputWrapper = document.createElement('div');
        inputWrapper.style.display = 'flex';
        inputWrapper.style.marginBottom = '10px';
        popupContainer.appendChild(inputWrapper);

        const input = document.createElement('input');
        input.type = 'text';
        input.placeholder = 'Enter new player name';
        Object.assign(input.style, {
            flex: '1',
            marginRight: '5px',
            padding: '4px 8px'
        });
        inputWrapper.appendChild(input);

        const addButton = document.createElement('button');
        addButton.textContent = 'Add';
        Object.assign(addButton.style, {
            padding: '4px 8px',
            cursor: 'pointer',
            backgroundColor: 'rgba(59, 182, 43, 1)',
            color: '#fff',
            border: 'none',
            borderRadius: '4px'
        });
        inputWrapper.appendChild(addButton);

        // UL list
        const listElement = document.createElement('ul');
        listElement.style.listStyle = 'none';
        listElement.style.paddingLeft = '0';
        popupContainer.appendChild(listElement);

        // Add function
        function addName() {
            const name = input.value.trim();
            const uppercaseName = name.toUpperCase();
            if (uppercaseName && !blacklistedPlayers.includes(uppercaseName)) {
                blacklistedPlayers.push(uppercaseName);
                savePlayerList();
                updateList(listElement);
            }
            input.value = '';
        }
        addButton.addEventListener('click', addName);
        input.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault();
                addName();
            }
        });

        // Show the list initially
        updateList(listElement);

        // IMPORT/EXPORT section
        const importExportWrapper = document.createElement('div');
        importExportWrapper.style.borderTop = '1px solid #2D3748';
        importExportWrapper.style.paddingTop = '10px';
        importExportWrapper.style.marginTop = '10px';
        popupContainer.appendChild(importExportWrapper);

        // Export button
        const exportButton = document.createElement('button');
        exportButton.textContent = 'Export';
        Object.assign(exportButton.style, {
            padding: '4px 8px',
            cursor: 'pointer',
            marginRight: '10px',
            backgroundColor: '#2D3748',
            color: '#fff',
            border: 'none',
            borderRadius: '4px'
        });
        exportButton.addEventListener('click', () => {
            const data = JSON.stringify(blacklistedPlayers, null, 2);

            // 1) Copy to clipboard
            navigator.clipboard.writeText(data)
              .then(() => {
                alert('Blacklist has been copied to your clipboard.');
                // 2) Ask if user wants to save as file
                const saveFile = confirm('Do you want to save it as a JSON file?');
                if (saveFile) {
                    // Create a Blob, then force a download
                    const blob = new Blob([data], { type: 'application/json' });
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = 'blacklist.json';
                    document.body.appendChild(a);
                    a.click();
                    document.body.removeChild(a);
                    URL.revokeObjectURL(url);
                }
              })
              .catch(err => {
                alert('Failed to copy blacklist: ' + err);
              });
        });
        importExportWrapper.appendChild(exportButton);

        // Import button
        const importButton = document.createElement('button');
        importButton.textContent = 'Import';
        Object.assign(importButton.style, {
            padding: '4px 8px',
            cursor: 'pointer',
            backgroundColor: '#2D3748',
            color: '#fff',
            border: 'none',
            borderRadius: '4px'
        });
        importButton.addEventListener('click', () => {
            const choice = confirm('Would you like to import from file instead of JSON text?\nClick "OK" for file, "Cancel" for text prompt.');
            if (choice) {
                // Import from file
                importFromFile(listElement);
            } else {
                // Use prompt
                importFromPrompt(listElement);
            }
        });
        importExportWrapper.appendChild(importButton);

        document.body.appendChild(popupContainer);

        // Register drag events
        dragBar.addEventListener('mousedown', onDragMouseDown);
        document.addEventListener('mousemove', onDragMouseMove);
        document.addEventListener('mouseup', onDragMouseUp);
    }

    // ---- IMPORT FROM FILE ----
    function importFromFile(listElement) {
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = 'application/json,.json';
        fileInput.style.display = 'none';

        fileInput.addEventListener('change', function() {
            if (fileInput.files && fileInput.files[0]) {
                const file = fileInput.files[0];
                const reader = new FileReader();
                reader.onload = function(e) {
                    try {
                        const content = e.target.result;
                        const importedList = JSON.parse(content);
                        if (Array.isArray(importedList)) {
                            // Convert all to uppercase
                            importedList.forEach((val, idx) => {
                                importedList[idx] = val.toUpperCase();
                            });
                            blacklistedPlayers = importedList;
                            savePlayerList();
                            updateList(listElement);
                            checkPlayers();
                            alert('Blacklist imported successfully from file!');
                        } else {
                            alert('Invalid JSON data in file: Must be an array, e.g. ["Alice","Bob"].');
                        }
                    } catch (err) {
                        alert('Invalid JSON file: ' + err);
                    }
                };
                reader.readAsText(file);
            }
        });

        // "Click" the input programmatically
        document.body.appendChild(fileInput);
        fileInput.click();
        // Remove it afterwards to keep the DOM clean
        fileInput.parentNode.removeChild(fileInput);
    }

    // ---- IMPORT FROM PROMPT ----
    function importFromPrompt(listElement) {
        const inputData = prompt('Paste the JSON data for the blacklist:');
        if (inputData) {
            try {
                const importedList = JSON.parse(inputData);
                if (Array.isArray(importedList)) {
                    // Optional: convert all to uppercase
                    importedList.forEach((val, idx) => {
                        importedList[idx] = val.toUpperCase();
                    });
                    blacklistedPlayers = importedList;
                    savePlayerList();
                    updateList(listElement);
                    checkPlayers();
                    alert('Blacklist imported successfully!');
                } else {
                    alert('Invalid JSON data: Must be an array, e.g. ["Alice","Bob"].');
                }
            } catch (e) {
                alert('Invalid JSON: ' + e);
            }
        }
    }

    function updateList(listElement) {
        listElement.innerHTML = '';
        blacklistedPlayers.forEach(name => {
            const li = document.createElement('li');
            li.textContent = name;
            Object.assign(li.style, {
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
                padding: '4px 0'
            });

            const removeBtn = document.createElement('button');
            removeBtn.textContent = 'X';
            Object.assign(removeBtn.style, {
                marginLeft: '10px',
                backgroundColor: '#C53030',
                color: '#fff',
                border: 'none',
                borderRadius: '4px',
                padding: '2px 8px',
                cursor: 'pointer'
            });

            removeBtn.addEventListener('click', () => {
                blacklistedPlayers = blacklistedPlayers.filter(player => player !== name);
                savePlayerList();
                updateList(listElement);
                checkPlayers();
            });

            li.appendChild(removeBtn);
            listElement.appendChild(li);
        });
    }

    function togglePopup(forceOpen) {
        if (!popupContainer) {
            createPopup();
        }
        popupVisible = (typeof forceOpen === 'boolean') ? forceOpen : !popupVisible;
        popupContainer.style.display = popupVisible ? 'block' : 'none';
    }

    // --------------------------------------------------
    // WARNING AT THE TOP
    // --------------------------------------------------
    let warningDiv = null;

    function showWarning(text) {
        if (!warningDiv) {
            warningDiv = document.createElement('div');
            Object.assign(warningDiv.style, {
                position: 'fixed',
                top: '0',
                left: '0',
                width: '100%',
                backgroundColor: 'red',
                color: 'white',
                padding: '10px',
                textAlign: 'center',
                zIndex: '100000',
                fontSize: '16px'
            });
            document.body.appendChild(warningDiv);
        }
        warningDiv.textContent = `Attention: ${text}`;
    }

    function removeWarning() {
        if (warningDiv) {
            warningDiv.remove();
            warningDiv = null;
        }
    }

    function checkPlayers() {
        const playerTags = document.querySelectorAll('p.chakra-text.css-0');
        let foundNames = [];

        playerTags.forEach(tag => {
            const name = tag.textContent.trim().toUpperCase();
            if (blacklistedPlayers.includes(name)) {
                if (tag.style.backgroundColor !== 'red' || tag.style.color !== 'white') {
                    tag.style.backgroundColor = 'red';
                    tag.style.color = 'white';
                }
                foundNames.push(name);
            } else {
                if (tag.style.backgroundColor === 'red' || tag.style.color === 'white') {
                    tag.style.backgroundColor = '';
                    tag.style.color = '';
                }
            }
        });

        if (foundNames.length > 0) {
            showWarning(`Players found: ${foundNames.join(', ')}`);
        } else {
            removeWarning();
        }
    }

    // --------------------------------------------------
    // MENU ITEM
    // --------------------------------------------------
    function addBlacklistMenuItem(menuContainer) {
        console.log("[Blacklist Script] Adding the blacklist menu item...");

        let blacklistLink = document.getElementById(MENU_ITEM_ID);
        if (!blacklistLink) {
            blacklistLink = document.createElement('a');
            blacklistLink.id = MENU_ITEM_ID;
            blacklistLink.textContent = 'Blacklist';
            blacklistLink.className = 'chakra-button css-1nal3hj';
            blacklistLink.style.cursor = 'pointer';

            const icon = document.createElement('span');
            icon.className = 'chakra-button__icon css-1wh2kri';
            icon.style.marginRight = '0.5rem';
            icon.innerHTML = `
                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
                     viewBox="0 0 24 24" fill="currentColor">
                    <path d="M12 2C6.49 2 2 6.49 2 12s4.49
                    10 10 10 10-4.49 10-10S17.51
                    2 12 2zm3 14H9v-2h6v2zm2-4H7V8h10v4z"/>
                </svg>`;
            blacklistLink.prepend(icon);

            menuContainer.appendChild(blacklistLink);
            blacklistLink.addEventListener('click', () => {
                togglePopup();
            });
        }
    }

    // --------------------------------------------------
    // MUTATIONOBSERVER MIT DEBOUNCE
    // --------------------------------------------------
    let checkTimeout = null;
    function triggerCheckWithDebounce() {
        if (checkTimeout) {
            clearTimeout(checkTimeout);
        }
        checkTimeout = setTimeout(() => {
            checkPlayers();
            checkTimeout = null;
        }, 300);
    }

    const observer = new MutationObserver(() => {
        triggerCheckWithDebounce();
    });
    observer.observe(document.body, { childList: true, subtree: true });

    // --------------------------------------------------
    // MENU FIND + ADD
    // --------------------------------------------------
    const intervalId = setInterval(() => {
        const menuContainer = [...document.querySelectorAll('div.chakra-stack')]
          .find(div => div.querySelector('a[href="/"]') && div.querySelector('a[href="/lobbies"]'));

        if (menuContainer) {
            console.log("[Blacklist Script] Menu found:", menuContainer);
            addBlacklistMenuItem(menuContainer);
            clearInterval(intervalId);
        } else {
            console.log("[Blacklist Script] Menu not found yet. Trying again...");
        }
    }, 1000);

    // Start
    checkPlayers();

})();