DevBlueChat

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

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==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);
})();