您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Twitch emoji blocking via a channel with a management interface and context menu (via Tampermonkey)
当前为
// ==UserScript== // @name Block Twitch Emotes - with button Block emote. // @namespace http://tampermonkey.net/ // @version 1.7.0 // @description Twitch emoji blocking via a channel with a management interface and context menu (via Tampermonkey) // @author // @license MIT // @match https://www.twitch.tv/* // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setValue // @grant GM_getValue // @icon https://cdn-icons-png.flaticon.com/512/10531/10531738.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 = '360px'; 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 closeButton = document.createElement('button'); closeButton.innerText = 'Close'; closeButton.style.background = '#DC3545'; closeButton.style.color = '#fff'; closeButton.style.border = '1px solid #ccc'; closeButton.style.width = '55px'; // Фиксированная ширина кнопки closeButton.style.height = '30px'; // Фиксированная высота кнопки closeButton.style.borderRadius = '8px'; closeButton.style.padding = '5px 10px'; closeButton.style.cursor = 'pointer'; closeButton.style.marginTop = '10px'; closeButton.onclick = () => { isPanelVisible = false; GM_setValue('isPanelVisible', isPanelVisible); controlPanel.style.display = 'none'; // Скрыть панель }; controlPanel.appendChild(closeButton); 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 = '230px'; list.style.height = '550px'; 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(); updateCounter(); }; 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(); updateCounter(); input.value = ''; } }; const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.gap = '6px'; buttonContainer.style.marginTop = '10px'; const clearAllButton = document.createElement('button'); clearAllButton.innerText = 'Delete all'; clearAllButton.style.background = '#DC3545'; clearAllButton.style.color = '#fff'; clearAllButton.style.border = 'none'; clearAllButton.style.borderRadius = '4px'; clearAllButton.style.padding = '5px 10px'; clearAllButton.style.cursor = 'pointer'; buttonContainer.appendChild(clearAllButton); clearAllButton.onclick = () => { blockedChannels = []; GM_setValue("blockedChannels", blockedChannels); updateChannelList(); updateCounter(); }; const exportButton = document.createElement('button'); exportButton.innerText = 'Export'; exportButton.style.background = '#007BFF'; exportButton.style.color = '#fff'; exportButton.style.border = 'none'; exportButton.style.borderRadius = '4px'; exportButton.style.padding = '5px 10px'; exportButton.style.cursor = 'pointer'; buttonContainer.appendChild(exportButton); exportButton.onclick = () => { const blob = new Blob([JSON.stringify(blockedChannels, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'blocked_channels.json'; link.click(); URL.revokeObjectURL(url); }; const importButton = document.createElement('button'); importButton.innerText = 'Import'; importButton.style.background = '#28A745'; importButton.style.color = '#fff'; importButton.style.border = 'none'; importButton.style.borderRadius = '4px'; importButton.style.padding = '5px 10px'; importButton.style.cursor = 'pointer'; buttonContainer.appendChild(importButton); importButton.onclick = () => { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = 'application/json'; fileInput.onchange = (event) => { const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { try { const importedChannels = JSON.parse(e.target.result); if (Array.isArray(importedChannels)) { blockedChannels = Array.from(new Set([...blockedChannels, ...importedChannels])); GM_setValue("blockedChannels", blockedChannels); updateChannelList(); updateCounter(); } else { alert('Invalid file format!'); } } catch { alert('Error reading file!'); } }; reader.readAsText(file); }; fileInput.click(); }; // Счётчик const counter = document.createElement('div'); counter.style.display = 'inline-block'; counter.style.backgroundColor = '#fff'; counter.style.color = '#4c2a5e'; counter.style.border = '1px solid #ccc'; counter.style.borderRadius = '8px'; counter.style.padding = '5px 10px'; counter.style.marginLeft = '6px'; counter.style.fontWeight = 'bold'; buttonContainer.appendChild(counter); function updateCounter() { counter.innerText = `Count: ${blockedChannels.length}`; } inputContainer.appendChild(input); inputContainer.appendChild(addButton); controlPanel.appendChild(inputContainer); controlPanel.appendChild(buttonContainer); 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)) { emote.style.display = 'none'; } }); } function isEmoteFromBlockedChannel(emoteName) { return blockedChannels.some(channel => emoteName.includes(channel)); } const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) hideEmotesForChannel(node); }); }); }); // Создаем кнопку "Open Panel Blocker" const openPanelButton = document.createElement('button'); openPanelButton.innerText = 'Open Panel Blocker'; openPanelButton.style.position = 'fixed'; // Фиксированное положение openPanelButton.style.width = '140px'; // Фиксированная ширина кнопки openPanelButton.style.height = '38px'; // Фиксированная высота кнопки openPanelButton.style.background = '#4c2a5e'; openPanelButton.style.color = '#fff'; openPanelButton.style.border = '1px solid #ccc'; openPanelButton.style.borderRadius = '8px'; openPanelButton.style.padding = '10px'; openPanelButton.style.cursor = 'pointer'; openPanelButton.style.zIndex = 10000; // Высокий z-index // Добавляем кнопку в DOM только после полной загрузки страницы window.addEventListener('load', () => { document.body.appendChild(openPanelButton); // Устанавливаем начальное положение кнопки const updateButtonPosition = () => { const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; // Позиция кнопки (например, 5% от высоты и 10% от ширины) openPanelButton.style.top = `${windowHeight * 0.01}px`; // 2% от высоты окна openPanelButton.style.right = `${windowWidth * 0.2}px`; // 20% от ширины окна }; // Устанавливаем положение кнопки сразу updateButtonPosition(); // Обновляем положение кнопки при изменении размеров окна window.addEventListener('resize', updateButtonPosition); }); // Обработчик клика для открытия панели openPanelButton.onclick = () => { isPanelVisible = true; GM_setValue('isPanelVisible', isPanelVisible); controlPanel.style.display = 'block'; }; function waitForChatContainer() { const chatContainer = document.querySelector('.chat-scrollable-area__message-container'); if (chatContainer) observer.observe(chatContainer, { childList: true, subtree: true }); else setTimeout(waitForChatContainer, 100); } waitForChatContainer(); // Periodic check to hide emotes that may have been re-rendered setInterval(() => { const chatContainer = document.querySelector('.chat-scrollable-area__message-container'); if (chatContainer) { const nodes = chatContainer.querySelectorAll('.chat-line__message'); nodes.forEach(node => hideEmotesForChannel(node)); } }, 100); // Checking every second GM_registerMenuCommand('Toggle Block Emotes Panel', () => { isPanelVisible = !isPanelVisible; GM_setValue('isPanelVisible', isPanelVisible); controlPanel.style.display = isPanelVisible ? 'block' : 'none'; }); if (isPanelVisible) controlPanel.style.display = 'block'; else controlPanel.style.display = 'none'; updateChannelList(); updateCounter(); // Добавляем контекстное меню document.addEventListener('contextmenu', (event) => { const target = event.target; if (target.tagName === 'IMG' && target.closest('.chat-line__message')) { event.preventDefault(); const emoteAlt = target.getAttribute('alt'); if (emoteAlt) { const emotePrefix = emoteAlt.split(/[^a-zA-Z0-9]/)[0]; const contextMenu = document.createElement('div'); contextMenu.style.position = 'absolute'; contextMenu.style.top = `${event.pageY}px`; contextMenu.style.left = `${event.pageX}px`; contextMenu.style.background = '#4c2a5e'; contextMenu.style.border = '1px solid #ccc'; contextMenu.style.padding = '5px'; contextMenu.style.zIndex = 10001; contextMenu.style.cursor = 'pointer'; contextMenu.innerText = `Block Emote (${emotePrefix})`; document.body.appendChild(contextMenu); contextMenu.addEventListener('click', () => { if (!blockedChannels.includes(emotePrefix)) { blockedChannels.push(emotePrefix); GM_setValue("blockedChannels", blockedChannels); updateChannelList(); updateCounter(); } contextMenu.remove(); }); document.addEventListener('click', () => contextMenu.remove(), { once: true }); } } }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址