507 lines
21 KiB
JavaScript
507 lines
21 KiB
JavaScript
|
|
// ==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);
|
|||
|
|
})();
|