Twitch 自动点击忠诚点数宝箱和统计

自动点击 Twitch 忠诚点数宝箱,并监控所有点数增加,切换直播间累积归零

安装此脚本?
作者推荐脚本

您可能也喜欢Twitch 截图助手

安装此脚本
// ==UserScript==
// @name         Twitch Auto Click Channel Points Chest and Statistics
// @name:zh-TW   Twitch 自動點擊忠誠點數寶箱和統計
// @name:zh-CN   Twitch 自动点击忠诚点数宝箱和统计
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Automatically click the Twitch channel points chest, monitor all point increases, and reset the accumulated total when switching channels.
// @description:zh-TW 自動點擊 Twitch 忠誠點數寶箱,並監控所有點數增加,切換直播間累積歸零
// @description:zh-CN 自动点击 Twitch 忠诚点数宝箱,并监控所有点数增加,切换直播间累积归零
// @author       chatgpt
// @match        https://www.twitch.tv/*
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    let totalPoints = 0;
    let lastUrl = location.href;
    const recentPopups = new Set();

    // 支援多語言與關鍵字的主按鈕尋找
    function findMainBtn() {
        // 常見關鍵字:點數、Points、忠誠、Channel Points
        return (
            document.querySelector('button[aria-label*="點數"]') ||
            document.querySelector('button[aria-label*="Points"]') ||
            document.querySelector('button[aria-label*="忠誠"]') ||
            document.querySelector('button[aria-label*="Channel"]')
        );
    }

    function createPanel() {
        const panel = document.createElement('span');
        panel.id = 'my-loyalty-points-panel';
        panel.style.background = '#18181b';
        panel.style.color = '#FFD600';
        panel.style.padding = '2px 8px';
        panel.style.marginLeft = '8px';
        panel.style.borderRadius = '8px';
        panel.style.fontSize = '14px';
        panel.style.verticalAlign = 'middle';
        panel.style.display = 'inline-block';
        panel.style.zIndex = 9999;
        panel.innerText = `累積領取:${totalPoints} 點`;
        return panel;
    }

    // 插入提示框到主按鈕的最後面
    function insertPanel() {
        const oldPanel = document.getElementById('my-loyalty-points-panel');
        if (oldPanel) oldPanel.remove();

        const mainBtn = findMainBtn();
        if (mainBtn) {
            if (!mainBtn.querySelector('#my-loyalty-points-panel')) {
                const panel = createPanel();
                mainBtn.appendChild(panel);
            }
        }
    }

    function updatePanel() {
        let panel = document.getElementById('my-loyalty-points-panel');
        if (!panel) {
            insertPanel();
            panel = document.getElementById('my-loyalty-points-panel');
        }
        if (panel) {
            panel.innerText = `累積領取:${totalPoints} 點`;
        }
    }

    function isInDialog(node) {
        while (node) {
            if (
                (node.getAttribute && node.getAttribute('role') === 'dialog') ||
                (node.classList && node.classList.contains('tw-modal'))
            ) {
                return true;
            }
            node = node.parentElement;
        }
        return false;
    }

    function checkAndClickChest() {
        const iconDivs = document.querySelectorAll('.claimable-bonus__icon');
        for (const iconDiv of iconDivs) {
            const btn = iconDiv.closest('button');
            if (
                btn &&
                !btn.disabled &&
                btn.offsetParent !== null &&
                !isInDialog(btn)
            ) {
                btn.click();
                return;
            }
        }
    }

    // 精準抓取浮動分數提示
    function handlePopupNode(node) {
        if (
            node.classList &&
            node.classList.contains('Layout-sc-1xcs6mc-0') &&
            node.classList.contains('bgzAOg')
        ) {
            for (const child of node.childNodes) {
                if (child.nodeType === Node.TEXT_NODE) {
                    const text = child.textContent.trim();
                    const match = text.match(/^\+(\d+)\s*點?$/);
                    if (match) {
                        const key = text + '_' + Date.now();
                        for (let k of recentPopups) {
                            if (k.startsWith(text)) return;
                        }
                        recentPopups.add(key);
                        setTimeout(() => recentPopups.delete(key), 1000);

                        const add = parseInt(match[1], 10);
                        if (!isNaN(add)) {
                            totalPoints += add;
                            updatePanel();
                        }
                    }
                }
            }
        }
    }

    // 監控所有新出現的 .Layout-sc-1xcs6mc-0.bgzAOg
    function observePointPopups() {
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (!(node instanceof HTMLElement)) continue;
                    if (
                        node.classList.contains('Layout-sc-1xcs6mc-0') &&
                        node.classList.contains('bgzAOg')
                    ) {
                        handlePopupNode(node);
                    }
                    // 也檢查所有子孫
                    node.querySelectorAll && node.querySelectorAll('.Layout-sc-1xcs6mc-0.bgzAOg').forEach(handlePopupNode);
                }
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    function watchUrlChange() {
        if (location.href !== lastUrl) {
            lastUrl = location.href;
            totalPoints = 0;
            updatePanel();
            recentPopups.clear();
        }
    }

    function main() {
        insertPanel();
        checkAndClickChest();
        watchUrlChange();
        observePointPopups();
        // 每秒自動補回提示框
        setInterval(() => {
            let panel = document.getElementById('my-loyalty-points-panel');
            if (!panel) insertPanel();
        }, 1000);
        // 其他定時任務
        setInterval(() => {
            checkAndClickChest();
            watchUrlChange();
        }, 1000);
    }

    window.addEventListener('load', () => setTimeout(main, 1500));
})();