Block Twitch Emotes with Color Panel 1.5

Twitch emoji blocking via a channel with a management interface (via Tampermonkey)

目前為 2024-12-09 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Block Twitch Emotes with Color Panel 1.5
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Twitch emoji blocking via a channel with a management interface (via Tampermonkey)
// @author       lelusandE 
// @license MIT
// @match        https://www.twitch.tv/*
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @icon         https://example.com/icon.png
// ==/UserScript==

(function () {
    'use strict';

    // Получаем список заблокированных каналов из хранилища
    let blockedChannels = GM_getValue('blockedChannels', []);

    // Состояние панели (открыта или закрыта)
    let isPanelVisible = GM_getValue('isPanelVisible', false);

    // Создание интерфейса управления (панель с кнопками)
    const controlPanel = document.createElement('div');
    controlPanel.style.position = 'fixed';
    controlPanel.style.bottom = '124px';
    controlPanel.style.left = '1210px';
    controlPanel.style.width = '350px';
    controlPanel.style.backgroundColor = '#4c2a5e';
    controlPanel.style.border = '1px solid #ccc';
    controlPanel.style.borderRadius = '8px';
    controlPanel.style.padding = '10px';
    controlPanel.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
    controlPanel.style.zIndex = 10000;
    controlPanel.style.fontFamily = 'Arial, sans-serif';

    const title = document.createElement('h4');
    title.innerText = 'Block Twitch Emotes';
    title.style.margin = '0 0 10px 0';
    title.style.color = '#fff';
    controlPanel.appendChild(title);

    const list = document.createElement('ul');
    list.id = 'blockedChannelsList';
    list.style.listStyle = 'none';
    list.style.padding = '0';
    list.style.margin = '0 0 10px 0';
    list.style.maxHeight = '100px';
    list.style.overflowY = 'auto';
    list.style.border = '1px solid #ddd';
    list.style.borderRadius = '4px';
    list.style.backgroundColor = '#4c2a5e';
    list.style.color = '#fff';

    // Функция обновления списка каналов
    function updateChannelList() {
        list.innerHTML = ''; // Очищаем список
        blockedChannels.forEach(channel => {
            const item = document.createElement('li');
            item.style.display = 'flex';
            item.style.justifyContent = 'space-between';
            item.style.padding = '5px';
            item.style.borderBottom = '1px solid #eee';

            const channelName = document.createElement('span');
            channelName.innerText = channel;
            item.appendChild(channelName);

            // Добавляем кнопку "Удалить канал"
            const removeButton = document.createElement('button');
            removeButton.innerText = 'Delete channel';
            removeButton.style.background = '#ff4d4d';
            removeButton.style.color = '#fff';
            removeButton.style.border = 'none';
            removeButton.style.borderRadius = '4px';
            removeButton.style.padding = '2px 6px';
            removeButton.style.cursor = 'pointer';
            removeButton.onclick = function () {
                // Удаляем канал из списка
                blockedChannels = blockedChannels.filter(c => c !== channel);
                GM_setValue("blockedChannels", blockedChannels);
                updateChannelList();
                // Обновляем список на панели
            };
            item.appendChild(removeButton);

            list.appendChild(item);
        });
    }

    controlPanel.appendChild(list);

    const inputContainer = document.createElement('div');
    inputContainer.style.display = 'flex';
    inputContainer.style.gap = '5px';

    const input = document.createElement('input');
    input.type = 'text';
    input.placeholder = 'type channel name';
    input.style.flex = '1';
    input.style.padding = '5px';
    input.style.border = '1px solid #ccc';
    input.style.borderRadius = '4px';

    const addButton = document.createElement('button');
    addButton.innerText = 'Add it';
    addButton.style.background = '#4CAF50';
    addButton.style.color = '#fff';
    addButton.style.border = 'none';
    addButton.style.borderRadius = '4px';
    addButton.style.padding = '5px 10px';
    addButton.style.cursor = 'pointer';
    addButton.onclick = (event) => {
        event.preventDefault(); // Предотвращаем перезагрузку страницы
        const channel = input.value.trim();
        if (channel && !blockedChannels.includes(channel)) {
            blockedChannels.push(channel);
            GM_setValue("blockedChannels", blockedChannels);
            updateChannelList();
            input.value = ''; // Очищаем поле ввода
        }
    };

    inputContainer.appendChild(input);
    inputContainer.appendChild(addButton);
    controlPanel.appendChild(inputContainer);

    document.body.appendChild(controlPanel);

    // Функция для скрытия смайликов, если они принадлежат заблокированному каналу
    function hideEmotesForChannel(node) {
        const emotes = node.querySelectorAll('.chat-line__message img');
        emotes.forEach(emote => {
            const emoteName = emote.getAttribute('alt');
            if (emoteName && isEmoteFromBlockedChannel(emoteName)) {
                console.log('Скрываем смайлик для канала:', emoteName); // Для отладки
                emote.style.display = 'none';
            }
        });
    }

    // Проверка, принадлежит ли смайлик заблокированному каналу
    function isEmoteFromBlockedChannel(emoteName) {
        return blockedChannels.some(channel => emoteName.includes(channel));
    }

    // Наблюдатель за изменениями в DOM для чата
    const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            if (mutation.addedNodes.length) {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) { // Проверяем, что это элемент
                        hideEmotesForChannel(node);
                    }
                });
            }
        });
    });

    // Функция для ожидания появления контейнера чата
    function waitForChatContainer() {
        const chatContainer = document.querySelector('.chat-scrollable-area__message-container') ||
            document.querySelector('.ScThumbnail__wrapper');

        if (chatContainer) {
            observer.observe(chatContainer, { childList: true, subtree: true });
            console.log("Найден контейнер чата.");
        } else {
            console.log("Чат еще не загружен. Повторная попытка...");
            setTimeout(waitForChatContainer, 1000); // Пробуем снова через 1 секунду
        }
    }

    // Запускаем функцию ожидания контейнера чата
    waitForChatContainer();

    // Функция для переключения видимости панели
    function togglePanelVisibility() {
        isPanelVisible = !isPanelVisible;
        GM_setValue('isPanelVisible', isPanelVisible);
        controlPanel.style.display = isPanelVisible ? 'block' : 'none';
    }

    // Добавляем команду в меню Tampermonkey для управления панелью
    GM_registerMenuCommand('Toggle Block Emotes Panel', togglePanelVisibility);

    // Восстанавливаем состояние панели при загрузке страницы
    if (isPanelVisible) {
        controlPanel.style.display = 'block';
    } else {
        controlPanel.style.display = 'none';
    }

    // Инициализируем список каналов
    updateChannelList();

})();