DevBlueChat

自动短信登录流程 + 文件下载监控 + 黑名单过滤,支持自定义配置,提升工作效率

// ==UserScript==
// @name         DevBlueChat
// @namespace    http://tampermonkey.net/
// @version      1.0.4
// @description  自动短信登录流程 + 文件下载监控 + 黑名单过滤,支持自定义配置,提升工作效率
// @author       Eachann
// @match        https://codigger.onecloud.cn/*
// @icon         https://files.catbox.moe/8l13tx.jpg
// @homepage     https://github.com/eachann1024/ILoveWork/blob/master/DevXiaoHui.js
// @supportURL   https://github.com/eachann1024/ILoveWork/issues
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // ==================== 配置管理 ====================
    const CONFIG_KEYS = {
        DOWNLOAD_ENABLED: 'downloadEnabled',
        BLACKLIST_ENABLED: 'blacklistEnabled',
        FILE_BLACKLIST: 'fileBlacklist',
        PHONE_NUMBER: 'phoneNumber',
        AUTO_RECONNECT: 'autoReconnect',
        CUSTOM_ICON_TITLE: 'customIconTitle'
    };

    // 默认配置
    const DEFAULT_CONFIG = {
        [CONFIG_KEYS.DOWNLOAD_ENABLED]: true,
        [CONFIG_KEYS.BLACKLIST_ENABLED]: false,
        [CONFIG_KEYS.FILE_BLACKLIST]: '.exe,.bat,.cmd,.scr,.pif',
        [CONFIG_KEYS.PHONE_NUMBER]: '',
        [CONFIG_KEYS.AUTO_RECONNECT]: true,
        [CONFIG_KEYS.CUSTOM_ICON_TITLE]: true
    };

    // 上传状态标记
    let isUploading = false;
    let uploadTimer = null;

    // 已下载文件记录 (使用 Set 存储文件的唯一标识)
    const downloadedFiles = new Set();

    // 下载节流控制
    let lastDownloadTime = 0;
    const DOWNLOAD_THROTTLE_INTERVAL = 3000; // 3秒内只允许触发一次下载

    // 生成文件唯一标识
    function generateFileId(requestData) {
        // 使用文件名、消息ID、用户ID等信息生成唯一标识
        const fileName = requestData.fileName || '';
        const msgId = requestData.msgId || '';
        const userId = requestData.chatUserId || '';
        return `${fileName}_${msgId}_${userId}`;
    }

    // 检查文件是否已下载过
    function isFileAlreadyDownloaded(requestData) {
        const fileId = generateFileId(requestData);
        return downloadedFiles.has(fileId);
    }

    // 记录文件已下载
    function markFileAsDownloaded(requestData) {
        const fileId = generateFileId(requestData);
        downloadedFiles.add(fileId);
        console.log('📝 记录文件下载:', requestData.fileName, '(ID:', fileId, ')');
    }

    // 设置上传状态
    function setUploadingState(isUpload) {
        isUploading = isUpload;
        if (isUpload) {
            console.log('📤 检测到文件上传,临时禁用下载功能');
            // 清除之前的定时器
            if (uploadTimer) {
                clearTimeout(uploadTimer);
            }
            // 20秒后恢复下载功能
            uploadTimer = setTimeout(() => {
                isUploading = false;
                console.log('✅ 文件上传冷却结束,恢复下载功能');
            }, 20000);
        }
    }

    // 获取配置
    function getConfig(key) {
        return GM_getValue(key, DEFAULT_CONFIG[key]);
    }

    // 设置配置
    function setConfig(key, value) {
        GM_setValue(key, value);
    }

    // 创建设置界面
    function createSettingsPanel() {
        const panel = document.createElement('div');
        panel.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: linear-gradient(135deg, #000000 0%, #1a1a1a 100%);
            border: 1px solid #333;
            border-radius: 16px;
            padding: 0;
            z-index: 10000;
            box-shadow: 0 20px 60px rgba(0,0,0,0.8);
            min-width: 480px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            color: white;
            overflow: hidden;
        `;

        const downloadEnabled = getConfig(CONFIG_KEYS.DOWNLOAD_ENABLED);
        const blacklistEnabled = getConfig(CONFIG_KEYS.BLACKLIST_ENABLED);
        const autoReconnect = getConfig(CONFIG_KEYS.AUTO_RECONNECT);
        const customIconTitle = getConfig(CONFIG_KEYS.CUSTOM_ICON_TITLE);

        panel.innerHTML = `
            <div style="padding: 32px;">

                <h3 style="margin: 0 0 32px 0; font-size: 28px; font-weight: 300; text-align: center;">脚本设置</h3>

                <!-- 自定义图标和标题开关 -->
                <div style="margin: 24px 0; display: flex; justify-content: space-between; align-items: center; padding: 16px 0;">
                    <div>
                        <div style="font-size: 18px; font-weight: 500; margin-bottom: 4px;">修改标题与图标</div>
                        <div style="color: #888; font-size: 14px;">DDDD</div>
                    </div>
                    <div id="iconTitleToggle" style="
                        width: 60px; height: 32px; border-radius: 16px; cursor: pointer; position: relative;
                        background: ${customIconTitle ? '#E31937' : '#333'};
                        transition: all 0.3s ease;
                    ">
                        <div style="
                            width: 28px; height: 28px; border-radius: 50%; background: white;
                            position: absolute; top: 2px; left: ${customIconTitle ? '30px' : '2px'};
                            transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                        "></div>
                    </div>
                </div>

                <!-- 自动重新连接开关 -->
                <div style="margin: 24px 0; display: flex; justify-content: space-between; align-items: center; padding: 16px 0;">
                    <div>
                        <div style="font-size: 18px; font-weight: 500; margin-bottom: 4px;">自动重新连接(Beta)</div>
                        <div style="color: #888; font-size: 14px;">连接断开时自动点击重新连接</div>
                    </div>
                    <div id="reconnectToggle" style="
                        width: 60px; height: 32px; border-radius: 16px; cursor: pointer; position: relative;
                        background: ${autoReconnect ? '#E31937' : '#333'};
                        transition: all 0.3s ease;
                    ">
                        <div style="
                            width: 28px; height: 28px; border-radius: 50%; background: white;
                            position: absolute; top: 2px; left: ${autoReconnect ? '30px' : '2px'};
                            transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                        "></div>
                    </div>
                </div>

                <!-- 下载功能开关 -->
                <div style="margin: 24px 0; display: flex; justify-content: space-between; align-items: center; padding: 16px 0;">
                    <div>
                        <div style="font-size: 18px; font-weight: 500; margin-bottom: 4px;">启用文件下载功能</div>
                        <div style="color: #888; font-size: 14px;">自动检测并下载聊天中的文件</div>
                    </div>
                    <div id="downloadToggle" style="
                        width: 60px; height: 32px; border-radius: 16px; cursor: pointer; position: relative;
                        background: ${downloadEnabled ? '#E31937' : '#333'};
                        transition: all 0.3s ease;
                    ">
                        <div style="
                            width: 28px; height: 28px; border-radius: 50%; background: white;
                            position: absolute; top: 2px; left: ${downloadEnabled ? '30px' : '2px'};
                            transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                        "></div>
                    </div>
                </div>

                <!-- 黑名单开关 -->
                <div style="margin: 24px 0; display: flex; justify-content: space-between; align-items: center; padding: 16px 0;">
                    <div>
                        <div style="font-size: 18px; font-weight: 500; margin-bottom: 4px;">启用黑名单过滤</div>
                        <div style="color: #888; font-size: 14px;">过滤指定类型的文件</div>
                    </div>
                    <div id="blacklistToggle" style="
                        width: 60px; height: 32px; border-radius: 16px; cursor: pointer; position: relative;
                        background: ${blacklistEnabled ? '#E31937' : '#333'};
                        transition: all 0.3s ease;
                    ">
                        <div style="
                            width: 28px; height: 28px; border-radius: 50%; background: white;
                            position: absolute; top: 2px; left: ${blacklistEnabled ? '30px' : '2px'};
                            transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(0,0,0,0.3);
                        "></div>
                    </div>
                </div>

                <!-- 黑名单输入 -->
                <div style="margin: 24px 0;">
                    <label style="font-size: 18px; font-weight: 500; display: block; margin-bottom: 12px;">文件扩展名黑名单</label>
                    <input type="text" id="fileBlacklist" value="${getConfig(CONFIG_KEYS.FILE_BLACKLIST)}"
                           style="
                               width: 100%; padding: 16px; border: 1px solid #333; border-radius: 8px;
                               background: #1a1a1a; color: white; font-size: 16px; box-sizing: border-box;
                               transition: border-color 0.3s ease;
                           "
                           placeholder="例如:.js,.zip,.exe,.bat">
                    <small style="color: #888; font-size: 12px; margin-top: 8px; display: block;">用逗号分隔多个扩展名</small>
                </div>

                <!-- 手机号输入 -->
                <div style="margin: 24px 0;">
                    <label style="font-size: 18px; font-weight: 500; display: block; margin-bottom: 12px;">手机号码</label>
                    <input type="tel" id="phoneNumber" value="${getConfig(CONFIG_KEYS.PHONE_NUMBER)}"
                           style="
                               width: 100%; padding: 16px; border: 1px solid #333; border-radius: 8px;
                               background: #1a1a1a; color: white; font-size: 16px; box-sizing: border-box;
                               transition: border-color 0.3s ease;
                           "
                           placeholder="请输入手机号码">
                    <small style="color: #888; font-size: 12px; margin-top: 8px; display: block;">用于自动登录功能</small>
                </div>

                <!-- 按钮区域 -->
                <div style="margin-top: 40px; display: flex; gap: 16px; justify-content: flex-end;">
                    <button id="cancelSettings" style="
                        padding: 12px 32px; border: 1px solid #333; border-radius: 8px;
                        background: transparent; color: #888; font-size: 16px; cursor: pointer;
                        transition: all 0.3s ease;
                    ">取消</button>
                    <button id="saveSettings" style="
                        padding: 12px 32px; border: none; border-radius: 8px;
                        background: #E31937; color: white; font-size: 16px; cursor: pointer;
                        transition: all 0.3s ease; font-weight: 500;
                    ">保存设置</button>
                </div>
            </div>
        `;

        document.body.appendChild(panel);

        // 切换按钮状态
        let downloadState = downloadEnabled;
        let blacklistState = blacklistEnabled;
        let reconnectState = autoReconnect;
        let iconTitleState = customIconTitle;

        // 自定义图标标题切换
        const iconTitleToggle = panel.querySelector('#iconTitleToggle');
        iconTitleToggle.onclick = () => {
            iconTitleState = !iconTitleState;
            const toggle = iconTitleToggle.querySelector('div');
            iconTitleToggle.style.background = iconTitleState ? '#E31937' : '#333';
            toggle.style.left = iconTitleState ? '30px' : '2px';
        };

        // 自动重新连接切换
        const reconnectToggle = panel.querySelector('#reconnectToggle');
        reconnectToggle.onclick = () => {
            reconnectState = !reconnectState;
            const toggle = reconnectToggle.querySelector('div');
            reconnectToggle.style.background = reconnectState ? '#E31937' : '#333';
            toggle.style.left = reconnectState ? '30px' : '2px';
        };

        // 下载功能切换
        const downloadToggle = panel.querySelector('#downloadToggle');
        downloadToggle.onclick = () => {
            downloadState = !downloadState;
            const toggle = downloadToggle.querySelector('div');
            downloadToggle.style.background = downloadState ? '#E31937' : '#333';
            toggle.style.left = downloadState ? '30px' : '2px';
        };

        // 黑名单切换
        const blacklistToggle = panel.querySelector('#blacklistToggle');
        blacklistToggle.onclick = () => {
            blacklistState = !blacklistState;
            const toggle = blacklistToggle.querySelector('div');
            blacklistToggle.style.background = blacklistState ? '#E31937' : '#333';
            toggle.style.left = blacklistState ? '30px' : '2px';
        };

        // 输入框焦点效果
        const fileBlacklistInput = panel.querySelector('#fileBlacklist');
        fileBlacklistInput.onfocus = () => {
            fileBlacklistInput.style.borderColor = '#E31937';
        };
        fileBlacklistInput.onblur = () => {
            fileBlacklistInput.style.borderColor = '#333';
        };

        // 按钮悬停效果
        const saveBtn = panel.querySelector('#saveSettings');
        const cancelBtn = panel.querySelector('#cancelSettings');

        saveBtn.onmouseenter = () => {
            saveBtn.style.background = '#ff1f47';
            saveBtn.style.transform = 'translateY(-2px)';
        };
        saveBtn.onmouseleave = () => {
            saveBtn.style.background = '#E31937';
            saveBtn.style.transform = 'translateY(0)';
        };

        cancelBtn.onmouseenter = () => {
            cancelBtn.style.borderColor = '#666';
            cancelBtn.style.color = '#fff';
        };
        cancelBtn.onmouseleave = () => {
            cancelBtn.style.borderColor = '#333';
            cancelBtn.style.color = '#888';
        };

        // 保存设置
        saveBtn.onclick = () => {
            setConfig(CONFIG_KEYS.DOWNLOAD_ENABLED, downloadState);
            setConfig(CONFIG_KEYS.BLACKLIST_ENABLED, blacklistState);
            setConfig(CONFIG_KEYS.FILE_BLACKLIST, fileBlacklistInput.value);
            setConfig(CONFIG_KEYS.PHONE_NUMBER, document.querySelector('#phoneNumber').value);
            setConfig(CONFIG_KEYS.AUTO_RECONNECT, reconnectState);
            setConfig(CONFIG_KEYS.CUSTOM_ICON_TITLE, iconTitleState);
            document.body.removeChild(panel);

            // 显示保存成功提示并刷新页面
            const notification = document.createElement('div');
            notification.style.cssText = `
                position: fixed; top: 20px; right: 20px; z-index: 10001;
                background: #E31937; color: white; padding: 16px 24px;
                border-radius: 8px; font-size: 16px; font-weight: 500;
                box-shadow: 0 4px 20px rgba(227, 25, 55, 0.3);
                animation: slideIn 0.3s ease;
            `;
            notification.innerHTML = '✅ 设置已保存,页面即将刷新...';

            // 添加动画样式
            const style = document.createElement('style');
            style.textContent = `
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
            `;
            document.head.appendChild(style);
            document.body.appendChild(notification);

            // 2秒后刷新页面
            setTimeout(() => {
                window.location.reload();
            }, 2000);
        };

        // 取消设置
        cancelBtn.onclick = () => {
            document.body.removeChild(panel);
        };
    }

    // 注册菜单命令
    GM_registerMenuCommand('打开设置', createSettingsPanel);

    // 检查当前路由是否为登录页
    function isLoginPage() {
        return window.location.href.includes('/chat/#/login');
    }

    // 检查当前路由是否为聊天页
    function isChatPage() {
        return window.location.href.includes('/chat/#/chat');
    }

    // ==================== HTTP 请求监听 ====================

    // 检查文件是否在黑名单中
    function isFileBlacklisted(fileName) {
        if (!getConfig(CONFIG_KEYS.BLACKLIST_ENABLED)) return false;

        const blacklist = getConfig(CONFIG_KEYS.FILE_BLACKLIST).split(',').map(ext => ext.trim().toLowerCase());
        const fileExt = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
        return blacklist.includes(fileExt);
    }

    // 触发下载按钮点击
    function triggerDownload() {
        // 检查下载节流
        const currentTime = Date.now();
        if (currentTime - lastDownloadTime < DOWNLOAD_THROTTLE_INTERVAL) {
            console.log('⏱️ 下载节流中,跳过本次触发 (距离上次下载', Math.round((currentTime - lastDownloadTime) / 1000), '秒)');
            return false;
        }

        try {
            // 多种选择器尝试查找下载图标
            const selectors = [
                'svg.svg-icon.link use[href="#icon-下载"]',
                'svg[aria-hidden="true"].svg-icon.link use[href="#icon-下载"]',
                'use[href="#icon-下载"]',
                'svg.svg-icon.link',
                '.svg-icon.link'
            ];

            let downloadIcons = [];

            // 尝试不同的选择器
            for (let selector of selectors) {
                downloadIcons = document.querySelectorAll(selector);
                if (downloadIcons.length > 0) {
                    // console.log(`✅ 找到 ${downloadIcons.length} 个下载图标,使用选择器: ${selector}`);
                    break;
                }
            }

            if (downloadIcons.length > 0) {
                // 获取最后一个(最新的)下载图标
                const lastDownloadIcon = downloadIcons[downloadIcons.length - 1];

                // 调试信息:输出元素结构
                console.warn('🔍 找到的下载图标元素:', {
                    tagName: lastDownloadIcon.tagName,
                    className: lastDownloadIcon.className,
                    outerHTML: lastDownloadIcon.outerHTML.substring(0, 200),
                    parentElement: lastDownloadIcon.parentElement ? lastDownloadIcon.parentElement.outerHTML.substring(0, 200) : 'null'
                });

                // 尝试不同的点击目标
                let clickTarget = null;

                if (lastDownloadIcon.tagName === 'use') {
                    // 如果是 use 元素,找到父级 svg
                    clickTarget = lastDownloadIcon.closest('svg');
                } else if (lastDownloadIcon.tagName === 'svg') {
                    // 如果直接是 svg 元素
                    clickTarget = lastDownloadIcon;
                } else {
                    // 其他情况,直接使用该元素
                    clickTarget = lastDownloadIcon;
                }

                console.warn('🎯 选择的点击目标:', {
                    tagName: clickTarget ? clickTarget.tagName : 'null',
                    className: clickTarget ? clickTarget.className : 'null',
                    outerHTML: clickTarget ? clickTarget.outerHTML.substring(0, 200) : 'null'
                });

                if (clickTarget) {
                    // 使用最有效的点击方式
                    try {
                        // 方式1: 直接点击
                        clickTarget.click();
                        lastDownloadTime = Date.now(); // 更新最后下载时间
                        console.log('✅ 自动触发文件下载 (直接点击)');
                        return true;
                    } catch (e) {
                        try {
                            // 方式2: 简化的事件分发 (已验证有效)
                            const clickEvent = new Event('click', { bubbles: true });
                            clickTarget.dispatchEvent(clickEvent);
                            lastDownloadTime = Date.now(); // 更新最后下载时间
                            console.log('✅ 自动触发文件下载 (事件分发)');
                            return true;
                        } catch (e2) {
                            console.error('❌ 下载触发失败:', e2);
                        }
                    }
                }
            } else {
                console.warn('⚠️ 未找到下载图标,可能页面还未完全加载');

                // 尝试查找所有可能的下载相关元素
                const allSvgs = document.querySelectorAll('svg');
                console.log(`🔍 页面中共有 ${allSvgs.length} 个 SVG 元素`);

                // 输出一些调试信息
                allSvgs.forEach((svg, index) => {
                    if (svg.classList.contains('svg-icon') || svg.classList.contains('link')) {
                        console.log(`SVG ${index}:`, svg.outerHTML.substring(0, 100));
                    }
                });
            }
        } catch (error) {
            console.error('❌ 触发下载失败:', error);
        }
        return false;
    }

    // 监听DOM变化,等待新的下载按钮出现
    function watchForNewDownloadButton() {
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList') {
                    // 检查新增的节点中是否有下载按钮
                    mutation.addedNodes.forEach((node) => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            // 在新增的节点中查找下载图标
                            const downloadIcon = node.querySelector && node.querySelector('svg.svg-icon.link use[href="#icon-下载"]');
                            if (downloadIcon) {
                                console.log('🎯 检测到新的下载按钮,尝试点击');
                                setTimeout(() => {
                                    if (triggerDownload()) {
                                        observer.disconnect(); // 成功后停止监听
                                    }
                                }, 100);
                            }
                        }
                    });
                }
            });
        });

        // 监听聊天区域的变化
        const chatContainer = document.querySelector('.chat-content') ||
                             document.querySelector('.message-list') ||
                             document.querySelector('.chat-messages') ||
                             document.body;

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

            // 5秒后停止监听,避免无限监听
            setTimeout(() => {
                observer.disconnect();
                console.warn('⏰ DOM监听超时,停止监听新下载按钮');
            }, 5000);
        }
    }

    // 拦截 XMLHttpRequest
    function interceptXHR() {
        const originalOpen = XMLHttpRequest.prototype.open;
        const originalSend = XMLHttpRequest.prototype.send;

        XMLHttpRequest.prototype.open = function(method, url, ...args) {
            this._url = url;
            return originalOpen.apply(this, [method, url, ...args]);
        };

        XMLHttpRequest.prototype.send = function(data) {
            const xhr = this;

            // 监听请求载荷(发送的数据)
            if (xhr._url) {
                // 检查是否是上传请求
                if (xhr._url.includes('file/upload')) {
                    setUploadingState(true);
                }
                // 检查是否是 add/record 请求
                else if (xhr._url.includes('add/record') && data) {
                    try {
                        let requestData;

                        // 尝试解析请求数据
                        if (typeof data === 'string') {
                            requestData = JSON.parse(data);
                        } else if (data instanceof FormData) {
                            // 如果是 FormData,尝试获取数据
                            const formDataObj = {};
                            for (let [key, value] of data.entries()) {
                                formDataObj[key] = value;
                            }
                            requestData = formDataObj;
                        } else {
                            requestData = data;
                        }

                        console.log('🔍 监听到 add/record 请求载荷:', requestData);

                        // 检查是否是文件类型
                        if (requestData && requestData.chatType === 'file') {
                            console.log('📁 检测到文件消息:', requestData.fileName);

                            // 检查是否启用下载功能且不在上传状态
                            if (!getConfig(CONFIG_KEYS.DOWNLOAD_ENABLED) || isUploading) {
                                console.log('⚠️ 文件下载功能已禁用或正在上传中');
                                return originalSend.call(this, data);
                            }

                            // 检查文件是否在黑名单中
                            if (isFileBlacklisted(requestData.fileName)) {
                                console.log('🚫 文件在黑名单中,跳过下载:', requestData.fileName);
                                return originalSend.call(this, data);
                            }

                            // 延迟触发下载,等待DOM更新
                            setTimeout(() => {
                                triggerDownload();
                            }, 1000);

                            // 如果第一次尝试失败,使用 MutationObserver 监听DOM变化
                            setTimeout(() => {
                                if (!triggerDownload()) {
                                    watchForNewDownloadButton();
                                }
                            }, 2000);
                        }
                    } catch (error) {
                        console.error('❌ 解析请求载荷失败:', error);
                    }
                }
            }

            return originalSend.call(this, data);
        };
    }

    // 拦截 fetch
    function interceptFetch() {
        const originalFetch = window.fetch;

        window.fetch = function(...args) {
            const url = args[0];
            const options = args[1] || {};

            // 检查是否是上传请求
            if (url && url.includes && url.includes('file/upload')) {
                setUploadingState(true);
            }
            // 检查是否是 add/record 请求并且有请求体
            else if (url && url.includes && url.includes('add/record') && options.body) {
                try {
                    let requestData;

                    // 尝试解析请求数据
                    if (typeof options.body === 'string') {
                        requestData = JSON.parse(options.body);
                    } else {
                        requestData = options.body;
                    }

                    console.log('🔍 监听到 fetch add/record 请求载荷:', requestData);

                    // 检查是否是文件类型
                    if (requestData && requestData.chatType === 'file') {
                        console.log('📁 检测到文件消息:', requestData.fileName);

                        // 检查是否启用下载功能且不在上传状态
                        if (!getConfig(CONFIG_KEYS.DOWNLOAD_ENABLED) || isUploading) {
                            console.log('⚠️ 文件下载功能已禁用或正在上传中');
                            return originalFetch.apply(this, args);
                        }

                        // 检查文件是否在黑名单中
                        if (isFileBlacklisted(requestData.fileName)) {
                            console.log('🚫 文件在黑名单中,跳过下载:', requestData.fileName);
                            return originalFetch.apply(this, args);
                        }

                        // 延迟触发下载,等待DOM更新
                        setTimeout(() => {
                            triggerDownload();
                        }, 1000);

                        // 如果第一次尝试失败,使用 MutationObserver 监听DOM变化
                        setTimeout(() => {
                            if (!triggerDownload()) {
                                watchForNewDownloadButton();
                            }
                        }, 2000);
                    }
                } catch (error) {
                    console.error('❌ 解析 fetch 请求载荷失败:', error);
                }
            }

            return originalFetch.apply(this, args);
        };
    }

    // ==================== 页面图标和标题修改 ====================

    // 设置图标 SVG (直接嵌入,无背景,居中显示)
    const SETTINGS_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z"/></svg>`;

    // 将 SVG 转换为 Data URL (无背景,透明)
    const CHROME_SETTINGS_ICON = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(SETTINGS_ICON_SVG)}`;

    // 修改页面图标和标题
    function updatePageIconAndTitle() {
        // 检查是否启用自定义图标和标题功能
        if (!getConfig(CONFIG_KEYS.CUSTOM_ICON_TITLE)) {
            console.log('⚠️ 自定义图标和标题功能已禁用');
            return;
        }

        try {
            // 修改页面标题
            document.title = 'Settings';

            // 移除所有现有的图标链接
            const existingIcons = document.querySelectorAll('link[rel*="icon"]');
            existingIcons.forEach(icon => icon.remove());

            // 创建新的 favicon (SVG格式,透明背景,居中显示)
            const favicon = document.createElement('link');
            favicon.rel = 'icon';
            favicon.type = 'image/svg+xml';
            favicon.href = CHROME_SETTINGS_ICON;
            document.head.appendChild(favicon);

            // 添加额外的图标类型以确保兼容性
            const iconTypes = [
                { rel: 'shortcut icon', type: 'image/svg+xml' },
                { rel: 'apple-touch-icon', type: 'image/svg+xml' }
            ];

            iconTypes.forEach(iconType => {
                const newIcon = document.createElement('link');
                newIcon.rel = iconType.rel;
                newIcon.type = iconType.type;
                newIcon.href = CHROME_SETTINGS_ICON;
                document.head.appendChild(newIcon);
            });

            console.log('✅ 页面图标和标题已更新为 Settings (使用嵌入的SVG图标,无背景,居中显示)');
        } catch (error) {
            console.error('❌ 更新页面图标和标题失败:', error);
        }
    }

    // ==================== 主要功能初始化 ====================

    // 初始化所有功能
    function initializeFeatures() {
        // 修改页面图标和标题
        updatePageIconAndTitle();

        // 监听页面标题变化
        watchTitleChanges();

        // 如果在登录页,执行登录逻辑
        if (isLoginPage()) {
            console.warn('当前是登录页面,执行自动登录');
            executeAutoLogin();
        }

        // 如果在聊天页或登录页,都启动HTTP监听
        if (isChatPage() || isLoginPage()) {
            console.warn('启动HTTP请求监听');
            interceptXHR();
            interceptFetch();
        }

        // 监听连接断开弹窗
        watchDisconnectModal();
    }

    /**
     * 等待元素加载完成
     * @param {string} selector - CSS选择器
     * @param {number} timeout - 超时时间(ms)
     * @returns {Promise<Element>}
     */
    function waitForElement(selector, timeout = 3000) {
        return new Promise((resolve, reject) => {
            const element = document.querySelector(selector);
            if (element) {
                return resolve(element);
            }

            const observer = new MutationObserver(() => {
                const element = document.querySelector(selector);
                if (element) {
                    resolve(element);
                    observer.disconnect();
                }
            });

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

            setTimeout(() => {
                observer.disconnect();
                console.error(`等待元素超时: ${selector}`);
                reject(new Error(`等待元素超时: ${selector}`));
            }, timeout);
        });
    }

    /**
     * 设置输入框的值并触发事件
     * @param {Element} input - 输入框元素
     * @param {string} value - 要设置的值
     */
    function setInputValue(input, value) {
        input.value = value;
        input.dispatchEvent(new Event('input', { bubbles: true }));
        input.dispatchEvent(new Event('change', { bubbles: true }));
    }

    // 主要逻辑 - 短信登录流程
    async function executeAutoLogin() {
        try {
            const phoneNumber = getConfig(CONFIG_KEYS.PHONE_NUMBER);
            if (!phoneNumber) {
                console.warn('未设置手机号,跳过自动登录');
                return;
            }

            // 1.5. 选择COC域名
            try {
                const cocDomain = document.querySelector('.domain-item span');
                if (cocDomain && cocDomain.textContent.includes('COC')) {
                    cocDomain.closest('.domain-item').click();
                    await new Promise(resolve => setTimeout(resolve, 100));
                } else {
                    // 如果没有直接找到,尝试查找所有域名选项
                    const domainItems = document.querySelectorAll('.domain-item');
                    for (let item of domainItems) {
                        if (item.textContent.includes('COC')) {
                            item.click();
                            await new Promise(resolve => setTimeout(resolve, 100));
                            break;
                        }
                    }
                }
            } catch (error) {
                console.error('域名选择失败:', error);
            }

            // 2. 点击短信登录标签
            const smsTab = await waitForElement('.ant-tabs-tab');
            if (smsTab) {
                // 查找包含"短信登录"文本的标签
                const tabs = document.querySelectorAll('.ant-tabs-tab');
                for (let tab of tabs) {
                    if (tab.textContent.includes('短信登录')) {
                        tab.click();
                        await new Promise(resolve => setTimeout(resolve, 100));
                        break;
                    }
                }
            }

            // 3. 等待手机号输入框并输入手机号
            const phoneInput = await waitForElement('input[name="phone"]');
            setInputValue(phoneInput, phoneNumber);
            await new Promise(resolve => setTimeout(resolve, 100));

            // 4. 点击发送验证码按钮
            const codeBtn = await waitForElement('.code-btn.mini-font-size');
            codeBtn.click();
            await new Promise(resolve => setTimeout(resolve, 200));

            // 5. 等待验证码输入框并自动填写验证码
            const codeInput = await waitForElement('input[name="code"]');
            setInputValue(codeInput, '123456');
            await new Promise(resolve => setTimeout(resolve, 100));

            // 6. 点击登录按钮
            const loginBtn = await waitForElement('.ant-btn.ant-btn-primary.login-btn');
            loginBtn.click();


        } catch (error) {
            console.error('自动短信登录脚本执行失败:', error);
        }
    }

    // 监听连接断开弹窗
    function watchDisconnectModal() {
        // 检查是否启用自动重新连接
        if (!getConfig(CONFIG_KEYS.AUTO_RECONNECT)) {
            console.log('⚠️ 自动重新连接功能已禁用');
            return;
        }

        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList') {
                    mutation.addedNodes.forEach((node) => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            // 检查是否是连接断开弹窗
                            const modalContent = node.querySelector('.ant-modal-confirm-content');
                            if (modalContent && modalContent.textContent.includes('连接断开,请刷新重试')) {
                                console.log('🔍 检测到连接断开弹窗,尝试自动重新连接');
                                // 查找并点击重新连接按钮
                                const reconnectBtn = node.querySelector('.ant-modal-confirm-btns .ant-btn:not(.ant-btn-primary)');
                                if (reconnectBtn) {
                                    setTimeout(() => {
                                        reconnectBtn.click();
                                        console.log('✅ 已点击重新连接按钮');
                                    }, 500);
                                }
                            }
                        }
                    });
                }
            });
        });

        // 监听整个文档的变化
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // 监听页面标题变化,确保始终显示 Settings
    function watchTitleChanges() {
        // 检查是否启用自定义图标和标题功能
        if (!getConfig(CONFIG_KEYS.CUSTOM_ICON_TITLE)) {
            return;
        }

        // 创建一个 MutationObserver 来监听 title 元素的变化
        const titleObserver = new MutationObserver(() => {
            if (getConfig(CONFIG_KEYS.CUSTOM_ICON_TITLE) && document.title !== 'Settings') {
                document.title = 'Settings';
                console.log('🔄 页面标题已重置为 Settings');
            }
        });

        // 监听 head 元素的变化
        if (document.head) {
            titleObserver.observe(document.head, {
                childList: true,
                subtree: true,
                characterData: true
            });
        }

        // 定期检查标题(备用方案)
        setInterval(() => {
            if (getConfig(CONFIG_KEYS.CUSTOM_ICON_TITLE) && document.title !== 'Settings') {
                document.title = 'Settings';
            }
        }, 2000);
    }

    // ==================== 脚本启动 ====================

    // 页面加载完成后执行
    if (document.readyState === 'complete') {
        initializeFeatures();
    } else {
        window.addEventListener('load', () => {
            initializeFeatures();
        });
    }

    // 添加延迟执行作为备用方案
    setTimeout(() => {
        initializeFeatures();
    }, 1000);
})();