Automatically claim channel points, enable theater mode, claim prime rewards, claim drops, and add redeem buttons for GOG and Legacy Games on Twitch and Amazon Gaming websites.
// ==UserScript== // @name Twitch Enhancements // @namespace http://tampermonkey.net/ // @version 0.5.3 // @description Automatically claim channel points, enable theater mode, claim prime rewards, claim drops, and add redeem buttons for GOG and Legacy Games on Twitch and Amazon Gaming websites. // @author JJJ // @match https://www.twitch.tv/* // @match https://gaming.amazon.com/* // @match https://www.twitch.tv/drops/inventory* // @match https://www.gog.com/en/redeem // @match https://promo.legacygames.com/* // @icon https://th.bing.com/th/id/R.d71be224f193da01e7e499165a8981c5?rik=uBYlAxJ4XyXmJg&riu=http%3a%2f%2fpngimg.com%2fuploads%2ftwitch%2ftwitch_PNG28.png&ehk=PMc5m5Fil%2bhyq1zilk3F3cuzxSluXFBE80XgxVIG0rM%3d&risl=&pid=ImgRaw&r=0 // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @license MIT // ==/UserScript== (function () { 'use strict'; // Add logger configuration const Logger = { styles: { info: 'color: #2196F3; font-weight: bold', warning: 'color: #FFC107; font-weight: bold', success: 'color: #4CAF50; font-weight: bold', error: 'color: #F44336; font-weight: bold' }, prefix: '[TwitchEnhancements]', getTimestamp() { return new Date().toISOString().split('T')[1].slice(0, -1); }, info(msg) { console.log(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.info); }, warning(msg) { console.warn(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.warning); }, success(msg) { console.log(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.success); }, error(msg) { console.error(`%c${this.prefix} ${this.getTimestamp()} - ${msg}`, this.styles.error); } }; // Twitch Constants const PLAYER_SELECTOR = '.video-player'; const THEATER_MODE_BUTTON_SELECTOR = 'button[aria-label="Modo cine (alt+t)"], button[aria-label="Theatre Mode (alt+t)"]'; const CLOSE_MENU_BUTTON_SELECTOR = 'button[aria-label="Close Menu"]'; const CLOSE_MODAL_BUTTON_SELECTOR = 'button[aria-label="Close modal"]'; const THEATER_MODE_CLASS = 'theatre-mode'; const CLAIMABLE_BONUS_SELECTOR = '.claimable-bonus__icon'; const CLAIM_DROPS_SELECTOR = 'button.ScCoreButton-sc-ocjdkq-0.eWlfQB'; const PRIME_REWARD_SELECTOR = 'button.tw-interactive.tw-button.tw-button--full-width[data-a-target="buy-box_call-to-action"] span.tw-button__text div.tw-inline-block p.tw-font-size-5.tw-md-font-size-4[title="Get game"]'; const PRIME_REWARD_SELECTOR_2 = 'p.tw-font-size-5.tw-md-font-size-4[data-a-target="buy-box_call-to-action-text"][title="Get game"]'; // Redeem on GOG Constants const GOG_REDEEM_CODE_INPUT_SELECTOR = '#codeInput'; const GOG_CONTINUE_BUTTON_SELECTOR = 'button[type="submit"][aria-label="Proceed to the next step"]'; const GOG_FINAL_REDEEM_BUTTON_SELECTOR = 'button[type="submit"][aria-label="Redeem the code"]'; // Redeem on Legacy Games Constants const LEGACY_GAMES_REDEEM_URL = 'https://promo.legacygames.com/royal-romances-cursed-hearts-ce-prime-deal/'; const LEGACY_GAMES_CODE_INPUT_SELECTOR = '#primedeal_game_code'; const LEGACY_GAMES_EMAIL_INPUT_SELECTOR = '#primedeal_email'; const LEGACY_GAMES_EMAIL_VALIDATE_INPUT_SELECTOR = '#primedeal_email_validate'; const LEGACY_GAMES_SUBMIT_BUTTON_SELECTOR = '#submitbutton'; const LEGACY_GAMES_NEWSLETTER_CHECKBOX_SELECTOR = '#primedeal_newsletter'; let claiming = false; // Check if MutationObserver is supported const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; // Function to click a button function clickButton(buttonSelector) { const observer = new MutationObserver((mutationsList, observer) => { for (let mutation of mutationsList) { if (mutation.addedNodes.length) { const button = document.querySelector(buttonSelector); if (button) { button.click(); observer.disconnect(); return; } } } }); observer.observe(document, { childList: true, subtree: true }); } // Function to enable theater mode function enableTheaterMode() { const player = document.querySelector(PLAYER_SELECTOR); if (player) { if (!player.classList.contains(THEATER_MODE_CLASS)) { clickButton(THEATER_MODE_BUTTON_SELECTOR); } } else { Logger.error('Player not found'); } } // Function to hide the global menu function hideGlobalMenu() { const GLOBAL_MENU_SELECTOR = 'div.ScBalloonWrapper-sc-14jr088-0.eEhNFm'; const globalMenu = document.querySelector(GLOBAL_MENU_SELECTOR); if (globalMenu) { globalMenu.style.display = 'none'; } else { Logger.error('Global menu not found'); } } // Function to automatically claim channel points function autoClaimBonus() { if (MutationObserver) { Logger.info('Auto claimer is enabled.'); let observer = new MutationObserver(mutationsList => { for (let mutation of mutationsList) { if (mutation.type === 'childList') { let bonus = document.querySelector(CLAIMABLE_BONUS_SELECTOR); if (bonus && !claiming) { bonus.click(); let date = new Date(); claiming = true; setTimeout(() => { Logger.success('Claimed at ' + date.toLocaleString()); claiming = false; }, Math.random() * 1000 + 2000); } } } }); observer.observe(document.body, { childList: true, subtree: true }); } else { Logger.warning('MutationObserver is not supported in this browser.'); } } // Function to claim prime rewards with retry function claimPrimeReward() { const maxAttempts = 5; let attempts = 0; const tryClaim = () => { if (attempts >= maxAttempts) { Logger.warning('Max attempts reached for claiming prime reward'); return; } attempts++; const element = document.querySelector(PRIME_REWARD_SELECTOR) || document.querySelector(PRIME_REWARD_SELECTOR_2); if (element) { element.click(); Logger.success('Prime reward claimed'); } else { Logger.info(`Attempt ${attempts}/${maxAttempts}: Waiting for prime reward button...`); setTimeout(tryClaim, 1000); } }; setTimeout(tryClaim, 2000); } // Function to claim drops function claimDrops() { var onMutate = function (mutationsList) { mutationsList.forEach(mutation => { if (document.querySelector(CLAIM_DROPS_SELECTOR)) document.querySelector(CLAIM_DROPS_SELECTOR).click(); }) } var observer = new MutationObserver(onMutate); observer.observe(document.body, { childList: true, subtree: true }); } // Function to add the "Redeem on GOG" button function addGogRedeemButton() { const claimCodeButton = document.querySelector('p[title="Claim Code"]'); if (claimCodeButton && !document.querySelector('.gog-redeem-button')) { const claimCodeWrapper = claimCodeButton.closest('.claim-button-wrapper'); if (claimCodeWrapper) { const gogRedeemButtonDiv = document.createElement('div'); gogRedeemButtonDiv.className = 'claim-button tw-align-self-center gog-redeem-button'; const gogRedeemButton = document.createElement('a'); gogRedeemButton.href = 'https://www.gog.com/en/redeem'; gogRedeemButton.rel = 'noopener noreferrer'; gogRedeemButton.className = 'tw-interactive tw-button tw-button--full-width'; gogRedeemButton.dataset.aTarget = 'redeem-on-gog'; gogRedeemButton.innerHTML = '<span class="tw-button__text" data-a-target="tw-button-text"><div class="tw-inline-flex"><p class="" title="Redeem on GOG">Redeem on GOG</p> <figure aria-label="ExternalLinkWithBox" class="tw-svg"><svg class="tw-svg__asset tw-svg__asset--externallinkwithbox tw-svg__asset--inherit" width="12px" height="12px" version="1.1" viewBox="0 0 11 11" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.3125 6.875V9.625C10.3125 10.3844 9.69689 11 8.9375 11H1.375C0.615608 11 0 10.3844 0 9.625V2.0625C0 1.30311 0.615608 0.6875 1.375 0.6875H4.125V2.0625H1.375V9.625H8.9375V6.875H10.3125ZM9.62301 2.34727L5.29664 6.67364L4.32437 5.70136L8.65073 1.375H6.18551V0H10.998V4.8125H9.62301V2.34727Z"></path></svg></figure></div></span>'; gogRedeemButtonDiv.appendChild(gogRedeemButton); claimCodeWrapper.appendChild(gogRedeemButtonDiv); gogRedeemButton.addEventListener('click', function (e) { e.preventDefault(); const codeInput = document.querySelector('input[aria-label]'); if (codeInput) { const code = codeInput.value; if (code) { navigator.clipboard.writeText(code).then(function () { window.location.href = 'https://www.gog.com/en/redeem'; }); } } }); const style = document.createElement('style'); style.innerHTML = ` .claim-button-wrapper { display: flex; flex-direction: column; margin-top: 15px; } .claim-button, .gog-redeem-button { margin: 5px 0; } .tw-mg-l-1 { margin-top: 10px; } .claimable-item { flex-direction: column !important; gap: 15px; } .tw-flex-grow-1 { width: 100%; } `; document.head.appendChild(style); } } } // Function to redeem code on GOG function redeemCodeOnGOG() { navigator.clipboard.readText().then(function (code) { const codeInput = document.querySelector(GOG_REDEEM_CODE_INPUT_SELECTOR); if (codeInput) { codeInput.value = code; // Simulate input event to ensure any listeners are triggered const inputEvent = new Event('input', { bubbles: true }); codeInput.dispatchEvent(inputEvent); // Click the continue button after a short delay setTimeout(() => { const continueButton = document.querySelector(GOG_CONTINUE_BUTTON_SELECTOR); if (continueButton) { continueButton.click(); // Wait for the "Redeem" button to appear and click it const checkRedeemButton = setInterval(() => { const redeemButton = document.querySelector(GOG_FINAL_REDEEM_BUTTON_SELECTOR); if (redeemButton) { clearInterval(checkRedeemButton); redeemButton.click(); } }, 500); // Check every 500ms for the Redeem button } }, 500); // Adjust the delay as needed } }).catch(function (err) { Logger.error('Failed to read clipboard contents: ' + err); }); } // Function to add the "Redeem on Legacy Games" button function addLegacyGamesRedeemButton() { const copyCodeButton = document.querySelector('button[aria-label="Copy code to your clipboard"]'); if (copyCodeButton && !document.querySelector('.legacy-games-redeem-button')) { const copyCodeWrapper = copyCodeButton.closest('.copy-button-wrapper'); if (copyCodeWrapper) { const legacyGamesRedeemButtonDiv = document.createElement('div'); legacyGamesRedeemButtonDiv.className = 'copy-button tw-align-self-center legacy-games-redeem-button'; const legacyGamesRedeemButton = document.createElement('button'); legacyGamesRedeemButton.ariaLabel = 'Redeem on Legacy Games'; legacyGamesRedeemButton.className = 'tw-interactive tw-button tw-button--full-width'; legacyGamesRedeemButton.dataset.aTarget = 'redeem-on-legacy-games'; legacyGamesRedeemButton.innerHTML = '<span class="tw-button__text" data-a-target="tw-button-text">Redeem on Legacy Games</span>'; legacyGamesRedeemButtonDiv.appendChild(legacyGamesRedeemButton); copyCodeWrapper.appendChild(legacyGamesRedeemButtonDiv); legacyGamesRedeemButton.addEventListener('click', function (e) { e.preventDefault(); const codeInput = document.querySelector('input[aria-label]'); if (codeInput) { const code = codeInput.value; if (code) { navigator.clipboard.writeText(code).then(function () { const email = GM_getValue('legacyGamesEmail', null); if (!email) { const userEmail = prompt('Please enter your email address:'); if (userEmail) { GM_setValue('legacyGamesEmail', userEmail); window.location.href = LEGACY_GAMES_REDEEM_URL; } } else { window.location.href = LEGACY_GAMES_REDEEM_URL; } }); } } }); const style = document.createElement('style'); style.innerHTML = ` .copy-button-wrapper { display: flex; flex-direction: column; margin-top: 15px; } .copy-button, .legacy-games-redeem-button { margin: 5px 0; } .tw-mg-l-1 { margin-top: 10px; } .claimable-item { flex-direction: column !important; gap: 15px; } .tw-flex-grow-1 { width: 100%; } `; document.head.appendChild(style); } } } // Function to redeem code on Legacy Games function redeemCodeOnLegacyGames() { const maxAttempts = 5; let attempts = 0; const tryRedeem = () => { if (attempts >= maxAttempts) return; attempts++; navigator.clipboard.readText().then(function (code) { const codeInput = document.querySelector(LEGACY_GAMES_CODE_INPUT_SELECTOR); const emailInput = document.querySelector(LEGACY_GAMES_EMAIL_INPUT_SELECTOR); const emailValidateInput = document.querySelector(LEGACY_GAMES_EMAIL_VALIDATE_INPUT_SELECTOR); const submitButton = document.querySelector(LEGACY_GAMES_SUBMIT_BUTTON_SELECTOR); const newsletterCheckbox = document.querySelector(LEGACY_GAMES_NEWSLETTER_CHECKBOX_SELECTOR); const email = GM_getValue('legacyGamesEmail', null); if (!codeInput || !emailInput || !emailValidateInput || !submitButton) { Logger.info('Waiting for elements to load...'); setTimeout(tryRedeem, 1000); return; } if (email && code) { // Fill in the form codeInput.value = code; emailInput.value = email; emailValidateInput.value = email; // Ensure newsletter checkbox is unchecked if (newsletterCheckbox) { newsletterCheckbox.checked = false; } // Trigger input events [codeInput, emailInput, emailValidateInput].forEach(input => { input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); }); // Submit the form setTimeout(() => { submitButton.click(); Logger.success('Form submitted with code: ' + code + ' and email: ' + email); }, 500); } }).catch(function (err) { Logger.error('Failed to read clipboard contents: ' + err); }); }; // Start the redemption process setTimeout(tryRedeem, 2000); } // Function to open all "Claim Game" buttons in new tabs function openClaimGameTabs() { const claimGameButtons = document.querySelectorAll('div[data-a-target="tw-core-button-label-text"].Layout-sc-1xcs6mc-0.bFxzAY'); claimGameButtons.forEach(button => { const parentButton = button.closest('a'); if (parentButton) { window.open(parentButton.href, '_blank'); } }); } if (window.location.hostname === 'gaming.amazon.com') { const observer = new MutationObserver((mutations, obs) => { const claimCodeButton = document.querySelector('p[title="Claim Code"]'); if (claimCodeButton) { addGogRedeemButton(); } const copyCodeButton = document.querySelector('button[aria-label="Copy code to your clipboard"]'); if (copyCodeButton) { addLegacyGamesRedeemButton(); } }); observer.observe(document, { childList: true, subtree: true }); addGogRedeemButton(); addLegacyGamesRedeemButton(); } if (window.location.hostname === 'www.gog.com' && window.location.pathname === '/en/redeem') { window.addEventListener('load', redeemCodeOnGOG); } if (window.location.hostname === 'promo.legacygames.com') { window.addEventListener('load', redeemCodeOnLegacyGames); } setTimeout(enableTheaterMode, 1000); setTimeout(autoClaimBonus, 1000); setTimeout(claimPrimeReward, 1000); setTimeout(() => clickButton(CLOSE_MENU_BUTTON_SELECTOR), 1000); setTimeout(() => clickButton(CLOSE_MODAL_BUTTON_SELECTOR), 1000); setTimeout(hideGlobalMenu, 1000); setTimeout(claimDrops, 1000); setInterval(function () { if (window.location.href.startsWith('https://www.twitch.tv/drops/inventory')) { window.location.reload(); } }, 15 * 60000); let o = new MutationObserver((m) => { let script = document.createElement("script"); script.innerHTML = ` // Add logger configuration for client-side script const Logger = { styles: { info: 'color: #2196F3; font-weight: bold', warning: 'color: #FFC107; font-weight: bold', success: 'color: #4CAF50; font-weight: bold', error: 'color: #F44336; font-weight: bold' }, prefix: '[TwitchEnhancements]', getTimestamp() { return new Date().toISOString().split('T')[1].slice(0, -1); }, info(msg) { console.log(\`%c\${this.prefix} \${this.getTimestamp()} - \${msg}\`, this.styles.info); }, warning(msg) { console.warn(\`%c\${this.prefix} \${this.getTimestamp()} - \${msg}\`, this.styles.warning); }, success(msg) { console.log(\`%c\${this.prefix} \${this.getTimestamp()} - \${msg}\`, this.styles.success); }, error(msg) { console.error(\`%c\${this.prefix} \${this.getTimestamp()} - \${msg}\`, this.styles.error); } }; const openClaimGameTabs = () => { // More specific selector targeting only prime offer buttons const allButtonTexts = document.querySelectorAll('div[data-a-target="tw-core-button-label-text"]'); // Filter buttons to only include those with text "Claim Game" or just "Claim" const claimGameButtons = Array.from(allButtonTexts).filter(button => { const text = button.textContent.trim(); return (text === "Claim Game" || text === "Claim") && button.closest('a') && // Must be inside an anchor tag button.closest('.prime-offer'); // Must be inside a prime offer }); Logger.info(\`Found \${claimGameButtons.length} valid claim buttons\`); // Open each valid claim button in a new tab claimGameButtons.forEach(button => { const parentButton = button.closest('a'); if (parentButton && parentButton.href && (parentButton.href.includes('gaming.amazon.com') || parentButton.href.includes('?ingress=twch'))) { window.open(parentButton.href, '_blank'); } }); }; const removeClaimedItems = () => { // Find ALL items in the list, not just claimed ones const allItems = document.querySelectorAll('.prime-offer'); let dismissedCount = 0; let dismissButtons = []; Logger.info(\`Found \${allItems.length} total items to dismiss\`); // First collect all dismiss buttons - use multiple methods to ensure we catch all // Method 1: Find buttons by attribute and data target document.querySelectorAll('button[aria-label="Dismiss"][data-a-target="prime-offer-dismiss-button"]').forEach(btn => { dismissButtons.push(btn); }); // Method 2: Find buttons by test selector attribute as backup document.querySelectorAll('button[data-test-selector="prime-offer-dismiss-button"]').forEach(btn => { if (!dismissButtons.includes(btn)) { dismissButtons.push(btn); } }); // Method 3: Find by class and structure if the above methods miss any document.querySelectorAll('.prime-offer__dismiss button').forEach(btn => { if (!dismissButtons.includes(btn)) { dismissButtons.push(btn); } }); // Deduplicate just in case dismissButtons = [...new Set(dismissButtons)]; Logger.info(\`Found \${dismissButtons.length} dismiss buttons to click\`); // Process dismiss buttons with a delay to avoid UI lockups if (dismissButtons.length > 0) { const clickNextButton = (index) => { if (index < dismissButtons.length) { try { dismissButtons[index].click(); dismissedCount++; // Show progress in console if (dismissedCount % 5 === 0 || dismissedCount === dismissButtons.length) { Logger.info(\`Dismissed \${dismissedCount} of \${dismissButtons.length} items...\`); } } catch (e) { Logger.error(\`Error clicking button \${index}: \` + e); } // Schedule next button click with a small delay setTimeout(() => clickNextButton(index + 1), 75); } else { Logger.success(\`Completed! Dismissed \${dismissedCount} items total.\`); // Look for any dismiss buttons that might have been missed const remainingButtons = document.querySelectorAll('button[aria-label="Dismiss"]'); if (remainingButtons.length > 0) { Logger.warning(\`Found \${remainingButtons.length} additional buttons to try\`); // Try to click any remaining dismiss buttons as a final pass remainingButtons.forEach(btn => { try { btn.click(); dismissedCount++; } catch(e) {} }); Logger.success(\`Final dismissal count: \${dismissedCount}\`); } } }; // Start the dismissal process clickNextButton(0); } else { Logger.warning('No dismiss buttons found to click'); // Last attempt fallback - try to find any button with "Dismiss" in aria-label const fallbackButtons = document.querySelectorAll('button[aria-label="Dismiss"]'); if (fallbackButtons.length > 0) { Logger.warning(\`Fallback: Found \${fallbackButtons.length} buttons with aria-label="Dismiss"\`); fallbackButtons.forEach(btn => { try { btn.click(); dismissedCount++; } catch(e) {} }); Logger.success(\`Fallback dismissal completed: \${dismissedCount} items dismissed\`); } } }; `; document.getElementById("PrimeOfferPopover-header").innerHTML = ""; document.getElementById("PrimeOfferPopover-header").appendChild(script); document.getElementById("PrimeOfferPopover-header").innerHTML += ` <div style="display: flex; gap: 10px; margin-bottom: 10px;"> <input type='button' style='border: none; background-color: #9147ff; color: white; padding: 10px 20px; font-size: 14px; border-radius: 4px; cursor: pointer; flex: 1;' class='tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-core-button tw-core-button--primary tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative' value='Claim All' onclick='openClaimGameTabs();'> <input type='button' style='border: none; background-color: #772ce8; color: white; padding: 10px 20px; font-size: 14px; border-radius: 4px; cursor: pointer; flex: 1;' class='tw-align-items-center tw-align-middle tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium tw-border-top-left-radius-medium tw-border-top-right-radius-medium tw-core-button tw-core-button--primary tw-inline-flex tw-interactive tw-justify-content-center tw-overflow-hidden tw-relative' value='Remove All' onclick='removeClaimedItems();'> </div> `; }); o.observe(document.body, { childList: true }); })();