5haddam / CatWar notification

// ==UserScript==
// @name         CatWar notification
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Уведомления для лс и чата игровой
// @author       5haddam
// @match        https://*.catwar.su/cw3/
// @icon         https://www.google.com/s2/favicons?sz=64&domain=catwar.su
// @grant        none
// @require      https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js
// @license      MIT; https://opensource.org/licenses/MIT
// @copyright 2023, 5haddam (https://openuserjs.org/users/5haddam) Мечтательнолапый (https://catwar.su/cat1524802)
// @updateURL https://openuserjs.org/meta/5haddam/CatWar_notification.meta.js
// @downloadURL https://openuserjs.org/install/5haddam/CatWar_notification.user.js
// ==/UserScript==

// ==OpenUserJS==
// @author 5haddam
// ==/OpenUserJS==

(function() {
    'use strict';

    let userChoice = localStorage.getItem('userChoice');
    let volume = localStorage.getItem('volume');
    let userChoiceLS = localStorage.getItem('userChoiceLS');
    let volumeLS = localStorage.getItem('volumeLS');

    let menuIsOpen = false;

    const audioChatNotification = new Audio('https://zvukitop.com/wp-content/uploads/2021/09/zvuk-soobsheniya-vk-1.mp3?_=6');
    const audioLSNotification = new Audio('https://zvukitop.com/wp-content/uploads/2021/09/zvuk-soobsheniya-vk-1.mp3?_=6');

    let processedElementsCounter = 0;

    userChoice === 'ping' ? processedElementsCounter = [...document.getElementsByClassName('myname')].length : null;

    const chatMsgElement = document.querySelector('#chat_msg');
    let previousElementCount = chatMsgElement.childElementCount;

    const handleNewElements = (mutationsList) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList') {
                mutation.addedNodes.forEach((node) => {
                    if (userChoice === 'all') {
                        const currentElementCount = chatMsgElement.childElementCount;
                        if (currentElementCount > previousElementCount) {
                            previousElementCount = currentElementCount;
                            audioChatNotification.volume = volume;
                            audioChatNotification.play();
                        }
                    } else if (userChoice === 'ping') {
                        const allMyNameElements = document.getElementsByClassName('myname').length;
                        if (node instanceof HTMLElement && node.classList.contains('myname') && allMyNameElements > processedElementsCounter) {
                            processedElementsCounter = allMyNameElements;
                            audioChatNotification.volume = volume;
                            audioChatNotification.play();
                        }
                    }
                });
            }
        }
    };

    function chooseNotificationType() {
        const chatForm = document.querySelector('#chat_form');
        const body = document.querySelector('body');
        const selectButton = document.createElement('div');
        selectButton.id = 'notificationType';
        selectButton.innerHTML = 'Настройки уведомлений';
        chatForm.append(selectButton);

        selectButton.addEventListener('click', (e) => {
            e.stopPropagation();
            if (!menuIsOpen) {
                showMenu();
                addStylesMenu();
            }
        });

        function showMenu() {
            menuIsOpen = true;
            const notificationTypeMenu = document.createElement('div');
            notificationTypeMenu.id = 'notificationTypeMenu';

            function addNotificationTypes() {
                const notificationAll = document.createElement('input');
                const notificationPing = document.createElement('input');
                const notificationNone = document.createElement('input');
                const notificationAllLabel = document.createElement('label');
                const notificationPingLabel = document.createElement('label');
                const notificationNoneLabel = document.createElement('label');
                const notificationAllDiv = document.createElement('div');
                const notificationPingDiv = document.createElement('div');
                const notificationNoneDiv = document.createElement('div');

                notificationAll.type = 'radio';
                notificationPing.type = 'radio';
                notificationNone.type = 'radio';

                notificationAll.name = 'notificationType';
                notificationPing.name = 'notificationType';
                notificationNone.name = 'notificationType';

                notificationAll.id = 'all';
                notificationPing.id = 'ping';
                notificationNone.id = 'none';

                notificationAll.value = 'all';
                notificationPing.value = 'ping';
                notificationNone.value = 'none';

                notificationAllLabel.setAttribute('for', 'all');
                notificationPingLabel.setAttribute('for', 'ping');
                notificationNoneLabel.setAttribute('for', 'none');

                notificationAllLabel.innerText = 'Все уведомления';
                notificationPingLabel.innerText = 'Только упоминания';
                notificationNoneLabel.innerText = 'Нет уведомлений';

                const chatNotification = {
                    'all': () => { notificationAll.checked = true },
                    'ping': () => { notificationPing.checked = true },
                    'none': () => { notificationNone.checked = true },
                }

                chatNotification[userChoice]();

                notificationAllDiv.append(notificationAll, notificationAllLabel);
                notificationPingDiv.append(notificationPing, notificationPingLabel);
                notificationNoneDiv.append(notificationNone, notificationNoneLabel);

                notificationTypeMenu.append(notificationAllDiv, notificationPingDiv, notificationNoneDiv);
                addVolumeControl();

                notificationAllLabel.addEventListener('click', (e) => e.stopPropagation());
                notificationPingLabel.addEventListener('click', (e) => e.stopPropagation());
                notificationNoneLabel.addEventListener('click', (e) => e.stopPropagation());

                notificationAllDiv.addEventListener('click', (e) => {
                    e.stopPropagation();

                    const selectedNotificationType = 'all';
                    saveUserChoice(selectedNotificationType);
                    userChoice = selectedNotificationType;

                    notificationAll.checked = true;
                });
                notificationPingDiv.addEventListener('click', (e) => {
                    e.stopPropagation();

                    const selectedNotificationType = 'ping';
                    saveUserChoice(selectedNotificationType);
                    userChoice = selectedNotificationType;

                    notificationPing.checked = true;

                    processedElementsCounter = [...document.getElementsByClassName('myname')].length;
                });
                notificationNoneDiv.addEventListener('click', (e) => {
                    e.stopPropagation();

                    const selectedNotificationType = 'none';
                    saveUserChoice(selectedNotificationType);
                    userChoice = selectedNotificationType;

                    notificationNone.checked = true;
                });
            }

            function addNotificationLS() {
                const notificationTitleLS = document.createElement('div');

                notificationTitleLS.innerText = 'ЛС';
                notificationTypeMenu.append(notificationTitleLS);

                const notificationOn = document.createElement('input');
                const notificationOff = document.createElement('input');
                const notificationOnLabel = document.createElement('label');
                const notificationOffLabel = document.createElement('label');
                const notificationOnDiv = document.createElement('div');
                const notificationOffDiv = document.createElement('div');

                notificationOn.type = 'radio';
                notificationOff.type = 'radio';

                notificationOn.name = 'notificationTypeLS';
                notificationOff.name = 'notificationTypeLS';

                notificationOn.id = 'on';
                notificationOff.id = 'off';

                notificationOn.value = 'on';
                notificationOff.value = 'off';

                notificationOnLabel.setAttribute('for', 'on');
                notificationOffLabel.setAttribute('for', 'off');

                notificationOnLabel.innerText = 'Уведомления вкл';
                notificationOffLabel.innerText = 'Уведомления выкл';

                const chatNotificationLS = {
                    'on': () => { notificationOn.checked = true },
                    'off': () => { notificationOff.checked = true },
                }

                chatNotificationLS[userChoiceLS]();

                notificationOnDiv.append(notificationOn, notificationOnLabel);
                notificationOffDiv.append(notificationOff, notificationOffLabel);

                notificationTypeMenu.append(notificationOnDiv, notificationOffDiv);
                addVolumeControlLS();

                notificationOnDiv.addEventListener('click', (e) => {
                    e.stopPropagation();

                    const selectedNotificationType = 'on';
                    saveUserChoiceLS(selectedNotificationType);
                    userChoiceLS = selectedNotificationType;

                    notificationOn.checked = true;
                });

                notificationOffDiv.addEventListener('click', (e) => {
                    e.stopPropagation();

                    const selectedNotificationType = 'off';
                    saveUserChoiceLS(selectedNotificationType);
                    userChoiceLS = selectedNotificationType;

                    notificationOff.checked = true;
                });

                notificationOnLabel.addEventListener('click', (e) => e.stopPropagation());
                notificationOffLabel.addEventListener('click', (e) => e.stopPropagation());
            };

            notificationTypeMenu.addEventListener('click', (e) => e.stopPropagation());

            addNotificationTypes();

            addNotificationLS();

            body.addEventListener('click', (e) => {
                notificationTypeMenu.remove();
                menuIsOpen = false;
            });

            chatForm.after(notificationTypeMenu);

            function addVolumeControl() {
                const volumeInput = document.createElement('input');
                const volumeLabel = document.createElement('label');
                const volumeDiv = document.createElement('div');

                volumeInput.type = 'range';
                volumeInput.min = '0';
                volumeInput.max = '1';
                volumeInput.step = '0.1';
                volumeInput.value = volume;
                volumeInput.style.width = '100px';

                volumeLabel.setAttribute('for', 'volume');
                volumeLabel.innerText = 'Громкость:';

                volumeDiv.append(volumeLabel, volumeInput);
                notificationTypeMenu.append(volumeDiv);

                volumeInput.addEventListener('input', (e) => {
                    volume = e.target.value;
                    audioChatNotification.volume = volume;
                    saveUserVolume(volume);
                });
            }

            function addVolumeControlLS() {
                const volumeInput = document.createElement('input');
                const volumeLabel = document.createElement('label');
                const volumeDiv = document.createElement('div');

                volumeInput.type = 'range';
                volumeInput.min = '0';
                volumeInput.max = '1';
                volumeInput.step = '0.1';
                volumeInput.value = volumeLS;
                volumeInput.style.width = '100px';

                volumeLabel.setAttribute('for', 'volumeLS');
                volumeLabel.innerText = 'Громкость:';

                volumeDiv.append(volumeLabel, volumeInput);
                notificationTypeMenu.append(volumeDiv);

                volumeInput.addEventListener('input', (e) => {
                    volumeLS = e.target.value;
                    audioLSNotification.volume = volumeLS;
                    saveUserVolumeLS(volumeLS);
                });
            }

            function getSelectedNotificationType() {
                const selectedNotificationType = document.querySelector('input[name="notificationType"]:checked');
                return selectedNotificationType ? selectedNotificationType.value : '';
            }

            function saveUserChoice(choice) {
                localStorage.setItem('userChoice', choice);
            }
            function saveUserChoiceLS(choice) {
                localStorage.setItem('userChoiceLS', choice);
            }
            function saveUserVolume(volume) {
                localStorage.setItem('volume', volume);
            }
            function saveUserVolumeLS(volume) {
                localStorage.setItem('volumeLS', volume);
            }
        }
    }

    document.querySelector('#newls').addEventListener('DOMCharacterDataModified', (e) => {
        const targetElement = e.target;
        const newText = targetElement.textContent.trim();

        if (newText !== '') {
            audioLSNotification.volume = volumeLS;
            audioLSNotification.play();
        }
    });

    function addStylesButton() {
        const chatForm = document.querySelector('#chat_form');
        const notificationType = document.querySelector('#notificationType');

        // Стили для #chat_form
        chatForm.style.position = 'relative';

        // Стили для #notificationType
        notificationType.style.width = '200px';
        notificationType.style.height = '18px';
        notificationType.style.backgroundColor = 'var(--bg-color0)';
        notificationType.style.borderRadius = '5px';
        notificationType.style.border = '0.5px solid var(--bg-color4)';
        notificationType.style.color = 'var(--tx-color2)';
        notificationType.style.right = '220px';
        notificationType.style.top = '24px';
        notificationType.style.position = 'absolute';
        notificationType.style.textAlign = 'center';
    }

    function addStylesMenu() {
        const notificationTypeMenu = document.querySelector('#notificationTypeMenu');

        // Стили для #notificationTypeMenu
        notificationTypeMenu.style.width = '200px';
        notificationTypeMenu.style.height = '250px';
        notificationTypeMenu.style.backgroundColor = 'var(--bg-color0)';
        notificationTypeMenu.style.borderRadius = '5px';
        notificationTypeMenu.style.border = '0.5px solid var(--bg-color4)';
        notificationTypeMenu.style.color = 'var(--tx-color2)';
        notificationTypeMenu.style.right = '29px';
        notificationTypeMenu.style.top = '66px';
        notificationTypeMenu.style.position = 'absolute';

        // Стили для #notificationTypeMenu > div:not(:last-child)
        const menuDivs = notificationTypeMenu.querySelectorAll('div:not(:last-child)');
        menuDivs.forEach((div) => {
            div.style.borderBottom = '0.5px solid var(--bg-color4)';
        });

        // Стили для #notificationTypeMenu > div
        const allMenuDivs = notificationTypeMenu.querySelectorAll('div');
        allMenuDivs.forEach((div) => {
            div.style.height = '30px';
            div.style.display = 'flex';
            div.style.alignItems = 'center';
        });
    }


    const observerChat = new MutationObserver(handleNewElements);
    observerChat.observe(document.body, { childList: true, subtree: true });



    chooseNotificationType();
    addStylesButton();
})();