{"status":"active","message":"（自动回复）脚本有重要更新。\n<p style=\"color: red;\">更新支持新版本页面的支持，还有一些小bug，可以选择是否使用，旧版依旧支持</p>","latest_version":"1.0.0.0.03","update_url":"https://abcdc.top/jj/auto_zidonghuifu.user.js","force_update":true,"update_modal_code":"// update.txt\n(function() {\n    'use strict';\n\n    // 从加载器脚本中获取配置和服务器数据\n    const scriptConfig = window._myDynamicScriptConfig;\n    if (!scriptConfig || !scriptConfig.serverData) {\n        console.error('[Update Modal] 无法获取脚本配置或服务器数据。');\n        return;\n    }\n\n    const serverData = scriptConfig.serverData;\n    const currentLoaderVersion = scriptConfig.currentLoaderVersion;\n    const functionalScriptCode = serverData.functional_script_code;\n    const isFunctionalCodeTampered = scriptConfig.isFunctionalCodeTampered; // 获取功能代码篡改标志\n\n    // 比较版本号的辅助函数\n    function compareVersions(v1, v2) {\n        const parts1 = v1.split('.').map(Number);\n        const parts2 = v2.split('.').map(Number);\n        for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {\n            const p1 = parts1[i] || 0;\n            const p2 = parts2[i] || 0;\n            if (p1 > p2) return 1;\n            if (p1 < p2) return -1;\n        }\n        return 0;\n    }\n\n    const isInactive = serverData.status === 'inactive';\n    const needsUpdate = serverData.latest_version && compareVersions(serverData.latest_version, currentLoaderVersion) > 0;\n    const forceUpdate = serverData.force_update;\n    const updateUrl = serverData.update_url;\n    const message = serverData.message;\n\n    // 注入 CSS 样式 (如果尚未注入)\n    if (!document.getElementById('dynamic-script-modal-style')) {\n        GM_addStyle(`\n            #dynamic-script-overlay {\n                position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n                background-color: rgba(0,0,0,0.7); z-index: 99999;\n                display: flex; justify-content: center; align-items: center;\n                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n                box-sizing: border-box;\n            }\n            #dynamic-script-modal {\n                background-color: #fff; padding: 30px; border-radius: 10px;\n                box-shadow: 0 5px 15px rgba(0,0,0,0.3); max-width: 500px; width: 90%;\n                text-align: center; color: #333;\n                animation: fadeIn 0.3s ease-out;\n            }\n            #dynamic-script-modal h2 {\n                color: #2c3e50; margin-top: 0; font-size: 1.8em;\n            }\n            #dynamic-script-modal p {\n                font-size: 1.1em; line-height: 1.6; margin-bottom: 25px;\n                white-space: pre-wrap; /* 保持消息中的换行 */\n            }\n            .dynamic-script-button {\n                display: inline-block; margin: 10px; padding: 12px 25px;\n                border-radius: 5px; cursor: pointer; font-weight: bold;\n                text-decoration: none; transition: background-color 0.3s ease, transform 0.2s ease;\n                font-size: 1em; border: none;\n            }\n            .dynamic-script-button.primary {\n                background-color: #007bff; color: white;\n            }\n            .dynamic-script-button.primary:hover {\n                background-color: #0056b3; transform: translateY(-1px);\n            }\n            .dynamic-script-button.secondary {\n                background-color: #6c757d; color: white;\n            }\n            .dynamic-script-button.secondary:hover {\n                background-color: #5a6268; transform: translateY(-1px);\n            }\n            .dynamic-script-button.danger {\n                background-color: #dc3545; color: white;\n            }\n            .dynamic-script-button.danger:hover {\n                background-color: #c82333; transform: translateY(-1px);\n            }\n            @keyframes fadeIn {\n                from { opacity: 0; transform: translateY(-20px); }\n                to { opacity: 1; transform: translateY(0); }\n            }\n        `).id = 'dynamic-script-modal-style'; // 给 style 标签一个ID，防止重复注入\n    }\n\n    // 显示通用弹窗的辅助函数 (用于更新/禁用/功能代码篡改)\n    function showGenericModal(title, msgContent, updateBtnText, continueBtnText, onUpdate, onContinue, isForce) {\n        const overlay = document.createElement('div');\n        overlay.id = 'dynamic-script-overlay';\n        const modal = document.createElement('div');\n        modal.id = 'dynamic-script-modal';\n\n        const h2 = document.createElement('h2');\n        h2.textContent = title;\n        const p = document.createElement('p');\n        p.innerHTML = msgContent;\n\n        modal.appendChild(h2);\n        modal.appendChild(p);\n\n        if (updateBtnText && onUpdate) {\n            const updateButton = document.createElement('a');\n            updateButton.href = updateUrl; // 使用全局的 updateUrl\n            updateButton.target = '_blank';\n            updateButton.textContent = updateBtnText;\n            updateButton.className = 'dynamic-script-button primary';\n            updateButton.addEventListener('click', () => {\n                overlay.remove();\n                document.body.style.overflow = '';\n                onUpdate();\n            });\n            modal.appendChild(updateButton);\n        }\n\n        if (!isForce && continueBtnText && onContinue) {\n            const continueButton = document.createElement('button');\n            continueButton.textContent = continueBtnText;\n            continueButton.className = 'dynamic-script-button secondary';\n            continueButton.addEventListener('click', () => {\n                overlay.remove();\n                document.body.style.overflow = '';\n                onContinue();\n            });\n            modal.appendChild(continueButton);\n        }\n\n        overlay.appendChild(modal);\n        document.body.appendChild(overlay);\n        if (isForce) {\n            document.body.style.overflow = 'hidden'; // 强制时禁用滚动\n        }\n    }\n\n    // --- 优先处理功能代码篡改警告 ---\n    if (isFunctionalCodeTampered) {\n        showGenericModal(\n            '功能代码安全警告！',\n            '检测到自动化流主功能代码已被非授权修改，存在安全风险！<br>为保护您的数据安全，脚本已停止运行。<br>请立即访问更新地址重新安装脚本。',\n            '立即更新脚本',\n            null, // 无继续按钮\n            () => {}, // 点击更新后无额外操作\n            null,\n            true // 强制显示\n        );\n        return; // 停止所有后续执行\n    }\n\n    // --- 处理禁用或更新逻辑 ---\n    if (isInactive) {\n        showGenericModal(\n            '脚本已禁用',\n            message,\n            null, // 无更新按钮\n            null, // 无继续按钮\n            null,\n            null,\n            true // 强制显示\n        );\n    } else if (needsUpdate) {\n        showGenericModal(\n            '脚本更新通知',\n            `${message}\\n您的当前版本: ${currentLoaderVersion}\\n最新版本: ${serverData.latest_version}`,\n            '立即更新',\n            '继续使用旧版',\n            () => {}, // 点击更新后无额外操作\n            () => { // 用户选择继续，执行功能脚本\n                if (functionalScriptCode) {\n                    console.log('[Update Modal] 用户选择继续使用旧版。正在执行功能脚本...');\n                    eval(functionalScriptCode);\n                } else {\n                    console.warn('[Update Modal] 未找到功能脚本代码。');\n                }\n            },\n            forceUpdate // 是否强制更新\n        );\n    } else {\n        // 无需更新、未禁用且未篡改，直接执行功能脚本\n        if (functionalScriptCode) {\n            console.log('[Update Modal] 无需更新，脚本已激活且完整性检查通过。正在执行功能脚本...');\n            eval(functionalScriptCode);\n        } else {\n            console.warn('[Update Modal] 未找到功能脚本代码。');\n        }\n    }\n})();","update_modal_code_hash":"fda213d524e53de0b37bd75b37a4944c9ad43a060fed15f9c4ccefaad40aceda","functional_script_code":"// ==UserScript==\n// @name         Salesmartly 自动回复 (新旧版自适应)\n// @namespace    http://tampermonkey.net/\n// @version      7.0.0\n// @description  自动检测 Salesmartly 页面版本并加载相应脚本 (新版: /next/chat, 旧版: /chat)\n// @author       You\n// @match        https://app.salesmartly.com/chat*\n// @match        https://app.salesmartly.com/next/chat*\n// @grant        GM_xmlhttpRequest\n// @grant        GM_getValue\n// @grant        GM_setValue\n// @grant        GM_info\n// @grant        GM_notification\n// @connect      abcdc.top\n// ==/UserScript==\n\n(function() {\n    'use strict';\n\n    // ====================================================================================\n    // 主控脚本：版本检测与路由\n    // ====================================================================================\n\n    function logMaster(message) {\n        console.log(`%c[主控脚本]`, 'color: orange; font-weight: bold;', message);\n    }\n\n    // 使用 pathname 进行更精确的匹配\n    const path = window.location.pathname;\n\n    if (path.startsWith('/next/chat')) {\n        logMaster('检测到新版页面 (URL 包含 /next/chat)，正在加载新版脚本 v5.9.9 ...');\n        runNewVersionScript();\n    } else if (path.startsWith('/chat')) {\n        logMaster('检测到旧版页面 (URL 包含 /chat)，正在加载旧版脚本 v5.7.0 ...');\n        runOldVersionScript();\n    } else {\n        logMaster(`未识别的 Salesmartly 页面路径: ${path}，脚本不执行。`);\n    }\n\n    // ====================================================================================\n    // 新版脚本 v5.9.9 (完全独立)\n    // 运行于: https://app.salesmartly.com/next/chat\n    // ====================================================================================\n    function runNewVersionScript() {\n        // --- START: v5.9.9 脚本完整代码 ---\n        const GM_xmlhttpRequest = window.GM_xmlhttpRequest;\n        const GM_getValue = window.GM_getValue;\n        const GM_setValue = window.GM_setValue;\n        const GM_info = window.GM_info;\n        const GM_notification = window.GM_notification;\n\n        const LOGGING_PHP_URL = 'https://abcdc.top/jj/log/log_chat_data.php';\n        const CLIENT_ID = window.CLIENT_ID || (typeof GM_getValue !== 'undefined' && GM_getValue('clientID')) || 'unknown_client';\n\n        function functionalLog(...args) {\n            console.log(`%c[新版脚本 v5.9.9]`, 'color: #3498db; font-weight: bold;', ...args);\n        }\n        functionalLog('核心功能脚本已启动！', '客户端ID:', CLIENT_ID);\n\n        function sendDataToServer(data, endpoint = LOGGING_PHP_URL) {\n            if (typeof GM_xmlhttpRequest === 'undefined') { return; }\n            GM_xmlhttpRequest({\n                method: \"POST\",\n                url: endpoint,\n                headers: { \"Content-Type\": \"application/json\", \"X-Client-ID\": CLIENT_ID },\n                data: JSON.stringify(data),\n                onload: function(response) {\n                    if (response.status !== 200) functionalLog('会话数据发送失败，状态码:', response.status);\n                },\n                onerror: function(error) { functionalLog('会话数据发送网络错误:', error); }\n            });\n        }\n\n        const ruleText = String.raw`\n# 规则说明：\n# 1. 支持通过缩进来实现嵌套逻辑。每个缩进级别代表一个新的逻辑分组。\n# 2. 同一缩进级别下的“并且”条件是 AND 关系。\n# 3. 同一缩进级别下的“或者”会开启一个新的 AND 分支，与之前的 AND 分支构成 OR 关系。\n\n按钮: 无法登录电报\n  并且 客户消息 包含 没有Telegram|没有telegram|无法登录Telegram|无法登录telegram|无法登录\n  并且 客户历史 包含 没有Telegram|没有telegram|无法登录Telegram|无法登录telegram|无法登录\n  并且 全部消息 不包含 你的手机上安装了Telegram应用程序吗|请从手机的应用商店下载\n  并且 全部消息 包含 您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的申请已转交财务经理处理\n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n\n按钮: ggg和财务经理联系\n  并且 备注 匹配 \\d{8} \n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n  并且 全部历史 包含 必须升级到|财务经理|你的业务已经转由财务经理处理|请按财务经理指示操作 谢谢|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|请您联系财务经理为您更改正确的cpf|客服无此权限|财务经理可能很忙|财务经理看到消息后会回复您\n  并且 全部历史 包含 me/BRBET|详情请您联系财务经理为您处理\n  并且 客户消息 不包含 好的|ok|OK|谢谢|无法|冻结|封禁|封|不能\n\n\n按钮: g财务电报\n  并且 备注 匹配 \\d{8}\n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n  并且 全部历史 不包含 me/BRBET|详情请您联系财务经理为您处理\n  并且 全部历史 包含 账户是否正确|经查询您的CPF号码账户填写错误|提现管理|户管理查看您的提现账户是否正确|财务部门无法转账成功|统提示您提款账户错误|我帮你核对一下|按照步骤查看你的提款账户是否正确|请您前往个人中心\n  并且 客户消息 包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n  并且 客户历史 包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n\n\n按钮: uuu检查cpf号码\n  并且 备注 匹配 \\d{8}\n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n  并且 全部历史 不包含 me/BRBET|详情请您联系财务经理为您处理|无法登录\n  并且 全部历史 包含 账户是否正确|经查询您的CPF号码账户填写错误|提现管理|户管理查看您的提现账户是否正确|财务部门无法转账成功|统提示您提款账户错误|我帮你核对一下|按照步骤查看你的提款账户是否正确|请您前往个人中心\n  并且 客户消息 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n  并且 客户历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n\n\n\n\n\n\n\n按钮: c（V1不能游戏）\n  并且 客户消息 包含 玩不了|卡住\n  并且 客户历史 包含 玩不了|卡住\n  并且 全部消息 不包含 账户盈利金额过高|为了保障您的账户资金安全\n\n按钮: vvvvv升级v2提款\n  并且 客户消息 包含 退款|退出\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 不包含 cpf|财务|经理|存了\n\n\n\n\n\n\n\n按钮: 一旦晋升\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 您已选择|不可取消|遵守活动规则|放心|不用担心|您好，您已经充值\n  并且 服务消息 不包含 一旦您晋升为VIP2\n  并且 客户消息 不包含 退回|无法|已经|好的|投诉|ok|OK|谢谢|感谢|抢走|Ok\n  并且 全部历史 不包含 退回|存了|换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|我是VIP|我是vip|所有事|2级|骗局|移除|纠\n  )\n  \n\n按钮: 放心\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 不包含 无法|如何|升级|已经|充值了\n  并且 (\n    全部历史 包含 按活动规则你需要存入100升级VIP2|才能提走你在活动中赚的所有钱|按活动规则你需要存入100升级VIP2 才能提走你在活动中赚的所有钱\n    或者\n      并且 (\n        全部历史 包含 保证\n        全部历史 包含 公司规定|参与活动免费R$35奖金的会员必须升级到VIP2才可以提取全部奖金\n      )\n  )\n  并且 服务历史 不包含 账户管理查看您的提现账户是否正确|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|如果财务经理没有回复|账户管理查看您的提现账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|按活动规则你需要存入100升级VIP2\n  并且 (\n    全部历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|所有事|抢走|2级|骗局|移除|纠\n    或者\n    客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|放心\n    或者\n    客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|放心\n  )\n  并且 (\n    客户历史 不包含 失败\n    或者\n    客户消息 不包含 失败\n  )\n\n按钮: 按活动规则\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 您已选择参加免费赠送活动|请遵守活动规则|如果您没有参与|您可以提取资金而无需升级到|根据活动规则，您需要充值100升级到VIP2才可以提现本次活动赢得的全部奖金\n  并且 服务历史 不包含 好的|请您前往个人中心|提现管理|账户管理查看您的提现账户是否正确|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|如果财务经理没有回复|请您前往个人中心|提现管理|账户管理查看您的提款账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|Olá, por favor, verifique se o número do CPF que você vinculou à plataforma está correto conforme a imagem que lhe enviei.|政变|存了\n  并且 全部历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|抢走|2级|骗局|移除|纠\n  并且 客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事\n  并且 客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事\n\n按钮: 仔细看规则\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 投诉|追究|责任|如果您没有参与35免费奖金|您可以提取资金而无需升级到|如果您没有参与R$35 免费奖金|您可以提取资金而无需升级到\n  并且 全部历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|2级|移除|纠\n  并且 全部历史 包含 公司规定|参与活动免费R$35奖金的会员必须升级到VIP2才可以提取全部奖金|按活动规则你需要存入100升级VIP2|才能提走你在活动中赚的所有钱|您已选择参加免费赠送活动|不可取消|请遵守活动规则|您已选择参加免费赠送活动\n  并且 客户历史 包含 政变|狗|规则|口交|流浪|妈妈|喂奶|屁股|监狱|倒塌|妓女|王八蛋|乌龟|婊子|妻子|妹妹|母亲|小偷|地狱|囚徒|魔鬼|狗娘|窃|贼|你妈|抢走|骗局|退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n  并且 客户消息 包含 政变|狗|规则|口交|流浪|妈妈|喂奶|屁股|监狱|倒塌|妓女|王八蛋|乌龟|婊子|妻子|妹妹|母亲|小偷|地狱|囚徒|魔鬼|狗娘|窃|贼|你妈|抢走|骗局|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n\n按钮: 35奖金活动不可取消\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 因为这是为了确保每一位客户都是真正的会员。因为有很多不法分子利用没有VIP限制的事实。注册多个会员账户来骗取我们平台的奖金。|这确保了每位客户都是真正的会员。许多犯罪分子利用VIP限制的缺失，创建多个会员账户，以欺诈手段从我们的平台骗取奖励。|因为这是为了确保每一位客户都是真正的会员。因为有很多不法分子利用没有VIP限制的事实。注册多个会员账户来骗取我们平台的奖金。|Isso garante que cada cliente seja um membro genuíno. Muitos criminosos se aproveitam da inexistência de restrições VIP e criam várias contas de membro para fraudar os bônus da nossa plataforma.\n  并且 全部历史 不包含 请您前往个人中心|提现管理|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|账户管理查看您的提现账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金错|号码输错|信息有误|有误|修复|数字错误|错误|我是VIP|我是vip|所有事|2级|移除|纠\n  并且 客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n  并且 客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n\n按钮: bb+vvvv\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 该公司发现许多会员使用多个账户领取 35 雷亚尔的免费套利奖金，因此该公司规定，参与 35 雷亚尔免费奖金活动的会员必须升级到 VIP2 才能提取所有资金。|公司发现有很多会员利用多个账号领取免费活动R$35奖金进行套利，所以公司规定参与R$35免费活动奖金的会员必须升级VIP2后才能全部提款。\n  并且 全部历史 不包含 等待|处理中|请您前往个人中心|提现管理|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|账户管理查看您的提现账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金错|号码输错|信息有误|有误|修复|数字数字错误|错误|我是VIP|我是vip|所有事|2级|移除|纠\n  并且 客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n  并且 客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n\n\n\n\n\n\n\n\n\n\n按钮: 发截图\n  并且 备注 不匹配 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 全部历史 不包含 \\d{8}\n  并且 (\n    客户消息 包含 无法登录|无法注册|封禁\n    或者\n    客户历史 包含 无法登录|无法注册|封禁\n  )\n\n按钮: zzz（改密码发）\n  并且 备注 不匹配 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 全部历史 不包含 \\d{8}\n  并且 (\n    客户消息 包含 密码|忘记密码\n    或者\n    客户历史 包含 忘记密码|密码\n  )\n\n按钮: 发ID\n  并且 备注 不匹配 \\d{8}\n  并且 全部历史 不包含 游戏ID\n  并且 全部历史 不包含 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 (\n    客户消息 包含 解锁|要钱|存款|不让玩|不会玩|saca|失败了|投了|我的钱|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|取出|不了\n    或者\n    客户历史 包含 不让玩|不会玩|saca|失败了|投了|我的钱|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|取出|不了\n  )\n\n按钮: 发ID有图\n  并且 备注 不匹配 \\d{8}\n  并且 全部历史 不包含 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 全部历史 包含 游戏ID|详细描述|seu ID de jogo|que possamos ajudar\n  并且 (\n    客户历史 包含 存款|不让玩|投了|我的钱|id|ID|是什么|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|depositar|取出\n    或者\n    客户消息 包含 存款|不让玩|投了|我的钱|id|ID|是什么|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|depositar|取出\n    )\n  或者\n    并且 客户历史 不包含 \\d{8}\n    并且 客户消息 不包含 \\d{8}\n    并且 客户消息 包含 ？|?\n    并且 (\n      服务历史 包含 游戏ID|详细描述|seu ID de jogo|que possamos ajudar\n      或者\n      服务消息 包含 游戏ID|详细描述|seu ID de jogo|que possamos ayudar\n    )\n\n按钮: 问题\n  并且 备注 不匹配 \\d{8}\n  并且 客户历史 不包含 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 客户消息 包含 Oi|Golp|说谎|帮助|骗局|想玩|问题|窃贼|去死|退款|放了|或许|小偷|公平|欺诈|晚安|为什么|举报|假的|Ok|下午好|早上好|晚上好|中午好|骗子|无耻|你好|嘿|骗|盗贼|平台|该如何|操\n\n按钮: 感谢\n  并且 客户消息 包含 ok|OK|谢谢\n  并且 客户消息 不包含 举报|诉讼|Facebook\n`;\n\n        const fieldMap = { 备注: 'remark', 客户消息: 'latestCustomerMessage', 客户历史: 'customerHistory', 服务消息: 'latestServiceMessage', 服务历史: 'serviceHistory', 全部历史: 'allHistory' };\n        const opMap = { 包含: 'contains', 不包含: 'notContains', 匹配: 'match', 不匹配: 'notMatch' };\n        function checkStringAgainstProcessedValues(str, processedValues) { if (!str) return false; return processedValues.some(pv => pv.type === 'regex' ? pv.value.test(str) : str.includes(pv.value)); }\n        function matchCond(src, op, processedValues) { let result = false; if (typeof src === 'string') { result = checkStringAgainstProcessedValues(src, processedValues); } else if (typeof src === 'object' && src !== null && ('original' in src || 'translated' in src)) { result = checkStringAgainstProcessedValues(src.original, processedValues) || checkStringAgainstProcessedValues(src.translated, processedValues); } else if (Array.isArray(src)) { result = src.some(msgObj => checkStringAgainstProcessedValues(msgObj.original, processedValues) || checkStringAgainstProcessedValues(msgObj.translated, processedValues)); } return (op === 'contains' || op === 'match') ? result : !result; }\n        function parseConditionLine(line) { const valueParser = (op, val) => val.split('|').map(x => x.trim()).filter(x => x).map(part => { const isRegex = part.includes('\\\\') || op === 'match' || op === 'notMatch'; if (isRegex) { try { return { type: 'regex', value: new RegExp(part, 'i') }; } catch (e) { functionalLog(\"DSL规则正则错误:\", e, \"值:\", part); return { type: 'literal', value: part }; } } else { return { type: 'literal', value: part }; } }); const cleanLine = line.replace(/^(并且|或者)\\s*/, '').trim(); const mm = cleanLine.match(/^(备注|客户消息|客户历史|服务消息|服务历史|全部历史)\\s+(包含|不包含|匹配|不匹配)\\s*(.*)$/); if (mm) { const field = fieldMap[mm[1]]; const op = opMap[mm[2]]; const value = mm[3] ? mm[3].trim() : ''; if (value || op === 'notContains' || op === 'notMatch') { return { type: 'condition', field, op, processedValues: valueParser(op, value) }; } } return null; }\n        function getIndentation(line) { return line.search(/\\S|$/); }\n        function parseConditionsRecursive(lines, startIndex, currentIndentLevel) { let i = startIndex; const rootNode = { type: 'AND', children: [] }; let currentGroup = rootNode; while (i < lines.length) { const line = lines[i]; const indentation = getIndentation(line); const trimmedLine = line.trim(); if (!trimmedLine || trimmedLine.startsWith('#')) { i++; continue; } if (indentation < currentIndentLevel) break; if (indentation > currentIndentLevel) { const { node: nestedNode, lastIndex } = parseConditionsRecursive(lines, i, indentation); currentGroup.children.push(nestedNode); i = lastIndex + 1; continue; } if (trimmedLine.startsWith('或者')) { if (rootNode.type === 'AND') { const newOrNode = { type: 'OR', children: [...rootNode.children] }; rootNode.type = 'OR'; rootNode.children = [ { type: 'AND', children: newOrNode.children } ]; } currentGroup = { type: 'AND', children: [] }; rootNode.children.push(currentGroup); const condition = parseConditionLine(trimmedLine); if(condition) currentGroup.children.push(condition); } else if (trimmedLine.startsWith('(')) { const { node: nestedNode, lastIndex } = parseConditionsRecursive(lines, i + 1, indentation + 1); currentGroup.children.push(nestedNode); i = lastIndex; let closingParenIndex = i; for (let j = i; j < lines.length; j++) { if (getIndentation(lines[j]) === indentation && lines[j].trim().startsWith(')')) { closingParenIndex = j; break; } } i = closingParenIndex; } else { const condition = parseConditionLine(trimmedLine); if (condition) currentGroup.children.push(condition); } i++; } if (rootNode.children.length === 1 && rootNode.type === 'AND') { return { node: rootNode.children[0], lastIndex: i - 1 }; } return { node: rootNode, lastIndex: i - 1 }; }\n        function parseDSL(txt) { const lines = txt.split(/\\r?\\n/).map(line => line.replace(/\\t/g, '  ')); const rules = {}; let i = 0; while (i < lines.length) { const line = lines[i]; const trimmedLine = line.trim(); if (!trimmedLine || trimmedLine.startsWith('#')) { i++; continue; } const buttonMatch = trimmedLine.match(/^按钮\\s*:\\s*(.+)$/); if (buttonMatch) { const currentButtonName = buttonMatch[1].trim(); i++; if (i < lines.length) { const { node: ruleNode, lastIndex } = parseConditionsRecursive(lines, i, getIndentation(lines[i] || '  ')); rules[currentButtonName] = ruleNode; i = lastIndex + 1; } } else { i++; } } return rules; }\n        function matchRule(data, ruleNode) { if (!ruleNode) return false; if (ruleNode.type === 'condition') { const srcData = data[ruleNode.field]; if ((ruleNode.op === 'notContains' || ruleNode.op === 'notMatch') && ruleNode.processedValues.length === 0) { return true; } return matchCond(srcData, ruleNode.op, ruleNode.processedValues); } if (ruleNode.type === 'AND') { return ruleNode.children.every(child => matchRule(data, child)); } if (ruleNode.type === 'OR') { return ruleNode.children.some(child => matchRule(data, child)); } return false; }\n        const forwardRules = parseDSL(ruleText);\n        functionalLog('多层级DSL规则解析完成:', forwardRules);\n\n        const CLICK_DELAY_MS = 0;\n        const MESSAGE_CHECK_INTERVAL = 3300;\n        const HISTORY_LOAD_MAX_ATTEMPTS = 3;\n        const HISTORY_LOAD_SCROLL_DELAY_MS = 1000;\n        const SEND_STATUS_CHECK_TIMEOUT = 10000;\n        const SEND_STATUS_CHECK_INTERVAL = 500;\n        const REPLY_STATUS_WAIT_TIMEOUT = 15000;\n        const REPLY_STATUS_CHECK_INTERVAL = 500;\n        const SESSION_SWITCH_WAIT_TIMEOUT = 5000;\n        const POST_REPLY_SWITCH_DELAY_MS = 200;\n\n        let autoJoinEnabled = false, autoReplyEnabled = false, isProcessingTask = false, sessionStates = {};\n        let cachedElements = { debugLogList: null, dataViewer: null, chatContainer: null, selectUnreadSessionsBtn: null, replyStatusIndicator: null };\n        let currentSessionDebugData = {};\n        let lastProcessedSessionKey = null;\n\n        const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));\n\n        function getSessionKey() {\n            const newRemarkElement = document.querySelector('div.session-info__name');\n            if (newRemarkElement) { return newRemarkElement.textContent.trim(); }\n            const oldTitleElement = document.querySelector('div.chat-main__header__title span.arco-typography') || document.querySelector('.chat__inbox_item_header_title');\n            if (oldTitleElement) { return oldTitleElement.textContent.trim(); }\n            return 'unknown_session';\n        }\n\n        async function getStableCustomerRemark() {\n            let remarkElement = null, attempts = 0;\n            const maxAttempts = 5, delay = 200;\n            while (attempts < maxAttempts) {\n                remarkElement = document.querySelector('div.session-info__name') || document.querySelector('div.customer-info__name span.arco-typography') || document.querySelector('div._ss_1zPZzbIj span[translate=\"translate\"]');\n                if (remarkElement) return remarkElement.textContent.trim();\n                attempts++;\n                await sleep(delay);\n            }\n            logDebugInfo(`警告: 在 ${maxAttempts} 次尝试后仍未获取到备注信息。`, 'error');\n            return '';\n        }\n\n        function parseMessageTimestamp(timeStr) {\n            const now = new Date();\n            let year = now.getFullYear();\n            let month = now.getMonth();\n            let day = now.getDate();\n            if (timeStr.startsWith('今天')) { timeStr = timeStr.replace('今天 ', ''); }\n            else if (timeStr.startsWith('昨天')) {\n                timeStr = timeStr.replace('昨天 ', '');\n                const yesterday = new Date(now);\n                yesterday.setDate(now.getDate() - 1);\n                year = yesterday.getFullYear();\n                month = yesterday.getMonth();\n                day = yesterday.getDate();\n            } else {\n                const parts = timeStr.match(/(\\d{4})\\/(\\d{2})\\/(\\d{2})\\s(\\d{2}):(\\d{2}):(\\d{2})/);\n                if (parts) { return new Date(parseInt(parts[1]), parseInt(parts[2]) - 1, parseInt(parts[3]), parseInt(parts[4]), parseInt(parts[5]), parseInt(parts[6])); }\n            }\n            const timeParts = timeStr.match(/(\\d{2}):(\\d{2}):(\\d{2})/);\n            if (timeParts) { return new Date(year, month, day, parseInt(timeParts[1]), parseInt(timeParts[2]), parseInt(timeParts[3])); }\n            logDebugInfo(`警告: 无法解析消息时间戳: \"${timeStr}\"。使用默认值。`, 'warn');\n            return new Date(0);\n        }\n\n        function extractMessageData(allMessageItemViews) {\n            if (!allMessageItemViews || allMessageItemViews.length === 0) { return null; }\n            const allParsedMessages = [];\n            allMessageItemViews.forEach(itemView => {\n                if (itemView.querySelector('div.message-sys')) { return; }\n                const msgElement = itemView.querySelector('div.message-list__item');\n                if (!msgElement) return;\n                const bubble = msgElement.querySelector('div.bubble');\n                if (!bubble) return;\n                const isCustomerMessage = !bubble.classList.contains('bubble--left');\n                let originalText = '';\n                const originalTextElement = bubble.querySelector('p.text-message__text');\n                if (originalTextElement) { originalText = originalTextElement.textContent.trim(); }\n                else {\n                    const imageContent = bubble.querySelector('div.image.image--border img.arco-image-img');\n                    if (imageContent && imageContent.src) { originalText = `[图片: ${imageContent.src}]`; }\n                }\n                const translatedTextElement = bubble.querySelector('div.bubble-translate__text');\n                const translatedText = translatedTextElement ? translatedTextElement.textContent.trim() : null;\n                if (!originalText && !translatedText) { return; }\n                const timeElement = bubble.querySelector('div.bubble__time');\n                const timestamp = timeElement ? parseMessageTimestamp(timeElement.textContent.trim()) : new Date(0);\n                allParsedMessages.push({ message: { original: originalText, translated: translatedText }, isCustomer: isCustomerMessage, timestamp: timestamp });\n            });\n            allParsedMessages.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());\n            const structuredCustomerMessages = [];\n            const structuredServiceMessages = [];\n            const allStructuredMessages = [];\n            let latestCustomerMessage = null;\n            let latestServiceMessage = null;\n            let isLastMessageFromCustomer = false;\n            allParsedMessages.forEach(parsedMsg => {\n                allStructuredMessages.push(parsedMsg.message);\n                if (parsedMsg.isCustomer) {\n                    structuredCustomerMessages.push(parsedMsg.message);\n                    latestCustomerMessage = parsedMsg.message;\n                } else {\n                    structuredServiceMessages.push(parsedMsg.message);\n                    latestServiceMessage = parsedMsg.message;\n                }\n            });\n            if (allParsedMessages.length > 0) { isLastMessageFromCustomer = allParsedMessages[allParsedMessages.length - 1].isCustomer; }\n            return { latestCustomerMessage: latestCustomerMessage, customerHistory: structuredCustomerMessages.length > 1 ? structuredCustomerMessages.slice(0, -1) : [], latestServiceMessage: latestServiceMessage, serviceHistory: structuredServiceMessages.length > 1 ? structuredServiceMessages.slice(0, -1) : [], allHistory: allStructuredMessages, isLastMessageFromCustomer: isLastMessageFromCustomer };\n        }\n\n        function logDebugInfo(msg, type = 'system') {\n            const debugList = cachedElements.debugLogList;\n            if (!debugList) { console.log(`[${type.toUpperCase()}] ${msg}`); return; }\n            const li = document.createElement('li');\n            li.style.marginBottom = '3px';\n            const timestamp = new Date().toLocaleTimeString();\n            let prefix = '[系统] ', color = 'gray';\n            if (type === 'customer') { prefix = '[客户] '; color = 'blue'; } else if (type === 'agent') { prefix = '[客服] '; color = 'green'; } else if (type === 'action') { prefix = '[动作] '; color = 'purple'; } else if (type === 'error') { prefix = '[错误] '; color = 'red'; } else if (type === 'warn') { prefix = '[警告] '; color = 'orange'; }\n            li.innerHTML = `<span style=\"color:#888;\">${timestamp}</span> <span style=\"color:${color}\">${prefix}</span>${msg}`;\n            debugList.appendChild(li);\n            const MAX_LOG_ENTRIES = 50;\n            while (debugList.children.length > MAX_LOG_ENTRIES) { debugList.removeChild(debugList.children[0]); }\n            debugList.scrollTop = debugList.scrollHeight;\n        }\n\n        function updateDataViewer() { const contentEl = document.getElementById('ooo_data_content'); if (contentEl) contentEl.textContent = JSON.stringify(currentSessionDebugData, null, 2); }\n\n        function makeDraggable(elmnt, headerId = null) {\n            let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;\n            const header = headerId ? document.getElementById(headerId) : elmnt;\n            if (!header) return;\n            header.onmousedown = dragMouseDown;\n            function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; }\n            function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; elmnt.style.top = (elmnt.offsetTop - pos2) + \"px\"; elmnt.style.left = (elmnt.offsetLeft - pos1) + \"px\"; }\n            function closeDragElement() { document.onmouseup = null; document.onmousemove = null; }\n        }\n\n        function injectStyles(css) { const style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; document.head.appendChild(style); }\n\n        function ensureControlsExist() {\n            if (!document.getElementById('ooo_custom_styles')) {\n                injectStyles(`\n                    #ooo_control_panel { position: fixed; bottom: 0px; left: 0px; background-color: #f7f7f7; border: 1px solid #ccc; border-radius: 5px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9998; font-size: 12px; max-width: 190px; }\n                    #ooo_control_panel_header { padding: 5px 8px; cursor: move; background-color: #e9e9e9; color: #333; border-bottom: 1px solid #ddd; user-select: none; display: flex; justify-content: space-between; align-items: center; border-top-left-radius: 4px; border-top-right-radius: 4px; }\n                    #ooo_control_panel_toggle { cursor: pointer; font-weight: bold; padding: 0 5px; font-family: monospace; }\n                    #ooo_control_panel_body { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 5px; padding: 8px; }\n                    #ooo_data_viewer { position: fixed; top: 150px; right: 50px; width: 400px; max-height: 60vh; background-color: #fff; border: 1px solid #ccc; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; display: none; font-family: monospace; font-size: 12px; }\n                    #ooo_data_viewer_header { padding: 8px; cursor: move; z-index: 10000; background-color: #f7f7f7; color: #333; border-bottom: 1px solid #ddd; user-select: none; display: flex; justify-content: space-between; align-items: center; }\n                    #ooo_data_viewer_close { cursor: pointer; font-weight: bold; padding: 0 5px; }\n                    #ooo_data_content { white-space: pre-wrap; word-wrap: break-word; padding: 10px; overflow-y: auto; max-height: calc(60vh - 40px); }\n                    #ooo_debug_log_window { position: fixed; top: 100px; right: 20px; width: 450px; height: 300px; min-width: 300px; min-height: 150px; background-color: #fff; border: 1px solid #ccc; border-radius: 5px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; display: none; font-family: monospace; font-size: 11px; resize: both; overflow: hidden; }\n                    #ooo_debug_log_header { padding: 5px 8px; cursor: move; background-color: #e9e9e9; color: #333; border-bottom: 1px solid #ddd; user-select: none; display: flex; justify-content: space-between; align-items: center; border-top-left-radius: 4px; border-top-right-radius: 4px; }\n                    #ooo_debug_log_header_title { font-weight: bold; }\n                    #ooo_debug_log_controls { display: flex; gap: 5px; }\n                    #ooo_debug_log_close, #ooo_debug_log_clear { cursor: pointer; font-weight: bold; padding: 0 5px; color: #555; }\n                    #ooo_debug_log_close:hover, #ooo_debug_log_clear:hover { color: #000; }\n                    #ooo_debug_log_content { padding: 5px; overflow-y: auto; height: calc(100% - 30px); list-style: none; margin: 0; }\n                    #ooo_debug_log_content li { border-bottom: 1px dotted #eee; padding: 2px 0; }\n                `);\n                const styleMarker = document.createElement('div');\n                styleMarker.id = 'ooo_custom_styles';\n                styleMarker.style.display = 'none';\n                document.head.appendChild(styleMarker);\n            }\n            if (!document.getElementById('ooo_control_panel')) {\n                const panel = document.createElement('div');\n                panel.id = 'ooo_control_panel';\n                panel.innerHTML = `<div id=\"ooo_control_panel_header\"><span>脚本控制</span><span id=\"ooo_control_panel_toggle\" title=\"收起/展开\">[-]</span></div><div id=\"ooo_control_panel_body\"></div>`;\n                document.body.appendChild(panel);\n                const panelBody = document.getElementById('ooo_control_panel_body');\n                const makeBtn = (textOn, textOff, initial, callback) => {\n                    const btn = document.createElement('span');\n                    btn.textContent = initial ? textOn : textOff;\n                    Object.assign(btn.style, { cursor: 'pointer', padding: '2px 6px', border: '1px solid #ccc', borderRadius: '3px', background: initial ? '#b6f1a2' : '#f0f0f0', userSelect: 'none', fontSize: '12px', whiteSpace: 'nowrap' });\n                    btn.onclick = () => { const state = callback(); btn.textContent = state ? textOn : textOff; btn.style.background = state ? '#b6f1a2' : '#f0f0f0'; };\n                    return btn;\n                };\n                const joinBtn = makeBtn('自动接入: 开', '自动接入: 关', autoJoinEnabled, () => { autoJoinEnabled = !autoJoinEnabled; logDebugInfo(`自动接入已${autoJoinEnabled ? '开启' : '关闭'}`, 'action'); return autoJoinEnabled; });\n                const replyBtn = makeBtn('自动回复: 开', '自动回复: 关', autoReplyEnabled, () => { autoReplyEnabled = !autoReplyEnabled; if (autoReplyEnabled) { sessionStates = {}; logDebugInfo('清空所有会话状态', 'system'); } logDebugInfo(`自动回复已${autoReplyEnabled ? '开启' : '关闭'}`, 'action'); return autoReplyEnabled; });\n                const viewDataBtn = document.createElement('span');\n                viewDataBtn.textContent = '查看数据';\n                Object.assign(viewDataBtn.style, { cursor: 'pointer', padding: '2px 6px', border: '1px solid #ccc', borderRadius: '3px', userSelect: 'none', background: '#f0f0f0', fontSize: '12px' });\n                viewDataBtn.onclick = () => { const viewer = document.getElementById('ooo_data_viewer'); if (viewer) viewer.style.display = viewer.style.display === 'block' ? 'none' : 'block'; };\n                const debugLogBtn = document.createElement('span');\n                debugLogBtn.textContent = '调试信息';\n                Object.assign(debugLogBtn.style, { cursor: 'pointer', padding: '2px 6px', border: '1px solid #ccc', borderRadius: '3px', userSelect: 'none', background: '#f0f0f0', fontSize: '12px' });\n                debugLogBtn.onclick = () => { const debugWin = document.getElementById('ooo_debug_log_window'); if (debugWin) debugWin.style.display = debugWin.style.display === 'block' ? 'none' : 'block'; };\n                panelBody.appendChild(joinBtn);\n                panelBody.appendChild(replyBtn);\n                panelBody.appendChild(viewDataBtn);\n                panelBody.appendChild(debugLogBtn);\n                document.getElementById('ooo_control_panel_toggle').onclick = () => {\n                    const body = document.getElementById('ooo_control_panel_body');\n                    const toggleBtn = document.getElementById('ooo_control_panel_toggle');\n                    if (body.style.display === 'none') { body.style.display = 'flex'; toggleBtn.textContent = '[-]'; } else { body.style.display = 'none'; toggleBtn.textContent = '[+]'; }\n                };\n                makeDraggable(panel, 'ooo_control_panel_header');\n            }\n            if (!document.getElementById('ooo_data_viewer')) {\n                const viewer = document.createElement('div');\n                viewer.id = 'ooo_data_viewer';\n                viewer.innerHTML = `<div id=\"ooo_data_viewer_header\"><span>当前会话匹配数据</span><span id=\"ooo_data_viewer_close\" title=\"关闭\">✖</span></div><pre id=\"ooo_data_content\">{}</pre>`;\n                document.body.appendChild(viewer);\n                document.getElementById('ooo_data_viewer_close').onclick = () => { viewer.style.display = 'none'; };\n                makeDraggable(viewer, 'ooo_data_viewer_header');\n                cachedElements.dataViewer = viewer;\n            }\n            if (!document.getElementById('ooo_debug_log_window')) {\n                const debugWin = document.createElement('div');\n                debugWin.id = 'ooo_debug_log_window';\n                debugWin.innerHTML = `<div id=\"ooo_debug_log_header\"><span id=\"ooo_debug_log_header_title\">调试信息</span><div id=\"ooo_debug_log_controls\"><span id=\"ooo_debug_log_clear\" title=\"清空日志\">🗑</span><span id=\"ooo_debug_log_close\" title=\"关闭\">✖</span></div></div><ul id=\"ooo_debug_log_content\"></ul>`;\n                document.body.appendChild(debugWin);\n                document.getElementById('ooo_debug_log_close').onclick = () => { debugWin.style.display = 'none'; };\n                document.getElementById('ooo_debug_log_clear').onclick = () => { const list = document.getElementById('ooo_debug_log_content'); if (list) list.innerHTML = ''; logDebugInfo('日志已清空。', 'system'); };\n                makeDraggable(debugWin, 'ooo_debug_log_header');\n                cachedElements.debugLogList = document.getElementById('ooo_debug_log_content');\n                logDebugInfo('调试信息窗口已准备就绪。', 'system');\n            }\n            cachedElements.selectUnreadSessionsBtn = document.getElementById('selectUnreadSessionsBtn');\n            cachedElements.replyStatusIndicator = document.getElementById('ss-reply-status');\n            cachedElements.chatContainer = document.querySelector('div.message-list') || document.querySelector('div.chat-main__content');\n        }\n\n        function getReplyStatus() {\n            if (!cachedElements.replyStatusIndicator) cachedElements.replyStatusIndicator = document.getElementById('ss-reply-status');\n            const statusElement = cachedElements.replyStatusIndicator;\n            if (!statusElement) { logDebugInfo('错误: 未找到智能客服助手状态指示器 (#ss-reply-status)。', 'error'); return 'status_element_missing'; }\n            return statusElement.textContent.trim();\n        }\n\n        async function isReplySystemIdle() {\n            const currentStatus = getReplyStatus();\n            if (currentStatus === '空闲') { return true; }\n            else if (currentStatus === 'status_element_missing') { logDebugInfo('警告: 智能回复系统状态指示器缺失，假设空闲以继续操作。', 'warn'); return true; }\n            return false;\n        }\n\n        async function switchSession() {\n            const startTime = Date.now();\n            let currentStatus = getReplyStatus();\n            while (currentStatus !== '空闲' && currentStatus !== 'status_element_missing') {\n                if (Date.now() - startTime > REPLY_STATUS_WAIT_TIMEOUT) { logDebugInfo(`警告: 等待智能回复系统空闲超时 (当前状态: ${currentStatus})。`, 'error'); return; }\n                await sleep(REPLY_STATUS_CHECK_INTERVAL);\n                currentStatus = getReplyStatus();\n            }\n            if (!cachedElements.selectUnreadSessionsBtn) cachedElements.selectUnreadSessionsBtn = document.getElementById('selectUnreadSessionsBtn');\n            const nxt = cachedElements.selectUnreadSessionsBtn;\n            if (!nxt) { logDebugInfo('错误: 未找到切换会话按钮 (#selectUnreadSessionsBtn)。', 'error'); return; }\n            const oldSessionKey = getSessionKey();\n            currentSessionDebugData = {};\n            updateDataViewer();\n            logDebugInfo(`系统: 清除旧会话【${oldSessionKey}】调试数据。`, 'system');\n            nxt.click();\n            logDebugInfo('动作: 点击切换会话按钮。', 'action');\n            const switchWaitStartTime = Date.now();\n            let newSessionKey = getSessionKey();\n            while (newSessionKey === oldSessionKey && (Date.now() - switchWaitStartTime < SESSION_SWITCH_WAIT_TIMEOUT)) { await sleep(200); newSessionKey = getSessionKey(); }\n            if (newSessionKey !== oldSessionKey) {\n                logDebugInfo(`系统: 成功切换到新会话【${newSessionKey}】。`, 'system');\n                sessionStates[newSessionKey] = sessionStates[newSessionKey] || { lastProcessedCustomerMessageKey: null, historyFullyLoaded: false };\n                sessionStates[newSessionKey].historyFullyLoaded = false;\n            } else { logDebugInfo('警告: 切换会话超时或未成功。当前会话仍为【' + oldSessionKey + '】。', 'error'); }\n        }\n\n        async function clickButton(name) {\n            const buttonsContainer = document.querySelector('div.chat-footer__actions') || document.body;\n            let targetButton = null;\n            const potentialButtons = buttonsContainer.querySelectorAll('button, span');\n            for (const btn of potentialButtons) { if (btn.textContent.trim() === name) { targetButton = btn; break; } }\n            if (!targetButton) { logDebugInfo(`错误: 未找到名称为【${name}】的按钮。`, 'error'); return false; }\n            logDebugInfo(`动作: 等待 ${CLICK_DELAY_MS}ms 后触发【${name}】按钮。`, 'action');\n            await sleep(CLICK_DELAY_MS);\n            targetButton.click();\n            logDebugInfo(`动作: 点击【${name}】按钮。`, 'action');\n            const initialMessageCount = document.querySelectorAll('div.vue-recycle-scroller__item-view').length;\n            const startTime = Date.now();\n            let messageSent = false;\n            while (Date.now() - startTime < SEND_STATUS_CHECK_TIMEOUT) {\n                const currentMessages = document.querySelectorAll('div.vue-recycle-scroller__item-view');\n                if (currentMessages.length > initialMessageCount) {\n                    const lastNewMessageView = currentMessages[currentMessages.length - 1];\n                    const lastNewBubble = lastNewMessageView ? lastNewMessageView.querySelector('div.bubble') : null;\n                    if (lastNewBubble && lastNewBubble.classList.contains('bubble--left')) { messageSent = true; break; }\n                }\n                await sleep(SEND_STATUS_CHECK_INTERVAL);\n            }\n            if (messageSent) { logDebugInfo('系统: 消息发送成功。', 'system'); return true; }\n            logDebugInfo('错误: 消息发送状态检测超时或失败。', 'error');\n            return false;\n        }\n\n        async function activateSessionButton() {\n            if (isProcessingTask || !autoJoinEnabled) return;\n            isProcessingTask = true;\n            try {\n                let joinBtn = document.querySelector('button.arco-btn-primary:not(.arco-btn-status-danger)');\n                if (!joinBtn) {\n                    const buttons = document.querySelectorAll('button');\n                    for (const btn of buttons) { if ((btn.textContent.includes('接入') || btn.textContent.includes('Join Chat') || btn.textContent.includes('Accept')) && !btn.classList.contains('arco-btn-status-danger')) { joinBtn = btn; break; } }\n                }\n                if (!joinBtn || joinBtn.disabled) { return; }\n                joinBtn.click();\n                logDebugInfo('动作: 点击接入会话按钮。', 'action');\n            } finally { isProcessingTask = false; }\n        }\n\n        async function loadAllHistory() {\n            if (!cachedElements.chatContainer) { cachedElements.chatContainer = document.querySelector('div.message-list') || document.querySelector('div.chat-main__content'); }\n            const chatContainer = cachedElements.chatContainer;\n            if (!chatContainer) { logDebugInfo('错误: 聊天容器未找到。', 'error'); return false; }\n            let attempts = 0;\n            logDebugInfo('系统: 开始加载历史消息...', 'system');\n            const initialMessageCount = document.querySelectorAll('div.vue-recycle-scroller__item-view').length;\n            while (attempts < HISTORY_LOAD_MAX_ATTEMPTS) {\n                const countBefore = document.querySelectorAll('div.vue-recycle-scroller__item-view').length;\n                chatContainer.scrollTop = 0;\n                await sleep(HISTORY_LOAD_SCROLL_DELAY_MS);\n                const countAfter = document.querySelectorAll('div.vue-recycle-scroller__item-view').length;\n                if (countAfter > countBefore) { logDebugInfo(`系统: 加载到 ${countAfter - countBefore} 条新历史。`, 'system'); attempts = 0; }\n                else { attempts++; if (chatContainer.scrollTop === 0 && countAfter === countBefore) { logDebugInfo('系统: 已滚动到顶部，历史加载完成。', 'system'); break; } }\n            }\n            if (document.querySelectorAll('div.vue-recycle-scroller__item-view').length === initialMessageCount && attempts === HISTORY_LOAD_MAX_ATTEMPTS) { logDebugInfo('警告: 历史加载未检测到新消息。', 'warn'); }\n            return true;\n        }\n\n        async function processSessionMessage() {\n            if (isProcessingTask) return;\n            isProcessingTask = true;\n            const currentSessionKey = getSessionKey();\n            let sessionDataToLog = { timestamp: new Date().toISOString(), sessionKey: currentSessionKey, actionTaken: 'none', status: 'processed' };\n            try {\n                ensureControlsExist();\n                if (lastProcessedSessionKey !== currentSessionKey) {\n                    logDebugInfo(`系统: 检测到会话切换至【${currentSessionKey}】。`, 'system');\n                    sessionStates[currentSessionKey] = sessionStates[currentSessionKey] || { lastProcessedCustomerMessageKey: null, historyFullyLoaded: false };\n                    sessionStates[currentSessionKey].historyFullyLoaded = false;\n                    currentSessionDebugData = {};\n                    updateDataViewer();\n                }\n                lastProcessedSessionKey = currentSessionKey;\n                let st = sessionStates[currentSessionKey];\n                if (!st) { st = sessionStates[currentSessionKey] = { lastProcessedCustomerMessageKey: null, historyFullyLoaded: false }; }\n                if (!autoReplyEnabled) { sessionDataToLog.status = 'skipped_auto_reply_off'; return; }\n                if (!st.historyFullyLoaded) {\n                    if (!await loadAllHistory()) {\n                        logDebugInfo('错误: 历史加载失败，将根据自动回复状态决定是否切换会话。', 'error');\n                        if (autoReplyEnabled && await isReplySystemIdle()) { logDebugInfo('动作: 自动回复开启且系统空闲，尝试切换会话。', 'action'); await switchSession(); }\n                        else if (!await isReplySystemIdle()) { logDebugInfo('系统: 智能回复系统忙碌，跳过会话切换。', 'system'); }\n                        else { logDebugInfo('系统: 自动回复关闭，不切换会话。', 'system'); }\n                        sessionDataToLog.status = 'skipped_history_load_fail';\n                        return;\n                    }\n                    st.historyFullyLoaded = true;\n                    logDebugInfo('系统: 会话历史加载完成。', 'system');\n                }\n                const remark = await getStableCustomerRemark();\n                const allMessagesInDom = document.querySelectorAll('div.vue-recycle-scroller__item-view');\n                const data = extractMessageData(allMessagesInDom);\n                currentSessionDebugData = { remark: remark, latestCustomerMessage: data ? data.latestCustomerMessage : null, customerHistory: data ? data.customerHistory : [], latestServiceMessage: data ? data.latestServiceMessage : null, serviceHistory: data ? data.serviceHistory : [], allHistory: data ? data.allHistory : [] };\n                updateDataViewer();\n                sessionDataToLog.remark = remark;\n                sessionDataToLog.latestCustomerMessage = currentSessionDebugData.latestCustomerMessage;\n                if (!data || !data.latestCustomerMessage || !data.isLastMessageFromCustomer) {\n                    logDebugInfo(data ? '系统: 客服是最后发言人，切换至下一会话。' : '系统: 当前会话无客户消息或消息数据提取失败，切换至下一会话。', 'system');\n                    if (autoReplyEnabled && await isReplySystemIdle()) { await switchSession(); }\n                    else if (!await isReplySystemIdle()) { logDebugInfo('系统: 智能回复系统忙碌，跳过会话切换。', 'system'); }\n                    sessionDataToLog.status = data ? 'skipped_agent_last_message' : 'skipped_no_customer_message';\n                    return;\n                }\n                const currentCustomerMessageKey = (data.latestCustomerMessage.original || '') + (data.latestCustomerMessage.translated || '');\n                if (st.lastProcessedCustomerMessageKey === currentCustomerMessageKey) {\n                    logDebugInfo('系统: 此客户消息已处理过。尝试切换至下一会话。', 'system');\n                    sessionDataToLog.status = 'skipped_already_processed_attempt_switch';\n                    if (autoReplyEnabled && await isReplySystemIdle()) { await switchSession(); }\n                    else if (!await isReplySystemIdle()) { logDebugInfo('系统: 智能回复系统忙碌，跳过会话切换。', 'system'); }\n                    return;\n                }\n                logDebugInfo(`客户: 新客户消息: ${data.latestCustomerMessage.original}`, 'customer');\n                let acted = false;\n                for (const [btnName, ruleNode] of Object.entries(forwardRules)) {\n                    if (matchRule(currentSessionDebugData, ruleNode)) {\n                        logDebugInfo(`系统: 规则匹配成功: 按钮【${btnName}】`, 'system');\n                        if (await isReplySystemIdle()) {\n                            if (await clickButton(btnName)) {\n                                acted = true;\n                                sessionDataToLog.actionTaken = 'button_clicked';\n                                sessionDataToLog.triggeredButton = btnName;\n                                st.lastProcessedCustomerMessageKey = currentCustomerMessageKey;\n                                break;\n                            }\n                        } else {\n                            logDebugInfo('系统: 智能回复系统忙碌，跳过点击按钮操作。', 'system');\n                            sessionDataToLog.status = 'skipped_reply_system_busy_for_action';\n                            return;\n                        }\n                    }\n                }\n                if (acted) {\n                    logDebugInfo('系统: 脚本已成功执行回复动作。', 'system');\n                    if (autoReplyEnabled && await isReplySystemIdle()) {\n                        logDebugInfo('系统: 智能回复系统空闲，脚本准备安全切换会话。', 'system');\n                        await sleep(POST_REPLY_SWITCH_DELAY_MS);\n                        await switchSession();\n                    } else if (!await isReplySystemIdle()) {\n                        logDebugInfo('系统: 智能回复系统忙碌，跳过会话切换。', 'system');\n                    } else {\n                        logDebugInfo('系统: 自动回复关闭，不切换会话。', 'system');\n                    }\n                } else {\n                    logDebugInfo('系统: 无匹配规则。', 'warn');\n                    sessionDataToLog.actionTaken = 'no_rule_matched';\n                }\n            } catch (e) {\n                logDebugInfo('错误: 处理会话消息时发生错误: ' + e.message, 'error');\n                console.error(e);\n                sessionDataToLog.status = 'error';\n                sessionDataToLog.errorMessage = e.message;\n            } finally {\n                isProcessingTask = false;\n                sendDataToServer(sessionDataToLog);\n            }\n        }\n\n        function initNewVersion() {\n            setTimeout(() => {\n                ensureControlsExist();\n                setInterval(() => { if (!isProcessingTask) activateSessionButton(); }, 1000);\n                setInterval(() => { if (!isProcessingTask) processSessionMessage(); }, MESSAGE_CHECK_INTERVAL);\n                logDebugInfo('脚本已启动 (v5.9.9)', 'system');\n            }, 2000);\n        }\n\n        initNewVersion();\n        // --- END: v5.9.9 脚本完整代码 ---\n    }\n\n\n    // ====================================================================================\n    // 旧版脚本 v5.7.0 (完全独立)\n    // 运行于: https://app.salesmartly.com/chat\n    // ====================================================================================\n    function runOldVersionScript() {\n        // --- START: v5.7.0 脚本完整代码 ---\n        const GM_xmlhttpRequest = window.GM_xmlhttpRequest;\n        const GM_getValue = window.GM_getValue;\n        const GM_setValue = window.GM_setValue;\n        const GM_info = window.GM_info;\n        const GM_notification = window.GM_notification;\n\n        (function setupLimitedConsole() {\n            const MAX_LOG_ENTRIES = 25;\n            const logBuffer = [];\n            let redrawQueued = false;\n            const originalConsole = { log: console.log, warn: console.warn, error: console.error, info: console.info, clear: console.clear };\n            const redrawConsole = () => {\n                originalConsole.clear();\n                originalConsole.info(`%c[旧版脚本 v5.7.0] 日志管理器: 自动刷新已开启，仅显示最近 ${MAX_LOG_ENTRIES} 条日志。`, 'color: #888;');\n                logBuffer.forEach(entry => { (originalConsole[entry.type] || originalConsole.log).apply(console, entry.args); });\n                redrawQueued = false;\n            };\n            const logHandler = (type, args) => {\n                logBuffer.push({ type: type, args: args });\n                while (logBuffer.length > MAX_LOG_ENTRIES) { logBuffer.shift(); }\n                if (!redrawQueued) { redrawQueued = true; setTimeout(redrawConsole, 100); }\n            };\n            console.log = (...args) => logHandler('log', args);\n            console.warn = (...args) => logHandler('warn', args);\n            console.error = (...args) => logHandler('error', args);\n            console.info = (...args) => logHandler('info', args);\n            console.clear = () => { logBuffer.length = 0; redrawConsole(); };\n            originalConsole.log('%c[旧版脚本 v5.7.0] 日志管理器: 初始化成功。', 'color: #888;');\n        })();\n\n        const LOGGING_PHP_URL = 'https://abcdc.top/jj/log/log_chat_data.php';\n        const CLIENT_ID = window.CLIENT_ID || (typeof GM_getValue !== 'undefined' && GM_getValue('clientID')) || 'unknown_client';\n\n        function functionalLog(...args) {\n            console.log(`%c[旧版脚本 v5.7.0]`, 'color: #2ecc71; font-weight: bold;', ...args);\n        }\n        functionalLog('核心功能脚本已启动！', '客户端ID:', CLIENT_ID);\n\n        function sendDataToServer(data, endpoint = LOGGING_PHP_URL) {\n            if (typeof GM_xmlhttpRequest === 'undefined') { return; }\n            GM_xmlhttpRequest({\n                method: \"POST\",\n                url: endpoint,\n                headers: { \"Content-Type\": \"application/json\", \"X-Client-ID\": CLIENT_ID },\n                data: JSON.stringify(data),\n                onload: function(response) {\n                    if (response.status !== 200) functionalLog('会话数据发送失败，状态码:', response.status);\n                },\n                onerror: function(error) { functionalLog('会话数据发送网络错误:', error); }\n            });\n        }\n        \n        const ruleText = String.raw`\n# 规则说明：\n# 1. 支持通过缩进来实现嵌套逻辑。每个缩进级别代表一个新的逻辑分组。\n# 2. 同一缩进级别下的“并且”条件是 AND 关系。\n# 3. 同一缩进级别下的“或者”会开启一个新的 AND 分支，与之前的 AND 分支构成 OR 关系。\n\n按钮: 无法登录电报\n  并且 客户消息 包含 没有Telegram|没有telegram|无法登录Telegram|无法登录telegram|无法登录\n  并且 客户历史 包含 没有Telegram|没有telegram|无法登录Telegram|无法登录telegram|无法登录\n  并且 全部消息 不包含 你的手机上安装了Telegram应用程序吗|请从手机的应用商店下载\n  并且 全部消息 包含 您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的申请已转交财务经理处理\n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n\n按钮: ggg和财务经理联系\n  并且 备注 匹配 \\d{8} \n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n  并且 全部历史 包含 必须升级到|财务经理|你的业务已经转由财务经理处理|请按财务经理指示操作 谢谢|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|请您联系财务经理为您更改正确的cpf|客服无此权限|财务经理可能很忙|财务经理看到消息后会回复您\n  并且 全部历史 包含 me/BRBET|详情请您联系财务经理为您处理\n  并且 客户消息 不包含 好的|ok|OK|谢谢|无法|冻结|封禁|封|不能\n\n\n按钮: g财务电报\n  并且 备注 匹配 \\d{8}\n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n  并且 全部历史 不包含 me/BRBET|详情请您联系财务经理为您处理\n  并且 全部历史 包含 账户是否正确|经查询您的CPF号码账户填写错误|提现管理|户管理查看您的提现账户是否正确|财务部门无法转账成功|统提示您提款账户错误|我帮你核对一下|按照步骤查看你的提款账户是否正确|请您前往个人中心\n  并且 客户消息 包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n  并且 客户历史 包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n\n\n按钮: uuu检查cpf号码\n  并且 备注 匹配 \\d{8}\n  并且 (\n    备注 匹配 (?i)V(2|[1-9]\\d*)\n    或者\n    备注 匹配 \\b(?!\\d{8}\\b)\\d{3,}\\b\n  )\n  并且 全部历史 不包含 me/BRBET|详情请您联系财务经理为您处理|无法登录\n  并且 全部历史 包含 账户是否正确|经查询您的CPF号码账户填写错误|提现管理|户管理查看您的提现账户是否正确|财务部门无法转账成功|统提示您提款账户错误|我帮你核对一下|按照步骤查看你的提款账户是否正确|请您前往个人中心\n  并且 客户消息 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n  并且 客户历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|更正|改变|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|移除|纠\n\n\n\n\n\n\n\n按钮: c（V1不能游戏）\n  并且 客户消息 包含 玩不了|卡住\n  并且 客户历史 包含 玩不了|卡住\n  并且 全部消息 不包含 账户盈利金额过高|为了保障您的账户资金安全\n\n按钮: vvvvv升级v2提款\n  并且 客户消息 包含 退款|退出\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 不包含 cpf|财务|经理|存了\n\n\n\n\n\n\n\n按钮: 一旦晋升\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 您已选择|不可取消|遵守活动规则|放心|不用担心|您好，您已经充值\n  并且 服务消息 不包含 一旦您晋升为VIP2\n  并且 客户消息 不包含 退回|无法|已经|好的|投诉|ok|OK|谢谢|感谢|抢走|Ok\n  并且 全部历史 不包含 退回|存了|换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|我是VIP|我是vip|所有事|2级|骗局|移除|纠\n  )\n  \n\n按钮: 放心\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 不包含 无法|如何|升级|已经|充值了\n  并且 (\n    全部历史 包含 按活动规则你需要存入100升级VIP2|才能提走你在活动中赚的所有钱|按活动规则你需要存入100升级VIP2 才能提走你在活动中赚的所有钱\n    或者\n      并且 (\n        全部历史 包含 保证\n        全部历史 包含 公司规定|参与活动免费R$35奖金的会员必须升级到VIP2才可以提取全部奖金\n      )\n  )\n  并且 服务历史 不包含 账户管理查看您的提现账户是否正确|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|如果财务经理没有回复|账户管理查看您的提现账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|按活动规则你需要存入100升级VIP2\n  并且 (\n    全部历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|所有事|抢走|2级|骗局|移除|纠\n    或者\n    客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|放心\n    或者\n    客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|放心\n  )\n  并且 (\n    客户历史 不包含 失败\n    或者\n    客户消息 不包含 失败\n  )\n\n按钮: 按活动规则\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 您已选择参加免费赠送活动|请遵守活动规则|如果您没有参与|您可以提取资金而无需升级到|根据活动规则，您需要充值100升级到VIP2才可以提现本次活动赢得的全部奖金\n  并且 服务历史 不包含 好的|请您前往个人中心|提现管理|账户管理查看您的提现账户是否正确|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|如果财务经理没有回复|请您前往个人中心|提现管理|账户管理查看您的提款账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|Olá, por favor, verifique se o número do CPF que você vinculou à plataforma está correto conforme a imagem que lhe enviei.|政变|存了\n  并且 全部历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|抢走|2级|骗局|移除|纠\n  并且 客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事\n  并且 客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事\n\n按钮: 仔细看规则\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 投诉|追究|责任|如果您没有参与35免费奖金|您可以提取资金而无需升级到|如果您没有参与R$35 免费奖金|您可以提取资金而无需升级到\n  并且 全部历史 不包含 换成|修改|编辑CPF|编辑 CPF|编辑选项|打错|解决|这是我的|不正确|如何解决|一个数字|这个问题|正确的数字|不是|更改|需要多久才能到账|我已经做完了|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金号码输错了|号码输错|信息有误|有误|修复|数字错误|错误|2级|移除|纠\n  并且 全部历史 包含 公司规定|参与活动免费R$35奖金的会员必须升级到VIP2才可以提取全部奖金|按活动规则你需要存入100升级VIP2|才能提走你在活动中赚的所有钱|您已选择参加免费赠送活动|不可取消|请遵守活动规则|您已选择参加免费赠送活动\n  并且 客户历史 包含 政变|狗|规则|口交|流浪|妈妈|喂奶|屁股|监狱|倒塌|妓女|王八蛋|乌龟|婊子|妻子|妹妹|母亲|小偷|地狱|囚徒|魔鬼|狗娘|窃|贼|你妈|抢走|骗局|退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n  并且 客户消息 包含 政变|狗|规则|口交|流浪|妈妈|喂奶|屁股|监狱|倒塌|妓女|王八蛋|乌龟|婊子|妻子|妹妹|母亲|小偷|地狱|囚徒|魔鬼|狗娘|窃|贼|你妈|抢走|骗局|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n\n按钮: 35奖金活动不可取消\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 因为这是为了确保每一位客户都是真正的会员。因为有很多不法分子利用没有VIP限制的事实。注册多个会员账户来骗取我们平台的奖金。|这确保了每位客户都是真正的会员。许多犯罪分子利用VIP限制的缺失，创建多个会员账户，以欺诈手段从我们的平台骗取奖励。|因为这是为了确保每一位客户都是真正的会员。因为有很多不法分子利用没有VIP限制的事实。注册多个会员账户来骗取我们平台的奖金。|Isso garante que cada cliente seja um membro genuíno. Muitos criminosos se aproveitam da inexistência de restrições VIP e criam várias contas de membro para fraudar os bônus da nossa plataforma.\n  并且 全部历史 不包含 请您前往个人中心|提现管理|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|账户管理查看您的提现账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金错|号码输错|信息有误|有误|修复|数字错误|错误|我是VIP|我是vip|所有事|2级|移除|纠\n  并且 客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n  并且 客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n\n按钮: bb+vvvv\n  并且 备注 匹配 (^[^a-zA-Z0-9]*\\d{8}[^a-zA-Z0-9]*$)|((?=.*\\d{8})(?=.*\\b\\d{1,2}\\b).*)\n  并且 全部历史 包含 该公司发现许多会员使用多个账户领取 35 雷亚尔的免费套利奖金，因此该公司规定，参与 35 雷亚尔免费奖金活动的会员必须升级到 VIP2 才能提取所有资金。|公司发现有很多会员利用多个账号领取免费活动R$35奖金进行套利，所以公司规定参与R$35免费活动奖金的会员必须升级VIP2后才能全部提款。\n  并且 全部历史 不包含 等待|处理中|请您前往个人中心|提现管理|系统提示您提款账户错误|经查询您的CPF号码账户填写错误|您的业务已经移交财务经理为您办理|您根据财务经理的提示操作即可提取账户全部资金|您的业务已移交财务经理为您办理|您联系财务经理进行咨询即可|添加财务经理电报后|如果财务经理没有回复|请及时截图给我|账户管理查看您的提现账户是否正确|财务部门反馈您的CPF号码错误导致无法给您汇款|请您根据我所发送图片所标注的|请您按照我发给您的图片进行检查您在平台绑定的CPF号码是否正确|不是|更改|如何更正|如何修正|自己的数据|如何更改|号码打错了|公积金错|号码输错|信息有误|有误|修复|数字错误|错误|我是VIP|我是vip|所有事|2级|移除|纠\n  并且 客户消息 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n  并且 客户历史 不包含 退回|投诉|ok|OK|谢谢|感谢|无法|已经|好的|我是VIP|我是vip|所有事|存了\n\n\n\n\n\n\n\n\n\n\n按钮: 发截图\n  并且 备注 不匹配 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 全部历史 不包含 \\d{8}\n  并且 (\n    客户消息 包含 无法登录|无法注册|封禁\n    或者\n    客户历史 包含 无法登录|无法注册|封禁\n  )\n\n按钮: zzz（改密码发）\n  并且 备注 不匹配 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 全部历史 不包含 \\d{8}\n  并且 (\n    客户消息 包含 密码|忘记密码\n    或者\n    客户历史 包含 忘记密码|密码\n  )\n\n按钮: 发ID\n  并且 备注 不匹配 \\d{8}\n  并且 全部历史 不包含 游戏ID\n  并且 全部历史 不包含 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 (\n    客户消息 包含 需要钱|解锁|要钱|存款|不让玩|不会玩|saca|失败了|投了|我的钱|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|取出|不了\n    或者\n    客户历史 包含 需要钱|不让玩|不会玩|saca|失败了|投了|我的钱|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|取出|不了\n    或者\n    全部历史 包含 Olá, qual problema você encontrou na plataforma? Por favor, nos informe com clareza para que possamos ajudar.\n      并且 全部历史 不包含 游戏ID\n    \n  )\n\n按钮: 发ID有图\n  并且 备注 不匹配 \\d{8}\n  并且 全部历史 不包含 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 全部历史 包含 游戏ID|详细描述|seu ID de jogo|que possamos ajudar\n  并且 (\n    客户历史 包含 存款|不让玩|投了|我的钱|id|ID|是什么|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|depositar|取出\n    或者\n    客户消息 包含 存款|不让玩|投了|我的钱|id|ID|是什么|到账|没能|获得|撤回|遇到|存入|不能游戏|赢了|不出|不回来|没有成功|Saque|saque|提取|sacar|奖励|奖金|如何提|取款|我无法|Sacar|Pix|pic|cpf|更改图片|CPF|提现|提款|无法提款|清酒|更改|更新|充值|depositar|取出\n    )\n  或者\n    并且 客户历史 不包含 \\d{8}\n    并且 客户消息 不包含 \\d{8}\n    并且 客户消息 包含 ？|?\n    并且 (\n      服务历史 包含 游戏ID|详细描述|seu ID de jogo|que possamos ajudar\n      或者\n      服务消息 包含 游戏ID|详细描述|seu ID de jogo|que possamos ajudar\n    )\n\n按钮: 问题\n  并且 备注 不匹配 \\d{8}\n  并且 客户历史 不包含 \\d{8}\n  并且 客户消息 不包含 \\d{8}\n  并且 客户消息 包含 登记|应得|？？|??|存入|帮帮我|欧巴|支持|Oi|Golp|说谎|帮助|骗局|想玩|问题|窃贼|去死|退款|放了|或许|小偷|公平|欺诈|晚安|为什么|举报|假的|Ok|下午好|早上好|晚上好|中午好|骗子|无耻|你好|嘿|骗|盗贼|平台|该如何|操|无赖|滚|蠢货\n  或者\n    并且 备注 不匹配 \\d{8}\n    并且 客户历史 不包含 \\d{8}\n    并且 客户消息 不包含 \\d{8}\n    并且 全部历史 不包含 Olá, qual problema você encontrou na plataforma? Por favor, nos informe com clareza para que possamos ajudar.\n    并且 客户历史 包含 游戏|嘿\n\n按钮: 感谢\n  并且 客户消息 包含 ok|OK|谢谢\n  并且 客户消息 不包含 举报|诉讼|Facebook\n`;\n\n        const CLICK_DELAY_MS = 0;\n        const MESSAGE_CHECK_INTERVAL = 3300;\n        const HISTORY_LOAD_MAX_ATTEMPTS = 3;\n        const HISTORY_LOAD_SCROLL_DELAY_MS = 1000;\n        const SEND_STATUS_CHECK_TIMEOUT = 10000;\n        const SEND_STATUS_CHECK_INTERVAL = 500;\n        const REPLY_STATUS_WAIT_TIMEOUT = 15000;\n        const REPLY_STATUS_CHECK_INTERVAL = 500;\n        const SESSION_SWITCH_WAIT_TIMEOUT = 5000;\n        const POST_REPLY_SWITCH_DELAY_MS = 200;\n\n        let autoJoinEnabled = false, autoReplyEnabled = false, isProcessingTask = false, sessionStates = {};\n        let cachedElements = { debugList: null, toggleContainer: null, rightColumn: null, chatContainer: null, selectUnreadSessionsBtn: null };\n        let currentSessionDebugData = {};\n        let lastProcessedSessionKey = null;\n\n        const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));\n\n        const fieldMap = { 备注: 'remark', 客户消息: 'latestCustomerMessage', 客户历史: 'customerHistory', 服务消息: 'latestServiceMessage', 服务历史: 'serviceHistory', 全部历史: 'allHistory' };\n        const opMap = { 包含: 'contains', 不包含: 'notContains', 匹配: 'match', 不匹配: 'notMatch' };\n        function checkStringAgainstProcessedValues(str, processedValues) { if (!str) return false; return processedValues.some(pv => { if (pv.type === 'regex') { return pv.value.test(str); } else { return str.includes(pv.value); } }); }\n        function matchCond(src, op, processedValues) { let result = false; if (typeof src === 'string') { result = checkStringAgainstProcessedValues(src, processedValues); } else if (typeof src === 'object' && src !== null && ('original' in src || 'translated' in src)) { result = checkStringAgainstProcessedValues(src.original, processedValues) || checkStringAgainstProcessedValues(src.translated, processedValues); } else if (Array.isArray(src)) { result = src.some(msgObj => checkStringAgainstProcessedValues(msgObj.original, processedValues) || checkStringAgainstProcessedValues(msgObj.translated, processedValues)); } return (op === 'contains' || op === 'match') ? result : !result; }\n        function parseConditionLine(line) { const valueParser = (op, val) => val.split('|').map(x => x.trim()).filter(x => x).map(part => { const isRegex = part.includes('\\\\') || op === 'match' || op === 'notMatch'; if (isRegex) { try { return { type: 'regex', value: new RegExp(part, 'i') }; } catch (e) { functionalLog(\"DSL规则正则错误:\", e, \"值:\", part); return { type: 'literal', value: part }; } } else { return { type: 'literal', value: part }; } }); const cleanLine = line.replace(/^(并且|或者)\\s*/, '').trim(); const mm = cleanLine.match(/^(备注|客户消息|客户历史|服务消息|服务历史|全部历史)\\s+(包含|不包含|匹配|不匹配)\\s*(.*)$/); if (mm) { const field = fieldMap[mm[1]]; const op = opMap[mm[2]]; const value = mm[3] ? mm[3].trim() : ''; if (value || op === 'notContains' || op === 'notMatch') { return { type: 'condition', field, op, processedValues: valueParser(op, value) }; } } return null; }\n        function getIndentation(line) { return line.search(/\\S|$/); }\n        function parseConditionsRecursive(lines, startIndex, currentIndentLevel) { let i = startIndex; const rootNode = { type: 'AND', children: [] }; let currentGroup = rootNode; while (i < lines.length) { const line = lines[i]; const indentation = getIndentation(line); const trimmedLine = line.trim(); if (!trimmedLine || trimmedLine.startsWith('#')) { i++; continue; } if (indentation < currentIndentLevel) break; if (indentation > currentIndentLevel) { const { node: nestedNode, lastIndex } = parseConditionsRecursive(lines, i, indentation); currentGroup.children.push(nestedNode); i = lastIndex + 1; continue; } if (trimmedLine.startsWith('或者')) { if (rootNode.type === 'AND') { const newOrNode = { type: 'OR', children: [...rootNode.children] }; rootNode.type = 'OR'; rootNode.children = [ { type: 'AND', children: newOrNode.children } ]; } currentGroup = { type: 'AND', children: [] }; rootNode.children.push(currentGroup); const condition = parseConditionLine(trimmedLine); if(condition) currentGroup.children.push(condition); } else if (trimmedLine.startsWith('(')) { const { node: nestedNode, lastIndex } = parseConditionsRecursive(lines, i + 1, indentation + 1); currentGroup.children.push(nestedNode); i = lastIndex; let closingParenIndex = i; for (let j = i; j < lines.length; j++) { if (getIndentation(lines[j]) === indentation && lines[j].trim().startsWith(')')) { closingParenIndex = j; break; } } i = closingParenIndex; } else { const condition = parseConditionLine(trimmedLine); if (condition) currentGroup.children.push(condition); } i++; } if (rootNode.children.length === 1 && rootNode.type === 'AND') { return { node: rootNode.children[0], lastIndex: i - 1 }; } return { node: rootNode, lastIndex: i - 1 }; }\n        function parseDSL(txt) { const lines = txt.split(/\\r?\\n/).map(line => line.replace(/\\t/g, '  ')); const rules = {}; let i = 0; while (i < lines.length) { const line = lines[i]; const trimmedLine = line.trim(); if (!trimmedLine || trimmedLine.startsWith('#')) { i++; continue; } const buttonMatch = trimmedLine.match(/^按钮\\s*:\\s*(.+)$/); if (buttonMatch) { const currentButtonName = buttonMatch[1].trim(); i++; if (i < lines.length) { const { node: ruleNode, lastIndex } = parseConditionsRecursive(lines, i, getIndentation(lines[i] || '  ')); rules[currentButtonName] = ruleNode; i = lastIndex + 1; } } else { i++; } } return rules; }\n        function matchRule(data, ruleNode) { if (!ruleNode) return false; if (ruleNode.type === 'condition') { const srcData = data[ruleNode.field]; if ((ruleNode.op === 'notContains' || ruleNode.op === 'notMatch') && ruleNode.processedValues.length === 0) { return true; } return matchCond(srcData, ruleNode.op, ruleNode.processedValues); } if (ruleNode.type === 'AND') { return ruleNode.children.every(child => matchRule(data, child)); } if (ruleNode.type === 'OR') { return ruleNode.children.some(child => matchRule(data, child)); } return false; }\n        const forwardRules = parseDSL(ruleText);\n        functionalLog('多层级DSL规则解析完成:', forwardRules);\n\n        function getSessionKey() { const h = document.querySelector('.chat__inbox_item_header_title'); return h ? h.textContent.trim() : 'unknown_session'; }\n        function getCustomerRemarkFromDOM() { const s = document.querySelector('div._ss_1zPZzbIj span[translate=\"translate\"]'); return s ? s.textContent.trim() : null; }\n        async function getStableCustomerRemark() { let remark = null, attempts = 0; const maxAttempts = 5, delay = 200; while (attempts < maxAttempts) { remark = getCustomerRemarkFromDOM(); if (remark !== null) return remark; attempts++; await sleep(delay); } functionalLog(`警告: 在 ${maxAttempts} 次尝试后仍未获取到备注信息。`, 'error'); return ''; }\n        function extractMessageData(allMessages) { if (!allMessages || allMessages.length === 0) return null; const structuredCustomerMessages = [], structuredServiceMessages = [], allStructuredMessages = []; allMessages.forEach(msgElement => { const contentWrapper = msgElement.querySelector('div._ss_210o5Mch'); if (!contentWrapper || msgElement.classList.contains('_ss_lqsE_fWp')) return; const originalTextSpan = contentWrapper.querySelector('div._ss_3qEoi54l > span'); let originalText = originalTextSpan ? originalTextSpan.textContent.trim() : ''; if (!originalText) { const imgElement = contentWrapper.querySelector('img._ss_7Pop0QdZ'); if (imgElement && imgElement.src) originalText = `[图片: ${imgElement.src}]`; } const translatedTextSpan = msgElement.querySelector('div._ss_1ZOfPKjz span'); const translatedText = translatedTextSpan ? translatedTextSpan.textContent.trim() : null; if (!originalText && !translatedText) return; const messageData = { original: originalText, translated: translatedText }; allStructuredMessages.push(messageData); if (contentWrapper.classList.contains('_ss_2IrYHHsj')) { structuredCustomerMessages.push(messageData); } else { structuredServiceMessages.push(messageData); } }); const lastMessageElement = Array.from(allMessages).reverse().find(el => el.querySelector('div._ss_210o5Mch') && !el.classList.contains('_ss_lqsE_fWp')); const isLastMessageFromCustomer = lastMessageElement ? lastMessageElement.querySelector('div._ss_210o5Mch').classList.contains('_ss_2IrYHHsj') : false; return { latestCustomerMessage: structuredCustomerMessages.length > 0 ? structuredCustomerMessages.at(-1) : null, customerHistory: structuredCustomerMessages.length > 1 ? structuredCustomerMessages.slice(0, -1) : [], latestServiceMessage: structuredServiceMessages.length > 0 ? structuredServiceMessages.at(-1) : null, serviceHistory: structuredServiceMessages.length > 1 ? structuredServiceMessages.slice(0, -1) : [], allHistory: allStructuredMessages, isLastMessageFromCustomer }; }\n        function logDebugInfo(msg, type = 'system') { if (!cachedElements.debugList) return; const li = document.createElement('li'); li.style.marginBottom = '3px'; let prefix = '[系统] ', color = 'gray'; if (type === 'customer') { prefix = '[客户] '; color = 'blue'; } else if (type === 'agent') { prefix = '[客服] '; color = 'green'; } else if (type === 'action') { prefix = '[动作] '; color = 'purple'; } else if (type === 'error') { prefix = '[错误] '; color = 'red'; } li.innerHTML = `<span style=\"color:${color}\">${prefix}</span>${msg}`; cachedElements.debugList.appendChild(li); if (cachedElements.debugList.children.length > 15) cachedElements.debugList.removeChild(cachedElements.debugList.children[0]); cachedElements.debugList.scrollTop = cachedElements.debugList.scrollHeight; }\n        function updateDataViewer() { const contentEl = document.getElementById('ooo_data_content'); if (contentEl) contentEl.textContent = JSON.stringify(currentSessionDebugData, null, 2); }\n        function makeDraggable(elmnt) { let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; const header = document.getElementById(elmnt.id + \"_header\"); if (header) header.onmousedown = dragMouseDown; else elmnt.onmousedown = dragMouseDown; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; elmnt.style.top = (elmnt.offsetTop - pos2) + \"px\"; elmnt.style.left = (elmnt.offsetLeft - pos1) + \"px\"; } function closeDragElement() { document.onmouseup = null; document.onmousemove = null; } }\n        function injectStyles(css) { const style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; document.head.appendChild(style); }\n        function ensureControlsExist() { if (!document.getElementById('ooo_debug_window')) { const menu = document.querySelector('ul.ivu-menu.ivu-menu-light.ivu-menu-vertical._ss_31U2pst2'); if (menu) { const win = document.createElement('li'); win.id = 'ooo_debug_window'; Object.assign(win.style, { padding: '8px', borderTop: '1px solid #eee', fontSize: '12px', whiteSpace: 'normal', wordBreak: 'break-word' }); win.innerHTML = '<strong>调试信息:</strong><ul id=\"ooo_debug_list\" style=\"list-style:none;padding:0 5px;margin:5px 0;\"></ul>'; menu.appendChild(win); menu.style.maxHeight = 'calc(100vh - 100px)'; menu.style.overflowY = 'auto'; cachedElements.debugList = document.getElementById('ooo_debug_list'); } } if (!document.getElementById('ooo_toggle_container')) { const ssPanel = document.querySelector('div.ss-panel.button-panel'); let targetPanel = ssPanel || document.getElementById('RightColumn'); if (targetPanel) { const c = document.createElement('div'); c.id = 'ooo_toggle_container'; Object.assign(c.style, { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', gap: '4px', padding: '4px', borderBottom: '1px solid #ddd', background: '#fafafa', fontSize: '12px' }); const makeBtn = (textOn, textOff, initial, callback) => { const btn = document.createElement('span'); btn.textContent = initial ? textOn : textOff; Object.assign(btn.style, { cursor: 'pointer', padding: '2px 6px', border: '1px solid #ccc', borderRadius: '3px', background: initial ? '#b6f1a2' : '', userSelect: 'none' }); btn.onclick = () => { const state = callback(); btn.textContent = state ? textOn : textOff; btn.style.background = state ? '#b6f1a2' : ''; }; return btn; }; const joinBtn = makeBtn('自动接入: 开', '自动接入: 关', autoJoinEnabled, () => { autoJoinEnabled = !autoJoinEnabled; logDebugInfo(`自动接入已${autoJoinEnabled ? '开启' : '关闭'}`, 'action'); return autoJoinEnabled; }); const replyBtn = makeBtn('自动回复: 开', '自动回复: 关', autoReplyEnabled, () => { autoReplyEnabled = !autoReplyEnabled; if (autoReplyEnabled) { sessionStates = {}; logDebugInfo('清空所有会话状态', 'system'); } logDebugInfo(`自动回复已${autoReplyEnabled ? '开启' : '关闭'}`, 'action'); return autoReplyEnabled; }); const viewDataBtn = document.createElement('span'); viewDataBtn.textContent = '查看数据'; Object.assign(viewDataBtn.style, { cursor: 'pointer', padding: '2px 6px', border: '1px solid #ccc', borderRadius: '3px', userSelect: 'none', background: '#f0f0f0' }); viewDataBtn.onclick = () => { const viewer = document.getElementById('ooo_data_viewer'); if (viewer) viewer.style.display = viewer.style.display === 'block' ? 'none' : 'block'; }; c.appendChild(joinBtn); c.appendChild(replyBtn); c.appendChild(viewDataBtn); targetPanel.prepend(c); cachedElements.toggleContainer = c; } else { functionalLog('警告: 未找到智能客服助手面板 (div.ss-panel.button-panel) 或 RightColumn 来放置控制按钮。', 'warn'); } } if (!document.getElementById('ooo_data_viewer')) { injectStyles(`#ooo_data_viewer { position: fixed; top: 150px; right: 50px; width: 400px; max-height: 60vh; background-color: #fff; border: 1px solid #ccc; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 9999; display: none; font-family: monospace; font-size: 12px; } #ooo_data_viewer_header { padding: 8px; cursor: move; z-index: 10000; background-color: #f7f7f7; color: #333; border-bottom: 1px solid #ddd; user-select: none; display: flex; justify-content: space-between; align-items: center; } #ooo_data_viewer_close { cursor: pointer; font-weight: bold; padding: 0 5px; } #ooo_data_content { white-space: pre-wrap; word-wrap: break-word; padding: 10px; overflow-y: auto; max-height: calc(60vh - 40px); }`); const viewer = document.createElement('div'); viewer.id = 'ooo_data_viewer'; viewer.innerHTML = `<div id=\"ooo_data_viewer_header\"><span>当前会话匹配数据</span><span id=\"ooo_data_viewer_close\" title=\"关闭\">✖</span></div><pre id=\"ooo_data_content\">{}</pre>`; document.body.appendChild(viewer); document.getElementById('ooo_data_viewer_close').onclick = () => { viewer.style.display = 'none'; }; makeDraggable(viewer); } }\n        function getReplyStatus() { const statusElement = document.getElementById('ss-reply-status'); if (!statusElement) { functionalLog('错误: 未找到智能客服助手状态指示器 (#ss-reply-status)。', 'error'); return 'status_element_missing'; } return statusElement.textContent.trim(); }\n        async function switchSession() { if (!cachedElements.selectUnreadSessionsBtn) { cachedElements.selectUnreadSessionsBtn = document.getElementById('selectUnreadSessionsBtn'); } const nxt = cachedElements.selectUnreadSessionsBtn; if (!nxt) { logDebugInfo('未找到切换会话按钮 (#selectUnreadSessionsBtn)。无法切换会话。', 'error'); return; } const startTime = Date.now(); let currentStatus = getReplyStatus(); while (currentStatus !== '空闲' && currentStatus !== 'status_element_missing') { if (Date.now() - startTime > REPLY_STATUS_WAIT_TIMEOUT) { logDebugInfo(`等待回复状态超时 (当前: ${currentStatus})，将尝试强制切换。`, 'error'); break; } await sleep(REPLY_STATUS_CHECK_INTERVAL); currentStatus = getReplyStatus(); } if (currentStatus === 'status_element_missing' || currentStatus !== '空闲') { logDebugInfo(`由于状态指示器未找到或未变为“空闲”状态 (当前: ${currentStatus})，取消切换会话。`, 'error'); return; } const oldSessionKey = getSessionKey(); currentSessionDebugData = {}; updateDataViewer(); logDebugInfo(`切换会话前，清除旧会话【${oldSessionKey}】数据。`, 'system'); nxt.click(); logDebugInfo('点击切换会话按钮。', 'action'); const switchWaitStartTime = Date.now(); let newSessionKey = getSessionKey(); while (newSessionKey === oldSessionKey && (Date.now() - switchWaitStartTime < SESSION_SWITCH_WAIT_TIMEOUT)) { await sleep(200); newSessionKey = getSessionKey(); } if (newSessionKey !== oldSessionKey) { logDebugInfo(`成功切换到新会话【${newSessionKey}】。`, 'system'); if (sessionStates[newSessionKey]) sessionStates[newSessionKey].historyFullyLoaded = false; } else { logDebugInfo('警告: 切换会话超时或未成功。', 'error'); } }\n        async function clickButton(name) { const buttonsContainer = document.querySelector('div.buttons-container'); if (!buttonsContainer) { logDebugInfo('错误: 未找到按钮容器 (div.buttons-container)。', 'error'); return false; } const btns = buttonsContainer.querySelectorAll('div.action-btn'); let targetSpanToClick = null; for (const b of btns) { const textSpan = Array.from(b.children).find(child => child.tagName === 'SPAN' && child.textContent.trim() === name); if (textSpan) { targetSpanToClick = textSpan; break; } } if (!targetSpanToClick) { logDebugInfo(`未找到【${name}】按钮。请检查按钮文本或DOM结构。`, 'error'); return false; } logDebugInfo(`等待 ${CLICK_DELAY_MS}ms 后触发【${name}】`, 'action'); await sleep(CLICK_DELAY_MS); targetSpanToClick.click(); logDebugInfo(`点击【${name}】`, 'action'); const initialMessageCount = document.querySelectorAll('div._ss_280NCnej').length; const startTime = Date.now(); let messageSent = false; while (Date.now() - startTime < SEND_STATUS_CHECK_TIMEOUT) { const currentMessages = document.querySelectorAll('div._ss_280NCnej'); if (currentMessages.length > initialMessageCount) { const newMessageElement = currentMessages[currentMessages.length - 1]; const senderType = newMessageElement.querySelector('[sendertype]'); if (senderType && senderType.getAttribute('sendertype') === '2' && !newMessageElement.querySelector('i.ivu-icon.ivu-icon-ios-loading')) { messageSent = true; break; } } await sleep(SEND_STATUS_CHECK_INTERVAL); } if (messageSent) { logDebugInfo('消息发送成功。', 'system'); return true; } logDebugInfo('消息发送状态检测超时或失败。', 'error'); return false; }\n        async function activateSessionButton() { if (isProcessingTask || !autoJoinEnabled) return; isProcessingTask = true; try { const btn = document.querySelector('button.ivu-btn-primary.ivu-btn-large'); if (!btn) return; btn.click(); logDebugInfo('点击接入', 'action'); } finally { isProcessingTask = false; } }\n        async function loadAllHistory() { if (!cachedElements.chatContainer) cachedElements.chatContainer = document.querySelector('div.ivu-scroll-wrapper._ss_2Kg3oju4.chat_list__scroll'); const chatContainer = cachedElements.chatContainer; if (!chatContainer) { logDebugInfo('聊天容器未找到。', 'error'); return false; } let attempts = 0; logDebugInfo('开始加载历史消息...', 'system'); while (attempts < HISTORY_LOAD_MAX_ATTEMPTS) { const countBefore = document.querySelectorAll('div._ss_280NCnej').length; chatContainer.scrollTop = 0; await sleep(HISTORY_LOAD_SCROLL_DELAY_MS); const countAfter = document.querySelectorAll('div._ss_280NCnej').length; if (countAfter > countBefore) { logDebugInfo(`加载到 ${countAfter - countBefore} 条新历史`, 'system'); attempts = 0; } else { attempts++; if (chatContainer.scrollTop === 0) { logDebugInfo('已滚动到顶部，历史加载完成。', 'system'); break; } } } return true; }\n        async function processSessionMessage() {\n            if (isProcessingTask) return;\n            isProcessingTask = true;\n            const currentSessionKey = getSessionKey();\n            let sessionDataToLog = { timestamp: new Date().toISOString(), sessionKey: currentSessionKey, actionTaken: 'none', status: 'processed' };\n            try {\n                ensureControlsExist();\n                if (lastProcessedSessionKey !== currentSessionKey) { logDebugInfo(`检测到会话切换至【${currentSessionKey}】。`, 'system'); if (sessionStates[currentSessionKey]) sessionStates[currentSessionKey].historyFullyLoaded = false; currentSessionDebugData = {}; updateDataViewer(); }\n                lastProcessedSessionKey = currentSessionKey;\n                let st = sessionStates[currentSessionKey];\n                if (!st) { st = sessionStates[currentSessionKey] = { lastProcessedCustomerMessageKey: null, historyFullyLoaded: false }; }\n                if (!autoReplyEnabled) { sessionDataToLog.status = 'skipped_auto_reply_off'; return; }\n                if (!st.historyFullyLoaded) { if (!await loadAllHistory()) { if (autoReplyEnabled) await switchSession(); sessionDataToLog.status = 'skipped_history_load_fail'; return; } st.historyFullyLoaded = true; }\n                const remark = await getStableCustomerRemark();\n                const allMessages = document.querySelectorAll('div._ss_280NCnej');\n                const data = extractMessageData(allMessages);\n                currentSessionDebugData = { remark: remark, latestCustomerMessage: data ? data.latestCustomerMessage : null, customerHistory: data ? data.customerHistory : [], latestServiceMessage: data ? data.latestServiceMessage : null, serviceHistory: data ? data.serviceHistory : [], allHistory: data ? data.allHistory : [] };\n                updateDataViewer();\n                sessionDataToLog.remark = remark;\n                sessionDataToLog.latestCustomerMessage = currentSessionDebugData.latestCustomerMessage;\n                if (!data || !data.latestCustomerMessage || !data.isLastMessageFromCustomer) { if (autoReplyEnabled) await switchSession(); sessionDataToLog.status = data ? 'skipped_agent_last_message' : 'skipped_no_customer_message'; return; }\n                const currentCustomerMessageKey = (data.latestCustomerMessage.original || '') + (data.latestCustomerMessage.translated || '');\n                if (st.lastProcessedCustomerMessageKey === currentCustomerMessageKey) { sessionDataToLog.status = 'skipped_already_processed'; return; }\n                logDebugInfo(`新客户消息: ${data.latestCustomerMessage.original}`, 'customer');\n                let acted = false;\n                for (const [btnName, ruleNode] of Object.entries(forwardRules)) {\n                    if (matchRule(currentSessionDebugData, ruleNode)) {\n                        logDebugInfo(`规则匹配成功: 按钮【${btnName}】`, 'system');\n                        if (await clickButton(btnName)) { acted = true; sessionDataToLog.actionTaken = 'button_clicked'; sessionDataToLog.triggeredButton = btnName; }\n                        break;\n                    }\n                }\n                if (acted) {\n                    st.lastProcessedCustomerMessageKey = currentCustomerMessageKey;\n                    if (autoReplyEnabled) {\n                        await sleep(500);\n                        const updatedData = extractMessageData(document.querySelectorAll('div._ss_280NCnej'));\n                        if (updatedData && !updatedData.isLastMessageFromCustomer) { logDebugInfo('最新消息来自客服，安全切换会话。', 'system'); await sleep(POST_REPLY_SWITCH_DELAY_MS); await switchSession(); }\n                        else { logDebugInfo('客户可能有新回复，取消切换。', 'customer'); st.lastProcessedCustomerMessageKey = null; }\n                    }\n                } else { logDebugInfo('无匹配规则。', 'system'); sessionDataToLog.actionTaken = 'no_rule_matched'; }\n            } catch (e) {\n                logDebugInfo('处理会话消息时发生错误: ' + e.message, 'error');\n                console.error(e);\n                sessionDataToLog.status = 'error';\n                sessionDataToLog.errorMessage = e.message;\n            } finally {\n                isProcessingTask = false;\n                sendDataToServer(sessionDataToLog);\n            }\n        }\n        function init() { ensureControlsExist(); setInterval(() => { if (!isProcessingTask) activateSessionButton(); }, 1000); setInterval(() => { if (!isProcessingTask) processSessionMessage(); }, MESSAGE_CHECK_INTERVAL); logDebugInfo('脚本已启动 (v5.7.0 - 集成多层级嵌套DSL)', 'system'); logDebugInfo('DSL解析器已升级，支持复杂的嵌套逻辑。', 'system'); logDebugInfo(`发送回复后，切换会话前的延时设置为 ${POST_REPLY_SWITCH_DELAY_MS}ms。`, 'system'); }\n        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();\n        // --- END: v5.7.0 脚本完整代码 ---\n    }\n\n})();","functional_script_code_hash":"362b6790c179a252a8f90b069730ca3974650b0d4c53ca718cc1a27e1cbc03c1","loader_script_hash":"2ff0375e049e25dcd83912222d9f5446a65aec601d90d89c91a52b7c9a25906e","config":{"feature_x_enabled":true,"keyword_list":["example","test"],"salesmartly_api_key":"YOUR_SALESMARTLY_API_KEY_IF_NEEDED"}}