Greasy Fork

来自缓存

Greasy Fork is available in English.

Microsoft Rewards助手

自动完成Microsoft Rewards必应搜索任务,支持SPA不刷新和页面刷新自动恢复,防卡死,状态记忆,带美观可视化界面。

// ==UserScript==
// @name         Microsoft Rewards助手
// @version      3.5.0
// @description  自动完成Microsoft Rewards必应搜索任务,支持SPA不刷新和页面刷新自动恢复,防卡死,状态记忆,带美观可视化界面。
// @author       Sentaku1129 & WretchedSniper UI enhanced & Copilot
// @match        *://*.bing.com/*
// @license      GNU GPLv3
// @icon         https://www.bing.com/favicon.ico
// @run-at       document-end
// @grant        none
// @namespace    http://greasyfork.icu/users/1029902
// ==/UserScript==

(function () {
    'use strict';

    // --- 主题配置 ---
    const theme = {
        main: "#0078d4",
        accent: "#4CAF50",
        error: "#f44336",
        bg: "#fff",
        fg: "#222",
        border: "#e0e0e0",
        shadow: "0 4px 20px 0 rgba(0,0,0,.10)",
        font: `-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"`
    };

    let mainPageSearchTerms = [];
    let iframeSearchTerms = [];
    let usedSearchTerms = [];
    let currentProgress = {
        current: 0, total: 0, lastChecked: 0, completed: false, noProgressCount: 0
    };
    let isSearching = false;
    let countdownTimer = null;

    const config = {
        restTime: 5 * 60,
        scrollTime: 10,
        waitTime: 10,
        maxNoProgressCount: 3
    };

    const searchState = {
        currentAction: 'idle',
        countdown: 0,
        needRest: false,
        isCollapsed: true
    };

    const STORAGE_KEY = 'rewardsHelperState_v3.5';

    // --- 状态管理 ---
    function saveState() {
        try {
            const stateToSave = {
                isSearching,
                currentProgress,
                usedSearchTerms,
                config,
                isCollapsed: searchState.isCollapsed
            };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(stateToSave));
        } catch (e) {}
    }
    function loadState() {
        const savedState = localStorage.getItem(STORAGE_KEY);
        if (savedState) {
            try {
                const restoredState = JSON.parse(savedState);
                isSearching = restoredState.isSearching || false;
                currentProgress = restoredState.currentProgress || currentProgress;
                usedSearchTerms = restoredState.usedSearchTerms || [];
                Object.assign(config, restoredState.config);
                searchState.isCollapsed = restoredState.isCollapsed === false ? false : true;
            } catch (e) {
                localStorage.removeItem(STORAGE_KEY);
            }
        }
    }

    // --- UI相关 ---
    function injectGlobalStyle() {
        const style = document.createElement('style');
        style.innerHTML = `
        #rewards-helper-container {
            transition: box-shadow .2s, width .2s, height .2s;
        }
        #rewards-helper-container::-webkit-scrollbar, #rewards-helper-content::-webkit-scrollbar, #rewards-search-terms-container::-webkit-scrollbar {
            width: 8px;
            background: #f6f6f6;
        }
        #rewards-helper-container::-webkit-scrollbar-thumb, #rewards-helper-content::-webkit-scrollbar-thumb, #rewards-search-terms-container::-webkit-scrollbar-thumb {
            background: ${theme.main}33;
            border-radius: 5px;
        }
        #rewards-helper-content {
            font-family: ${theme.font};
            font-size: 15px;
            color: ${theme.fg};
        }
        #rewards-helper-container input[type=number] {
            border: 1px solid ${theme.border};
            border-radius: 3px;
            padding: 2px 5px;
            font-size: 13px;
        }
        #rewards-helper-container button {
            transition: background .15s, color .15s;
        }
        #rewards-helper-container .term-pill {
            display: inline-block;
            background: ${theme.main}12;
            border-radius: 14px;
            padding: 2px 10px;
            margin: 2px 2px 2px 0;
            font-size: 13px;
            color: ${theme.main};
            border: 1px solid ${theme.main}22;
            user-select: text;
        }
        #rewards-helper-container .term-pill.used {
            opacity: .45;
            background: #aaa1;
            color: #999;
            border: 1px solid #ccc;
            text-decoration: line-through;
        }
        #rewards-helper-container .heading {
            font-size: 14px;
            color: ${theme.main};
            font-weight: bold;
            margin-bottom: 2px;
            letter-spacing: .05em;
        }
        #rewards-helper-container .action-btn {
            background: ${theme.main};
            color: #fff;
            border: none;
            border-radius: 4px;
            font-weight: bold;
            font-size: 15px;
            padding: 6px 0;
            margin-bottom: 0;
            width: 100%;
            transition: background .15s;
        }
        #rewards-helper-container .action-btn.stop {
            background: ${theme.error};
        }
        #rewards-helper-container .status-message {
            font-size: 13px;
            color: #888;
            margin-top: -1px;
            min-height: 18px;
        }
        #rewards-helper-container .progress-bar {
            background: #eee;
            height: 10px;
            border-radius: 6px;
            overflow: hidden;
            margin: 4px 0 6px 0;
        }
        #rewards-helper-container .progress-bar-inner {
            background: ${theme.accent};
            height: 100%;
            transition: width .5s;
        }
        #rewards-helper-container .countdown {
            font-size: 13px;
            color: ${theme.main};
            margin-top: 3px;
            font-weight: bold;
        }
        @media (max-width: 500px) {
            #rewards-helper-container {
                width: 98vw !important;
                right: 1vw !important;
                left: unset !important;
                min-width: 0 !important;
                max-width: none !important;
            }
        }
        `;
        document.head.appendChild(style);
    }

    function createUI() {
        injectGlobalStyle();
        const container = document.createElement('div');
        container.id = 'rewards-helper-container';
        container.style.cssText = `
            position: fixed; top: 32px; right: 24px; background: ${theme.bg};
            border: 1.5px solid ${theme.border}; border-radius: 10px; padding: 0 0 10px 0;
            z-index: 10000; width: 340px; min-width: 260px; max-width: 97vw;
            box-shadow: ${theme.shadow}; user-select: none; font-family: ${theme.font};
            font-size: 15px;
        `;

        const header = document.createElement('div');
        header.style.cssText = `
            font-weight: bold;
            background: ${theme.main};
            color: #fff;
            border-radius: 10px 10px 0 0;
            padding: 10px 12px 8px 18px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
            font-size: 18px;
        `;
        header.innerHTML = `<span>🎁 Microsoft Rewards 助手</span>`;

        const controls = document.createElement('div');
        controls.style.cssText = `display:flex;align-items:center;gap:10px;`;

        const minimizeBtn = document.createElement('span');
        minimizeBtn.id = 'minimize-btn';
        minimizeBtn.textContent = '-';
        minimizeBtn.title = '折叠/展开';
        minimizeBtn.style.cssText = `
            cursor:pointer;font-size:22px;transform:scaleX(1.3);margin-right:10px;user-select:none;
        `;
        minimizeBtn.onclick = () => { toggleCollapse(); };
        controls.appendChild(minimizeBtn);

        const closeBtn = document.createElement('span');
        closeBtn.textContent = '×';
        closeBtn.title = '关闭助手';
        closeBtn.style.cssText = `
            cursor:pointer;font-size:24px;user-select:none;opacity:.85;
        `;
        closeBtn.onclick = () => { container.style.display = 'none'; };
        controls.appendChild(closeBtn);

        header.appendChild(controls);

        const content = document.createElement('div');
        content.id = 'rewards-helper-content';
        content.style.cssText = `padding: 14px 18px 0 18px;`;

        const progress = document.createElement('div');
        progress.id = 'rewards-progress';
        progress.style.cssText = `font-size:15px;margin-bottom:6px;font-weight:500;letter-spacing:.02em;`;
        progress.innerHTML = `进度: <span class="progress-value">加载中...</span>`;
        content.appendChild(progress);

        const progressBar = document.createElement('div');
        progressBar.className = 'progress-bar';
        progressBar.style.width = "100%";
        const progressBarInner = document.createElement('div');
        progressBarInner.className = 'progress-bar-inner';
        progressBarInner.style.width = "0%";
        progressBar.appendChild(progressBarInner);
        content.appendChild(progressBar);

        const statusLine = document.createElement('div');
        statusLine.id = 'search-status';
        statusLine.className = 'status-message';
        statusLine.textContent = "";
        content.appendChild(statusLine);

        const countdown = document.createElement('div');
        countdown.id = 'countdown';
        countdown.className = 'countdown';
        countdown.style.display = 'none';
        content.appendChild(countdown);

        const searchTermsContainer = document.createElement('div');
        searchTermsContainer.id = 'rewards-search-terms-container';
        searchTermsContainer.style.cssText = `margin-top:8px;max-height:120px;overflow-y:auto;`;

        const mainTermsTitle = document.createElement('div');
        mainTermsTitle.className = 'heading';
        mainTermsTitle.textContent = '主页面搜索词';
        searchTermsContainer.appendChild(mainTermsTitle);
        const mainTerms = document.createElement('div');
        mainTerms.id = 'main-search-terms';
        mainTerms.style.cssText = `margin-bottom: 6px;line-height:1.7;`;
        searchTermsContainer.appendChild(mainTerms);

        const iframeTermsTitle = document.createElement('div');
        iframeTermsTitle.className = 'heading';
        iframeTermsTitle.textContent = '侧栏推荐搜索词';
        searchTermsContainer.appendChild(iframeTermsTitle);
        const iframeTerms = document.createElement('div');
        iframeTerms.id = 'iframe-search-terms';
        iframeTerms.style.cssText = `margin-bottom:3px;line-height:1.7;`;
        searchTermsContainer.appendChild(iframeTerms);

        content.appendChild(searchTermsContainer);

        const divider = document.createElement('hr');
        divider.style.cssText = `border:0;border-top:1px solid ${theme.border};margin:10px 0 8px 0;`;
        content.appendChild(divider);

        const configSection = document.createElement('div');
        configSection.id = 'rewards-config-section';
        configSection.style.cssText = `margin-bottom:10px;`;

        const configTitle = document.createElement('div');
        configTitle.className = 'heading';
        configTitle.textContent = '配置参数:';
        configSection.appendChild(configTitle);

        const configForm = document.createElement('div');
        configForm.style.cssText = `
            display: grid; grid-template-columns: 110px auto; gap: 4px 6px; align-items:center;
            font-size:13px;margin-top:2px;
        `;
        configForm.innerHTML = `
            <label for="rest-time">休息时间(分):</label>
            <input type="number" id="rest-time" value="${config.restTime / 60}" min="1" max="30">
            <label for="scroll-time">滚动时间(秒):</label>
            <input type="number" id="scroll-time" value="${config.scrollTime}" min="3" max="30">
            <label for="wait-time">等待时间(秒):</label>
            <input type="number" id="wait-time" value="${config.waitTime}" min="3" max="30">
            <label for="max-no-progress">容错次数:</label>
            <input type="number" id="max-no-progress" value="${config.maxNoProgressCount}" min="1" max="10">
        `;
        configSection.appendChild(configForm);
        content.appendChild(configSection);

        const buttonsContainer = document.createElement('div');
        buttonsContainer.id = 'rewards-buttons-container';
        buttonsContainer.style.cssText = `
            display:flex;flex-direction:column;align-items:stretch;margin-top:0;gap:7px 0;padding:0 18px;
        `;

        const startSearchBtn = document.createElement('button');
        startSearchBtn.id = 'start-search-btn';
        startSearchBtn.className = 'action-btn';
        startSearchBtn.textContent = '开始自动搜索';
        startSearchBtn.onclick = function () {
            if (!isSearching) startAutomatedSearch();
            else stopAutomatedSearch();
        };
        buttonsContainer.appendChild(startSearchBtn);

        container.appendChild(header);
        container.appendChild(content);
        container.appendChild(buttonsContainer);
        document.body.appendChild(container);

        makeDraggable(container, header);

        setTimeout(() => {
            const restTimeInput = document.getElementById('rest-time');
            const scrollTimeInput = document.getElementById('scroll-time');
            const waitTimeInput = document.getElementById('wait-time');
            const maxNoProgressInput = document.getElementById('max-no-progress');
            function updateConfigAndSave() {
                config.restTime = (parseInt(restTimeInput.value) || 5) * 60;
                config.scrollTime = parseInt(scrollTimeInput.value) || 10;
                config.waitTime = parseInt(waitTimeInput.value) || 10;
                config.maxNoProgressCount = parseInt(maxNoProgressInput.value) || 3;
                saveState();
            }
            if (restTimeInput) restTimeInput.addEventListener('change', updateConfigAndSave);
            if (scrollTimeInput) scrollTimeInput.addEventListener('change', updateConfigAndSave);
            if (waitTimeInput) waitTimeInput.addEventListener('change', updateConfigAndSave);
            if (maxNoProgressInput) maxNoProgressInput.addEventListener('change', updateConfigAndSave);
        }, 600);
    }

    function toggleCollapse() {
        searchState.isCollapsed = !searchState.isCollapsed;
        saveState();
        applyCollapseState();
    }
    function applyCollapseState() {
        const searchTermsContainer = document.getElementById('rewards-search-terms-container');
        const configSection = document.getElementById('rewards-config-section');
        const minimizeBtn = document.getElementById('minimize-btn');
        if (searchState.isCollapsed) {
            if (searchTermsContainer) searchTermsContainer.style.display = 'none';
            if (configSection) configSection.style.display = 'none';
            if (minimizeBtn) minimizeBtn.textContent = '+';
        } else {
            if (searchTermsContainer) searchTermsContainer.style.display = 'block';
            if (configSection) configSection.style.display = 'block';
            if (minimizeBtn) minimizeBtn.textContent = '-';
        }
    }
    function updateStatus(message) {
        const statusElement = document.getElementById('search-status');
        if (statusElement) statusElement.textContent = message || '';
    }
    function updateCountdown(seconds, action) {
        const countdownElement = document.getElementById('countdown');
        if (countdownElement) {
            if (seconds > 0) {
                let actionText = "";
                switch (action) {
                    case 'scrolling': actionText = '滚动中'; break;
                    case 'waiting': actionText = '等待中'; break;
                    case 'resting': actionText = '休息中'; break;
                    default: actionText = '倒计时';
                }
                countdownElement.textContent = `${actionText}: ${seconds}秒`;
                countdownElement.style.display = 'block';
            } else {
                countdownElement.style.display = 'none';
            }
        }
    }
    function updateProgressUI() {
        const elem = document.getElementById('rewards-progress');
        const bar = document.querySelector('.progress-bar-inner');
        let current = currentProgress.current, total = currentProgress.total;
        if (!total || total === 0) total = 1;
        let percent = Math.min(100, Math.round((current / total) * 100));
        if (elem) {
            let done = currentProgress.completed;
            elem.innerHTML = `进度: <span class="progress-value">${current}/${total}${done ? " (已完成)" : ""}</span>`;
        }
        if (bar) bar.style.width = percent + "%";
    }
    function updateSearchTermsUI() {
        const mainTermsContainer = document.getElementById('main-search-terms');
        if (mainTermsContainer) {
            mainTermsContainer.innerHTML = "";
            mainPageSearchTerms.forEach(term => {
                const div = document.createElement('span');
                div.className = "term-pill" + (usedSearchTerms.includes(term) ? " used" : "");
                div.textContent = term;
                mainTermsContainer.appendChild(div);
            });
        }
        const iframeTermsContainer = document.getElementById('iframe-search-terms');
        if (iframeTermsContainer) {
            iframeTermsContainer.innerHTML = "";
            iframeSearchTerms.forEach(term => {
                const div = document.createElement('span');
                div.className = "term-pill" + (usedSearchTerms.includes(term) ? " used" : "");
                div.textContent = term;
                iframeTermsContainer.appendChild(div);
            });
        }
    }

    function makeDraggable(container, header) {
        let offsetX, offsetY, isDragging = false;
        header.addEventListener('mousedown', (e) => {
            if (window.getComputedStyle(e.target).cursor === 'pointer') return;
            isDragging = true;
            if (container.style.right) {
                container.style.left = container.offsetLeft + 'px';
                container.style.right = '';
            }
            offsetX = e.clientX - container.offsetLeft;
            offsetY = e.clientY - container.offsetTop;
            document.body.style.userSelect = 'none';
            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp, { once: true });
            function onMouseMove(e) {
                if (!isDragging) return;
                container.style.top = (e.clientY - offsetY) + 'px';
                container.style.left = (e.clientX - offsetX) + 'px';
            }
            function onMouseUp() {
                isDragging = false;
                document.body.style.userSelect = '';
                document.removeEventListener('mousemove', onMouseMove);
            }
        });
    }

    // ----------- 搜索词采集 -----------
    function getSearchTermsFromMainDoc() {
        const suggestionsContainer = document.querySelector('.richrsrailsugwrapper');
        if (suggestionsContainer) {
            const terms = [];
            const suggestions = suggestionsContainer.querySelectorAll('.richrsrailsuggestion_text');
            suggestions.forEach(suggestion => {
                if (suggestion.textContent) terms.push(suggestion.textContent);
            });
            mainPageSearchTerms = [...new Set(terms)];
            updateSearchTermsUI();
            return true;
        }
        return false;
    }
    function getDataFromIframe() {
        const iframe = document.querySelector('iframe');
        if (!iframe) return false;
        try {
            const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
            const progressElement = iframeDoc.querySelector('.daily_search_row span:last-child');
            if (progressElement) {
                const match = progressElement.textContent.match(/(\d+)\/(\d+)/);
                if (match) {
                    const current = parseInt(match[1]);
                    const total = parseInt(match[2]);
                    currentProgress.total = total;
                    if (currentProgress.lastChecked > 0 && current <= currentProgress.lastChecked && isSearching) {
                        currentProgress.noProgressCount++;
                        if (currentProgress.noProgressCount >= config.maxNoProgressCount) {
                            searchState.needRest = true;
                        }
                    } else if (current > currentProgress.lastChecked) {
                        currentProgress.noProgressCount = 0;
                        searchState.needRest = false;
                    }
                    currentProgress.current = current;
                    currentProgress.lastChecked = current;
                    if (current >= total) {
                        currentProgress.completed = true;
                    }
                }
            }
            // 搜索词
            const searchTermsContainer = iframeDoc.querySelector('.ss_items_wrapper');
            if (searchTermsContainer) {
                const terms = [];
                const spans = searchTermsContainer.querySelectorAll('span');
                spans.forEach(span => { if (span.textContent) terms.push(span.textContent); });
                iframeSearchTerms = [...new Set(terms)];
                updateSearchTermsUI();
            }
            return true;
        } catch (e) { return false; }
    }
    function getRewardsData(callback) {
        openRewardsSidebar();
        setTimeout(() => {
            getDataFromIframe();
            getSearchTermsFromMainDoc();
            updateProgressUI();
            updateSearchTermsUI();
            if (callback) callback();
        }, 1500);
    }
    function openRewardsSidebar() {
        const pointsContainer = document.querySelector('.points-container');
        if (pointsContainer) {
            pointsContainer.click();
            return true;
        }
        return false;
    }

    // ----------- 搜索流程 -----------
    function getSearchTerm() {
        let allTerms = [...new Set([...mainPageSearchTerms, ...iframeSearchTerms])];
        let availableTerms = allTerms.filter(term => !usedSearchTerms.includes(term));
        if (availableTerms.length === 0 && allTerms.length > 0) {
            usedSearchTerms = [];
            availableTerms = allTerms;
        }
        if (availableTerms.length > 0) {
            const randomIndex = Math.floor(Math.random() * availableTerms.length);
            const term = availableTerms[randomIndex];
            usedSearchTerms.push(term);
            updateSearchTermsUI();
            return term;
        }
        return null;
    }

    // 兼容SPA的搜索触发
    function performSearch(term, callback) {
        if (!term) return false;
        const searchBox = document.querySelector('#sb_form_q');
        if (searchBox) {
            searchBox.value = term;
            const searchForm = document.querySelector('#sb_form');
            if (searchForm) {
                // 记录跳转前的URL
                const beforeUrl = location.href;
                searchForm.submit();
                setTimeout(() => {
                    if (location.href === beforeUrl) {
                        waitForSearchResultChange(term, callback);
                    }
                }, 1500);
                return true;
            }
        }
        return false;
    }

    // 监听搜索结果区变化(兼容SPA)
    function waitForSearchResultChange(lastTerm, callback) {
        const content = document.querySelector('#b_content');
        if (!content) {
            setTimeout(() => waitForSearchResultChange(lastTerm, callback), 500);
            return;
        }
        let observer = new MutationObserver(() => {
            const input = document.querySelector('#sb_form_q');
            if (input && input.value === lastTerm) {
                observer.disconnect();
                if (callback) callback();
            }
        });
        observer.observe(content, { childList: true, subtree: true });
        setTimeout(() => observer.disconnect(), 15000);
    }

    function simulateScrolling(callback) {
        updateStatus('正在滚动页面...');
        searchState.currentAction = 'scrolling';
        startCountdown(config.scrollTime, 'scrolling', callback);
        const scrollInterval = setInterval(() => {
            if (searchState.currentAction !== 'scrolling') {
                clearInterval(scrollInterval); return;
            }
            const scrollAmount = Math.floor(Math.random() * 300) + 100;
            const scrollDirection = Math.random() > 0.3 ? 1 : -1;
            window.scrollBy(0, scrollAmount * scrollDirection);
        }, 1000);
    }

    function checkProgress(callback) {
        openRewardsSidebar();
        setTimeout(() => {
            getDataFromIframe();
            getSearchTermsFromMainDoc();
            updateProgressUI();
            updateSearchTermsUI();
            saveState();
            if (currentProgress.completed) {
                updateStatus('搜索任务已完成!');
                stopAutomatedSearch();
            } else if (searchState.needRest) {
                startResting();
            } else if (callback) {
                callback();
            }
        }, 1500);
    }

    function waitForNextSearch() {
        updateStatus('等待下一次搜索...');
        startCountdown(config.waitTime, 'waiting', performNextSearch);
    }

    function performNextSearch() {
        if (!isSearching) return;

        if (currentProgress.completed) {
            updateStatus('搜索任务已完成!');
            stopAutomatedSearch();
            return;
        }

        const searchTerm = getSearchTerm();
        if (!searchTerm) {
            updateStatus('无可用搜索词,正在刷新词库...');
            getRewardsData(() => {
                const newSearchTerm = getSearchTerm();
                if (newSearchTerm) {
                    updateStatus(`已获取新词: ${newSearchTerm}`);
                    performSearch(newSearchTerm, () => {
                        setTimeout(() => {
                            simulateScrolling(() => {
                                checkProgress(() => {
                                    waitForNextSearch();
                                });
                            });
                        }, 2000);
                    });
                } else {
                    updateStatus('无法获取搜索词,将休息后重试。');
                    startResting();
                }
            });
            return;
        }

        const remainingSearches = currentProgress.total - currentProgress.current;
        updateStatus(`正在搜索: ${searchTerm} [剩余约: ${remainingSearches > 0 ? remainingSearches : 'N/A'}]`);

        let searchCallbackCalled = false;
        function searchCallbackWrapper() {
            if (searchCallbackCalled) return;
            searchCallbackCalled = true;
            setTimeout(() => {
                simulateScrolling(() => {
                    checkProgress(() => {
                        waitForNextSearch();
                    });
                });
            }, 2000);
        }
        performSearch(searchTerm, searchCallbackWrapper);
        setTimeout(searchCallbackWrapper, 18000); // 兜底防断链
    }

    function startResting() {
        searchState.needRest = false;
        currentProgress.noProgressCount = 0;
        updateStatus(`连续 ${config.maxNoProgressCount} 次无进度,休息 ${config.restTime / 60} 分钟...`);
        saveState();
        startCountdown(config.restTime, 'resting', () => {
            updateStatus('休息结束,继续搜索。');
            performNextSearch();
        });
    }

    function startAutomatedSearch() {
        getRewardsData(() => {
            if (currentProgress.completed) {
                 updateStatus('任务已完成,无需启动。');
                 return;
            }
            if (mainPageSearchTerms.length === 0 && iframeSearchTerms.length === 0) {
                alert('未能获取任何搜索词,请检查是否已登录或页面是否正常。');
                return;
            }
            startSearchProcess();
        });
    }
    function startSearchProcess() {
        isSearching = true;
        saveState();
        searchState.needRest = false;
        currentProgress.noProgressCount = 0;
        usedSearchTerms = [];
        document.getElementById('start-search-btn').textContent = '停止搜索';
        document.getElementById('start-search-btn').classList.add('stop');
        updateStatus('自动搜索已开始...');
        updateProgressUI();
        updateSearchTermsUI();
        performNextSearch();
    }
    function stopAutomatedSearch() {
        if (countdownTimer) { clearInterval(countdownTimer); }
        isSearching = false;
        saveState();
        searchState.currentAction = 'idle';
        updateCountdown(0, '');
        const startBtn = document.getElementById('start-search-btn');
        if (startBtn) {
            startBtn.textContent = '开始自动搜索';
            startBtn.classList.remove('stop');
        }
        updateStatus('搜索已停止');
    }

    function showCompletionNotification() {
        const notification = document.createElement('div');
        notification.style.cssText = `
            position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);
            background-color: #0078d4;color: white;padding: 20px;border-radius: 5px;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);z-index: 10001;text-align: center;font-size: 16px;
        `;
        notification.innerHTML = `
            <div style="font-weight: bold; margin-bottom: 10px; font-size: 18px;">任务完成!</div>
            <div>已完成所有 ${currentProgress.total} 次搜索任务</div>
            <button id="notification-close" style="
                margin-top: 15px;padding: 5px 15px;background-color: white;color: #0078d4;
                border: none;border-radius: 3px;cursor: pointer;">关闭</button>
        `;
        document.body.appendChild(notification);
        document.getElementById('notification-close').addEventListener('click', function () {
            notification.remove();
        });
        setTimeout(() => { if (document.body.contains(notification)) notification.remove(); }, 10000);
    }
    function startCountdown(seconds, action, callback) {
        if (countdownTimer) { clearInterval(countdownTimer); }
        searchState.currentAction = action;
        searchState.countdown = seconds;
        updateCountdown(seconds, action);
        countdownTimer = setInterval(() => {
            searchState.countdown--;
            updateCountdown(searchState.countdown, action);
            if (searchState.countdown <= 0) {
                clearInterval(countdownTimer);
                countdownTimer = null;
                if (callback) callback();
            }
        }, 1000);
    }

    // --- 启动 ---
    window.addEventListener('load', function () {
        loadState();
        createUI();
        applyCollapseState();
        updateProgressUI();
        updateSearchTermsUI();
        if (isSearching) {
            updateStatus('页面刷新,自动恢复继续自动搜索...');
            document.getElementById('start-search-btn').textContent = '停止搜索';
            document.getElementById('start-search-btn').classList.add('stop');
            performNextSearch();
        } else {
            setTimeout(() => { getRewardsData(); }, 2000);
        }
    });
})();