Greasy Fork 支持简体中文。

验证码自动识别

使用 Python API 自动识别验证码

// ==UserScript==
// @name         验证码自动识别
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  使用 Python API 自动识别验证码
// @author       Yoke
// @match        http://*/*
// @match        https://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @connect      82.157.111.62
// @license      MIT
// @require      https://cdnjs.cloudflare.com/ajax/libs/axios/1.7.3/axios.min.js
// @icon         data:image/png;base64,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
// @connect      localhost
// ==/UserScript==

(function () {
    'use strict';

    // 修改规则存储结构
    function saveRules(rules) {
        // 将规则按组整理
        const groupedRules = {};
        Object.entries(rules).forEach(([url, rule]) => {
            const ruleKey = `${rule.imgSelector}|${rule.inputSelector}`;
            if (!groupedRules[ruleKey]) {
                groupedRules[ruleKey] = {
                    urls: [],
                    rule: {
                        imgSelector: rule.imgSelector,
                        inputSelector: rule.inputSelector,
                        enabled: rule.enabled
                    }
                };
            }
            if (!groupedRules[ruleKey].urls.includes(url)) {
                groupedRules[ruleKey].urls.push(url);
            }
        });

        GM_setValue('captchaRules', groupedRules);
        return groupedRules;
    }

    // 修改规则加载函数
    function loadRules() {
        const groupedRules = GM_getValue('captchaRules', {});
        const rules = {};

        // 如果是旧格式的规则,直接返回
        if (Object.values(groupedRules).every(rule => !rule.urls)) {
            return groupedRules;
        }

        // 转换新格式的规则
        Object.values(groupedRules).forEach(group => {
            group.urls.forEach(url => {
                rules[url] = { ...group.rule };
            });
        });

        return rules;
    }

    // 存储页面规则
    let rules = loadRules();
    let isSelecting = false;
    let currentSelector = null;

    // 添加设置菜单
    GM_registerMenuCommand('添加验证码规则', addNewRule);
    GM_registerMenuCommand('管理验证码规则', manageRules);

    // 创建选择器提示框
    const tooltip = document.createElement('div');
    tooltip.style.cssText = `
        position: fixed;
        top: 10px;
        left: 50%;
        transform: translateX(-50%);
        background: #333;
        color: white;
        padding: 10px;
        border-radius: 5px;
        z-index: 999999;
        display: none;
    `;
    document.body.appendChild(tooltip);

    // 创建元素高亮框
    const highlighter = document.createElement('div');
    highlighter.style.cssText = `
        position: absolute;
        border: 2px solid #ff0000;
        background: rgba(255, 0, 0, 0.1);
        pointer-events: none;
        z-index: 999998;
        display: none;
    `;
    document.body.appendChild(highlighter);

    // 添加日志函数
    function log(message, type = 'info') {
        const styles = {
            info: 'color: #2196F3',
            success: 'color: #4CAF50',
            warning: 'color: #FFC107',
            error: 'color: #F44336'
        };

        console.log(`%c[验证码识别] ${message}`, styles[type]);
    }

    // 获取元素的选择器
    function getSelector(element) {
        // 递归向上查找,直到找到带有id、class或name的元素,或者到达body
        function findParentWithIdentifier(el, maxDepth = 3) {
            let path = [];
            let currentEl = el;
            let depth = 0;

            // 先添加当前元素
            let currentSelector = currentEl.tagName.toLowerCase();

            // 检查当前元素的标识符
            const identifiers = [];

            // 检查 name 属性
            if (currentEl.name) {
                identifiers.push(`[name="${currentEl.name}"]`);
            }

            // 检查 class
            if (currentEl.className && typeof currentEl.className === 'string' && currentEl.className.trim()) {
                identifiers.push('.' + currentEl.className.trim().split(/\s+/).join('.'));
            }

            // 如果有标识符,使用它们
            if (identifiers.length > 0) {
                currentSelector += identifiers.join('');
            }

            path.push(currentSelector);

            while (currentEl && currentEl !== document.body && depth < maxDepth) {
                currentEl = currentEl.parentElement;
                if (!currentEl || currentEl === document.body) break;

                log(`正在查找父元素: ${currentEl.tagName.toLowerCase()}`, 'info');

                // 检查 ID
                if (currentEl.id) {
                    const selector = '#' + currentEl.id + ' > ' + path.join(' > ');
                    log(`找到ID选择器: ${selector}`, 'success');
                    return selector;
                }

                // 检查 name 和 class
                const parentIdentifiers = [];
                if (currentEl.name) {
                    parentIdentifiers.push(`[name="${currentEl.name}"]`);
                }
                if (currentEl.className && typeof currentEl.className === 'string' && currentEl.className.trim()) {
                    parentIdentifiers.push('.' + currentEl.className.trim().split(/\s+/).join('.'));
                }

                let selector = currentEl.tagName.toLowerCase();
                if (parentIdentifiers.length > 0) {
                    selector += parentIdentifiers.join('');
                    const fullSelector = selector + ' > ' + path.join(' > ');
                    log(`找到带标识符的选择器: ${fullSelector}`, 'success');
                    return fullSelector;
                }

                // 如果没有标识符,使用位置
                const parent = currentEl.parentElement;
                if (parent) {
                    const siblings = Array.from(parent.children);
                    const index = siblings.indexOf(currentEl);
                    if (siblings.length > 1) {
                        selector += `:nth-child(${index + 1})`;
                    }
                }

                path.unshift(selector);
                depth++;
            }

            const finalSelector = path.join(' > ');
            log(`使用路径选择器: ${finalSelector}`, 'warning');
            return finalSelector;
        }

        // 首先检查元素本身
        const identifiers = [];

        // 检查 ID
        if (element.id) {
            const selector = '#' + element.id;
            log(`直接使用ID选择器: ${selector}`, 'success');
            return selector;
        }

        // 检查 name
        if (element.name) {
            identifiers.push(`[name="${element.name}"]`);
        }

        // 检查 class
        if (element.className && typeof element.className === 'string' && element.className.trim()) {
            identifiers.push('.' + element.className.trim().split(/\s+/).join('.'));
        }

        // 如果有标识符,使用它们
        if (identifiers.length > 0) {
            const selector = element.tagName.toLowerCase() + identifiers.join('');
            log(`直接使用标识符选择器: ${selector}`, 'success');
            return selector;
        }

        log('元素没有ID、name或class,开始向上查找父元素...', 'info');
        return findParentWithIdentifier(element);
    }

    // 开始选择元素
    function startSelection(type) {
        isSelecting = true;
        currentSelector = type;
        tooltip.style.display = 'block';
        tooltip.textContent = `请点击${type === 'img' ? '验证码图片' : '输入框'}`;

        document.addEventListener('mouseover', handleMouseOver);
        document.addEventListener('mouseout', handleMouseOut);
        document.addEventListener('click', handleClick, true);
    }

    // 处理鼠标悬停
    function handleMouseOver(e) {
        if (!isSelecting) return;

        const element = e.target;
        const rect = element.getBoundingClientRect();

        highlighter.style.display = 'block';
        highlighter.style.left = rect.left + window.scrollX + 'px';
        highlighter.style.top = rect.top + window.scrollY + 'px';
        highlighter.style.width = rect.width + 'px';
        highlighter.style.height = rect.height + 'px';
    }

    // 处理鼠标移出
    function handleMouseOut() {
        if (!isSelecting) return;
        highlighter.style.display = 'none';
    }

    // 处理点击选择
    function handleClick(e) {
        if (!isSelecting) return;

        e.preventDefault();
        e.stopPropagation();

        const element = e.target;
        const selector = getSelector(element);

        if (currentSelector === 'img') {
            tempRule.imgSelector = selector;
            startSelection('input');
        } else {
            tempRule.inputSelector = selector;
            finishSelection();
        }
    }

    // 结束选择
    function finishSelection() {
        isSelecting = false;
        currentSelector = null;
        tooltip.style.display = 'none';
        highlighter.style.display = 'none';

        document.removeEventListener('mouseover', handleMouseOver);
        document.removeEventListener('mouseout', handleMouseOut);
        document.removeEventListener('click', handleClick, true);

        saveRule();
    }

    // 临时存储规则
    let tempRule = {};

    // 添加新规则
    function addNewRule() {
        // 获取当前页面的URL信息
        const currentURL = new URL(window.location.href);
        let defaultPattern = currentURL.protocol + '//' + currentURL.hostname;

        // 添加端口(如果不是默认端口)
        if (currentURL.port &&
            !((currentURL.protocol === 'http:' && currentURL.port === '80') ||
                (currentURL.protocol === 'https:' && currentURL.port === '443'))) {
            defaultPattern += ':' + currentURL.port;
        }

        // 添加路径
        defaultPattern += currentURL.pathname;

        tempRule = {
            url: defaultPattern,
            enabled: true
        };

        startSelection('img');
    }

    // 保存规则
    function saveRule() {
        const currentUrl = window.location.href;
        rules[currentUrl] = {
            imgSelector: tempRule.imgSelector,
            inputSelector: tempRule.inputSelector,
            enabled: true
        };

        // 保存并重新加载规则
        const groupedRules = saveRules(rules);
        rules = loadRules();

        showToast('规则已保存', 'success');
        log('规则已保存', 'success');
    }

    // 修改模态框样式
    const modalStyle = document.createElement('style');
    modalStyle.textContent = `
        .captcha-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 999999;
        }
        .captcha-modal-content {
            background: white;
            padding: 30px;
            border-radius: 8px;
            width: 800px;
            max-width: 90%;
            max-height: 90vh;
            display: flex;
            flex-direction: column;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        }
        .captcha-modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
            padding-bottom: 15px;
            border-bottom: 1px solid #eee;
            flex-shrink: 0;
        }
        .captcha-modal-title {
            font-size: 20px;
            font-weight: bold;
            color: #333;
        }
        .captcha-modal-close {
            cursor: pointer;
            padding: 8px 15px;
            border: none;
            background: #f44336;
            color: white;
            border-radius: 4px;
            font-size: 14px;
            transition: opacity 0.2s;
        }
        .captcha-modal-close:hover {
            opacity: 0.9;
        }
        .captcha-modal-body {
            overflow-y: auto;
            flex-grow: 1;
            padding-right: 10px;
        }
        .captcha-rule-item {
            border: 1px solid #e0e0e0;
            padding: 20px;
            margin-bottom: 15px;
            border-radius: 8px;
            background: #fff;
            transition: all 0.3s ease;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
        }
        .captcha-rule-item:hover {
            border-color: #1a73e8;
            box-shadow: 0 4px 8px rgba(0,0,0,0.1);
        }
        .rule-field {
            margin-bottom: 15px;
            line-height: 1.6;
            display: flex;
            align-items: center;
        }
        .rule-field strong {
            min-width: 100px;
            color: #666;
        }
        .editable-value {
            cursor: pointer;
            padding: 4px 8px;
            border-radius: 4px;
            color: #1a73e8;
            background: #f8f9fa;
            flex-grow: 1;
            margin-left: 10px;
            transition: all 0.2s ease;
        }
        .editable-value:hover {
            background: #e8f0fe;
        }
        .edit-input {
            width: 100%;
            padding: 8px 12px;
            border: 2px solid #1a73e8;
            border-radius: 4px;
            font-size: 14px;
            margin-left: 10px;
            flex-grow: 1;
            outline: none;
            transition: all 0.2s ease;
        }
        .edit-input:focus {
            box-shadow: 0 0 0 3px rgba(26,115,232,0.2);
        }
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 24px;
            margin-left: 10px;
        }
        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 24px;
        }
        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 16px;
            width: 16px;
            left: 4px;
            bottom: 4px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
        .toggle-switch input:checked + .toggle-slider {
            background-color: #4CAF50;
        }
        .toggle-switch input:checked + .toggle-slider:before {
            transform: translateX(26px);
        }
        .toggle-label {
            margin-left: 70px;
            color: #666;
        }
        .captcha-rule-actions {
            display: flex;
            justify-content: flex-end;
            margin-top: 15px;
            padding-top: 15px;
            border-top: 1px solid #eee;
        }
        .captcha-btn {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.2s ease;
        }
        .captcha-btn:hover {
            transform: translateY(-1px);
        }
        .captcha-btn-delete {
            background: #f44336;
            color: white;
        }
        .captcha-btn-clear {
            background: #ff9800;
            color: white;
            margin-top: 20px;
            width: 100%;
            padding: 12px;
            font-size: 16px;
        }
        .captcha-empty-tip {
            text-align: center;
            color: #666;
            padding: 40px 0;
            font-size: 16px;
            background: #f8f9fa;
            border-radius: 8px;
            margin: 20px 0;
        }
        /* 自定义滚动条 */
        .captcha-modal-body::-webkit-scrollbar {
            width: 8px;
        }
        .captcha-modal-body::-webkit-scrollbar-track {
            background: #f1f1f1;
            border-radius: 4px;
        }
        .captcha-modal-body::-webkit-scrollbar-thumb {
            background: #888;
            border-radius: 4px;
        }
        .captcha-modal-body::-webkit-scrollbar-thumb:hover {
            background: #555;
        }
        /* 开关按钮容器 */
        .toggle-container {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        /* 开关按钮 */
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 40px;
            height: 20px;
            margin: 0;
        }
        /* 隐藏原始复选框 */
        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
            margin: 0;
        }
        /* 开关滑块 */
        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .3s;
            border-radius: 20px;
        }
        /* 开关圆点 */
        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 16px;
            width: 16px;
            left: 2px;
            bottom: 2px;
            background-color: white;
            transition: .3s;
            border-radius: 50%;
        }
        /* 选中状态 */
        .toggle-switch input:checked + .toggle-slider {
            background-color: #4CAF50;
        }
        .toggle-switch input:checked + .toggle-slider:before {
            transform: translateX(20px);
        }
        /* 开关文字标签 */
        .toggle-label {
            font-size: 14px;
            color: #666;
            margin-left: 8px;
            user-select: none;
        }
        /* 规则项样式优化 */
        .rule-field {
            display: flex;
            align-items: center;
            margin-bottom: 12px;
            padding: 8px;
            background: #f8f9fa;
            border-radius: 4px;
        }
        .rule-field strong {
            min-width: 100px;
            color: #666;
            font-size: 14px;
        }
        /* 状态字段特殊处理 */
        .rule-field.status-field {
            background: transparent;
            padding: 8px 0;
            border-top: 1px solid #eee;
            margin-top: 12px;
        }
        .url-list-field {
            flex-direction: column;
            gap: 8px;
        }
        .url-list {
            display: flex;
            flex-direction: column;
            gap: 8px;
            margin-top: 8px;
        }
        .url-item {
            display: flex;
            align-items: center;
            gap: 8px;
            background: #f8f9fa;
            padding: 8px;
            border-radius: 4px;
            border: 1px solid #e0e0e0;
        }
        .url-item .editable-value {
            flex: 1;
            margin: 0;
        }
        .url-delete-btn {
            padding: 4px 8px;
            background: none;
            border: none;
            color: #666;
            cursor: pointer;
            font-size: 16px;
            line-height: 1;
            border-radius: 4px;
        }
        .url-delete-btn:hover {
            background: #fee;
            color: #f44336;
        }
        .add-url-btn {
            margin-top: 8px;
            padding: 8px;
            background: none;
            border: 1px dashed #ccc;
            color: #666;
            cursor: pointer;
            border-radius: 4px;
            width: 100%;
            transition: all 0.2s;
        }
        .add-url-btn:hover {
            border-color: #1a73e8;
            color: #1a73e8;
            background: #f8f9fa;
        }
    `;
    document.head.appendChild(modalStyle);

    modalStyle.textContent += `
        /* URL 列表容器 */
        .url-list-field {
            flex-direction: column;
            margin-bottom: 20px;
        }

        /* URL 列表标题 */
        .url-list-field strong {
            display: block;
            margin-bottom: 12px;
            color: #333;
            font-size: 14px;
        }

        /* URL 列表 */
        .url-list {
            display: flex;
            flex-direction: column;
            gap: 8px;
            margin-bottom: 12px;
        }

        /* 单个 URL 项 */
        .url-item {
            display: flex;
            align-items: center;
            gap: 8px;
            background: #f8f9fa;
            border: 1px solid #e0e0e0;
            border-radius: 6px;
            overflow: hidden;
        }

        /* URL 文本 */
        .url-item .editable-value {
            flex: 1;
            padding: 8px 12px;
            color: #333;
            font-size: 14px;
            cursor: pointer;
            transition: background 0.2s;
        }

        .url-item .editable-value:hover {
            background: #f0f7ff;
        }

        /* URL 输入框 */
        .url-item .edit-input {
            flex: 1;
            padding: 8px 12px;
            border: none;
            outline: none;
            font-size: 14px;
            background: #fff;
        }

        .url-item .edit-input:focus {
            background: #fff;
            box-shadow: inset 0 0 0 2px #1a73e8;
        }

        /* 删除按钮 */
        .url-delete-btn {
            width: 32px;
            height: 32px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: none;
            border: none;
            color: #666;
            font-size: 18px;
            cursor: pointer;
            transition: all 0.2s;
            padding: 0;
            margin-right: 4px;
        }

        .url-delete-btn:hover {
            color: #f44336;
        }

        /* 添加 URL 按钮 */
        .add-url-btn {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            padding: 8px;
            background: #f8f9fa;
            border: 1px dashed #ccc;
            border-radius: 6px;
            color: #666;
            font-size: 14px;
            cursor: pointer;
            transition: all 0.2s;
        }

        .add-url-btn:hover {
            border-color: #1a73e8;
            color: #1a73e8;
            background: #f0f7ff;
        }

        /* 空状态 */
        .url-list:empty + .add-url-btn {
            margin-top: 0;
        }

        /* URL 项样式 */
        .url-item {
            display: flex;
            align-items: center;
            gap: 8px;
            background: #f8f9fa;
            border: 1px solid #e0e0e0;
            border-radius: 6px;
            overflow: hidden;
            min-height: 40px;
        }

        .url-item.new-item {
            background: #fff;
            border: 1px dashed #ccc;
        }

        /* URL 文本 */
        .url-item .editable-value {
            flex: 1;
            padding: 8px 12px;
            color: #333;
            font-size: 14px;
            cursor: pointer;
            transition: background 0.2s;
        }

        /* URL 输入框 */
        .url-item .edit-input {
            flex: 1;
            padding: 8px 12px;
            border: none;
            outline: none;
            font-size: 14px;
            background: #fff;
        }

        .url-item .edit-input::placeholder {
            color: #999;
        }

        /* 删除按钮 */
        .url-delete-btn {
            width: 32px;
            height: 32px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: none;
            border: none;
            color: #666;
            font-size: 18px;
            cursor: pointer;
            transition: all 0.2s;
            padding: 0;
            margin-right: 4px;
        }

        .url-delete-btn:hover {
            color: #f44336;
        }
    `;

    // 修改 URL 项的样式
    modalStyle.textContent += `
        /* URL 项样式 */
        .url-item {
            display: flex;
            align-items: center;
            gap: 8px;
            background: #f8f9fa;
            border: 1px solid #e0e0e0;
            border-radius: 6px;
            overflow: hidden;
            min-height: 40px;
            padding-right: 8px;  /* 为删除按钮留出空间 */
        }

        /* URL 文本 */
        .url-item .editable-value {
            flex: 1;
            padding: 8px 12px;
            color: #333;
            font-size: 14px;
            cursor: pointer;
            transition: background 0.2s;
        }

        /* URL 输入框 */
        .url-item .edit-input {
            flex: 1;
            padding: 8px 12px;
            border: none;
            outline: none;
            font-size: 14px;
            background: #fff;
        }

        /* 删除按钮 */
        .url-delete-btn {
            width: 24px;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: none;
            border: none;
            color: #999;
            font-size: 18px;
            cursor: pointer;
            transition: all 0.2s;
            padding: 0;
            border-radius: 4px;
            flex-shrink: 0;  /* 防止按钮被压缩 */
        }

        .url-delete-btn:hover {
            color: #f44336;
            background: #fee;
        }

        /* 新项目样式 */
        .url-item.new-item {
            background: #fff;
            border: 1px dashed #ccc;
        }
    `;

    // 添加删除按钮相关样式
    modalStyle.textContent += `
        /* 规则操作区域 */
        .rule-actions {
            position: absolute;
            top: 15px;
            right: 15px;
            z-index: 1;
        }

        /* 删除规则按钮 */
        .rule-delete-btn {
            padding: 6px 12px;
            background: none;
            border: 1px solid #e0e0e0;
            border-radius: 4px;
            color: #f44336;
            cursor: pointer;
            font-size: 13px;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .rule-delete-btn:hover {
            background: #fef2f2;
            border-color: #f44336;
        }

        /* 规则项容器需要添加相对定位 */
        .captcha-rule-item {
            position: relative;
            padding: 20px;
            background: #fff;
            border: 1px solid #e0e0e0;
            border-radius: 8px;
            margin-bottom: 15px;
            transition: all 0.3s ease;
        }

        .captcha-rule-item:hover {
            box-shadow: 0 2px 8px rgba(0,0,0,0.05);
        }
    `;

    // 管理规则函数
    function manageRules() {
        // 创建模态框
        const modal = document.createElement('div');
        modal.className = 'captcha-modal';

        const content = document.createElement('div');
        content.className = 'captcha-modal-content';

        // 添加标题和关闭按钮
        const header = document.createElement('div');
        header.className = 'captcha-modal-header';

        const title = document.createElement('div');
        title.className = 'captcha-modal-title';
        title.textContent = '验证码规则管理';

        const closeBtn = document.createElement('button');
        closeBtn.className = 'captcha-modal-close';
        closeBtn.textContent = '关闭';
        closeBtn.onclick = () => modal.remove();

        header.appendChild(title);
        header.appendChild(closeBtn);
        content.appendChild(header);

        // 添加内容容器
        const modalBody = document.createElement('div');
        modalBody.className = 'captcha-modal-body';
        content.appendChild(modalBody);

        // 将规则按组显示
        const groupedRules = saveRules(rules);
        Object.values(groupedRules).forEach(group => {
            const ruleItem = createRuleItem(group.urls, group.rule);
            modalBody.appendChild(ruleItem);
        });

        // 如果没有规则,显示提示
        if (Object.keys(rules).length === 0) {
            const emptyTip = document.createElement('div');
            emptyTip.className = 'captcha-empty-tip';
            emptyTip.textContent = '暂无规则';
            modalBody.appendChild(emptyTip);
        }

        // 添加清空按钮到 modalBody
        const clearAllBtn = document.createElement('button');
        clearAllBtn.className = 'captcha-btn captcha-btn-clear';
        clearAllBtn.textContent = '清空所有规则';
        modalBody.appendChild(clearAllBtn);

        modal.appendChild(content);
        document.body.appendChild(modal);

        // 点击模态框背景关闭
        modal.onclick = (e) => {
            if (e.target === modal) {
                modal.remove();
            }
        };
    }

    // 检查当前页面是否匹配规则
    function checkPageRules() {
        const currentUrl = window.location.href;
        const currentUrlObj = new URL(currentUrl);

        for (let ruleUrl in rules) {
            try {
                // 将规则URL转换为正则表达式模式
                let pattern;
                if (ruleUrl.includes('*')) {
                    // 如果包含通配符,转换为正则表达式
                    pattern = new RegExp('^' + ruleUrl
                        .replace(/\./g, '\\.')  // 转义点号
                        .replace(/\*/g, '.*')   // 将星号转换为正则通配符
                        + '$');
                } else {
                    // 如果不包含通配符,直接比较URL的议、主机和口部
                    const ruleUrlObj = new URL(ruleUrl);
                    if (ruleUrlObj.protocol === currentUrlObj.protocol &&
                        ruleUrlObj.host === currentUrlObj.host) {
                        return rules[ruleUrl];
                    }
                    continue;
                }

                if (pattern.test(currentUrl) && rules[ruleUrl].enabled) {
                    return rules[ruleUrl];
                }
            } catch (e) {
                console.error('规则URL格式错误:', ruleUrl, e);
                continue;
            }
        }
        return null;
    }

    // 添加提示框样式
    const toastStyle = document.createElement('style');
    toastStyle.textContent = `
        .captcha-toast {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 12px 24px;
            background: rgba(0, 0, 0, 0.8);
            color: white;
            border-radius: 4px;
            font-size: 14px;
            z-index: 999999;
            transition: opacity 0.3s, transform 0.3s;
            opacity: 0;
            transform: translateX(100%);
        }
        .captcha-toast.show {
            opacity: 1;
            transform: translateX(0);
        }
        .captcha-toast.error {
            background: rgba(244, 67, 54, 0.9);
        }
        .captcha-toast.success {
            background: rgba(76, 175, 80, 0.9);
        }
    `;
    document.head.appendChild(toastStyle);

    // 添加提示框函数
    function showToast(message, type = 'info', duration = 3000) {
        // 移除现有的提示框
        const existingToast = document.querySelector('.captcha-toast');
        if (existingToast) {
            existingToast.remove();
        }

        const toast = document.createElement('div');
        toast.className = `captcha-toast ${type}`;
        toast.textContent = message;
        document.body.appendChild(toast);

        // 显示动画
        setTimeout(() => {
            toast.classList.add('show');
        }, 10);

        // 自动隐藏
        setTimeout(() => {
            toast.classList.remove('show');
            setTimeout(() => toast.remove(), 300);
        }, duration);
    }

    function imgToBase64(imgElement) {
        return new Promise((resolve, reject) => {
            // 创建一个新的 Canvas 元素
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            // 确保图像加载完成
            imgElement.onload = function () {
                // 设置 canvas 大小与图像一致
                canvas.width = imgElement.width;
                canvas.height = imgElement.height;

                // 在 canvas 上绘制图像
                ctx.drawImage(imgElement, 0, 0);

                // 将 canvas 内容转换为 Base64 字符串
                const base64Image = canvas.toDataURL('image/png');

                // 返回 Base64 字符串
                resolve(base64Image);
            };

            // 如果图像已经加载完毕,则直接执行回调
            if (imgElement.complete) {
                imgElement.onload();
            }

            // 处理加载失败的情况
            imgElement.onerror = function () {
                reject(new Error('图片加载失败'));
            };
        });
    }

    // 修改 blob 处理函数
    async function blobToBase64(blobUrl) {
        try {
            // 1. 先尝试直接通过 fetch 获取
            try {
                const response = await fetch(blobUrl);
                const blob = await response.blob();
                return new Promise((resolve, reject) => {
                    const reader = new FileReader();
                    reader.onloadend = () => resolve(reader.result);
                    reader.onerror = reject;
                    reader.readAsDataURL(blob);
                });
            } catch (error) {
                log('直接fetch失败,尝试其他方法', 'warning');
            }

            // 2. 尝试通过 canvas 获取
            const img = new Image();
            img.crossOrigin = 'anonymous';  // 允许跨域

            return new Promise((resolve, reject) => {
                img.onload = () => {
                    const canvas = document.createElement('canvas');
                    const ctx = canvas.getContext('2d');
                    canvas.width = img.width;
                    canvas.height = img.height;

                    try {
                        ctx.drawImage(img, 0, 0);
                        const dataUrl = canvas.toDataURL('image/png');
                        resolve(dataUrl);
                    } catch (e) {
                        reject(new Error(`Canvas 转换失败: ${e.message}`));
                    }
                };

                img.onerror = () => {
                    reject(new Error('图片加载失败'));
                };

                img.src = blobUrl;
            });
        } catch (error) {
            throw new Error(`Blob转换失败: ${error.message}`);
        }
    }

    // 识别验证码
    async function recognizeCaptcha(imageElement) {
        try {
            log('开始识别验证码...', 'info');
            let imageData;

            // 处理 Blob URL
            if (imageElement.src.startsWith('blob:')) {
                try {
                    imageData = await blobToBase64(imageElement.src);
                    log('Blob转换成功', 'success');
                } catch (error) {
                    // 如果转换失败,尝试直接使用图片元素
                    log('Blob转换失败,尝试直接使用图片元素', 'warning');
                    imageData = await imgToBase64(imageElement);
                }
            }
            // 处理 Base64
            else if (imageElement.src.startsWith('data:image')) {
                imageData = imageElement.src;
            }
            // 处理普通 URL
            else {
                imageData = await imgToBase64(imageElement);
            }

            // 检查图片数据是否有效
            if (!imageData || !imageData.includes('base64,')) {
                throw new Error('无效的图片数据');
            }

            // 使用 GM_xmlhttpRequest 发送请求
            return new Promise((resolve, reject) => {
                const timeout = setTimeout(() => {
                    reject(new Error('验证码服务请求超时'));
                }, 10000);

                GM_xmlhttpRequest({
                    method: 'POST',
                    url: 'http://82.157.111.62:8000/ocr',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                    },
                    data: `image=${encodeURIComponent(imageData.split(',')[1])}`,
                    onload: function (response) {
                        clearTimeout(timeout);
                        try {
                            const result = JSON.parse(response.responseText);
                            if (!result.code) {
                                throw new Error(result.error || '识别失败');
                            }
                            log(`识别结果: ${result.data}`, 'success');
                            resolve(result.data);
                        } catch (error) {
                            reject(error);
                        }
                    },
                    onerror: function (error) {
                        clearTimeout(timeout);
                        reject(new Error('验证码服务请求失败'));
                    }
                });
            });

        } catch (error) {
            log(`验证码识别失败: ${error.message}`, 'error');
            showToast(`验证码识别失败: ${error.message}`, 'error');
            return '';
        }
    }

    // 处理验证码
    async function processCaptcha(rule) {
        try {
            log('开始处理验证码...', 'info');

            // 等待验证码元素出现
            async function waitForElement(selector, timeout = 10000) {
                const startTime = Date.now();

                while (Date.now() - startTime < timeout) {
                    const element = document.querySelector(selector);
                    if (element) {
                        return element;
                    }
                    await new Promise(resolve => setTimeout(resolve, 100));
                }
                return null;
            }

            // 等待验证码图片和输入框出现
            log('等待验证码元素加载...', 'info');
            const imgElement = await waitForElement(rule.imgSelector);
            const inputElement = await waitForElement(rule.inputSelector);

            if (!imgElement || !inputElement) {
                log('未找到验证码图片或输入框元素', 'error');
                return;
            }

            log(`找到验证码图片: ${rule.imgSelector}`, 'success');
            log(`找到输入框: ${rule.inputSelector}`, 'success');

            const recognizeAndFill = async () => {
                try {
                    // 检查图片是否有效
                    if (!imgElement.complete || !imgElement.naturalWidth) {
                        log('等待图片加载...', 'info');
                        await new Promise(resolve => imgElement.onload = resolve);
                    }

                    // 检查图片是否有实际内容
                    if (!imgElement.src || imgElement.src === 'about:blank') {
                        log('验证码图片未加载,等待src更新...', 'warning');
                        return;
                    }

                    await new Promise(resolve => setTimeout(resolve, 100));
                    log('开始识别验证码...', 'info');

                    const captchaText = await recognizeCaptcha(imgElement);
                    if (captchaText) {
                        // 使用多种方式设置输入框的值
                        const setInputValue = (input, value) => {
                            // 防止值闪烁
                            const preventFlash = (e) => {
                                e.stopImmediatePropagation();  // 阻止其他事件处理器
                                e.preventDefault();
                                return false;
                            };

                            // 临时添加事件拦截
                            input.addEventListener('input', preventFlash, true);
                            input.addEventListener('change', preventFlash, true);
                            input.addEventListener('focus', preventFlash, true);
                            input.addEventListener('blur', preventFlash, true);

                            try {
                                // 1. 使用 Object.defineProperty 设置值
                                const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
                                if (descriptor && descriptor.set) {
                                    descriptor.set.call(input, value);
                                }

                                // 2. 直接设置value属性
                                input.value = value;

                                // 3. 使用setAttribute
                                input.setAttribute('value', value);

                                // 4. 模拟用户输入
                                const inputEvent = new InputEvent('input', {
                                    bubbles: true,
                                    cancelable: true,
                                    inputType: 'insertText',
                                    data: value,
                                    composed: true
                                });
                                input.dispatchEvent(inputEvent);

                                // 5. 触发 change 事件
                                const changeEvent = new Event('change', {
                                    bubbles: true,
                                    cancelable: true
                                });
                                input.dispatchEvent(changeEvent);

                            } finally {
                                // 移除临时事件拦截
                                setTimeout(() => {
                                    input.removeEventListener('input', preventFlash, true);
                                    input.removeEventListener('change', preventFlash, true);
                                    input.removeEventListener('focus', preventFlash, true);
                                    input.removeEventListener('blur', preventFlash, true);
                                }, 0);
                            }
                        };

                        //此段逻辑借鉴Crab大佬的代码,十分感谢
                        function fire(element, eventName) {
                            var event = document.createEvent("HTMLEvents");
                            event.initEvent(eventName, true, true);
                            element.dispatchEvent(event);
                        }
                        function FireForReact(element, eventName) {
                            try {
                                let env = new Event(eventName);
                                element.dispatchEvent(env);
                                var funName = Object.keys(element).find(p => Object.keys(element[p]).find(f => f.toLowerCase().endsWith(eventName)));
                                if (funName != undefined) {
                                    element[funName].onChange(env)
                                }
                            }
                            catch (e) { }
                        }

                        let ans = captchaText.replace(/\s+/g, "");
                        if (inputElement.tagName == "TEXTAREA") {
                            inputElement.innerHTML = ans;
                        } else {
                            inputElement.value = ans;
                            if (typeof (InputEvent) !== "undefined") {
                                inputElement.value = ans;
                                inputElement.dispatchEvent(new InputEvent('input'));
                                var eventList = ['input', 'change', 'focus', 'keypress', 'keyup', 'keydown', 'select'];
                                for (var i = 0; i < eventList.length; i++) {
                                    fire(inputElement, eventList[i]);
                                }
                                FireForReact(inputElement, 'change');
                                inputElement.value = ans;
                            }
                            else if (KeyboardEvent) {
                                inputElement.dispatchEvent(new KeyboardEvent("input"));
                            }
                        }

                        log(`验证码已填入: ${captchaText}`, 'success');
                        showToast(`验证码已填入: ${captchaText}`, 'success');
                    } else {
                        log('验证码识别结果为空', 'warning');
                        showToast('验证码识别失败', 'error');
                    }
                } catch (error) {
                    log(`处理验证码时出错: ${error.message}`, 'error');
                    showToast(`处理验证码时出错: ${error.message}`, 'error');
                }
            };

            // 监听验证码图片变化
            const observer = new MutationObserver(async (mutations) => {
                for (let mutation of mutations) {
                    if (mutation.type === 'attributes' &&
                        (mutation.attributeName === 'src' || mutation.attributeName === 'data-src')) {
                        log('检测到验证码图片更新', 'info');

                        // 等待图片加载完成
                        if (!imgElement.complete) {
                            await new Promise(resolve => imgElement.onload = resolve);
                        }

                        // 额外等待一下确保图片完全加载
                        await new Promise(resolve => setTimeout(resolve, 200));

                        // 识别新的验证码
                        await recognizeAndFill();
                    }
                }
            });

            observer.observe(imgElement, {
                attributes: true,
                attributeFilter: ['src', 'data-src']
            });
            log('已设置验证码图片监听', 'success');

            // 监听图片点击事件
            imgElement.addEventListener('click', async (e) => {
                // 移除阻止默认行为,允许正常点击刷新
                // e.preventDefault();
                // e.stopPropagation();

                log('验证码图片被点击', 'info');

                // 等待新验证码加载
                await new Promise(resolve => setTimeout(resolve, 500));

                // MutationObserver 会自动处理新的验证码识别
            });
            log('已设置点击事件监听', 'success');

            // 只有当图片实际加载了内容才进行首次识别
            if (imgElement.complete && imgElement.naturalWidth && imgElement.src && imgElement.src !== 'about:blank') {
                await recognizeAndFill();
            } else {
                log('等待验证码图片首次加载...', 'info');
            }
        } catch (error) {
            log(`验证码处理失败: ${error.message}`, 'error');
            showToast(`验证码处理失败: ${error.message}`, 'error');
        }
    }

    // 主函数
    function main() {
        const rule = checkPageRules();
        if (rule) {
            // 等待页面加载完成
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', () => processCaptcha(rule));
            } else {
                processCaptcha(rule);
            }
        }
    }

    // 修改规则项的生成代码
    function createRuleItem(urls, rule) {
        const ruleItem = document.createElement('div');
        ruleItem.className = 'captcha-rule-item';

        // URL 列表字段
        const urlListDiv = document.createElement('div');
        urlListDiv.className = 'rule-field url-list-field';

        const urlLabel = document.createElement('span');
        urlLabel.innerHTML = '<strong>URL列表:</strong>';
        urlListDiv.appendChild(urlLabel);

        const urlList = document.createElement('div');
        urlList.className = 'url-list';

        // 将单个 URL 转换为数组格式
        const urlArray = Array.isArray(urls) ? urls : [urls];

        // 创建 URL 列表
        urlArray.forEach(url => {
            const urlItem = createUrlItem(url, rule, rule);
            urlList.appendChild(urlItem);
        });

        // 添加新 URL 按钮
        const addUrlBtn = document.createElement('button');
        addUrlBtn.className = 'add-url-btn';
        addUrlBtn.innerHTML = '+ 添加URL';
        addUrlBtn.onclick = () => {
            const urlItem = createUrlItem('', rule, rule);
            urlList.appendChild(urlItem);
            const input = urlItem.querySelector('input');
            input.style.display = 'block';
            input.focus();
        };

        urlListDiv.appendChild(urlList);
        urlListDiv.appendChild(addUrlBtn);
        ruleItem.appendChild(urlListDiv);

        // 其他字段(选择器等)
        const fields = [
            { label: '图片选择器', key: 'imgSelector', value: rule.imgSelector },
            { label: '输入框选择器', key: 'inputSelector', value: rule.inputSelector }
        ];

        fields.forEach(field => {
            const fieldDiv = document.createElement('div');
            fieldDiv.className = 'rule-field';

            const textSpan = document.createElement('span');
            textSpan.innerHTML = `<strong>${field.label}:</strong> `;
            fieldDiv.appendChild(textSpan);

            const valueSpan = document.createElement('span');
            valueSpan.className = 'editable-value';
            valueSpan.textContent = field.value;
            valueSpan.title = '点击编辑';
            fieldDiv.appendChild(valueSpan);

            const input = document.createElement('input');
            input.type = 'text';
            input.className = 'edit-input';
            input.value = field.value;
            input.style.display = 'none';
            fieldDiv.appendChild(input);

            // 编辑功能
            valueSpan.onclick = () => {
                valueSpan.style.display = 'none';
                input.style.display = 'inline-block';
                input.focus();
                input.select();
            };

            input.onblur = () => {
                const newValue = input.value.trim();
                if (newValue && newValue !== field.value) {
                    // 更新所有相关 URL 的规则
                    urlArray.forEach(url => {
                        if (rules[url]) {
                            rules[url][field.key] = newValue;
                        }
                    });
                    GM_setValue('captchaRules', rules);
                    valueSpan.textContent = newValue;
                    showToast('规则已更新', 'success');
                }
                valueSpan.style.display = 'inline-block';
                input.style.display = 'none';
            };

            ruleItem.appendChild(fieldDiv);
        });

        // 状态切换
        const statusDiv = document.createElement('div');
        statusDiv.className = 'rule-field status-field';
        statusDiv.innerHTML = `
            <strong>状态:</strong>
            <div class="toggle-container">
                <label class="toggle-switch">
                    <input type="checkbox" ${rule.enabled ? 'checked' : ''}>
                    <span class="toggle-slider"></span>
                </label>
                <span class="toggle-label">${rule.enabled ? '启用' : '禁用'}</span>
            </div>
        `;

        // 状态切换事件
        const checkbox = statusDiv.querySelector('input[type="checkbox"]');
        checkbox.onchange = () => {
            urlArray.forEach(url => {
                if (rules[url]) {
                    rules[url].enabled = checkbox.checked;
                }
            });
            statusDiv.querySelector('.toggle-label').textContent = checkbox.checked ? '启用' : '禁用';
            GM_setValue('captchaRules', rules);
            showToast(`规则已${checkbox.checked ? '启用' : '禁用'}`, 'success');
        };

        ruleItem.appendChild(statusDiv);

        // 添加删除规则按钮
        const actionsDiv = document.createElement('div');
        actionsDiv.className = 'rule-actions';

        const deleteRuleBtn = document.createElement('button');
        deleteRuleBtn.className = 'rule-delete-btn';
        deleteRuleBtn.innerHTML = `
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                <path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/>
                <line x1="10" y1="11" x2="10" y2="17"/>
                <line x1="14" y1="11" x2="14" y2="17"/>
            </svg>
            删除规则
        `;
        deleteRuleBtn.onclick = () => {
            // 删除所有相关的 URL 规则
            urlArray.forEach(url => {
                if (rules[url]) {
                    delete rules[url];
                }
            });
            GM_setValue('captchaRules', rules);

            // 添加淡出动画
            ruleItem.style.opacity = '0';
            ruleItem.style.transform = 'translateY(-10px)';

            setTimeout(() => {
                ruleItem.remove();
                showToast('规则已删除', 'success');

                // 检查是否需要显示空状态提示
                const modalBody = document.querySelector('.captcha-modal-body');
                if (Object.keys(rules).length === 0 && modalBody) {
                    const emptyTip = document.createElement('div');
                    emptyTip.className = 'captcha-empty-tip';
                    emptyTip.textContent = '暂无规则';
                    modalBody.appendChild(emptyTip);
                }
            }, 300);
        };

        actionsDiv.appendChild(deleteRuleBtn);
        ruleItem.appendChild(actionsDiv);

        return ruleItem;
    }

    // 创建单个 URL 项
    function createUrlItem(url, rule, ruleGroup) {
        const urlItem = document.createElement('div');
        urlItem.className = 'url-item';

        const valueSpan = document.createElement('span');
        valueSpan.className = 'editable-value';
        valueSpan.textContent = url || '请输入URL';
        valueSpan.title = '点击编辑';

        const input = document.createElement('input');
        input.type = 'text';
        input.className = 'edit-input';
        input.value = url;
        input.placeholder = '请输入URL';
        input.style.display = 'none';

        const deleteBtn = document.createElement('button');
        deleteBtn.className = 'url-delete-btn';
        deleteBtn.innerHTML = '×';
        deleteBtn.title = '删除';
        deleteBtn.onclick = (e) => {
            e.stopPropagation();
            if (urlItem.parentElement.children.length > 1) {
                if (url) {
                    delete rules[url];
                    GM_setValue('captchaRules', rules);
                }
                urlItem.remove();
                showToast('URL已删除', 'success');
            } else {
                showToast('至少保留一个URL', 'warning');
            }
        };

        // 修改样式和交互
        if (!url) {
            valueSpan.style.display = 'none';
            input.style.display = 'block';
            urlItem.classList.add('new-item');
        }

        valueSpan.onclick = () => {
            valueSpan.style.display = 'none';
            input.style.display = 'block';
            input.focus();
            input.select();
        };

        input.onblur = () => {
            const newValue = input.value.trim();
            if (newValue) {
                if (newValue !== url) {
                    if (url) {
                        delete rules[url];
                    }
                    rules[newValue] = {
                        imgSelector: ruleGroup.imgSelector,
                        inputSelector: ruleGroup.inputSelector,
                        enabled: ruleGroup.enabled
                    };

                    // 保存并重新加载规则
                    const groupedRules = saveRules(rules);
                    rules = loadRules();

                    valueSpan.textContent = newValue;
                    showToast('URL已更新', 'success');
                }
                valueSpan.style.display = 'block';
                input.style.display = 'none';
                urlItem.classList.remove('new-item');
            } else if (!url) {
                urlItem.remove();
            }
        };

        urlItem.appendChild(valueSpan);
        urlItem.appendChild(input);
        urlItem.appendChild(deleteBtn);

        return urlItem;
    }

    main();
})();