您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Places bots in their own category in the viewer list.
// ==UserScript== // @name Viewer list bot remover // @namespace https://greasyfork.org/scripts?set=586259 // @version 1.2.7 // @description Places bots in their own category in the viewer list. // @author Sonyo // @match http*://www.twitch.tv/* // @grant none // @license MIT // @icon https://cdn-icons-png.flaticon.com/512/9092/9092067.png // ==/UserScript== /* * OPTIONS * Modify the following variable for the behavior concerning moderator bots: */ const modBotsBehavior = 2; /* * 0: Keep them in the Moderators panel * 1: Place them with the other bots * 2: Place them in their own panel */ let viewersPanelName = ""; let moderatorsPanelName = ""; const botsPanelTitle = "Bots"; const botsPanelDescription = "Description for bots... Well they're bots :)"; const botImageSource = "https://cdn-icons-png.flaticon.com/512/9092/9092067.png"; const modBotsPanelTitle = "Moderator bots"; const modBotsPanelDescription = "Bots used for the moderation of this channel."; const modBotImageSource = "https://static-cdn.jtvnw.net/badges/v1/3267646d-33f0-4b17-b3df-f923a41db1d0/2"; var botList; var viewerListShown = false; var botsPanel = null; function delay(milliseconds) { return new Promise(resolve => { setTimeout(resolve, milliseconds); }); } async function detectLanguage() { var div = document.querySelector('.gWqzmh'); let count = 0; while (div === null) { await delay(1000); div = document.querySelector('.gWqzmh'); count++; if (count > 15) { console.log("[Viewer list bot remover]: Search bar not found, script not working"); return; } } let searchText = div.placeholder; switch (searchText) { case "Search": viewersPanelName = "Viewers"; moderatorsPanelName = "Moderators"; break; case "Søg": viewersPanelName = "Seere"; moderatorsPanelName = "Moderatorer"; break; case "Suchen": viewersPanelName = "Zuschauer"; moderatorsPanelName = "Moderatoren"; break; case "Buscar": viewersPanelName = "Espectadores"; moderatorsPanelName = "Moderadores"; break; case "Rechercher": viewersPanelName = "Spectateurs"; moderatorsPanelName = "Modérateurs"; break; case "Cerca": viewersPanelName = "Spettatori"; moderatorsPanelName = "Moderatori"; break; case "Keresés": viewersPanelName = "Nézők"; moderatorsPanelName = "Moderátorok"; break; case "Zoeken": viewersPanelName = "Kijkers"; moderatorsPanelName = "Moderators"; break; case "Søk": viewersPanelName = "Seere"; moderatorsPanelName = "Moderatorer"; break; case "Wyszukaj": viewersPanelName = "widzowie"; moderatorsPanelName = "Moderatorzy"; break; case "Pesquisa": viewersPanelName = "Espetadores"; moderatorsPanelName = "Moderadores"; break; case "Căutare": viewersPanelName = "Vizualizatori"; moderatorsPanelName = "Moderatori"; break; case "Hľadať": viewersPanelName = "Diváci"; moderatorsPanelName = "Moderátori"; break; case "Etsi": viewersPanelName = "Katsojat"; moderatorsPanelName = "Moderaattorit"; break; case "Sök": viewersPanelName = "Tittare"; moderatorsPanelName = "Moderatorer"; break; case "Tìm kiếm": viewersPanelName = "Người xem"; moderatorsPanelName = "Người điều hành"; break; case "Ara": viewersPanelName = "İzleyici"; moderatorsPanelName = "Moderatörler"; break; case "Hledat": viewersPanelName = "Diváci"; moderatorsPanelName = "Moderátoři"; break; case "Αναζήτηση": viewersPanelName = "Θεατές"; moderatorsPanelName = "Επόπτες"; break; case "Търсене": viewersPanelName = "Зрители"; moderatorsPanelName = "Модератори"; break; case "Поиск": viewersPanelName = "Зрители"; moderatorsPanelName = "Модераторы"; break; case "ค้นหา": viewersPanelName = "ผู้ชม"; moderatorsPanelName = "ผู้ดำเนินรายการ"; break; case "搜索": viewersPanelName = "观众"; moderatorsPanelName = "管理员"; break; case "搜尋": viewersPanelName = "觀眾"; moderatorsPanelName = "Mod"; break; case "検索": viewersPanelName = "視聴者数"; moderatorsPanelName = "モデレーター"; break; case "검색": viewersPanelName = "시청자 수"; moderatorsPanelName = "매니저"; break; default: alert("Unknown language !"); break; } //alert(`viewers:${viewersPanelName}, mods:${moderatorsPanelName}`); } async function getBotList() { botList = []; await fetch('https://api.twitchinsights.net/v1/bots/all') .then(response => response.json()) .then(json => { for (let bot of json.bots) { botList.push(bot[0]); } botList.sort(); }); } void async function () { 'use strict'; await getBotList(); await detectLanguage(); let prevUrl = undefined; setInterval(async () => { const currUrl = window.location.href; if (currUrl != prevUrl) { prevUrl = currUrl; await setup(); } }, 1000); }(); async function setup() { var communityButton = document.querySelector('[data-test-selector="chat-viewer-list"]'); let count = 0; while (communityButton === null) { await delay(1000); communityButton = document.querySelector('[data-test-selector="chat-viewer-list"]'); count++; if (count > 15) { console.log("[Viewer list bot remover]: Community button not found, script not working"); return; } } viewerListShown = false; communityButton.addEventListener("click", communityButtonClick); } async function getContainer() { // Get the viewers container var scrollable = null; let count = 1; while (scrollable === null) { scrollable = document.querySelector('[class="scrollable-area scrollable-area--suppress-scroll-x"]'); count++; if (count > 300) // 50ms * 300 = 15s { console.log("[Viewer list bot remover]: Loading took too long"); return; } await delay(100); } return scrollable.lastChild.firstChild.firstChild; } function binarySearch(name) { let start = 0; let end = botList.length - 1; while (start <= end) { let mid = Math.floor((start + end) / 2); if (botList[mid] === name) return true; if (botList[mid] < name) start = mid + 1; else end = mid - 1; } return false; } function removeBots(panel, container) { let viewers = panel.firstChild.lastChild; let bots = []; for (let i = 0; i < viewers.children.length; i++) { let viewer = viewers.children[i]; let name = viewer.firstChild.firstChild.firstChild.firstChild.textContent; let remove = binarySearch(name.toLowerCase()); if (remove) { viewer.remove(); bots.push(viewer); i--; } } // If there is no more viewers, remove the panel if (viewers.children.length === 0) { panel.remove(); } return bots; } function createBotsPanel(panel, container, options) { let newPanel = panel.cloneNode(true); let botImg = document.createElement("img"); botImg.setAttribute("class", "InjectLayout-sc-1i43xsx-0 dztNuO tw-image"); botImg.setAttribute("alt", "Bot badge"); botImg.setAttribute("src", options.ImageSrc); newPanel.firstChild.firstChild.firstChild.children[0].remove(); newPanel.firstChild.firstChild.firstChild.insertBefore(botImg, newPanel.firstChild.children[0].firstChild.firstChild); newPanel.firstChild.firstChild.firstChild.children[1].firstChild.innerHTML = options.Title; newPanel.firstChild.firstChild.firstChild.children[2].remove(); let viewers = newPanel.firstChild.children[1]; while (viewers.firstChild) { viewers.removeChild(viewers.firstChild); } container.appendChild(newPanel); return newPanel; } function handleViewerPanel(panel, container) { let bots = removeBots(panel, container); if (bots.length === 0) { return; } if (botsPanel === null) { botsPanel = createBotsPanel(panel, container, {ImageSrc: botImageSource, Title: botsPanelTitle, Description: botsPanelDescription}); } let viewers = botsPanel.firstChild.lastChild; for (let bot of bots) { viewers.appendChild(bot); } } function handleModeratorPanel(panel, container) { if (modBotsBehavior < 0 || modBotsBehavior > 2) { alert("[Viewer list bot remover]: modBotsBehavior incorrectly set."); return; } if (modBotsBehavior === 0) { return; } let bots = removeBots(panel, container); if (bots.length === 0) { return; } let viewers; if (modBotsBehavior === 1) { botsPanel = createBotsPanel(panel, container, {ImageSrc: botImageSource, Title: botsPanelTitle, Description: botsPanelDescription}); viewers = botsPanel.firstChild.lastChild; } if (modBotsBehavior === 2) { let modBotsPanel = createBotsPanel(panel, container, {ImageSrc: modBotImageSource, Title: modBotsPanelTitle, Description: modBotsPanelDescription}) viewers = modBotsPanel.firstChild.lastChild; } for (let bot of bots) { viewers.insertBefore(bot, viewers.firstChild); } } async function communityButtonClick() { if (viewerListShown) { viewerListShown = false; return; } viewerListShown = true; var container = await getContainer(); if (container.children.length === 1) { // No one in chat return; } let endScroll = container.lastChild.lastChild.lastChild.lastChild; endScroll.remove(); botsPanel = null; for (let i = 1; i < container.children.length; i++) { let panel = container.children[i]; let panelName = panel.firstChild.firstChild.firstChild.children[1].firstChild.firstChild.textContent; switch (panelName) { case moderatorsPanelName: handleModeratorPanel(panel, container); break; case viewersPanelName: handleViewerPanel(panel, container); break; } if (panel.parentElement === null) { // Panel was empty, and got removed i--; } } container.lastChild.lastChild.lastChild.appendChild(endScroll); }