// ==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 // @downloadURL none // ==/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)); })();