// ==UserScript==
// @name         BIN文件上传提取Token工具
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  上传BIN文件提取RoleToken并生成WSS链接
// @author       豆包编程助手
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// ==/UserScript==

(function() {
    'use strict';

    // 界面状态变量
    let toolContainer = null;
    let isToolVisible = false;

    // 创建工具界面
    function createToolUI() {
        // 检查是否已存在界面
        if (document.getElementById('bin-token-extractor')) {
            toolContainer = document.getElementById('bin-token-extractor');
            return toolContainer;
        }

        // 创建容器（固定宽度380px）
        const container = document.createElement('div');
        container.id = 'bin-token-extractor';
        container.style.cssText = `
            position: fixed;
            top: 50%;
            right: 20px;
            transform: translateY(-50%);
            background: linear-gradient(180deg, #ffffff 0%, #f9fbfd 100%);
            border-radius: 16px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.12);
            width: 380px;
            max-height: 80vh;
            overflow-y: auto;
            padding: 25px;
            z-index: 99999;
            display: none; /* 默认隐藏 */
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        `;

        // 优化后的HTML结构
        container.innerHTML = `
            <div style="text-align: center; margin-bottom: 15px;">
                <h2 style="margin: 0 0 10px 0; color: #2c3e50; font-size: 18px; font-weight: 600;">BIN文件Token提取工具</h2>
                <p style="margin: 0; color: #7f8c8d; font-size: 13px;">上传文件，提取RoleToken并生成WSS链接</p>
            </div>

            <div id="uploadArea" style="border: 2px dashed #d1d9e6; border-radius: 12px; padding: 30px 15px; margin-bottom: 20px; cursor: pointer; transition: all 0.3s; background-color: #f8fafc; position: relative;">
                <div style="font-size: 48px; color: #3b82f6; margin-bottom: 12px;">📂</div>
                <p style="font-size: 16px; color: #334155; margin-bottom: 5px; font-weight: 500;">点击或拖放BIN文件</p>
                <p style="font-size: 12px; color: #94a3b8; margin: 0;">仅支持 .bin 格式文件</p>
                <input type="file" id="fileInput" style="position: absolute; width: 100%; height: 100%; top: 0; left: 0; opacity: 0; cursor: pointer;" accept=".bin">
            </div>

            <div id="fileInfo" style="margin-top: 10px; padding: 10px; background-color: #eff6ff; border-radius: 8px; font-size: 13px; color: #3b82f6; display: none; border-left: 3px solid #3b82f6;"></div>

            <div id="progressContainer" style="margin: 15px 0; display: none;">
                <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
                    <span style="font-size: 12px; color: #64748b; font-weight: 500;">上传进度</span>
                    <span id="progressText" style="font-size: 12px; color: #64748b;">0%</span>
                </div>
                <div style="height: 6px; background-color: #e2e8f0; border-radius: 3px; overflow: hidden;">
                    <div id="progressBar" style="height: 100%; background: linear-gradient(90deg, #3b82f6 0%, #60a5fa 100%); width: 0%; transition: width 0.4s ease;"></div>
                </div>
            </div>

            <div id="statusMessage" style="padding: 10px; border-radius: 8px; margin: 10px 0; text-align: center; font-weight: 500; display: none; font-size: 13px; transition: all 0.3s;"></div>

            <button id="uploadBtn" style="background: linear-gradient(90deg, #3b82f6 0%, #60a5fa 100%); color: white; border: none; padding: 12px 15px; width: 100%; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all 0.3s; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3); margin-top: 5px; opacity: 0.7; transform: translateY(2px);" disabled>上传并提取Token</button>

            <div id="resultContainer" style="margin-top: 20px; padding: 18px; background-color: #f8fafc; border-radius: 12px; text-align: left; display: none; border: 1px solid #e2e8f0; transform: translateY(10px); opacity: 0; transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);">
                <h3 style="color: #1e40af; margin: 0 0 12px 0; font-size: 15px; display: flex; align-items: center;">
                    <span style="margin-right: 8px;">🔗</span>完整WebSocket链接
                </h3>
                <div id="wssLinkDisplay" style="word-break: break-all; font-family: monospace; font-size: 12px; color: #1e293b; line-height: 1.6; padding: 12px; background-color: white; border-radius: 8px; border: 1px solid #e2e8f0; margin-top: 5px; max-height: 120px; overflow-y: auto;"></div>
                <button id="copyWssLinkBtn" style="background: #10b981; color: white; border: none; padding: 7px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; transition: all 0.3s; margin-top: 10px; box-shadow: 0 2px 6px rgba(16, 185, 129, 0.25);">
                    <span style="margin-right: 5px;">📋</span>复制WSS链接
                </button>
            </div>
        `;

        document.body.appendChild(container);
        toolContainer = container;
        return container;
    }

    // 切换工具显示/隐藏状态
    function toggleTool() {
        if (!toolContainer) {
            createToolUI();
        }

        isToolVisible = !isToolVisible;

        if (isToolVisible) {
            // 显示工具并添加淡入动画
            toolContainer.style.display = 'block';
            setTimeout(() => {
                toolContainer.style.opacity = '1';
                toolContainer.style.transform = 'translateY(-50%) scale(1)';
            }, 10);
            // 初始化工具功能
            initToolFunctions();
        } else {
            // 添加淡出动画后隐藏
            toolContainer.style.opacity = '0';
            toolContainer.style.transform = 'translateY(-50%) scale(0.95)';
            setTimeout(() => {
                toolContainer.style.display = 'none';
            }, 300);
        }
    }

    // 初始化工具功能
    function initToolFunctions() {
        if (!toolContainer) return;

        // 获取DOM元素
        const uploadArea = toolContainer.querySelector('#uploadArea');
        const fileInput = toolContainer.querySelector('#fileInput');
        const fileInfo = toolContainer.querySelector('#fileInfo');
        const uploadBtn = toolContainer.querySelector('#uploadBtn');
        const progressContainer = toolContainer.querySelector('#progressContainer');
        const progressBar = toolContainer.querySelector('#progressBar');
        const progressText = toolContainer.querySelector('#progressText');
        const statusMessage = toolContainer.querySelector('#statusMessage');
        const resultContainer = toolContainer.querySelector('#resultContainer');
        const wssLinkDisplay = toolContainer.querySelector('#wssLinkDisplay');
        const copyWssLinkBtn = toolContainer.querySelector('#copyWssLinkBtn');

        let selectedFile = null;
        let extractedToken = null;

        // 点击上传区域触发文件选择
        uploadArea.addEventListener('click', function(e) {
            if (e.target !== fileInput) {
                fileInput.click();
            }
        });

        // 拖放功能
        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            uploadArea.addEventListener(eventName, preventDefaults, false);
        });

        function preventDefaults(e) {
            e.preventDefault();
            e.stopPropagation();
        }

        ['dragenter', 'dragover'].forEach(eventName => {
            uploadArea.addEventListener(eventName, highlight, false);
        });

        ['dragleave', 'drop'].forEach(eventName => {
            uploadArea.addEventListener(eventName, unhighlight, false);
        });

        function highlight() {
            uploadArea.style.borderColor = '#3b82f6';
            uploadArea.style.backgroundColor = 'rgba(59, 130, 246, 0.05)';
            uploadArea.style.transform = 'scale(1.02)';
        }

        function unhighlight() {
            uploadArea.style.borderColor = '#d1d9e6';
            uploadArea.style.backgroundColor = '#f8fafc';
            uploadArea.style.transform = 'scale(1)';
        }

        // 文件拖放处理
        uploadArea.addEventListener('drop', handleDrop, false);

        function handleDrop(e) {
            const dt = e.dataTransfer;
            const files = dt.files;

            if (files.length) {
                handleFiles(files);
            }
        }

        // 文件选择处理
        fileInput.addEventListener('change', function() {
            if (this.files.length) {
                handleFiles(this.files);
            }
        });

        function handleFiles(files) {
            const file = files[0];

            // 检查文件类型
            if (!file.name.toLowerCase().endsWith('.bin')) {
                showStatus('请选择.bin格式的文件', 'error');
                return;
            }

            selectedFile = file;
            updateFileInfo(file);
            uploadBtn.disabled = false;
            uploadBtn.style.opacity = '1';
            uploadBtn.style.transform = 'translateY(0)';
        }

        function updateFileInfo(file) {
            fileInfo.textContent = `已选择: ${file.name} (${formatFileSize(file.size)})`;
            fileInfo.style.display = 'block';
            // 添加淡入动画
            fileInfo.style.opacity = '0';
            setTimeout(() => {
                fileInfo.style.opacity = '1';
            }, 10);
        }

        function formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }

        // 上传按钮点击事件
        uploadBtn.addEventListener('click', function() {
            if (!selectedFile) {
                showStatus('请先选择文件', 'error');
                return;
            }

            // 禁用上传按钮，防止重复点击
            uploadBtn.disabled = true;
            uploadBtn.style.opacity = '0.7';
            uploadBtn.style.transform = 'translateY(2px)';

            // 隐藏之前的结果
            resultContainer.style.display = 'none';

            // 显示进度条
            progressContainer.style.display = 'block';
            progressBar.style.width = '0%';
            progressText.textContent = '0%';

            // 直接上传文件
            uploadFile(selectedFile);
        });

        function uploadFile(file) {
            showStatus('正在上传文件...', '');

            // 读取文件内容
            const reader = new FileReader();
            reader.onload = function(e) {
                const arrayBuffer = e.target.result;

                // 配置请求
                GM_xmlhttpRequest({
                    method: 'POST',
                    url: 'https://xxz-xyzw.hortorgames.com/login/authuser?_seq=1',
                    data: arrayBuffer,
                    headers: {
                        'Content-Type': 'application/octet-stream'
                    },
                    responseType: 'arraybuffer',
                    onprogress: function(e) {
                        if (e.lengthComputable) {
                            const percentComplete = (e.loaded / e.total) * 100;
                            progressBar.style.width = percentComplete + '%';
                            progressText.textContent = Math.round(percentComplete) + '%';
                        }
                    },
                    onload: function(response) {
                        if (response.status >= 200 && response.status < 300) {
                            try {
                                // 处理二进制响应
                                const arrayBuffer = response.response;
                                if (arrayBuffer) {
                                    // 提取RoleToken
                                    extractRoleToken(arrayBuffer);
                                    showStatus('Token提取成功！', 'success');
                                } else {
                                    showStatus('上传成功，但响应为空', 'error');
                                }
                            } catch (e) {
                                showStatus('处理响应时出错: ' + e.message, 'error');
                            }
                        } else {
                            showStatus('上传失败: ' + response.statusText, 'error');
                        }

                        // 重新启用上传按钮
                        uploadBtn.disabled = false;
                        uploadBtn.style.opacity = '1';
                        uploadBtn.style.transform = 'translateY(0)';
                    },
                    onerror: function() {
                        showStatus('上传过程中发生错误', 'error');
                        uploadBtn.disabled = false;
                        uploadBtn.style.opacity = '1';
                        uploadBtn.style.transform = 'translateY(0)';
                    }
                });
            };

            reader.onerror = function() {
                showStatus('读取文件失败', 'error');
                uploadBtn.disabled = false;
                uploadBtn.style.opacity = '1';
                uploadBtn.style.transform = 'translateY(0)';
            };

            reader.readAsArrayBuffer(file);
        }

        function extractRoleToken(arrayBuffer) {
            try {
                // 将ArrayBuffer转换为Uint8Array以便处理
                const bytes = new Uint8Array(arrayBuffer);

                // 转换为ASCII字符串以便搜索
                let asciiString = '';
                for (let i = 0; i < bytes.length; i++) {
                    // 只转换可打印的ASCII字符（32-126）
                    if (bytes[i] >= 32 && bytes[i] <= 126) {
                        asciiString += String.fromCharCode(bytes[i]);
                    } else {
                        asciiString += '.'; // 用点号表示不可打印字符
                    }
                }

                // 搜索Token的位置 - 查找 "Token" 字符串
                const tokenIndex = asciiString.indexOf('Token');

                if (tokenIndex !== -1) {
                    // 找到Token标记，提取Token值
                    let tokenStart = tokenIndex + 5; // "Token"长度为5

                    // 跳过可能的非Base64字符，直到找到Base64字符
                    while (tokenStart < asciiString.length) {
                        const char = asciiString[tokenStart];
                        if (isBase64Char(char)) {
                            break;
                        }
                        tokenStart++;
                    }

                    // 提取Base64 Token
                    let tokenEnd = tokenStart;
                    while (tokenEnd < asciiString.length && isBase64Char(asciiString[tokenEnd])) {
                        tokenEnd++;
                    }

                    const tokenValue = asciiString.substring(tokenStart, tokenEnd);

                    if (tokenValue.length > 0) {
                        extractedToken = tokenValue;
                        resultContainer.style.display = 'block';

                        // 触发动画
                        setTimeout(() => {
                            resultContainer.style.transform = 'translateY(0)';
                            resultContainer.style.opacity = '1';
                        }, 10);

                        // 生成并显示完整的WSS链接
                        generateAndDisplayWssLink(extractedToken);

                        // 平滑滚动到结果区域
                        resultContainer.scrollIntoView({ behavior: 'smooth' });
                    } else {
                        showStatus('找到Token标记但未找到Token值', 'error');
                    }
                } else {
                    showStatus('在响应中未找到Token标记', 'error');
                }
            } catch (error) {
                showStatus('提取Token时发生错误: ' + error.message, 'error');
            }
        }

        function isBase64Char(char) {
            // Base64字符集: A-Z, a-z, 0-9, +, /, =
            return /[A-Za-z0-9+/=]/.test(char);
        }

        function showStatus(message, type) {
            statusMessage.textContent = message;
            statusMessage.className = '';
            statusMessage.style.backgroundColor = '';
            statusMessage.style.color = '';
            statusMessage.style.boxShadow = 'none';

            if (type === 'success') {
                statusMessage.style.backgroundColor = 'rgba(16, 185, 129, 0.1)';
                statusMessage.style.color = '#059669';
                statusMessage.style.borderLeft = '3px solid #10b981';
            } else if (type === 'error') {
                statusMessage.style.backgroundColor = 'rgba(239, 68, 68, 0.1)';
                statusMessage.style.color = '#dc2626';
                statusMessage.style.borderLeft = '3px solid #ef4444';
            } else {
                statusMessage.style.backgroundColor = 'rgba(59, 130, 246, 0.1)';
                statusMessage.style.color = '#2563eb';
                statusMessage.style.borderLeft = '3px solid #3b82f6';
            }

            statusMessage.style.display = 'block';
            statusMessage.style.opacity = '0';
            statusMessage.style.transform = 'translateY(10px)';

            // 触发淡入动画
            setTimeout(() => {
                statusMessage.style.opacity = '1';
                statusMessage.style.transform = 'translateY(0)';
            }, 10);

            // 3秒后自动隐藏非错误状态
            if (type !== 'error') {
                setTimeout(() => {
                    statusMessage.style.opacity = '0';
                    statusMessage.style.transform = 'translateY(10px)';
                    setTimeout(() => {
                        statusMessage.style.display = 'none';
                    }, 300);
                }, 3000);
            }
        }

        // 复制完整WSS链接按钮事件
        copyWssLinkBtn.addEventListener('click', function() {
            const wssLink = wssLinkDisplay.textContent;

            if (!wssLink) return;

            GM_setClipboard(wssLink);
            // 按钮点击反馈
            this.style.backgroundColor = '#059669';
            setTimeout(() => {
                this.style.backgroundColor = '#10b981';
            }, 200);
            showStatus('WSS链接已复制', 'success');
        });

        // 生成并显示完整的WSS链接
        function generateAndDisplayWssLink(token) {
            // 生成随机的会话ID和连接ID
            const currentTime = Date.now();
            const sessId = currentTime * 100 + Math.floor(Math.random() * 100);
            const connId = currentTime + Math.floor(Math.random() * 10);

            // 构建WebSocket参数
            const wssParams = `{"roleToken":"${token}","sessId":${sessId},"connId":${connId},"isRestore":0}`;

            // 显示完整的WSS链接参数
            wssLinkDisplay.textContent = wssParams;
        }
    }

    // 创建切换按钮（兼具显示和关闭功能）
    function createToggleButton() {
        // 创建按钮
        const toggleBtn = document.createElement('button');
        toggleBtn.innerHTML = `<span style="margin-right: 6px;">🔑</span>BIN Token提取`;
        toggleBtn.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: linear-gradient(90deg, #3b82f6 0%, #60a5fa 100%);
            color: white;
            border: none;
            padding: 10px 18px;
            border-radius: 50px;
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
            box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3);
            white-space: nowrap;
            z-index: 99998;
            transition: all 0.3s;
        `;

        // 添加悬停效果
        toggleBtn.addEventListener('mouseenter', () => {
            toggleBtn.style.transform = 'translateY(-2px)';
            toggleBtn.style.boxShadow = '0 6px 20px rgba(59, 130, 246, 0.4)';
        });

        toggleBtn.addEventListener('mouseleave', () => {
            toggleBtn.style.transform = 'translateY(0)';
            toggleBtn.style.boxShadow = '0 4px 15px rgba(59, 130, 246, 0.3)';
        });

        // 添加点击事件 - 切换显示/隐藏
        toggleBtn.addEventListener('click', toggleTool);

        document.body.appendChild(toggleBtn);
    }

    // 页面加载完成后创建切换按钮
    window.addEventListener('load', createToggleButton);
})();