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.0
// @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==

const viewersPanelName = "Viewers"; // Need change if using a different language !
const botsPanelDescription = "Description for bots... Well they're bots :)";
const botsPanelTitle = "Bots";

// Must keep in alphabetical order !!
const botList = [
    "01ella",
    "0ax2",
    "24_7_chatting_on_discord",
    "a_ok",
    "aliceydra",
    "aliengathering",
    "anotherttvviewer",
    "aten",
    "audycia",
    "buttsbot",
    "commanderroot",
    "creatisbot",
    "digitalinstinct",
    "discordstreamercommunity",
    "drapsnatt",
    "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);
    });
}

void function() {
    'use strict';

    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 > 150) // 100ms * 150 = 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) {
    let viewers = panel.firstChild.lastChild;

    let bots = [];
    for (let i = 0; i < viewers.children.length - 1; 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 === 1)
    {
        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);
    if (bots.length === 0)
    {
        return;
    }

    let botsPanel = createBotsPanel(container);
    let viewers = botsPanel.firstChild.lastChild;
    for (let bot of bots)
    {
        viewers.lastChild.after(bot);
    }
}

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或关注我们的公众号极客氢云获取最新地址