Files
xyzw_web_helper/BIN文件上传提取Token工具-0.5.user.js
2025-10-17 20:56:50 +08:00

507 lines
21 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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