Files
YuerKIng/BIN文件上传提取Token工具-0.5.user.js

507 lines
21 KiB
JavaScript
Raw Permalink Normal View History

2025-10-17 21:29:39 +08:00
// ==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);
})();