观众盲盒数据统计

统计观众盲盒数据

// ==UserScript==
// @name         观众盲盒数据统计
// @namespace    https://greasyfork.org/zh-CN/scripts/526518
// @version      1.0.1
// @description  统计观众盲盒数据
// @author       史蒂夫
// @match        http*://audiences.me/bonusHistory.php*
// @icon         https://audiences.me/favicon.ico
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @connect      audiences.me
// @license      GPL
// ==/UserScript==

(function () {
    'use strict';

    const CONFIG = {
        BOX_OPERATION_TEXT: '你开启的盲盒中含有:',
        SPECIAL_ITEMS: [
            '100 爆米花',
            '88888 爆米花',
            '1000000 爆米花',
            '7天彩虹ID',
            '30天彩虹ID',
            '1天VIP',
            '7天限时电影票'
        ],
        PAGINATION_CONTAINER: 'p:has(font.gray)',
        TABLE_SELECTOR: 'table.comments_table'
    };

    let stats = GM_getValue('boxStats', {
        boxAttempts: 0,
        totalConsumed: 0,
        popcornHits: { times: 0, total: 0 },
        uploadStats: { times: 0, total: 0 },
        specialItems: Object.fromEntries(CONFIG.SPECIAL_ITEMS.map(item => [item, 0]))
    });

    let isProcessing = false;
    let currentPage = 1;
    let totalPages = 1;

    // 初始化界面
    const { resultContainer, toggleButton } = createUIElements();

    GM_addStyle(`
        .stats-loading {
            position: fixed;
            top: 60px;
            right: 20px;
            padding: 10px;
            background: rgba(0, 0, 0, 0.9);
            color: #00ff9d;
            border-radius: 5px;
            z-index: 9999;
        }
    `);

    async function main() {
        if (isProcessing) return;
        isProcessing = true;
        showLoading('正在统计,请稍等一会...');

        try {
            await resetStats();
            const currentDoc = document;

            // 动态计算总页数
            totalPages = getTotalPages(currentDoc);

            // 生成所有页码URL
            const baseUrl = new URL(window.location.href);
            const pageUrls = [];
            for (let page = 0; page < totalPages; page++) {
                const url = new URL(baseUrl);
                url.searchParams.set('page', page);
                pageUrls.push(url.href);
            }

            // 处理所有页面
            for (const url of pageUrls) {
                await processPage(url);
            }

            updateStatsDisplay();
            GM_setValue('boxStats', stats);
        } catch (error) {
            console.error('统计出错:', error);
            showLoading(`错误: ${error.message}`, 3000);
        } finally {
            isProcessing = false;
            hideLoading();
        }
    }

    async function processPage(url) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function (response) {
                    const parser = new DOMParser();
                    const doc = parser.parseFromString(response.responseText, "text/html");
                    parseTable(doc.querySelector(CONFIG.TABLE_SELECTOR));
                    resolve();
                },
                onerror: reject
            });
        });
    }

    function parseTable(table) {
        if (!table) return;

        table.querySelectorAll('tr:not(:first-child)').forEach(row => {
            const reasonCell = row.cells[2];
            if (!reasonCell.textContent.includes(CONFIG.BOX_OPERATION_TEXT)) return;

            stats.boxAttempts++;
            const amount = parseInt(row.cells[1].textContent);
            stats.totalConsumed += Math.abs(amount);

            processBoxContents(reasonCell.textContent);
        });
    }

    function processBoxContents(text) {
        const items = text.split(CONFIG.BOX_OPERATION_TEXT)[1]
            .split(/[,、]/)
            .map(item => item.trim());

        items.forEach(item => {
            // 处理爆米花
            const popcornMatch = item.match(/(\d+)\s*爆米花/);
            if (popcornMatch) {
                stats.popcornHits.times++;
                stats.popcornHits.total += parseInt(popcornMatch[1]);
            }

            // 处理上传量
            const uploadMatch = item.match(/(\d+)G\s*上传量/);
            if (uploadMatch) {
                stats.uploadStats.times++;
                stats.uploadStats.total += parseInt(uploadMatch[1]);
            }

            // 处理特殊物品
            CONFIG.SPECIAL_ITEMS.forEach(specialItem => {
                // if (item.includes(specialItem)) { // 模糊匹配
                if (item == specialItem) { // 精确匹配
                    stats.specialItems[specialItem]++;
                }
            });
        });
    }

    function getTotalPages(doc) {
        // 获取所有包含page参数的链接
        const pageLinks = Array.from(doc.querySelectorAll(`${CONFIG.PAGINATION_CONTAINER} a[href*="page="]`));
        const pages = pageLinks.map(link => {
            const match = link.href.match(/page=(\d+)/);
            return match ? parseInt(match[1], 10) : -1;
        }).filter(page => page >= 0);
        return pages.length > 0 ? Math.max(...pages) + 1 : 1;
    }

    function createUIElements() {
        const resultContainer = document.createElement('div');
        resultContainer.innerHTML = `
            <h3 style="margin:0 0 15px 0; border-bottom:1px solid #444; padding-bottom:8px;">
                🎁 盲盒统计 (共<span id="stats-pages"> 0 </span>页)
            </h3>
            <div style="line-height:1.5; font-size:14px;">
                ${createStatsTemplate()}
            </div>
        `;

        resultContainer.style.cssText = `
            position: fixed; top: 60px; right: 20px; padding: 15px;
            background: rgba(0, 0, 0, 0.95); color: white; z-index: 9999;
            border-radius: 10px; width: 360px; max-height: 90vh;
            overflow-y: auto; box-shadow: 0 4px 12px rgba(0,0,0,0.25);
            font-family: Arial, sans-serif; display: none;
        `;

        const toggleButton = document.createElement('button');
        toggleButton.innerHTML = '📊 盲盒统计';
        toggleButton.style.cssText = `
            position: fixed; top: 20px; right: 20px; z-index: 10000;
            padding: 8px 16px; background: #2196F3; color: white;
            border: none; border-radius: 25px; cursor: pointer;
            box-shadow: 0 2px 8px rgba(33,150,243,0.4); transition: 0.2s;
        `;

        toggleButton.addEventListener('click', async () => {
            resultContainer.style.display = resultContainer.style.display === 'none' ? 'block' : 'none';
            if (resultContainer.style.display === 'block') {
                await main();
            }
        });

        document.body.appendChild(resultContainer);
        document.body.appendChild(toggleButton);
        return { resultContainer, toggleButton };
    }

    function createStatsTemplate() {
        return `
            <p>▶ 开启次数:<b class="stats-value">${stats.boxAttempts}</b> 次</p>
            <p>▶ 总消耗爆米花:<b class="stats-value">${stats.totalConsumed}</b></p>
            <p>▶ 获得爆米花:<b class="stats-value">${stats.popcornHits.times}</b> 次(共 <b>${stats.popcornHits.total}</b> 爆米花)</p>
            <p>▶ 获得上传量:<b class="stats-value">${stats.uploadStats.times}</b> 次(共 <b>${stats.uploadStats.total}G</b> 上传量)</p>
            <div style="margin:15px 0 5px 0; padding:8px 0; border-top:1px solid #444;">
            <p style="margin:0 0 8px 0; color:#00ff9d;">★ 特殊物品统计 ★</p>
            <ul style="margin:0; padding-left:20px; columns: 2;">
                ${CONFIG.SPECIAL_ITEMS.map(item => `
                    <li style="break-inside: avoid;">${item}:<b>${stats.specialItems[item] ?? 0}</b></li>
                `).join('')}
            </ul>
        </div>
    `;
    }

    function updateStatsDisplay() {
        resultContainer.innerHTML = `
            <h3 style="margin:0 0 15px 0; border-bottom:1px solid #444; padding-bottom:8px;">
                🎁 盲盒统计 (共<span id="stats-pages"> ${totalPages} </span>页)
            </h3>
            <div style="line-height:1.5; font-size:14px;">
                ${createStatsTemplate()}
            </div>
        `;
    }

    async function resetStats() {
        stats = {
            boxAttempts: 0,
            totalConsumed: 0,
            popcornHits: { times: 0, total: 0 },
            uploadStats: { times: 0, total: 0 },
            specialItems: Object.fromEntries(CONFIG.SPECIAL_ITEMS.map(item => [item, 0]))
        };
        GM_setValue('boxStats', stats);
    }

    function showLoading(text, timeout = 5000) {
        const existing = document.querySelector('.stats-loading');
        if (existing) existing.remove();

        const loading = document.createElement('div');
        loading.className = 'stats-loading';
        loading.textContent = text;
        document.body.appendChild(loading);

        if (timeout) {
            setTimeout(() => loading.remove(), timeout);
        }
    }

    function hideLoading() {
        document.querySelectorAll('.stats-loading').forEach(el => el.remove());
    }

    // 初始化执行
    if (!location.search.includes('page=')) {
        main();
    }
})();