// ==UserScript==
// @name Viewer list bot remover
// @namespace https://gf.qytechs.cn/scripts?set=586193
// @version 1.1.1
// @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==
let viewersPanelName = "";
let moderatorsPanelName = "";
const botsPanelDescription = "Description for bots... Well they're bots :)";
const botsPanelTitle = "Bots";
// Must keep in alphabetical order !!
const botList = [
"01ella",
"0ax2",
"0niva",
"24_7_chatting_on_discord",
"a_ok",
"aliceydra",
"aliengathering",
"anotherttvviewer",
"aten",
"audycia",
"buttsbot",
"commanderroot",
"creatisbot",
"digitalinstinct",
"discordstreamercommunity",
"drapsnatt",
"freakybabetv",
"iisabei",
"kattah",
"lurxx",
"lylituf",
"maddynsun",
"moobot",
"nightbot",
"network_streamer_discord",
"paradise_for_streamers",
"pdp_bot",
"qnfgogtktls",
"rogueg1rl",
"roxesy",
"soundalerts",
"srekrapstob",
"streamelements",
"thisisunreallol",
"waptart",
];
//const botsPanelHtml = '<div class="Layout-sc-1xcs6mc-0 krdEPl"><button class="InjectLayout-sc-1i43xsx-0 daSCXI"><div class="Layout-sc-1xcs6mc-0 bJDKHm"><img alt="Badge VIP de communauté" class="InjectLayout-sc-1i43xsx-0 lfGYGL tw-image" srcset="https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/2 1x,https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/3 2x" src="https://static-cdn.jtvnw.net/badges/v1/b817aba4-fad8-49e2-b88a-7cc744dfa6ec/2"><div class="Layout-sc-1xcs6mc-0 iBQNvW"><p class="CoreText-sc-1txzju1-0 hronDY">Bots</p></div><div direction="up" class="ScExpandableIndicator-sc-f0maby-0 judUOu expandable-indicator__wrapper"><figure class="ScFigure-sc-wkgzod-0 bIevzH tw-svg"><svg width="2rem" height="2rem" viewBox="0 0 20 20"><path d="M6.5 5.5 11 10l-4.5 4.5L8 16l6-6-6-6-1.5 1.5z"></path></svg></figure></div></div></button><div class="Layout-sc-1xcs6mc-0 hvqWsm">Description for bots... Well they\'re bots :)</div><div class="Layout-sc-1xcs6mc-0"></div></div>';
//const botsPanelHtml = '<div class="Layout-sc-1xcs6mc-0 krdEPl"><button class="InjectLayout-sc-1i43xsx-0 daSCXI"><div class="Layout-sc-1xcs6mc-0 bJDKHm"><img alt="Bot badge" class="InjectLayout-sc-1i43xsx-0 lfGYGL tw-image" src="https://cdn-icons-png.flaticon.com/512/9092/9092067.png"><div class="Layout-sc-1xcs6mc-0 iBQNvW"><p class="CoreText-sc-1txzju1-0 hronDY">Bots</p></div><div direction="up" class="ScExpandableIndicator-sc-f0maby-0 judUOu expandable-indicator__wrapper"><figure class="ScFigure-sc-wkgzod-0 bIevzH tw-svg"><svg width="2rem" height="2rem" viewBox="0 0 20 20"><path d="M6.5 5.5 11 10l-4.5 4.5L8 16l6-6-6-6-1.5 1.5z"></path></svg></figure></div></div></button><div class="Layout-sc-1xcs6mc-0 hvqWsm">Description for bots... Well they\'re bots :)</div><div class="Layout-sc-1xcs6mc-0"></div></div>';
var viewerListShown = false;
function delay(milliseconds) {
return new Promise(resolve => {
setTimeout(resolve, milliseconds);
});
}
async function detectLanguage() {
var div = document.querySelector('[class="ScInputBase-sc-vu7u7d-0 ScInput-sc-19xfhag-0 gXVFsI jhrDHh InjectLayout-sc-1i43xsx-0 kvZgmT tw-input tw-input--large"]');
let count = 0;
while (div === null)
{
await delay(1000);
div = document.querySelector('[class="ScInputBase-sc-vu7u7d-0 ScInput-sc-19xfhag-0 gXVFsI jhrDHh InjectLayout-sc-1i43xsx-0 kvZgmT tw-input tw-input--large"]');
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}`);
}
void async function() {
'use strict';
await detectLanguage();
let prevUrl = undefined;
setInterval(async () => {
const currUrl = window.location.href;
if (currUrl != prevUrl)
{
prevUrl = currUrl;
await setup();
}
}, 60);
}();
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", buttonClick);
}
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(50);
}
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 isLastPanel = container.lastChild === panel;
let bots = [];
for (let i = 0; i < viewers.children.length - (isLastPanel ? 1 : 0); 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 === (isLastPanel ? 1 : 0))
{
if (isLastPanel)
{
let endScroll = viewers.lastChild;
panel.previousSibling.firstChild.lastChild.lastChild.after(endScroll);
}
panel.remove();
}
return bots;
}
// Also works
/*function createBotsPanel(container) {
let botsPanel = document.createElement("div");
botsPanel.innerHTML = botsPanelHtml;
container.lastChild.after(botsPanel);
return botsPanel;
}*/
function createBotsPanel(container) {
let botsPanel = container.lastChild.cloneNode(true); // Doesn't copy event listeners FeelsSadMan
container.lastChild.firstChild.lastChild.lastChild.remove();
let botImg = document.createElement("img");
botImg.setAttribute("class", "InjectLayout-sc-1i43xsx-0 lfGYGL tw-image");
botImg.setAttribute("alt", "Bot badge");
botImg.setAttribute("src", "https://cdn-icons-png.flaticon.com/512/9092/9092067.png");
botsPanel.firstChild.children[0].firstChild.children[0].remove();
botsPanel.firstChild.children[0].firstChild.insertBefore(botImg, botsPanel.firstChild.children[0].firstChild.children[0]);
botsPanel.firstChild.children[0].firstChild.children[1].firstChild.innerHTML = botsPanelTitle;
botsPanel.firstChild.children[1].innerHTML = botsPanelDescription;
let viewers = botsPanel.firstChild.children[2];
while (viewers.children[1])
{
viewers.removeChild(viewers.firstChild);
}
container.lastChild.after(botsPanel);
return botsPanel;
}
function handleViewerPanel(panel, container) {
let bots = removeBots(panel, container);
if (bots.length === 0)
{
return;
}
let botsPanel = createBotsPanel(container);
let viewers = botsPanel.firstChild.lastChild;
for (let bot of bots)
{
viewers.insertBefore(bot, viewers.lastChild);
}
}
async function buttonClick() {
if (viewerListShown)
{
viewerListShown = false;
return;
}
viewerListShown = true;
var container = await getContainer();
for (let i = 1; i < container.children.length; i++)
{
let panelName = container.children[i].firstChild.firstChild.firstChild.children[1].firstChild.firstChild.textContent;
if (panelName === viewersPanelName)
{
// Viewers
handleViewerPanel(container.children[i], container);
}
}
}