Greasy Fork

Greasy Fork is available in English.

请求监听过滤器

监听所有HTTP和HTTPS请求

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         请求监听过滤器
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  监听所有HTTP和HTTPS请求
// @author       晚风
// @match        http://*/*
// @match        https://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @run-at       document-start
// @inject-into  page
// @license MIT
// ==/UserScript==

// 立即执行重定向检查,在任何其他代码执行之前
(function() {
    if (window.location.href.includes('cryptbox.sankuai.com/file/')) {
        // 阻止页面继续加载
        window.stop();

        const currentUrl = window.location.href;
        const newUrl = currentUrl.replace(
            /^https:\/\/cryptbox\.sankuai\.com\/file\/(.+)$/,
            'https://distribute-platform-pub.sankuai.com/distribute/download/v1/$1'
        );

        if (currentUrl !== newUrl) {
            // 创建一个隐藏的 iframe 来触发下载
            const iframe = document.createElement('iframe');
            iframe.style.display = 'none';
            iframe.src = newUrl;
            document.body.appendChild(iframe);

            // 等待一段时间后关闭页面
            setTimeout(() => {
                window.close();
            }, 1000); // 给予1秒时间让下载开始

            return;
        }
    }
})();

class RequestMonitor {
    constructor() {
        if (window._RequestMonitor) {
            return window._RequestMonitor;
        }
        window._RequestMonitor = this;

        // 初始化状态
        this.logs = [];
        this.filterKeyword = '';
        this.initialized = false;
        this.requestCount = {
            total: 0,
            xhr: 0,
            fetch: 0
        };

        // 重定向规则
        this.redirectRules = [
            {
                pattern: /^https:\/\/cryptbox\.sankuai\.com\/file\/(.+)$/,
                replacement: 'https://distribute-platform-pub.sankuai.com/distribute/download/v1/$1'
            }
        ];

        // SVG 图标
        this.icons = {
            copy: `<svg viewBox="64 64 896 896" width="14" height="14" fill="currentColor">
                <path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z" />
            </svg>`,
            success: `<svg viewBox="64 64 896 896" width="14" height="14" fill="currentColor">
                <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"/>
            </svg>`
        };

        // 设置默认状态为折叠
        this.isPanelExpanded = false;

        this.init();
    }

    init() {
        if (this.initialized) return;
        this.setupRequestInterceptors();
        this.createUI();
        this.initialized = true;
    }

    // UI 相关方法
    createUI() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', () => this.renderUI(), { once: true });
        } else {
            this.renderUI();
        }
    }

    renderUI() {
        // 创建主容器
        const container = this.createMainContainer();

        // 创建头部
        const header = this.createHeader();
        container.appendChild(header);

        // 创建统计栏
        const stats = this.createStatsBar();
        container.appendChild(stats);

        // 创建请求列表
        const content = this.createRequestList();
        container.appendChild(content);

        // 创建详情面板
        this.createDetailPanel();

        // 添加到页面
        document.body.appendChild(container);
    }

    // 创建主容器
    createMainContainer() {
        const container = document.createElement('div');
        container.id = 'requestMonitor';
        container.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            width: ${this.isPanelExpanded ? '400px' : '40px'};
            height: ${this.isPanelExpanded ? '90vh' : '40px'};
            background: rgba(255, 255, 255, 0.95);
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            z-index: 999999;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            backdrop-filter: blur(10px);
            border: 1px solid rgba(0, 0, 0, 0.1);
            transition: all 0.3s ease;
            overflow: hidden;
            transform-origin: right center;
            display: ${this.isPanelExpanded ? 'flex' : 'block'};
            flex-direction: column;
        `;

        // 创建折叠按钮
        const toggleButton = document.createElement('div');
        toggleButton.style.cssText = `
            position: ${this.isPanelExpanded ? 'absolute' : 'relative'};
            top: ${this.isPanelExpanded ? '12px' : '0'};
            right: ${this.isPanelExpanded ? '12px' : '0'};
            width: ${this.isPanelExpanded ? '24px' : '40px'};
            height: ${this.isPanelExpanded ? '24px' : '40px'};
            background: ${this.isPanelExpanded ? 'rgba(24, 144, 255, 0.1)' : '#1890ff'};
            border-radius: ${this.isPanelExpanded ? '4px' : '8px'};
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            color: ${this.isPanelExpanded ? '#1890ff' : 'white'};
            font-size: 18px;
            transition: all 0.3s;
            z-index: 1;
        `;
        toggleButton.innerHTML = this.isPanelExpanded ? '−' : '+';

        toggleButton.onmouseover = () => {
            toggleButton.style.background = this.isPanelExpanded ?
                'rgba(24, 144, 255, 0.2)' :
                'rgba(24, 144, 255, 0.8)';
        };

        toggleButton.onmouseout = () => {
            toggleButton.style.background = this.isPanelExpanded ?
                'rgba(24, 144, 255, 0.1)' :
                '#1890ff';
        };

        toggleButton.onclick = (e) => {
            e.stopPropagation();
            this.togglePanel(!this.isPanelExpanded);
        };

        container.appendChild(toggleButton);
        return container;
    }

    // 创建头部
    createHeader() {
        const header = document.createElement('div');
        header.style.cssText = `
            padding: 12px 16px;
            padding-right: 48px;
            background: rgba(245, 245, 245, 0.95);
            border-radius: 8px 8px 0 0;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
            user-select: none;
            border-bottom: 1px solid rgba(0, 0, 0, 0.05);
        `;

        // 标题
        const title = document.createElement('span');
        title.textContent = `${document.title} - 请求监听器`;
        title.style.cssText = `
            font-weight: 600;
            color: #333;
            font-size: 14px;
        `;

        // 搜索
        const filterInput = document.createElement('input');
        filterInput.placeholder = '搜索请求...';
        filterInput.style.cssText = `
            margin-left: 12px;
            padding: 6px 12px;
            border: 1px solid rgba(0, 0, 0, 0.1);
            border-radius: 4px;
            flex-grow: 1;
            font-size: 12px;
            outline: none;
            transition: all 0.3s;
            background: rgba(255, 255, 255, 0.9);
        `;

        // 添加拖拽功能
        this.setupDrag(header);

        // 添加事件监听
        filterInput.oninput = () => {
            this.filterKeyword = filterInput.value.toLowerCase();
            this.updateList();
        };

        header.appendChild(title);
        header.appendChild(filterInput);
        return header;
    }

    // 创建统计栏
    createStatsBar() {
        const stats = document.createElement('div');
        stats.id = 'requestStats';
        stats.style.cssText = `
            padding: 8px 16px;
            background: rgba(250, 250, 250, 0.95);
            border-bottom: 1px solid rgba(0, 0, 0, 0.05);
            font-size: 12px;
            color: #666;
            display: flex;
            gap: 16px;
        `;
        stats.innerHTML = `
            <span>总请求: <b id="totalCount">0</b></span>
            <span>XHR: <b id="xhrCount">0</b></span>
            <span>Fetch: <b id="fetchCount">0</b></span>
        `;
        return stats;
    }

    // 创建请求列表
    createRequestList() {
        const content = document.createElement('div');
        content.style.cssText = `
            flex: 1;
            overflow-y: auto;
            display: ${this.isPanelExpanded ? 'block' : 'none'};
            background: rgba(255, 255, 255, 0.95);
            padding: 0;
        `;

        const requestList = document.createElement('div');
        requestList.id = 'requestList';
        content.appendChild(requestList);

        return content;
    }

    // 创建详情面板
    createDetailPanel() {
        const detailPanel = document.createElement('div');
        detailPanel.id = 'requestDetail';
        detailPanel.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 800px;
            max-height: 80vh;
            background: rgba(255, 255, 255, 0.98);
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
            z-index: 1000000;
            display: none;
            overflow: hidden;
            border: 1px solid rgba(0, 0, 0, 0.1);
            backdrop-filter: blur(10px);
        `;

        const header = document.createElement('div');
        header.style.cssText = `
            padding: 16px;
            background: rgba(245, 245, 245, 0.95);
            border-bottom: 1px solid rgba(0, 0, 0, 0.05);
            display: flex;
            justify-content: space-between;
            align-items: center;
        `;

        const title = document.createElement('h3');
        title.style.cssText = `
            margin: 0;
            font-size: 16px;
            color: #333;
        `;
        title.textContent = '请求详情';

        const closeBtn = document.createElement('button');
        closeBtn.textContent = '×';
        closeBtn.style.cssText = `
            background: none;
            border: none;
            font-size: 24px;
            color: #999;
            cursor: pointer;
            padding: 0 8px;
            line-height: 1;
        `;

        const content = document.createElement('div');
        content.style.cssText = `
            padding: 16px;
            overflow-y: auto;
            max-height: calc(80vh - 60px);
        `;

        closeBtn.onclick = () => detailPanel.style.display = 'none';

        header.appendChild(title);
        header.appendChild(closeBtn);
        detailPanel.appendChild(header);
        detailPanel.appendChild(content);
        document.body.appendChild(detailPanel);
    }

    // 设置拖拽功能
    setupDrag(header) {
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;

        header.onmousedown = (e) => {
            isDragging = true;
            const container = document.getElementById('requestMonitor');
            const rect = container.getBoundingClientRect();
            initialX = e.clientX - rect.left;
            initialY = e.clientY - rect.top;
        };

        document.onmousemove = (e) => {
            if (isDragging) {
                const container = document.getElementById('requestMonitor');
                const viewportWidth = window.innerWidth;

                // 算新位置
                currentX = e.clientX - initialX;
                currentY = e.clientY - initialY;

                // 计算距离右侧的距离
                const rightDistance = viewportWidth - (currentX + container.offsetWidth);

                // 更新位置,使用 right 而不是 left
                container.style.right = `${rightDistance}px`;
                container.style.top = `${currentY}px`;
                container.style.left = 'auto';
            }
        };

        document.onmouseup = () => isDragging = false;
    }

    // 请求拦截相关方法
    setupRequestInterceptors() {
        this.interceptXHR();
        this.interceptFetch();
    }

    // XHR拦截实现
    interceptXHR() {
        const originalXHR = XMLHttpRequest.prototype;
        const originalOpen = originalXHR.open;
        const originalSend = originalXHR.send;
        const self = this;

        originalXHR.open = function(method, url) {
            if (self.shouldSkipRequest(url)) return;

            this._requestData = {
                method,
                url: url instanceof URL ? url.href : url,
                status: null,
                response: null,
                type: 'xhr',
                requestData: null,
                urlParams: self.getUrlParams(url),
                timestamp: new Date().toLocaleTimeString()
            };
            return originalOpen.apply(this, arguments);
        };

        originalXHR.send = function(data) {
            if (this._requestData) {
                if (data) {
                    try {
                        this._requestData.requestData = typeof data === 'string'
                            ? JSON.parse(data) : data;
                    } catch (e) {
                        this._requestData.requestData = data;
                    }
                }

                this.addEventListener('load', () => {
                    this._requestData.status = this.status;
                    try {
                        this._requestData.response = this.responseText;
                    } catch (e) {
                        this._requestData.response = '[无法读取响应内容]';
                    }

                    self.logs.push(this._requestData);
                    self.requestCount.total++;
                    self.requestCount.xhr++;
                    self.updateStats();
                    self.updateList();
                });
            }
            return originalSend.apply(this, arguments);
        };
    }

    // Fetch拦截实现
    interceptFetch() {
        const originalFetch = window.fetch;
        const self = this;

        window.fetch = async function(input, init = {}) {
            const url = input instanceof Request ? input.url : input;
            if (self.shouldSkipRequest(url)) return;

            const method = init.method || (input instanceof Request ? input.method : 'GET');
            const logEntry = {
                method,
                url: url instanceof URL ? url.href : url,
                status: null,
                response: null,
                type: 'fetch',
                requestData: init.body || null,
                urlParams: self.getUrlParams(url),
                timestamp: new Date().toLocaleTimeString()
            };

            try {
                const response = await originalFetch.apply(this, arguments);
                const clone = response.clone();
                logEntry.status = clone.status;

                try {
                    const responseText = await clone.text();
                    logEntry.response = responseText;
                } catch (e) {
                    logEntry.response = '[无法读取响应内容]';
                }

                self.logs.push(logEntry);
                self.requestCount.total++;
                self.requestCount.fetch++;
                self.updateStats();
                self.updateList();
                return response;
            } catch (error) {
                logEntry.status = 'ERROR';
                logEntry.response = error.message;
                self.logs.push(logEntry);
                self.updateStats();
                self.updateList();
                throw error;
            }
        };
    }

    // 更新统计信息
    updateStats() {
        const totalEl = document.getElementById('totalCount');
        const xhrEl = document.getElementById('xhrCount');
        const fetchEl = document.getElementById('fetchCount');

        if (totalEl) totalEl.textContent = this.requestCount.total;
        if (xhrEl) xhrEl.textContent = this.requestCount.xhr;
        if (fetchEl) fetchEl.textContent = this.requestCount.fetch;
    }

    // 更新请求列表
    updateList() {
        const requestList = document.getElementById('requestList');
        if (!requestList) return;

        const filteredLogs = this.filterKeyword ?
            this.logs.filter(log => {
                const searchStr = this.filterKeyword.toLowerCase();
                return (
                    log.url.toLowerCase().includes(searchStr) ||
                    (log.response && log.response.toLowerCase().includes(searchStr))
                );
            }) :
            this.logs;

        requestList.innerHTML = '';
        filteredLogs.forEach(log => this.createRequestItem(log, requestList));
    }

    // 创建请求列表项
    createRequestItem(log, container) {
        const item = document.createElement('div');
        item.style.cssText = `
            padding: 12px 16px;
            border-bottom: 1px solid rgba(0, 0, 0, 0.05);
            cursor: pointer;
            font-size: 12px;
            transition: all 0.2s;
            position: relative;
            background: rgba(255, 255, 255, 0.95);
        `;

        item.onmouseover = () => {
            item.style.backgroundColor = 'rgba(24, 144, 255, 0.05)';
        };

        item.onmouseout = () => {
            item.style.backgroundColor = 'rgba(255, 255, 255, 0.95)';
        };

        const statusColor = log.status >= 200 && log.status < 300 ? '#52c41a' : '#f5222d';

        item.innerHTML = `
            <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">
                <span style="
                    font-weight: 600;
                    color: #1890ff;
                    padding: 2px 8px;
                    background: rgba(24, 144, 255, 0.1);
                    border-radius: 4px;
                ">${log.method}</span>
                <span style="
                    color: ${statusColor};
                    padding: 2px 8px;
                    background: ${log.status >= 200 && log.status < 300 ? 'rgba(82, 196, 26, 0.1)' : 'rgba(245, 34, 45, 0.1)'};
                    border-radius: 4px;
                ">${log.status || 'pending'}</span>
                <span style="color: #999; font-size: 11px;">${log.timestamp}</span>
            </div>
            <div style="
                word-break: break-all;
                color: #666;
                padding: 4px 8px;
                background: rgba(0, 0, 0, 0.02);
                border-radius: 4px;
                font-family: monospace;
            ">${log.url}</div>
        `;

        item.onclick = () => this.showRequestDetail(log);
        container.appendChild(item);
    }

    // 工具方法
    shouldSkipRequest(url) {
        return url.includes('cryptbox.sankuai.com/api_sdk/file_download/url_generate?current_url=') ||
               url.includes('cryptbox.sankuai.com/api_sdk/wenshu_url/judge_and_generate?current_url=');
    }

    getUrlParams(url) {
        try {
            const urlObj = new URL(url);
            const params = {};
            for (const [key, value] of urlObj.searchParams) {
                params[key] = value;
            }
            return Object.keys(params).length > 0 ? params : null;
        } catch (e) {
            return null;
        }
    }

    handleRedirect(url) {
        for (const rule of this.redirectRules) {
            if (rule.pattern.test(url)) {
                const newUrl = url.replace(rule.pattern, rule.replacement);
                console.log('重定向到:', newUrl);
                window.location.replace(newUrl);
                return true;
            }
        }
        return false;
    }

    // 显示请求详情
    showRequestDetail(log) {
        const detailPanel = document.getElementById('requestDetail');
        const detailContent = detailPanel.querySelector('div:last-child');

        // 创建复制按钮
        const createCopyButton = (text) => {
            const buttonContainer = document.createElement('div');
            buttonContainer.style.cssText = `
                position: absolute;
                right: 8px;
                top: 8px;
            `;

            const button = document.createElement('button');
            button.innerHTML = this.icons.copy;
            button.style.cssText = `
                padding: 4px 8px;
                background: rgba(24, 144, 255, 0.1);
                border: 1px solid rgba(24, 144, 255, 0.2);
                border-radius: 4px;
                color: #1890ff;
                cursor: pointer;
                font-size: 14px;
                transition: all 0.3s;
                display: flex;
                align-items: center;
                justify-content: center;
                min-width: 28px;
                height: 28px;
                opacity: 0.8;
            `;

            button.onmouseover = () => {
                button.style.background = 'rgba(24, 144, 255, 0.2)';
                button.style.opacity = '1';
            };

            button.onmouseout = () => {
                button.style.background = 'rgba(24, 144, 255, 0.1)';
                button.style.opacity = '0.8';
            };

            button.onclick = (e) => {
                e.stopPropagation();
                navigator.clipboard.writeText(text).then(() => {
                    button.innerHTML = this.icons.success;
                    setTimeout(() => button.innerHTML = this.icons.copy, 1000);
                });
            };

            buttonContainer.appendChild(button);
            return buttonContainer;
        };

        // 格式化数据显示
        const formatData = (data, type = '') => {
            try {
                if (!data) return '';
                let formattedData = data;
                if (typeof data === 'string') {
                    try {
                        formattedData = JSON.stringify(JSON.parse(data), null, 2);
                    } catch {
                        if (data.trim().startsWith('<')) {
                            formattedData = data.replace(/</g, '&lt;').replace(/>/g, '&gt;');
                        }
                    }
                } else {
                    formattedData = JSON.stringify(data, null, 2);
                }

                const container = document.createElement('div');
                container.style.cssText = 'margin-bottom: 16px; position: relative;';

                const title = document.createElement('div');
                title.style.cssText = 'font-weight: 600; color: #1890ff; margin-bottom: 4px;';
                title.textContent = `${type}:`;

                const pre = document.createElement('pre');
                pre.style.cssText = 'margin: 0; padding: 8px; background: rgba(0,0,0,0.02); border-radius: 4px; overflow-x: auto; max-height: 400px;';
                pre.textContent = formattedData;

                const copyButton = createCopyButton(formattedData);

                container.appendChild(title);
                container.appendChild(pre);
                pre.appendChild(copyButton);

                return container;
            } catch (e) {
                console.warn(`格式化${type}失败:`, e);
                return document.createElement('div');
            }
        };

        // 清空现内容
        detailContent.innerHTML = '';

        // 创建基本信息区
        const basicInfo = document.createElement('div');
        basicInfo.style.cssText = 'margin-bottom: 16px;';
        basicInfo.innerHTML = `
            <div style="display: flex; margin-bottom: 8px;">
                <span style="font-weight: 600; color: #1890ff; width: 80px;">方法:</span>
                <span>${log.method}</span>
            </div>
            <div style="display: flex; margin-bottom: 8px;">
                <span style="font-weight: 600; color: #1890ff; width: 80px;">状态:</span>
                <span style="color: ${log.status >= 200 && log.status < 300 ? '#52c41a' : '#f5222d'}">${log.status || 'pending'}</span>
            </div>
        `;

        // URL 部分
        const urlContainer = document.createElement('div');
        urlContainer.style.cssText = 'margin-bottom: 8px; position: relative;';
        const urlTitle = document.createElement('div');
        urlTitle.style.cssText = 'font-weight: 600; color: #1890ff; margin-bottom: 4px;';
        urlTitle.textContent = 'URL:';
        const urlContent = document.createElement('div');
        urlContent.style.cssText = 'word-break: break-all; padding: 8px; background: rgba(0,0,0,0.02); border-radius: 4px;';
        urlContent.textContent = log.url;
        urlContainer.appendChild(urlTitle);
        urlContainer.appendChild(urlContent);
        urlContent.appendChild(createCopyButton(log.url));

        // 添加所有内容
        detailContent.appendChild(basicInfo);
        detailContent.appendChild(urlContainer);

        // URL参数
        if (log.urlParams) {
            detailContent.appendChild(formatData(log.urlParams, 'URL参数'));
        }

        // 请求数据
        if (log.requestData) {
            detailContent.appendChild(formatData(log.requestData, '请求数据'));
        }

        // 响应数据
        if (log.response) {
            detailContent.appendChild(formatData(log.response, '响应数据'));
        }

        detailPanel.style.display = 'block';
    }

    // 修改 togglePanel 方法
    togglePanel(expand) {
        this.isPanelExpanded = expand;
        const container = document.getElementById('requestMonitor');
        const toggleButton = container.querySelector('div:first-child');
        const content = container.querySelector('#requestList')?.parentElement;
        const stats = container.querySelector('#requestStats');
        const header = container.querySelector('div:nth-child(2)');

        // 更新容器样式
        container.style.width = expand ? '400px' : '40px';
        container.style.height = expand ? '90vh' : '40px';
        container.style.display = expand ? 'flex' : 'block';

        // 更新按钮样式
        toggleButton.style.position = expand ? 'absolute' : 'relative';
        toggleButton.style.top = expand ? '12px' : '0';
        toggleButton.style.right = expand ? '12px' : '0';
        toggleButton.style.width = expand ? '24px' : '40px';
        toggleButton.style.height = expand ? '24px' : '40px';
        toggleButton.style.background = expand ? 'rgba(24, 144, 255, 0.1)' : '#1890ff';
        toggleButton.style.borderRadius = expand ? '4px' : '8px';
        toggleButton.style.color = expand ? '#1890ff' : 'white';
        toggleButton.innerHTML = expand ? '−' : '+';

        // 显示/隐藏其他元素
        if (header) header.style.display = expand ? 'flex' : 'none';
        if (stats) stats.style.display = expand ? 'flex' : 'none';
        if (content) content.style.display = expand ? 'block' : 'none';

        // 展开时重新计算内容高度
        if (expand) {
            // 触发内容重新渲染
            this.updateList();
        }
    }
}

// 只有在不需要重定向时才初始化监控器
if (!window.location.href.includes('cryptbox.sankuai.com/file/')) {
    new RequestMonitor();
}