Drawaria Moderator Menu With Tools

Enhanced real-time moderator tool for Drawaria with additional features, custom chat messages, and improved visuals

// ==UserScript==
// @name         Drawaria Moderator Menu With Tools
// @namespace    drawaria.moderator.tool
// @version      3.0
// @description  Enhanced real-time moderator tool for Drawaria with additional features, custom chat messages, and improved visuals
// @author       YouTube And Minish
// @match        https://drawaria.online/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Initialize data structures
    const bannedPlayers = new Set();
    const mutedPlayers = new Set();
    const reportedPlayers = [];
    const warnedPlayers = new Map();
    const suspiciousPlayers = new Set();

    // Style definitions
    const styles = `
        #moderatorMenuContainer {
            position: fixed;
            bottom: 60px;
            right: 10px;
            z-index: 1000;
            background-color: #333;
            border-radius: 5px;
            padding: 0;
            color: white;
            width: 250px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
            cursor: move;
            font-family: Arial, sans-serif;
        }
        #moderatorMenuTitle {
            padding: 10px;
            background-color: #555;
            border-radius: 5px 5px 0 0;
            text-align: center;
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        #moderatorMenuContent {
            padding: 10px;
            display: none;
        }
        .menuButton {
            display: block;
            width: 100%;
            margin-bottom: 5px;
            padding: 10px;
            background-color: #444;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s;
            text-align: left;
        }
        .menuButton:hover {
            background-color: #666;
        }
        .menuButton i {
            margin-right: 10px;
        }
        .playerList {
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #555;
            border-radius: 5px;
            margin-top: 10px;
        }
        .playerListItem {
            padding: 5px;
            border-bottom: 1px solid #555;
        }
        .highlight {
            background-color: #ff9999;
        }
    `;

    // Create the moderator menu
    function createModeratorMenu() {
        const menuContainer = document.createElement('div');
        menuContainer.id = 'moderatorMenuContainer';
        document.body.appendChild(menuContainer);

        const styleElement = document.createElement('style');
        styleElement.textContent = styles;
        document.head.appendChild(styleElement);

        const menuTitle = document.createElement('div');
        menuTitle.id = 'moderatorMenuTitle';
        menuTitle.innerHTML = '<span>Moderator Menu</span><span id="toggleArrow">▼</span>';
        menuContainer.appendChild(menuTitle);

        const menuContent = document.createElement('div');
        menuContent.id = 'moderatorMenuContent';
        menuContainer.appendChild(menuContent);

        menuTitle.addEventListener('click', () => {
            menuContent.style.display = menuContent.style.display === 'none' ? 'block' : 'none';
            const toggleArrow = document.getElementById('toggleArrow');
            toggleArrow.innerText = menuContent.style.display === 'none' ? '▼' : '▲';
        });

        // Add buttons to the menu
        addMenuButton(menuContent, '<i class="fas fa-ban"></i> Ban Player', handleBanButtonClick);
        addMenuButton(menuContent, '<i class="fas fa-undo"></i> Unban Player', handleUnbanButtonClick);
        addMenuButton(menuContent, '<i class="fas fa-volume-mute"></i> Mute Player', handleMuteButtonClick);
        addMenuButton(menuContent, '<i class="fas fa-volume-up"></i> Unmute Player', handleUnmuteButtonClick);
        addMenuButton(menuContent, '<i class="fas fa-exclamation-triangle"></i> Warn Player', handleWarnButtonClick);
        addMenuButton(menuContent, '<i class="fas fa-flag"></i> Report Player', handleReportButtonClick);
        addMenuButton(menuContent, '<i class="fas fa-eye"></i> View Reports', handleViewReportsClick);
        addMenuButton(menuContent, '<i class="fas fa-users"></i> View Banned Players', handleViewBannedPlayersClick);
        addMenuButton(menuContent, '<i class="fas fa-microphone-alt-slash"></i> View Muted Players', handleViewMutedPlayersClick);
        addMenuButton(menuContent, '<i class="fas fa-user-times"></i> Kick Player', handleKickButtonClick);
        addMenuButton(menuContent, '<i class="fas fa-exclamation"></i> Highlight Suspicious', handleHighlightSuspiciousClick);

        // Create player lists
        createPlayerList(menuContent, 'bannedPlayersList', 'Banned Players');
        createPlayerList(menuContent, 'mutedPlayersList', 'Muted Players');
        createPlayerList(menuContent, 'warnedPlayersList', 'Warned Players');
        createPlayerList(menuContent, 'reportedPlayersList', 'Reported Players');
        createPlayerList(menuContent, 'suspiciousPlayersList', 'Suspicious Players');

        // Make the menu draggable
        makeDraggable(menuContainer);
    }

    // Add a button to the menu
    function addMenuButton(menu, text, onClick) {
        const button = document.createElement('button');
        button.innerHTML = text;
        button.className = 'menuButton';
        button.addEventListener('click', onClick);
        menu.appendChild(button);
    }

    // Create a player list
    function createPlayerList(menu, id, title) {
        const listTitle = document.createElement('h3');
        listTitle.textContent = title;
        menu.appendChild(listTitle);

        const listContainer = document.createElement('div');
        listContainer.id = id;
        listContainer.className = 'playerList';
        menu.appendChild(listContainer);
    }

    // Function to handle the "Ban" button click event
    function handleBanButtonClick() {
        const nickname = prompt("Enter the player's nickname to ban:");

        if (nickname) {
            bannedPlayers.add(nickname);
            addModeratorMessages(nickname, 'Ban');
            removePlayer(nickname);
            updatePlayerList();
            disableChatWithUser(nickname);
            broadcastBanMessage(nickname);
            updateBannedPlayersList();
        }
    }

    // Function to handle the "Unban" button click event
    function handleUnbanButtonClick() {
        const nickname = prompt("Enter the player's nickname to unban:");

        if (nickname) {
            if (bannedPlayers.has(nickname)) {
                bannedPlayers.delete(nickname);
                addModeratorMessages(nickname, 'Unban');
                updatePlayerList();
                enableChatWithUser(nickname);
                broadcastUnbanMessage(nickname);
                updateBannedPlayersList();
            } else {
                alert('Player is not banned.');
            }
        }
    }

    // Function to handle the "Mute" button click event
    function handleMuteButtonClick() {
        const nickname = prompt("Enter the player's nickname to mute:");

        if (nickname) {
            mutedPlayers.add(nickname);
            addModeratorMessages(nickname, 'Mute');
            disableChatWithUser(nickname);
            updateMutedPlayersList();
        }
    }

    // Function to handle the "Unmute" button click event
    function handleUnmuteButtonClick() {
        const nickname = prompt("Enter the player's nickname to unmute:");

        if (nickname) {
            if (mutedPlayers.has(nickname)) {
                mutedPlayers.delete(nickname);
                addModeratorMessages(nickname, 'Unmute');
                enableChatWithUser(nickname);
                updateMutedPlayersList();
            } else {
                alert('Player is not muted.');
            }
        }
    }

    // Function to handle the "Warn" button click event
    function handleWarnButtonClick() {
        const nickname = prompt("Enter the player's nickname to warn:");
        const reason = prompt("Enter the reason for the warning:");

        if (nickname && reason) {
            if (warnedPlayers.has(nickname)) {
                warnedPlayers.set(nickname, warnedPlayers.get(nickname) + 1);
            } else {
                warnedPlayers.set(nickname, 1);
            }
            addModeratorMessages(nickname, 'Warn', reason);
            updateWarnedPlayersList();
            if (warnedPlayers.get(nickname) >= 3) {
                if (confirm(`Player ${nickname} has 3 warnings. Do you want to ban them?`)) {
                    handleBanButtonClick(nickname);
                }
            }
        }
    }

    // Function to handle the "Report" button click event
    function handleReportButtonClick() {
        const nickname = prompt("Enter the player's nickname to report:");
        const reason = prompt("Enter the reason for the report:");

        if (nickname && reason) {
            reportedPlayers.push({ nickname, reason, timestamp: new Date() });
            addModeratorMessages(nickname, 'Report', reason);
            updateReportedPlayersList();
        }
    }

    // Function to handle the "View Reports" button click event
    function handleViewReportsClick() {
        const sortedReports = reportedPlayers.sort((a, b) => b.timestamp - a.timestamp);
        const reports = sortedReports.map(player => `${player.nickname}: ${player.reason} (at ${player.timestamp.toLocaleString()})`).join('\n');
        alert(reports || 'No reports available.');
    }

    // Function to handle the "View Banned Players" button click event
    function handleViewBannedPlayersClick() {
        const banned = Array.from(bannedPlayers).join('\n');
        alert(banned || 'No banned players.');
    }

    // Function to handle the "View Muted Players" button click event
    function handleViewMutedPlayersClick() {
        const muted = Array.from(mutedPlayers).join('\n');
        alert(muted || 'No muted players.');
    }

    // Function to handle the "Kick" button click event
    function handleKickButtonClick() {
        const nickname = prompt("Enter the player's nickname to kick:");

        if (nickname) {
            addModeratorMessages(nickname, 'Kick');
            removePlayer(nickname);
            updatePlayerList();
            broadcastKickMessage(nickname);
        }
    }

    // Function to handle the "Highlight Suspicious" button click event
    function handleHighlightSuspiciousClick() {
        const nickname = prompt("Enter the player's nickname to highlight as suspicious:");

        if (nickname) {
            suspiciousPlayers.add(nickname);
            highlightPlayer(nickname);
            updateSuspiciousPlayersList();
        }
    }

    // Function to add moderator messages with custom formatting
    function addModeratorMessages(nickname, action, additionalInfo = '') {
        const chatBox = document.getElementById('chatbox_messages');

        if (chatBox) {
            let messages = [];

            if (action === 'Ban') {
                messages = [
                    `<div class="chatmessage systemchatmessage5" data-ts="${Date.now()}">Moderator: Banned ${nickname}</div>`,
                    `<div class="chatmessage systemchatmessage7" data-ts="${Date.now()}">Complain about the moderator on Discord, on this channel: <a href="https://discord.gg/XeVKWWs" target="_blank">#report-a-moderator</a></div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">Someone voted to ban from the room: ${nickname}</div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">${nickname} has been banned from the room!</div>`,
                    `<div class="chatmessage systemchatmessage7" data-ts="${Date.now()}">${nickname} - player has left the room</div>`
                ];
            } else if (action === 'Unban') {
                messages = [
                    `<div class="chatmessage systemchatmessage5" data-ts="${Date.now()}">Moderator: Unbanned ${nickname}</div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">${nickname} has been unbanned from the room!</div>`
                ];
            } else if (action === 'Mute') {
                messages = [
                    `<div class="chatmessage systemchatmessage5" data-ts="${Date.now()}">Moderator: Muted ${nickname}</div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">${nickname} has been muted in the room!</div>`
                ];
            } else if (action === 'Unmute') {
                messages = [
                    `<div class="chatmessage systemchatmessage5" data-ts="${Date.now()}">Moderator: Unmuted ${nickname}</div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">${nickname} has been unmuted in the room!</div>`
                ];
            } else if (action === 'Warn') {
                messages = [
                    `<div class="chatmessage systemchatmessage5" data-ts="${Date.now()}">Moderator: Warned ${nickname} for: ${additionalInfo}</div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">${nickname} has been warned! (${warnedPlayers.get(nickname)}/3)</div>`
                ];
            } else if (action === 'Report') {
                messages = [
                    `<div class="chatmessage systemchatmessage5" data-ts="${Date.now()}">Moderator: Reported ${nickname}</div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">${nickname} has been reported for: ${additionalInfo}</div>`
                ];
            } else if (action === 'Kick') {
                messages = [
                    `<div class="chatmessage systemchatmessage5" data-ts="${Date.now()}">Moderator: Kicked ${nickname}</div>`,
                    `<div class="chatmessage systemchatmessage" data-ts="${Date.now()}">${nickname} has been kicked from the room!</div>`
                ];
            }

            messages.forEach(message => {
                chatBox.innerHTML += message;
            });
        }
    }

    // Function to remove a player from the list
    function removePlayer(nickname) {
        const playerList = document.getElementById('playerlist');

        if (playerList) {
            const playerToRemove = Array.from(playerList.querySelectorAll('.playerlist-row')).find(player => {
                const nameElement = player.querySelector('.playerlist-name a');
                return nameElement && nameElement.textContent.trim() === nickname;
            });

            if (playerToRemove) {
                playerToRemove.remove();
            }
        }
    }

    // Function to update the player list
    function updatePlayerList() {
        const playerList = document.getElementById('playerlist');

        if (playerList) {
            Array.from(playerList.querySelectorAll('.playerlist-row')).forEach(player => {
                const nameElement = player.querySelector('.playerlist-name a');
                if (nameElement) {
                    const playerName = nameElement.textContent.trim();
                    if (bannedPlayers.has(playerName)) {
                        player.remove();
                    }
                }
            });
        }
    }

    // Function to sanitize nickname for use in CSS selectors
    function sanitizeNickname(nickname) {
        return nickname.replace(/[^a-zA-Z0-9]/g, '');
    }

    // Function to disable chat with the banned player
    function disableChatWithUser(nickname) {
        const sanitizedNickname = sanitizeNickname(nickname);
        const chatWithUser = document.querySelector(`.chat-with-${sanitizedNickname}`);

        if (chatWithUser) {
            chatWithUser.style.display = 'none';
        }
    }

    // Function to enable chat with the unbanned player
    function enableChatWithUser(nickname) {
        const sanitizedNickname = sanitizeNickname(nickname);
        const chatWithUser = document.querySelector(`.chat-with-${sanitizedNickname}`);

        if (chatWithUser) {
            chatWithUser.style.display = 'block';
        }
    }

    // Function to broadcast the ban message to all players
    function broadcastBanMessage(nickname) {
        if (window.myRoom.players && Array.isArray(window.myRoom.players)) {
            const message = JSON.stringify([
                "bc_uc_freedrawsession_changedroom",
                null,
                null,
                window.myRoom.players.filter(player => player.nickname !== nickname)
            ]);
            sendMessageToAll(`42${message}`);
        }
    }

    // Function to broadcast the unban message to all players
    function broadcastUnbanMessage(nickname) {
        if (window.myRoom.players && Array.isArray(window.myRoom.players)) {
            const message = JSON.stringify([
                "bc_uc_freedrawsession_changedroom",
                null,
                null,
                window.myRoom.players
            ]);
            sendMessageToAll(`42${message}`);
        }
    }

    // Function to broadcast the kick message to all players
    function broadcastKickMessage(nickname) {
        if (window.myRoom.players && Array.isArray(window.myRoom.players)) {
            const message = JSON.stringify([
                "bc_uc_freedrawsession_changedroom",
                null,
                null,
                window.myRoom.players.filter(player => player.nickname !== nickname)
            ]);
            sendMessageToAll(`42${message}`);
        }
    }

    // Function to update the banned players list
    function updateBannedPlayersList() {
        updatePlayerListContainer('bannedPlayersList', Array.from(bannedPlayers));
    }

    // Function to update the muted players list
    function updateMutedPlayersList() {
        updatePlayerListContainer('mutedPlayersList', Array.from(mutedPlayers));
    }

    // Function to update the warned players list
    function updateWarnedPlayersList() {
        const warnedList = Array.from(warnedPlayers).map(([name, count]) => `${name} (${count}/3)`);
        updatePlayerListContainer('warnedPlayersList', warnedList);
    }

    // Function to update the reported players list
    function updateReportedPlayersList() {
        const reportedList = reportedPlayers.map(player => `${player.nickname}: ${player.reason}`);
        updatePlayerListContainer('reportedPlayersList', reportedList);
    }

    // Function to update the suspicious players list
    function updateSuspiciousPlayersList() {
        updatePlayerListContainer('suspiciousPlayersList', Array.from(suspiciousPlayers));
    }

    // Generic function to update player list containers
    function updatePlayerListContainer(containerId, playerList) {
        const listContainer = document.getElementById(containerId);
        if (listContainer) {
            listContainer.innerHTML = '';
            playerList.forEach(player => {
                const playerItem = document.createElement('div');
                playerItem.textContent = player;
                playerItem.className = 'playerListItem';
                listContainer.appendChild(playerItem);
            });
        }
    }

    // Function to highlight a suspicious player
    function highlightPlayer(nickname) {
        const playerList = document.getElementById('playerlist');
        if (playerList) {
            const playerToHighlight = Array.from(playerList.querySelectorAll('.playerlist-row')).find(player => {
                const nameElement = player.querySelector('.playerlist-name a');
                return nameElement && nameElement.textContent.trim() === nickname;
            });

            if (playerToHighlight) {
                playerToHighlight.classList.add('highlight');
            }
        }
    }

    // WebSocket handling
    const originalSend = WebSocket.prototype.send;
    WebSocket.prototype.send = function (...args) {
        if (window.sockets.indexOf(this) === -1) {
            window.sockets.push(this);

            if (window.sockets.indexOf(this) === 0) {
                this.addEventListener('message', handleIncomingMessage);
            }
        }

        return originalSend.call(this, ...args);
    };

    // Initialize
    createModeratorMenu();

    // Room & Socket Control
    window.myRoom = {
        players: []
    };
    window.sockets = [];

    // Ensure the WebSocket messages are correctly formatted and sent to all players
    function sendMessageToAll(message) {
        window.sockets.forEach(socket => {
            if (socket.readyState === WebSocket.OPEN) {
                socket.send(message);
            }
        });
    }

    // Function to handle incoming WebSocket messages
    function handleIncomingMessage(event) {
        let message = String(event.data);

        if (message.startsWith('42')) {
            let payload = JSON.parse(message.slice(2));

            if (payload[0] == 'bc_uc_freedrawsession_changedroom' || payload[0] == 'mc_roomplayerschange') {
                window.myRoom.players = payload[3];
                updatePlayerList();
                updateBannedPlayersList();
                updateMutedPlayersList();
                updateWarnedPlayersList();
                updateReportedPlayersList();
                updateSuspiciousPlayersList();
            }
        } else if (message.startsWith('430')) {
            let configs = JSON.parse(message.slice(3))[0];
            window.myRoom.players = configs.players;
            window.myRoom.id = configs.roomid;
            updatePlayerList();
            updateBannedPlayersList();
            updateMutedPlayersList();
            updateWarnedPlayersList();
            updateReportedPlayersList();
            updateSuspiciousPlayersList();
        }
    }

    // Connect to the WebSocket server
    function connectToWebSocketServer() {
        const socket = new WebSocket('wss://sv3.drawaria.online/socket.io/?sid1=s%3A11JLEUID6B8TEN3-RCEDSV1h8ZzvoF3a.99s79DOPC%2Fb2ajOJBEBNkhMT0Z1FSm8jzcCGzohk%2FeI&hostname=drawaria.online&EIO=3&transport=websocket');

        socket.onopen = () => {
            console.log('WebSocket connection opened');
            window.sockets.push(socket);
            socket.addEventListener('message', handleIncomingMessage);
        };

        socket.onclose = () => {
            console.log('WebSocket connection closed');
            window.sockets = window.sockets.filter(s => s !== socket);
        };

        socket.onerror = (error) => {
            console.error('WebSocket error:', error);
        };
    }

    // Connect to the WebSocket server
    connectToWebSocketServer();

    // Make an element draggable
    function makeDraggable(element) {
        let isDragging = false;
        let offsetX, offsetY;

        element.addEventListener('mousedown', (e) => {
            isDragging = true;
            offsetX = e.clientX - element.getBoundingClientRect().left;
            offsetY = e.clientY - element.getBoundingClientRect().top;
            element.style.cursor = 'grabbing';
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                element.style.left = `${e.clientX - offsetX}px`;
                element.style.top = `${e.clientY - offsetY}px`;
                element.style.bottom = 'auto';
                element.style.right = 'auto';
            }
        });

        document.addEventListener('mouseup', () => {
            isDragging = false;
            element.style.cursor = 'move';
        });
    }

    // Add custom chat commands for moderators
    function addCustomChatCommands() {
        const originalSendChat = window.sendChat;
        window.sendChat = function(message) {
            if (message.startsWith('/')) {
                const [command, ...args] = message.slice(1).split(' ');
                handleChatCommand(command, args);
            } else {
                originalSendChat.call(this, message);
            }
        };
    }

    // Handle custom chat commands
    function handleChatCommand(command, args) {
        switch(command.toLowerCase()) {
            case 'ban':
                if (args.length > 0) {
                    handleBanButtonClick(args.join(' '));
                }
                break;
            case 'unban':
                if (args.length > 0) {
                    handleUnbanButtonClick(args.join(' '));
                }
                break;
            case 'mute':
                if (args.length > 0) {
                    handleMuteButtonClick(args.join(' '));
                }
                break;
            case 'unmute':
                if (args.length > 0) {
                    handleUnmuteButtonClick(args.join(' '));
                }
                break;
            case 'kick':
                if (args.length > 0) {
                    handleKickButtonClick(args.join(' '));
                }
                break;
            case 'warn':
                if (args.length > 1) {
                    handleWarnButtonClick(args[0], args.slice(1).join(' '));
                }
                break;
            case 'report':
                if (args.length > 1) {
                    handleReportButtonClick(args[0], args.slice(1).join(' '));
                }
                break;
            case 'highlight':
                if (args.length > 0) {
                    handleHighlightSuspiciousClick(args.join(' '));
                }
                break;
            default:
                addModeratorMessages('System', 'Unknown command', `Unknown command: /${command}`);
        }
    }

    // Initialize custom chat commands
    addCustomChatCommands();

})();