Twitch Enhancements

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.4
// @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
// @grant        GM_registerMenuCommand
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // Configuration settings
    const CONFIG = {
        enableAutoClaimPoints: GM_getValue('enableAutoClaimPoints', true),
        enableTheaterMode: GM_getValue('enableTheaterMode', true),
        enableClaimPrimeRewards: GM_getValue('enableClaimPrimeRewards', true),
        enableClaimDrops: GM_getValue('enableClaimDrops', true),
        enableGogRedeemButton: GM_getValue('enableGogRedeemButton', true),
        enableLegacyGamesRedeemButton: GM_getValue('enableLegacyGamesRedeemButton', true),
        enableHideGlobalMenu: GM_getValue('enableHideGlobalMenu', true),
        enableAutoRefreshDrops: GM_getValue('enableAutoRefreshDrops', true),
        enableClaimAllButton: GM_getValue('enableClaimAllButton', true),
        enableRemoveAllButton: GM_getValue('enableRemoveAllButton', true),
        settingsKey: GM_getValue('settingsKey', 'F2') // Default to F2 if not set
    };

    // 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;

    // Settings Dialog Functions
    function createSettingsDialog() {
        const dialogHTML = `
            <div id="twitchEnhancementsDialog" class="te-dialog">
                <h3>Twitch Enhancements Settings</h3>
                ${createToggle('enableAutoClaimPoints', 'Auto Claim Channel Points', 'Automatically claim channel points')}
                ${createToggle('enableTheaterMode', 'Auto Theater Mode', 'Automatically enable theater mode')}
                ${createToggle('enableClaimPrimeRewards', 'Auto Claim Prime Rewards', 'Automatically claim prime rewards')}
                ${createToggle('enableClaimDrops', 'Auto Claim Drops', 'Automatically claim Twitch drops')}
                ${createToggle('enableGogRedeemButton', 'GOG Redeem Button', 'Add GOG redeem button on Amazon Gaming')}
                ${createToggle('enableLegacyGamesRedeemButton', 'Legacy Games Button', 'Add Legacy Games redeem button on Amazon Gaming')}
                ${createToggle('enableHideGlobalMenu', 'Hide Global Menu', 'Hide the global menu on Twitch')}
                ${createToggle('enableAutoRefreshDrops', 'Auto Refresh Drops', 'Automatically refresh drops inventory page every 15 minutes')}
                ${createToggle('enableClaimAllButton', 'Enable Claim All Button', 'Add Claim All button on Amazon Gaming')}
                ${createToggle('enableRemoveAllButton', 'Enable Remove All Button', 'Add Remove All button on Amazon Gaming')}
                <div class="te-key-setting">
                    <label for="settingsKey" class="te-key-label">Settings Toggle Key:</label>
                    <div class="te-key-input-container">
                        <input type="text" id="settingsKey" class="te-key-input" value="${CONFIG.settingsKey}" readonly>
                        <button id="changeKeyButton" class="te-key-button">Change Key</button>
                    </div>
                    <div id="keyInstructions" class="te-key-instructions" style="display:none;">Press any key...</div>
                </div>
                <div class="te-button-container">
                    <button id="saveSettingsButton" class="te-button te-button-save">Save</button>
                    <button id="cancelSettingsButton" class="te-button te-button-cancel">Cancel</button>
                </div>
            </div>
        `;

        const styleSheet = `
            <style>
                .te-dialog {
                    position: fixed;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    background: rgba(18, 16, 24, 0.9);
                    border: 1px solid #772ce8;
                    border-radius: 8px;
                    padding: 20px;
                    box-shadow: 0 0 20px rgba(0, 0, 0, 0.7);
                    z-index: 9999999; /* Increased z-index to ensure it appears above all elements */
                    color: white;
                    width: 350px;
                    font-family: 'Roobert', 'Inter', Helvetica, Arial, sans-serif;
                }
                .te-dialog h3 {
                    margin-top: 0;
                    font-size: 1.4em;
                    text-align: center;
                    margin-bottom: 20px;
                    color: #bf94ff;
                }
                .te-toggle-container {
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    margin-bottom: 15px;
                }
                .te-toggle-label {
                    flex-grow: 1;
                    font-size: 0.95em;
                }
                .te-toggle {
                    position: relative;
                    display: inline-block;
                    width: 50px;
                    height: 24px;
                }
                .te-toggle input {
                    position: absolute;
                    width: 100%;
                    height: 100%;
                    opacity: 0;
                    cursor: pointer;
                    margin: 0;
                }
                .te-toggle-slider {
                    position: absolute;
                    cursor: pointer;
                    top: 0;
                    left: 0;
                    right: 0;
                    bottom: 0;
                    background-color: #333;
                    transition: .4s;
                    border-radius: 24px;
                }
                .te-toggle-slider:before {
                    position: absolute;
                    content: "";
                    height: 16px;
                    width: 16px;
                    left: 4px;
                    bottom: 4px;
                    background-color: white;
                    transition: .4s;
                    border-radius: 50%;
                }
                .te-toggle input:checked + .te-toggle-slider {
                    background-color: #9147ff;
                }
                .te-toggle input:checked + .te-toggle-slider:before {
                    transform: translateX(26px);
                }
                .te-button-container {
                    display: flex;
                    justify-content: space-between;
                    margin-top: 20px;
                }
                .te-button {
                    padding: 8px 16px;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 0.95em;
                    transition: background-color 0.3s;
                }
                .te-button-save {
                    background-color: #9147ff;
                    color: white;
                }
                .te-button-save:hover {
                    background-color: #772ce8;
                }
                .te-button-cancel {
                    background-color: #464649;
                    color: white;
                }
                .te-button-cancel:hover {
                    background-color: #2d2d30;
                }
                .te-key-setting {
                    margin-top: 20px;
                    padding-top: 15px;
                    border-top: 1px solid #464649;
                }
                .te-key-label {
                    display: block;
                    margin-bottom: 10px;
                    font-size: 0.95em;
                }
                .te-key-input-container {
                    display: flex;
                    gap: 10px;
                }
                .te-key-input {
                    flex: 1;
                    background-color: #18181b;
                    color: white;
                    border: 1px solid #464649;
                    border-radius: 4px;
                    padding: 8px;
                    text-align: center;
                    font-size: 14px;
                }
                .te-key-button {
                    background-color: #464649;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    padding: 8px 12px;
                    cursor: pointer;
                    font-size: 0.85em;
                }
                .te-key-button:hover {
                    background-color: #5c5c5f;
                }
                .te-key-instructions {
                    margin-top: 10px;
                    font-size: 0.85em;
                    color: #bf94ff;
                    text-align: center;
                }
            </style>
        `;

        const dialogWrapper = document.createElement('div');
        dialogWrapper.innerHTML = styleSheet + dialogHTML;
        document.body.appendChild(dialogWrapper);

        // Add event listeners to toggles with improved feedback - MODIFIED
        // Store toggle changes in memory instead of immediately updating CONFIG
        const pendingChanges = {}; // Object to track pending changes

        document.querySelectorAll('.te-toggle input').forEach(toggle => {
            toggle.addEventListener('change', (event) => {
                const { id, checked } = event.target;
                Logger.info(`Toggle changed: ${id} = ${checked}`);
                // Instead of updating CONFIG directly, store the pending change
                pendingChanges[id] = checked;
            });
        });

        // Add event listeners to buttons
        document.getElementById('saveSettingsButton').addEventListener('click', () => saveAndCloseDialog(pendingChanges));
        document.getElementById('cancelSettingsButton').addEventListener('click', closeDialog);

        // Add event listener for change key button
        const changeKeyButton = document.getElementById('changeKeyButton');
        changeKeyButton.addEventListener('click', function () {
            const keyInput = document.getElementById('settingsKey');
            const keyInstructions = document.getElementById('keyInstructions');

            // Show instructions and focus on input
            keyInstructions.style.display = 'block';
            keyInstructions.textContent = 'Press key combination (e.g. Ctrl+Shift+K)...';
            keyInput.value = 'Press keys...';

            // Change button text to indicate canceling is possible
            changeKeyButton.textContent = 'Cancel';

            // Flag to track if we're in key capture mode
            let capturingKey = true;

            // Variables to store key combination
            let modifiers = {
                ctrl: false,
                alt: false,
                shift: false,
                meta: false
            };
            let mainKey = '';

            // Function to format current key combination
            const formatKeyCombination = () => {
                const parts = [];
                if (modifiers.ctrl) parts.push('Ctrl');
                if (modifiers.alt) parts.push('Alt');
                if (modifiers.shift) parts.push('Shift');
                if (modifiers.meta) parts.push('Meta');
                if (mainKey && !['Control', 'Alt', 'Shift', 'Meta'].includes(mainKey)) {
                    parts.push(mainKey);
                }
                return parts.join('+');
            };

            // Function to update the input with current combination
            const updateKeyDisplay = () => {
                const combination = formatKeyCombination();
                if (combination) {
                    keyInput.value = combination;
                } else {
                    keyInput.value = 'Press keys...';
                }
            };

            // Function to handle key down
            const handleKeyDown = function (e) {
                if (!capturingKey) return;

                e.preventDefault();
                e.stopPropagation();

                // Track modifier keys
                if (e.key === 'Control' || e.key === 'Alt' || e.key === 'Shift' || e.key === 'Meta') {
                    switch (e.key) {
                        case 'Control': modifiers.ctrl = true; break;
                        case 'Alt': modifiers.alt = true; break;
                        case 'Shift': modifiers.shift = true; break;
                        case 'Meta': modifiers.meta = true; break;
                    }
                } else {
                    // Track main key
                    mainKey = e.key;
                }

                // Update the display
                updateKeyDisplay();
            };

            // Function to handle key up
            const handleKeyUp = function (e) {
                if (!capturingKey) return;

                e.preventDefault();
                e.stopPropagation();

                // Handle modifier keys being released
                if (e.key === 'Control' || e.key === 'Alt' || e.key === 'Shift' || e.key === 'Meta') {
                    switch (e.key) {
                        case 'Control': modifiers.ctrl = false; break;
                        case 'Alt': modifiers.alt = false; break;
                        case 'Shift': modifiers.shift = false; break;
                        case 'Meta': modifiers.meta = false; break;
                    }

                    // Update the display
                    updateKeyDisplay();
                } else {
                    // If a non-modifier key was released, complete the capture
                    const keyCombination = formatKeyCombination();

                    // Only save if we have a valid combination (at least one key)
                    if (keyCombination && keyCombination !== 'Press keys...') {
                        keyInput.value = keyCombination;

                        // Exit key capture mode
                        document.removeEventListener('keydown', handleKeyDown, true);
                        document.removeEventListener('keyup', handleKeyUp, true);
                        keyInstructions.style.display = 'none';
                        changeKeyButton.textContent = 'Change Key';
                        capturingKey = false;

                        // Log the captured combination
                        Logger.info(`Key combination captured: ${keyCombination}`);
                    }
                }
            };

            // Function to cancel key capture
            const cancelCapture = function () {
                if (!capturingKey) return;

                document.removeEventListener('keydown', handleKeyDown, true);
                document.removeEventListener('keyup', handleKeyUp, true);
                keyInput.value = CONFIG.settingsKey;
                keyInstructions.style.display = 'none';
                changeKeyButton.textContent = 'Change Key';
                capturingKey = false;
            };

            // Allow canceling key capture by clicking the button again
            changeKeyButton.addEventListener('click', cancelCapture, { once: true });

            // Capture key events
            document.addEventListener('keydown', handleKeyDown, true);
            document.addEventListener('keyup', handleKeyUp, true);
        });
    }

    function createToggle(id, label, title) {
        return `
            <div class="te-toggle-container" title="${title}">
                <label class="te-toggle">
                    <input type="checkbox" id="${id}" ${CONFIG[id] ? 'checked' : ''}>
                    <span class="te-toggle-slider"></span>
                </label>
                <label for="${id}" class="te-toggle-label">${label}</label>
            </div>
        `;
    }

    // Modified saveAndCloseDialog function to apply changes dynamically
    function saveAndCloseDialog(pendingChanges = {}) {
        // Create a deep copy of the CONFIG object before any changes are made
        const oldConfig = JSON.parse(JSON.stringify(CONFIG));
        let changesMade = false;

        // Improved debugging output
        Logger.info("Checking for settings changes...");

        // Save toggle settings
        Object.keys(CONFIG).forEach(key => {
            if (key === 'settingsKey') return; // Handle separately

            // Check if this setting has a pending change
            if (pendingChanges.hasOwnProperty(key)) {
                const oldValue = oldConfig[key];
                const newValue = pendingChanges[key];

                // Log the comparison for debugging
                Logger.info(`Comparing ${key}: old=${oldValue} (${typeof oldValue}), new=${newValue} (${typeof newValue})`);

                // Compare values - both should be booleans for toggle settings
                if (oldValue !== newValue) {
                    changesMade = true;
                    Logger.info(`Changed ${key} from ${oldValue} to ${newValue}`);
                    CONFIG[key] = newValue;
                    GM_setValue(key, newValue);
                }
            } else {
                // If no pending change, get value from form element
                const element = document.getElementById(key);
                if (element) {
                    const oldValue = oldConfig[key];
                    const newValue = element.checked;

                    // Log the comparison for debugging
                    Logger.info(`Comparing ${key}: old=${oldValue} (${typeof oldValue}), new=${newValue} (${typeof newValue})`);

                    // Compare values
                    if (oldValue !== newValue) {
                        changesMade = true;
                        Logger.info(`Changed ${key} from ${oldValue} to ${newValue}`);
                        CONFIG[key] = newValue;
                        GM_setValue(key, newValue);
                    }
                }
            }
        });

        // Save settings key
        const keyInput = document.getElementById('settingsKey');
        if (keyInput && keyInput.value !== oldConfig.settingsKey) {
            changesMade = true;
            Logger.info(`Changed settings key from ${oldConfig.settingsKey} to ${keyInput.value}`);
            CONFIG.settingsKey = keyInput.value;
            GM_setValue('settingsKey', keyInput.value);
        }

        closeDialog();

        if (changesMade) {
            Logger.success('Settings saved and applied immediately');
            applySettingsChanges(oldConfig);
        } else {
            // Show more helpful message when no changes are detected
            Logger.info('No changes detected. Settings remain the same.');
        }
    }

    // Function to dynamically apply settings changes
    function applySettingsChanges(oldConfig) {
        // Restart observers or update UI elements based on config changes

        // Handle auto refresh drops changes
        if (oldConfig.enableAutoRefreshDrops !== CONFIG.enableAutoRefreshDrops) {
            setupAutoRefreshDrops();
        }

        // Handle claim points observer changes
        if (oldConfig.enableAutoClaimPoints !== CONFIG.enableAutoClaimPoints) {
            restartClaimPointsObserver();
        }

        // Handle claim drops observer changes
        if (oldConfig.enableClaimDrops !== CONFIG.enableClaimDrops) {
            restartClaimDropsObserver();
        }

        // Handle Amazon gaming buttons changes
        if (oldConfig.enableGogRedeemButton !== CONFIG.enableGogRedeemButton ||
            oldConfig.enableLegacyGamesRedeemButton !== CONFIG.enableLegacyGamesRedeemButton) {
            updateRedeeemButtons();
        }

        // Handle PrimeOfferPopover changes for Claim All/Remove All buttons
        if (oldConfig.enableClaimAllButton !== CONFIG.enableClaimAllButton ||
            oldConfig.enableRemoveAllButton !== CONFIG.enableRemoveAllButton) {
            if (document.getElementById("PrimeOfferPopover-header")) {
                updatePrimeOfferButtons();
            }
        }

        // Handle Theater Mode changes
        if (!oldConfig.enableTheaterMode && CONFIG.enableTheaterMode) {
            enableTheaterMode();
        }

        // Handle Hide Global Menu changes
        if (CONFIG.enableHideGlobalMenu) {
            hideGlobalMenu();
        } else if (!CONFIG.enableHideGlobalMenu && oldConfig.enableHideGlobalMenu) {
            showGlobalMenu();
        }
    }

    // Function to show global menu (when setting is turned off)
    function showGlobalMenu() {
        const GLOBAL_MENU_SELECTOR = 'div.ScBalloonWrapper-sc-14jr088-0.eEhNFm';
        const globalMenu = document.querySelector(GLOBAL_MENU_SELECTOR);
        if (globalMenu) {
            globalMenu.style.display = '';
            Logger.info('Global menu restored');
        }
    }

    // Variables to track observers
    let claimPointsObserver = null;
    let claimDropsObserver = null;
    let autoRefreshInterval = null;

    // Function to restart claim points observer
    function restartClaimPointsObserver() {
        if (claimPointsObserver) {
            claimPointsObserver.disconnect();
            claimPointsObserver = null;
            Logger.info('Auto claim points observer disconnected');
        }

        if (CONFIG.enableAutoClaimPoints) {
            setupAutoClaimBonus();
        }
    }

    // Function to restart claim drops observer
    function restartClaimDropsObserver() {
        if (claimDropsObserver) {
            claimDropsObserver.disconnect();
            claimDropsObserver = null;
            Logger.info('Claim drops observer disconnected');
        }

        if (CONFIG.enableClaimDrops) {
            setupClaimDrops();
        }
    }

    // Function to setup auto refresh drops timer
    function setupAutoRefreshDrops() {
        if (autoRefreshInterval) {
            clearInterval(autoRefreshInterval);
            autoRefreshInterval = null;
            Logger.info('Auto refresh drops timer cleared');
        }

        if (CONFIG.enableAutoRefreshDrops) {
            autoRefreshInterval = setInterval(function () {
                if (window.location.href.startsWith('https://www.twitch.tv/drops/inventory')) {
                    Logger.info('Auto-refreshing drops inventory page');
                    window.location.reload();
                }
            }, 15 * 60000);
            Logger.info('Auto refresh drops timer started');
        }
    }

    // Function to update redeem buttons
    function updateRedeeemButtons() {
        if (window.location.hostname === 'gaming.amazon.com') {
            if (CONFIG.enableGogRedeemButton) {
                addGogRedeemButton();
            } else {
                // Remove GOG buttons
                const gogButtons = document.querySelectorAll('.gog-redeem-button');
                gogButtons.forEach(button => button.remove());
                Logger.info('GOG redeem buttons removed');
            }

            if (CONFIG.enableLegacyGamesRedeemButton) {
                addLegacyGamesRedeemButton();
            } else {
                // Remove Legacy Games buttons
                const legacyButtons = document.querySelectorAll('.legacy-games-redeem-button');
                legacyButtons.forEach(button => button.remove());
                Logger.info('Legacy Games redeem buttons removed');
            }
        }
    }

    // Function to update the Prime Offer Popover buttons
    function updatePrimeOfferButtons() {
        const primeOfferHeader = document.getElementById("PrimeOfferPopover-header");
        if (!primeOfferHeader) return;

        let o = new MutationObserver((m) => {
            if (!CONFIG.enableClaimAllButton && !CONFIG.enableRemoveAllButton) {
                // Remove all custom buttons
                const customButtonsContainer = document.querySelector('#PrimeOfferPopover-header > div');
                if (customButtonsContainer) {
                    customButtonsContainer.remove();
                }
                return;
            }

            // Trigger a refresh of the buttons
            const headerElement = document.getElementById("PrimeOfferPopover-header");
            if (headerElement) {
                // Force refresh by triggering our main observer
                const dummyDiv = document.createElement('div');
                document.body.appendChild(dummyDiv);
                document.body.removeChild(dummyDiv);
            }
        });

        // Trigger the observer
        o.observe(document.body, { childList: true });
        setTimeout(() => o.disconnect(), 500); // Disconnect after a short time
    }

    // Function to setup auto claim bonus
    function setupAutoClaimBonus() {
        if (!CONFIG.enableAutoClaimPoints || !MutationObserver) return;

        Logger.info('Auto claimer is enabled.');

        claimPointsObserver = new MutationObserver(mutationsList => {
            for (let mutation of mutationsList) {
                if (mutation.type === 'childList' && CONFIG.enableAutoClaimPoints) {
                    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);
                    }
                }
            }
        });

        claimPointsObserver.observe(document.body, { childList: true, subtree: true });
    }

    // Function to setup claim drops
    function setupClaimDrops() {
        if (!CONFIG.enableClaimDrops || !MutationObserver) return;

        var onMutate = function (mutationsList) {
            mutationsList.forEach(mutation => {
                if (CONFIG.enableClaimDrops && document.querySelector(CLAIM_DROPS_SELECTOR)) {
                    document.querySelector(CLAIM_DROPS_SELECTOR).click();
                }
            });
        };

        claimDropsObserver = new MutationObserver(onMutate);
        claimDropsObserver.observe(document.body, { childList: true, subtree: true });
        Logger.info('Claim drops observer started');
    }

    function closeDialog() {
        const dialog = document.getElementById('twitchEnhancementsDialog');
        if (dialog) {
            dialog.remove();
        }
    }

    function toggleSettingsDialog() {
        const dialog = document.getElementById('twitchEnhancementsDialog');
        if (dialog) {
            dialog.remove();
        } else {
            createSettingsDialog();
        }
    }

    // Register menu command
    GM_registerMenuCommand('Twitch Enhancements Settings', toggleSettingsDialog);

    // Function to click a button
    function clickButton(buttonSelector) {
        if (!MutationObserver) return;

        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() {
        if (!CONFIG.enableTheaterMode) return;

        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() {
        if (!CONFIG.enableHideGlobalMenu) return;

        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 (!CONFIG.enableAutoClaimPoints || !MutationObserver) return;

        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 });
    }

    // Function to claim prime rewards with retry
    function claimPrimeReward() {
        if (!CONFIG.enableClaimPrimeRewards) return;

        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() {
        if (!CONFIG.enableClaimDrops || !MutationObserver) return;

        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() {
        if (!CONFIG.enableGogRedeemButton) return;

        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>&nbsp;&nbsp;<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() {
        if (!CONFIG.enableLegacyGamesRedeemButton) return;

        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 && CONFIG.enableGogRedeemButton) {
                addGogRedeemButton();
            }
            const copyCodeButton = document.querySelector('button[aria-label="Copy code to your clipboard"]');
            if (copyCodeButton && CONFIG.enableLegacyGamesRedeemButton) {
                addLegacyGamesRedeemButton();
            }
        });

        observer.observe(document, {
            childList: true,
            subtree: true
        });

        if (CONFIG.enableGogRedeemButton) addGogRedeemButton();
        if (CONFIG.enableLegacyGamesRedeemButton) 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(setupAutoClaimBonus, 1000);
    setTimeout(claimPrimeReward, 1000);
    setTimeout(() => clickButton(CLOSE_MENU_BUTTON_SELECTOR), 1000);
    setTimeout(() => clickButton(CLOSE_MODAL_BUTTON_SELECTOR), 1000);
    setTimeout(hideGlobalMenu, 1000);
    setTimeout(setupClaimDrops, 1000);

    // Auto refresh drops inventory page
    if (CONFIG.enableAutoRefreshDrops) {
        setInterval(function () {
            if (window.location.href.startsWith('https://www.twitch.tv/drops/inventory')) {
                window.location.reload();
            }
        }, 15 * 60000);
    }

    // Add keyboard shortcut to toggle settings - now using the configured key
    document.addEventListener('keyup', (event) => {
        // Parse the configured key combination
        const parts = CONFIG.settingsKey.split('+');
        const requiredModifiers = {
            Ctrl: parts.includes('Ctrl'),
            Alt: parts.includes('Alt'),
            Shift: parts.includes('Shift'),
            Meta: parts.includes('Meta')
        };

        // The main key is the last part if it's not a modifier
        const mainKey = parts.filter(part => !['Ctrl', 'Alt', 'Shift', 'Meta'].includes(part)).pop();

        // Check if the event matches our configured combination
        const matchesModifiers =
            (!requiredModifiers.Ctrl || event.ctrlKey) &&
            (!requiredModifiers.Alt || event.altKey) &&
            (!requiredModifiers.Shift || event.shiftKey) &&
            (!requiredModifiers.Meta || event.metaKey);

        const matchesMainKey = mainKey ? event.key === mainKey : true;

        if (matchesModifiers && matchesMainKey) {
            // Only trigger on the exact key combination
            if (
                // If Ctrl is in the combination, ensure it's pressed
                (!parts.includes('Ctrl') || event.ctrlKey) &&
                // If Alt is in the combination, ensure it's pressed
                (!parts.includes('Alt') || event.altKey) &&
                // If Shift is in the combination, ensure it's pressed
                (!parts.includes('Shift') || event.shiftKey) &&
                // If Meta is in the combination, ensure it's pressed
                (!parts.includes('Meta') || event.metaKey) &&
                // If a main key is specified, ensure it matches
                (mainKey ? event.key === mainKey : true)
            ) {
                // Prevent default behavior
                event.preventDefault();
                toggleSettingsDialog();

                // Log for debugging
                Logger.info(`${CONFIG.settingsKey} key combination pressed - toggling settings dialog`);
            }
        }
    });

    // Make sure event is captured at the document level with capture phase
    document.addEventListener('keydown', (event) => {
        // Parse the configured key combination
        const parts = CONFIG.settingsKey.split('+');
        const requiredModifiers = {
            Ctrl: parts.includes('Ctrl'),
            Alt: parts.includes('Alt'),
            Shift: parts.includes('Shift'),
            Meta: parts.includes('Meta')
        };

        // The main key is the last part if it's not a modifier
        const mainKey = parts.filter(part => !['Ctrl', 'Alt', 'Shift', 'Meta'].includes(part)).pop();

        // Check if the event matches our configured combination
        const matchesModifiers =
            (!requiredModifiers.Ctrl || event.ctrlKey) &&
            (!requiredModifiers.Alt || event.altKey) &&
            (!requiredModifiers.Shift || event.shiftKey) &&
            (!requiredModifiers.Meta || event.metaKey);

        const matchesMainKey = mainKey ? event.key === mainKey : true;

        if (matchesModifiers && matchesMainKey) {
            // Prevent default behavior for our combination
            event.preventDefault();
        }
    }, true);

    let o = new MutationObserver((m) => {
        if (!CONFIG.enableClaimAllButton && !CONFIG.enableRemoveAllButton) return;

        // Check if the PrimeOfferPopover-header element exists
        const primeOfferHeader = document.getElementById("PrimeOfferPopover-header");
        if (!primeOfferHeader) {
            // If we're on a page where this element doesn't exist, we should stop
            return;
        }

        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\`);
                }
            }
        };
    `;

        // Safely clear and append to the header
        primeOfferHeader.innerHTML = "";
        primeOfferHeader.appendChild(script);

        if (CONFIG.enableClaimAllButton || CONFIG.enableRemoveAllButton) {
            primeOfferHeader.innerHTML += `
            <div style="display: flex; gap: 10px; margin-bottom: 10px;">
                ${CONFIG.enableClaimAllButton ? `
                <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();'>
                ` : ''}
                ${CONFIG.enableRemoveAllButton ? `
                <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 });
})();

QingJ © 2025

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