// ==UserScript== // @name Twitch Auto Click Channel Points Chest and Statistics // @name:zh-TW Twitch 自動點擊忠誠點數寶箱和統計 // @name:zh-CN Twitch 自动点击忠诚点数宝箱和统计 // @namespace http://tampermonkey.net/ // @version 2.5 // @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 // @downloadURL none // ==/UserScript== (function() { 'use strict'; let totalPoints = 0; let lastUrl = location.href; let lastCheckTime = 0; let isProcessing = false; const recentPopups = new Set(); const DEBOUNCE_DELAY = 500; const CHECK_INTERVAL = 3000; // 檢查寶箱的固定間隔為 3 秒 const PANEL_UPDATE_INTERVAL = 2000; // 面板更新間隔縮短為 2 秒,確保面板優先顯示 // 優先處理面板顯示 function findMainBtn() { for (let i = 0; i < 3; i++) { // 最多嘗試3次 const btn = ( document.querySelector('button[aria-label*="點數"]') || document.querySelector('button[aria-label*="Points"]') || document.querySelector('button[aria-label*="忠誠"]') || document.querySelector('button[aria-label*="Channel"]') ); if (btn) return btn; } return null; } function createPanel() { const panel = document.createElement('span'); panel.id = 'my-loyalty-points-panel'; panel.style.cssText = ` background: #18181b; color: #FFD600; padding: 2px 6px; margin-left: 6px; border-radius: 6px; font-size: 14px; vertical-align: middle; display: inline-block; z-index: 9999; `; panel.innerText = `${totalPoints} Point`; return panel; } function insertPanel() { const oldPanel = document.getElementById('my-loyalty-points-panel'); if (oldPanel) oldPanel.remove(); const mainBtn = findMainBtn(); if (mainBtn && !mainBtn.querySelector('#my-loyalty-points-panel')) { const panel = createPanel(); mainBtn.appendChild(panel); return true; } return false; } function updatePanel() { let panel = document.getElementById('my-loyalty-points-panel'); if (!panel) { if (!insertPanel()) { // 如果插入失敗,設定短時間後重試 setTimeout(updatePanel, 500); return; } panel = document.getElementById('my-loyalty-points-panel'); } if (panel) { panel.innerText = `${totalPoints} Point`; } } // 點數監控相關 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(); } } } } } } function observePointPopups() { let throttleTimer; const observer = new MutationObserver((mutations) => { if (throttleTimer) return; throttleTimer = setTimeout(() => { 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); } } throttleTimer = null; }, 500); // 降低節流時間以確保不錯過點數更新 }); observer.observe(document.body, { childList: true, subtree: true, characterData: false, attributes: false }); } // 寶箱點擊相關 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 now = Date.now(); if (isProcessing || now - lastCheckTime < DEBOUNCE_DELAY) return; isProcessing = true; lastCheckTime = now; 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(); break; } } isProcessing = false; } function watchUrlChange() { if (location.href !== lastUrl) { lastUrl = location.href; totalPoints = 0; updatePanel(); recentPopups.clear(); } } function main() { // 優先初始化面板 const initPanel = () => { if (!insertPanel()) { setTimeout(initPanel, 500); } }; initPanel(); // 初始化點數監控 observePointPopups(); // 設定面板更新間隔 setInterval(() => { updatePanel(); }, PANEL_UPDATE_INTERVAL); // 設定寶箱檢查和 URL 變化監控 setInterval(() => { checkAndClickChest(); watchUrlChange(); }, CHECK_INTERVAL); } // 縮短初始啟動時間,優先啟動面板功能 window.addEventListener('load', () => { setTimeout(() => { insertPanel(); observePointPopups(); }, 1000); // 延遲啟動其他功能 setTimeout(main, 3000); }); })();