Viewer list bot remover

Places bots in their own category in the viewer list.

目前为 2023-03-31 提交的版本。查看 最新版本

// ==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);
        }
    }
}

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址