BotMenu Client [Indev]

Интерфейс для управления командами ботов с поддержкой категорий и сортировки

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         BotMenu Client [Indev]
// @name:en      BotMenu Client [Indev]
// @name:ru      БотМеню Клиент [Indev]
// @namespace    https://github.com/Zeroxel/BotMenu/
// @version      5.0
// @description:ru  Интерфейс для управления командами ботов с поддержкой категорий и сортировки
// @description:en  Interface for bot team management with support for categorization and sorting
// @author       gtnntg
// @match        *://multiplayerpiano.net/*
// @match        *://mpp.8448.space/*
// @license      ARC
// @grant        GM_info
// @supportURL   https://github.com/Zeroxel/BotMenu/discussions/categories/bot-menu-client
// @description Интерфейс для управления командами ботов с поддержкой категорий и сортировки
// ==/UserScript==

/*global MPP*/
const useversion = GM_info.script.version;

(function () {
    'use strict';

    const botsData = {}; // Хранение данных ботов { botId: { name, categories, userRank } }
    const botmenu = {
        client: {
            /*
             BotMenu Client -> User
            */
            send(msg,name,id,color) {
                MPP.chat.receive({
                    "m": "a",
                    "t": Date.now(),
                    "a": msg,
                    "p": {
                        "_id": id,
                        "name": name,
                        "color": color,
                        "id": id
                    }
                });
            }
            //---------------
        },
        bot: {
            /*
            BotMenu Client -> BotMenu Server
            */
            send(botId,message) {
                MPP.client.sendArray([{
                    m: "custom",
                    data: {
                        m: "BotMenuClient",
                        language: localStorage.getItem('language'),
                        type: "msg",
                        message:message,
                    },
                    target: { mode: "id" , id: botId},
                }]);
            },
            type(botId, typename) {
                MPP.client.sendArray([{
                    m: "custom",
                    data: {
                        m: "BotMenuClient",
                        language: localStorage.getItem('language'),
                        type: typename,
                    },
                    target: { mode: "id" , id: botId},
                }]);
            },
            //-----------------
            reqcmds() {
                MPP.client.sendArray([{
                    m: "custom",
                    data: {
                        m: "BotMenuClient",
                        language: localStorage.getItem('language'),
                        type: "Requestcommands"
                    },
                    target: { mode: "subscribed" },
                }]);
                botmenu.client.send('Запрос на команды был отправлен всем ботам находящиеся в данной комнате','Bot Menu Client','Bot Menu Client','#0066ff')
            }
        }
    }

    // Создаём контейнер для вкладок и кнопок
    const container = document.createElement('div');
    container.id = 'bot-menu';
    container.style.position = 'fixed';
    container.style.bottom = '20px';
    container.style.right = '10px';
    container.style.width = '300px';
    container.style.maxHeight = '500px';
    container.style.minHeight = '50px';
    container.style.backgroundColor = '#121212';
    container.style.color = '#fff';
    container.style.border = '1px solid #333';
    container.style.borderRadius = '8px';
    container.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.5)';
    container.style.padding = '10px';
    container.style.overflowY = 'auto';
    container.style.zIndex = '10000';
    container.style.display = 'block';
    document.body.appendChild(container);

    // Кнопка сворачивания
    const toggleButton = document.createElement('button');
    toggleButton.innerText = '⮝';
    toggleButton.style.alignSelf = 'flex-end';
    toggleButton.style.backgroundColor = '#333';
    toggleButton.style.color = '#fff';
    toggleButton.style.border = 'none';
    toggleButton.style.borderRadius = '4px';
    toggleButton.style.cursor = 'pointer';
    toggleButton.style.marginBottom = '10px';
    toggleButton.onclick = toggleContainer;
    container.appendChild(toggleButton);

    const QButton = document.createElement('button');
    QButton.innerText = '🔎';
    QButton.style.alignSelf = 'flex';
    QButton.style.backgroundColor = '#333';
    QButton.style.color = '#fff';
    QButton.style.border = 'none';
    QButton.style.borderRadius = '4px';
    QButton.style.marginLeft = '10px';
    QButton.style.cursor = 'pointer';
    QButton.style.marginBottom = '10px';
    QButton.onclick = botmenu.bot.reqcmds;
    container.appendChild(QButton);

    const tabs = document.createElement('div'); // Контейнер для вкладок
    tabs.id = 'bot-tabs';
    tabs.style.display = 'flex';
    tabs.style.flexDirection = 'column';
    tabs.style.flexWrap = 'nowrap';
    tabs.style.marginBottom = '10px';
    container.appendChild(tabs);

    const commandsContainer = document.createElement('div'); // Контейнер для кнопок команд
    commandsContainer.id = 'commands-container';
    commandsContainer.style.display = 'none';
    commandsContainer.style.padding = '10px';
    commandsContainer.style.borderTop = '1px solid #555';
    commandsContainer.style.marginTop = '10px';
    container.appendChild(commandsContainer);

    let isCollapsed = false;

    // Сворачивание/разворачивание контейнера
    function toggleContainer() {
        isCollapsed = !isCollapsed;
        if (isCollapsed) {
            tabs.style.display = 'none';
            commandsContainer.style.display = 'none';
            toggleButton.innerText = '⮟';
            container.style.maxHeight = '50px';
        } else {
            tabs.style.display = 'flex';
            toggleButton.innerText = '⮝';
            container.style.maxHeight = '500px';
        }
    }


    // Функция для добавления новой вкладки
    function addTab(botId, botName) {
        const tab = document.createElement('button');
        tab.innerText = botName;
        tab.style.padding = '5px 10px';
        tab.style.margin = '5px 5px 0 0';
        tab.style.border = '1px solid #444';
        tab.style.borderRadius = '5px';
        tab.style.backgroundColor = '#2e2e2e';
        tab.style.color = '#fff';
        tab.style.cursor = 'pointer';
        tab.style.flex = '1 0 auto';
        tab.setAttribute('data-id', botId); // Присваиваем data-id для идентификации
        tab.onclick = () => showCommands(botId);
        tabs.appendChild(tab);
    }

    // Функция для отображения команд
    function showCommands(botId) {
        commandsContainer.innerHTML = ''; // Очищаем контейнер команд
        commandsContainer.style.display = 'block'; // Показываем контейнер

        const botData = botsData[botId];
        if (!botData) return; // Если данных нет, выходим

        /* const title = document.createElement('h3');
    title.innerText = `${botData.name}`;
    title.style.marginBottom = '10px';
    commandsContainer.appendChild(title);*/

        // Сортируем категории по позиции
        botData.categories.sort((a, b) => a.position - b.position);

        // Обрабатываем каждую категорию
        botData.categories.forEach((category) => {
            const categoryTitle = document.createElement('h4');
            categoryTitle.innerText = category.name; // Название категории
            categoryTitle.style.marginTop = '10px';
            categoryTitle.style.color = category.categoryColor ? category.categoryColor : '#aaa';
            categoryTitle.style.cursor = 'pointer'; // Добавляем курсор pointer для показа кликабельности

            // Добавляем иконку для показа состояния (свернуто/развернуто)
            const categoryIcon = document.createElement('span');
            categoryIcon.innerText = ' ▼'; // По умолчанию развернуто
            categoryIcon.style.fontSize = '12px';
            categoryTitle.appendChild(categoryIcon);

            commandsContainer.appendChild(categoryTitle);

            // Создаем контейнер для команд категории
            const commandList = document.createElement('div');
            commandList.style.marginLeft = '10px';
            commandsContainer.appendChild(commandList);

            // Добавляем обработчик клика для сворачивания/разворачивания
            categoryTitle.onclick = () => {
                if (commandList.style.display === 'none') {
                    commandList.style.display = 'block';
                    categoryIcon.innerText = ' ▼';
                } else {
                    commandList.style.display = 'none';
                    categoryIcon.innerText = ' ►';
                }
            };

            // Обрабатываем команды внутри категории
            category.commands.forEach((cmd) => {
                const commandBlock = document.createElement('div'); // Блок для команды
                commandBlock.style.display = 'flex';
                commandBlock.style.flexDirection = 'column';
                commandBlock.style.marginBottom = '10px';

                const btn = document.createElement('button'); // Кнопка команды
                btn.innerText = cmd.label;
                btn.title = cmd.description;
                btn.style.padding = '8px';
                btn.style.border = '1px solid #ccc';
                btn.style.borderRadius = '5px';
                btn.style.backgroundColor = !cmd.bcolor ? '#e0e0e0' : cmd.bcolor ;
                btn.style.color = !cmd.color ? '#000' : cmd.color ;
                btn.style.cursor = 'pointer';

                const inputs = []; // Массив для хранения полей ввода

                // Если команда имеет параметры, создаем поля ввода
                if (cmd.parameters && cmd.parameters.length > 0) {
                    cmd.parameters.forEach(param => {
                        const Input = document.createElement('input');
                        Input.type = 'text';
                        Input.placeholder = `Введите ${param}`;
                        Input.style.marginBottom = '5px';
                        Input.style.backgroundColor = '#333';
                        Input.style.color = '#fff';
                        Input.style.borderRadius = '4px';
                        commandBlock.appendChild(Input);
                        inputs.push(Input); // Добавляем поле в массив
                    });
                }

                // Обработчик нажатия на кнопку
                btn.onclick = () => {
                    if (!cmd.message) {
                        let finalCommand = cmd.command; // Изначальная команда

                        // Проверяем, заполнены ли все параметры
                        for (let i = 0; i < inputs.length; i++) {
                            const value = inputs[i].value.trim();
                            if (!value) {
                                botmenu.client.send(`Параметр "${cmd.parameters[i]}" не может быть пустым!`,'Bot Menu Client [Error]','Bot Menu Client','#0066ff')
                                return; // Отменяем выполнение, если параметр пуст
                            }
                            finalCommand = finalCommand.replace(`[${cmd.parameters[i]}]`, value); // Заменяем параметр
                        }

                        // Вставляем готовую команду в чат
                        const chatInput = document.querySelector('#chat-input');
                        if (chatInput) {
                            chatInput.value = finalCommand;
                            chatInput.focus(); // Устанавливаем фокус на чат
                        } else {
                            console.error('Chat input not found!');
                        }}else {
                            let msguser = cmd.message
                            botmenu.bot.send(botId,msguser)
                        }
                };

                commandBlock.appendChild(btn); // Добавляем кнопку в блок команды
                commandList.appendChild(commandBlock); // Добавляем блок команды в контейнер категории
            });
        });
    }

    // Делает контейнер перетаскиваемым
    class BotChatPanel {
        constructor() {
            this.createPanel();
        }

        createPanel() {
            this.panel = document.createElement("div");
            this.panel.id = "bot-chat-panel";
            this.panel.style.position = "fixed";
            this.panel.style.bottom = "20px";
            this.panel.style.right = "20px";
            this.panel.style.width = "300px";
            this.panel.style.height = "400px";
            this.panel.style.background = "#222";
            this.panel.style.color = "#fff";
            this.panel.style.borderRadius = "10px";
            this.panel.style.padding = "10px";
            this.panel.style.overflowY = "auto";
            this.panel.style.display = "none";
            this.panel.style.boxShadow = "0 0 10px rgba(0,0,0,0.5)";

            const closeButton = document.createElement("button");
            closeButton.innerText = "✖";
            closeButton.style.position = "absolute";
            closeButton.style.top = "5px";
            closeButton.style.right = "5px";
            closeButton.style.background = "transparent";
            closeButton.style.color = "#fff";
            closeButton.style.border = "none";
            closeButton.style.cursor = "pointer";
            closeButton.onclick = () => this.panel.style.display = "none";
            this.panel.appendChild(closeButton);
            document.body.appendChild(this.panel);
        }

        showMessage(botName, botColor, message) {
            const messageDiv = document.createElement("div");
            messageDiv.style.padding = "5px";
            messageDiv.style.borderBottom = "1px solid #444";

            const botLabel = document.createElement("span");
            botLabel.innerText = `[${botName}] `;
            botLabel.style.color = botColor;
            botLabel.style.fontWeight = "bold";

            const textSpan = document.createElement("span");
            textSpan.innerText = message;

            messageDiv.appendChild(botLabel);
            messageDiv.appendChild(textSpan);
            this.panel.appendChild(messageDiv);

            this.panel.style.display = "block";
        }
    }

    // Создаем панель
    const botChat = new BotChatPanel();

    // Функция для обработки сообщений с directsend
    function handleCommandResponse(response) {
        if (response.directsend === "true") {
            botChat.showMessage(response.botName, response.botColor, response.message);
        } else {
            console.log("Обычный ответ в чат", response.message);
        }
    }

    function makeDraggable(element) {
        let offsetX = 0, offsetY = 0, mouseDown = false;

        element.addEventListener('mousedown', (e) => {
            mouseDown = true;
            offsetX = e.clientX - element.offsetLeft;
            offsetY = e.clientY - element.offsetTop;
        });

        document.addEventListener('mousemove', (e) => {
            if (!mouseDown) return;
            element.style.left = `${e.clientX - offsetX}px`;
            element.style.top = `${e.clientY - offsetY}px`;
        });

        document.addEventListener('mouseup', () => {
            mouseDown = false;
        });
    }

    makeDraggable(container);

    // Обработчик входящих сообщений
    MPP.client.on('custom', (data) => {
        if (!data.data || data.data.m !== 'BotMenu') return;

        const { version, botName, botColor, categories, userRank } = data.data;
        const botId = data.p;

        if (!data.data.mode) {
            botmenu.client.send(`[${botName}](${botId}) This bot does not have a Mode `,'Bot Menu Client [Error]','Bot Menu Client','#0066ff')
            return
        }

        if (data.data.mode === 'CSC') {
        if (!botId || !botName || !Array.isArray(categories)) {
            console.debug('Invalid bot data received:', data.data);
            return;
        }
        if (!version) {
            botmenu.client.send(`[${botName}](${botId}) This bot does not have a Version `,'Bot Menu Client [Error]','Bot Menu Client','#0066ff')
            return
        }

        if (version < useversion) {
            botmenu.bot.type(botId , `old.version`);
            botmenu.client.send(`This bot is outdated, if you own it update the bot [${botName}](${botId}) to the latest version.`,'Bot Menu Client [Error]','Bot Menu Client','#0066ff')
            return;
        }

        // Проверяем, есть ли данные для бота
        if (!botsData[botId]) {
            botsData[botId] = { name: botName, categories: [], userRank };

            // Обрабатываем категории
            categories.forEach((category) => {
                const { categoryName, categoryColor, position, commands, requiredRank } = category;

                botsData[botId].categories.push({
                    name: categoryName,
                    categoryColor,
                    position,
                    commands,
                    requiredRank
                });
            });

            addTab(botId, botName); // Добавляем вкладку для бота
            botmenu.client.send(`Данные были получены с ${botName}`,'Bot Menu Client','Bot Menu Client','#0066ff')
            botmenu.bot.type(botId , 'confirm')
        } else {
            // Обновляем существующие данные
            botsData[botId].categories = categories.map((category) => ({
                name: category.categoryName,
                categoryColor: category.categoryColor,
                position: category.position,
                commands: category.commands,
                requiredRank: category.requiredRank
            }));
            botsData[botId].userRank = userRank;
            botmenu.client.send(`Данные бота ${botName} были обновлены`,'Bot Menu Client','Bot Menu Client','#0066ff');
        }
        } else if (data.data.mode === 'MSG' || data.data.mode === ('Message' || 'message')){
            let msg = data.data.message;
            botmenu.client.send(`${msg}`,`${botName}`,`Bot Menu (${botId})`,`${botColor}`);
        }
    });

    // При подключении отправляем +custom
    MPP.client.on('hi', () => {
        MPP.client.sendArray([{ m: '+custom' }]);
    });
})();