Viewer list bot remover

Places bots in their own category in the viewer list.

// ==UserScript==
// @name         Viewer list bot remover
// @namespace    https://gf.qytechs.cn/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);
}

QingJ © 2025

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