mirror of
https://github.com/violettoolssite/CFspider.git
synced 2026-04-05 11:29:03 +08:00
添加混淆加密的workers
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,6 +15,7 @@ edgetunnel_proxy.py
|
||||
test.py
|
||||
test_*.py
|
||||
*.html
|
||||
!x27cn-pages/index.html
|
||||
|
||||
# 镜像输出目录
|
||||
mirror/
|
||||
|
||||
646
x27cn-pages/index.html
Normal file
646
x27cn-pages/index.html
Normal file
@@ -0,0 +1,646 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>CFspider X27CN Decrypt - 专用加密解密工具</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #e94560;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #888;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
color: #fff;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
textarea, input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border: 2px solid rgba(233, 69, 96, 0.3);
|
||||
border-radius: 10px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-family: 'Consolas', monospace;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
textarea:focus, input:focus {
|
||||
outline: none;
|
||||
border-color: #e94560;
|
||||
box-shadow: 0 0 15px rgba(233, 69, 96, 0.3);
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 180px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #e94560, #f45c73);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 20px rgba(233, 69, 96, 0.4);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 25px;
|
||||
padding: 20px;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.result.show {
|
||||
display: block;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.result-title {
|
||||
color: #4ade80;
|
||||
margin-bottom: 15px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
word-break: break-all;
|
||||
font-family: 'Consolas', monospace;
|
||||
color: #4ade80;
|
||||
font-size: 13px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.field-row {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.field-row:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.field-key {
|
||||
color: #60a5fa;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.field-value {
|
||||
color: #4ade80;
|
||||
word-break: break-all;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.field-value:hover {
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
.vless-link {
|
||||
color: #fbbf24;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.copy-success {
|
||||
color: #4ade80;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.copy-success.show {
|
||||
display: block;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: rgba(96, 165, 250, 0.1);
|
||||
border: 1px solid rgba(96, 165, 250, 0.3);
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
color: #93c5fd;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.info-box code {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: 'Consolas', monospace;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.json-view {
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
margin-top: 15px;
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Consolas', monospace;
|
||||
font-size: 12px;
|
||||
color: #e2e8f0;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.json-key { color: #60a5fa; }
|
||||
.json-string { color: #4ade80; }
|
||||
.json-number { color: #fbbf24; }
|
||||
.json-boolean { color: #f472b6; }
|
||||
.json-null { color: #9ca3af; }
|
||||
|
||||
.tab-group {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 8px 16px;
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background: #e94560;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab:hover:not(.active) {
|
||||
background: rgba(255,255,255,0.2);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>CFspider X27CN Decrypt</h1>
|
||||
<p class="subtitle">CFspider 项目专用加密解密工具 v2.0 - 支持智能识别多种格式</p>
|
||||
|
||||
<div class="info-box">
|
||||
<strong>CFspider 项目专用解密工具</strong><br><br>
|
||||
本工具用于解密 CFspider Workers 返回的 X27CN 加密响应。<br><br>
|
||||
<strong>支持的格式:</strong><br>
|
||||
• 纯十六进制 X27CN 加密字符串(新版)<br>
|
||||
• 带颜文字/生僻字的混淆格式(旧版)<br>
|
||||
• JSON 格式 {"d":"..."} 包装的加密数据
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>粘贴 Workers 响应 (任何格式均可)</label>
|
||||
<textarea id="encryptedData" placeholder="直接粘贴从 Workers 获取的任何响应...
|
||||
|
||||
示例1 (加密字符串):
|
||||
(◕‿◕)龖7f8a2b3c龘d5e6f7a8(◠‿◠)靐b9c0d1e2...
|
||||
|
||||
示例2 (旧版JSON):
|
||||
{"s":"ok","d":"7f8a2b3c..."}
|
||||
|
||||
直接粘贴,自动识别!"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>解密密钥 (Key) - 默认无需修改</label>
|
||||
<input type="text" id="decryptKey" value="x27cn2026" placeholder="x27cn2026">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="decrypt()">Decrypt</button>
|
||||
|
||||
<div class="result" id="result">
|
||||
<div class="result-title">Decrypted Successfully</div>
|
||||
|
||||
<div class="tab-group">
|
||||
<button class="tab active" onclick="showTab('formatted')">格式化视图</button>
|
||||
<button class="tab" onclick="showTab('json')">JSON 视图</button>
|
||||
<button class="tab" onclick="showTab('raw')">原始数据</button>
|
||||
</div>
|
||||
|
||||
<div id="tab-formatted" class="result-content"></div>
|
||||
<div id="tab-json" class="json-view" style="display:none;"></div>
|
||||
<div id="tab-raw" class="result-content" style="display:none;"></div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-secondary" onclick="copyVless()">Copy VLESS</button>
|
||||
<button class="btn btn-secondary" onclick="copyUUID()">Copy UUID</button>
|
||||
<button class="btn btn-secondary" onclick="copyJson()">Copy JSON</button>
|
||||
</div>
|
||||
<div class="copy-success" id="copySuccess">Copied!</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.1); text-align: center; color: #666; font-size: 12px;">
|
||||
<p><strong>CFspider</strong> - Cloudflare Workers Proxy Solution</p>
|
||||
<p>X27CN 加密算法 · XOR + 位旋转 + 颜文字混淆</p>
|
||||
<p style="margin-top: 10px; color: #888;">© 2026 CFspider Project · Apache 2.0 License</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const X27CN_KEY = 'x27cn2026';
|
||||
|
||||
// 颜文字和中文乱码列表
|
||||
const KAOMOJI = ['(◕‿◕)','(。◕‿◕。)','(◠‿◠)','(✿◠‿◠)','(◡‿◡)','(◕ᴗ◕)','(◔‿◔)','(✧ω✧)','(◕◡◕)','(◠ᴗ◠)','♪(´▽`)','(◕‿◕✿)','(✿╹◡╹)','(◕ˇ∀ˇ◕)','(◔◡◔)'];
|
||||
const CHAOS = ['龖','龘','靐','齉','齾','爨','灪','麤','鱻','驫','骉','羴','猋','蟲','贔','矗','飝','厵','靇','雥'];
|
||||
|
||||
let decryptedData = null;
|
||||
let decryptedJson = null;
|
||||
|
||||
// 清理加密字符串 - 支持纯hex和混淆格式
|
||||
function cleanEncryptedString(str) {
|
||||
console.log('[清理] 输入长度:', str.length);
|
||||
|
||||
// 检查是否为纯hex格式(新版Worker)
|
||||
if (/^[0-9a-fA-F]+$/.test(str.trim())) {
|
||||
console.log('[清理] 检测到纯hex格式');
|
||||
return str.trim();
|
||||
}
|
||||
|
||||
// 移除所有非hex字符(适用于混淆格式和乱码格式)
|
||||
let result = str.replace(/[^0-9a-fA-F]/g, '');
|
||||
console.log('[清理] 清理后长度:', result.length);
|
||||
|
||||
// 确保长度是偶数
|
||||
if (result.length % 2 !== 0) {
|
||||
result = result.substring(0, result.length - 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// x27cn 解密 - 增强版
|
||||
function x27cnDecrypt(encryptedStr, key = X27CN_KEY) {
|
||||
if (!encryptedStr) {
|
||||
console.log('[x27cn] 错误: 输入为空');
|
||||
return null;
|
||||
}
|
||||
|
||||
const hex = cleanEncryptedString(encryptedStr);
|
||||
console.log(`[x27cn] 清理后十六进制长度: ${hex.length}, 前32字符: ${hex.substring(0, 32)}`);
|
||||
|
||||
if (hex.length === 0) {
|
||||
console.log('[x27cn] 错误: 清理后没有有效的十六进制字符');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果长度是奇数,尝试移除末尾字符
|
||||
let finalHex = hex;
|
||||
if (hex.length % 2 !== 0) {
|
||||
console.log('[x27cn] 警告: 十六进制长度为奇数,尝试修复');
|
||||
finalHex = hex.substring(0, hex.length - 1);
|
||||
}
|
||||
|
||||
if (finalHex.length < 4) {
|
||||
console.log('[x27cn] 错误: 数据太短');
|
||||
return null;
|
||||
}
|
||||
|
||||
const keyBytes = new TextEncoder().encode(key);
|
||||
const bytes = new Uint8Array(finalHex.length / 2);
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
bytes[i] = parseInt(finalHex.substr(i * 2, 2), 16);
|
||||
}
|
||||
|
||||
const result = new Uint8Array(bytes.length);
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
let b = bytes[i];
|
||||
b = (b - i + 256) & 0xFF;
|
||||
b = ((b >> 3) | (b << 5)) & 0xFF;
|
||||
b = b ^ keyBytes[i % keyBytes.length];
|
||||
result[i] = b;
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = new TextDecoder('utf-8', { fatal: false }).decode(result);
|
||||
console.log(`[x27cn] 解密成功,长度: ${decoded.length}, 前50字符: ${decoded.substring(0, 50)}`);
|
||||
return decoded;
|
||||
} catch (e) {
|
||||
console.log('[x27cn] UTF-8解码失败,尝试其他编码');
|
||||
return new TextDecoder('utf-8').decode(result);
|
||||
}
|
||||
}
|
||||
|
||||
// 智能提取加密数据
|
||||
function extractEncryptedData(input) {
|
||||
input = input.trim();
|
||||
|
||||
// 1. 尝试解析为JSON,提取d字段
|
||||
try {
|
||||
const json = JSON.parse(input);
|
||||
if (json.d) return json.d;
|
||||
if (json.data) return json.data;
|
||||
// 如果整个JSON就是加密后的内容(不太可能)
|
||||
} catch (e) {}
|
||||
|
||||
// 2. 检查是否包含JSON中的d字段
|
||||
const dMatch = input.match(/"d"\s*:\s*"([^"]+)"/);
|
||||
if (dMatch) return dMatch[1];
|
||||
|
||||
const dataMatch = input.match(/"data"\s*:\s*"([^"]+)"/);
|
||||
if (dataMatch) return dataMatch[1];
|
||||
|
||||
// 3. 查找包含颜文字的加密字符串
|
||||
for (const k of KAOMOJI) {
|
||||
if (input.includes(k)) {
|
||||
// 找到颜文字,整个输入就是加密字符串
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 查找包含生僻字的加密字符串
|
||||
for (const c of CHAOS) {
|
||||
if (input.includes(c)) {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 检查是否是纯十六进制
|
||||
const hexOnly = input.replace(/\s/g, '');
|
||||
if (/^[0-9a-fA-F]+$/.test(hexOnly) && hexOnly.length >= 20) {
|
||||
return hexOnly;
|
||||
}
|
||||
|
||||
// 6. 尝试提取任何看起来像加密数据的部分
|
||||
const hexMatch = input.match(/[0-9a-fA-F]{20,}/);
|
||||
if (hexMatch) return hexMatch[0];
|
||||
|
||||
// 7. 返回原始输入
|
||||
return input;
|
||||
}
|
||||
|
||||
// JSON 语法高亮
|
||||
function syntaxHighlight(json) {
|
||||
if (typeof json !== 'string') {
|
||||
json = JSON.stringify(json, null, 2);
|
||||
}
|
||||
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
|
||||
let cls = 'json-number';
|
||||
if (/^"/.test(match)) {
|
||||
if (/:$/.test(match)) {
|
||||
cls = 'json-key';
|
||||
} else {
|
||||
cls = 'json-string';
|
||||
}
|
||||
} else if (/true|false/.test(match)) {
|
||||
cls = 'json-boolean';
|
||||
} else if (/null/.test(match)) {
|
||||
cls = 'json-null';
|
||||
}
|
||||
return '<span class="' + cls + '">' + match + '</span>';
|
||||
});
|
||||
}
|
||||
|
||||
function showTab(tabName) {
|
||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('[id^="tab-"]').forEach(t => t.style.display = 'none');
|
||||
|
||||
document.querySelector(`[onclick="showTab('${tabName}')"]`).classList.add('active');
|
||||
document.getElementById('tab-' + tabName).style.display = 'block';
|
||||
}
|
||||
|
||||
function decrypt() {
|
||||
const input = document.getElementById('encryptedData').value.trim();
|
||||
const key = document.getElementById('decryptKey').value.trim() || X27CN_KEY;
|
||||
const resultDiv = document.getElementById('result');
|
||||
|
||||
if (!input) {
|
||||
alert('请输入加密数据!');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 智能提取加密数据
|
||||
const encryptedData = extractEncryptedData(input);
|
||||
|
||||
// 解密
|
||||
const decrypted = x27cnDecrypt(encryptedData, key);
|
||||
if (!decrypted) {
|
||||
throw new Error('解密失败 - 无法解析数据');
|
||||
}
|
||||
|
||||
decryptedData = decrypted;
|
||||
|
||||
// 尝试解析JSON
|
||||
try {
|
||||
decryptedJson = JSON.parse(decrypted);
|
||||
|
||||
// 格式化视图
|
||||
let html = '';
|
||||
for (const [key, value] of Object.entries(decryptedJson)) {
|
||||
const isVless = key === 'vless' || (typeof value === 'string' && value.startsWith('vless://'));
|
||||
const isUuid = key === 'uuid' || key === 'UUID';
|
||||
const displayValue = typeof value === 'object' ? JSON.stringify(value, null, 2) : value;
|
||||
|
||||
html += `<div class="field-row">
|
||||
<div class="field-key">${key}:</div>
|
||||
<div class="field-value ${isVless ? 'vless-link' : ''}"
|
||||
onclick="copyField('${key}')"
|
||||
title="点击复制">${displayValue}</div>
|
||||
</div>`;
|
||||
}
|
||||
document.getElementById('tab-formatted').innerHTML = html;
|
||||
|
||||
// JSON视图
|
||||
document.getElementById('tab-json').innerHTML = syntaxHighlight(decryptedJson);
|
||||
|
||||
} catch (e) {
|
||||
decryptedJson = null;
|
||||
document.getElementById('tab-formatted').innerHTML = `<code>${decrypted}</code>`;
|
||||
document.getElementById('tab-json').innerHTML = '<span style="color:#f87171;">非 JSON 格式</span>';
|
||||
}
|
||||
|
||||
// 原始数据
|
||||
document.getElementById('tab-raw').textContent = decrypted;
|
||||
|
||||
resultDiv.classList.add('show');
|
||||
showTab('formatted');
|
||||
|
||||
} catch (e) {
|
||||
// 显示更详细的错误信息
|
||||
const hex = cleanEncryptedString(extractEncryptedData(input));
|
||||
alert(`解密失败: ${e.message}\n\n调试信息:\n- 输入长度: ${input.length}\n- 清理后十六进制长度: ${hex.length}\n- 前32字符: ${hex.substring(0, 32)}\n\n请确保粘贴了完整的 Workers 响应\n\n如果问题持续,请检查控制台(F12)获取更多信息`);
|
||||
}
|
||||
}
|
||||
|
||||
function copyField(key) {
|
||||
if (!decryptedJson || decryptedJson[key] === undefined) return;
|
||||
const value = typeof decryptedJson[key] === 'object'
|
||||
? JSON.stringify(decryptedJson[key])
|
||||
: String(decryptedJson[key]);
|
||||
copyToClipboard(value);
|
||||
}
|
||||
|
||||
function copyVless() {
|
||||
let vless = '';
|
||||
if (decryptedJson) {
|
||||
vless = decryptedJson.vless || decryptedJson.l || '';
|
||||
if (!vless) {
|
||||
for (const v of Object.values(decryptedJson)) {
|
||||
if (typeof v === 'string' && v.startsWith('vless://')) {
|
||||
vless = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!vless && decryptedData) {
|
||||
const m = decryptedData.match(/vless:\/\/[^\s"]+/);
|
||||
if (m) vless = m[0];
|
||||
}
|
||||
if (vless) {
|
||||
copyToClipboard(vless);
|
||||
} else {
|
||||
alert('未找到 VLESS 链接!');
|
||||
}
|
||||
}
|
||||
|
||||
function copyUUID() {
|
||||
let uuid = '';
|
||||
if (decryptedJson) {
|
||||
uuid = decryptedJson.uuid || decryptedJson.UUID || decryptedJson.u || '';
|
||||
}
|
||||
if (!uuid && decryptedData) {
|
||||
const m = decryptedData.match(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i);
|
||||
if (m) uuid = m[0];
|
||||
}
|
||||
if (uuid) {
|
||||
copyToClipboard(uuid);
|
||||
} else {
|
||||
alert('未找到 UUID!');
|
||||
}
|
||||
}
|
||||
|
||||
function copyJson() {
|
||||
if (decryptedData) {
|
||||
copyToClipboard(decryptedData);
|
||||
}
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showCopySuccess();
|
||||
}).catch(() => {
|
||||
const ta = document.createElement('textarea');
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(ta);
|
||||
showCopySuccess();
|
||||
});
|
||||
}
|
||||
|
||||
function showCopySuccess() {
|
||||
const el = document.getElementById('copySuccess');
|
||||
el.classList.add('show');
|
||||
setTimeout(() => el.classList.remove('show'), 2000);
|
||||
}
|
||||
|
||||
// 粘贴时自动解密
|
||||
document.getElementById('encryptedData').addEventListener('paste', function() {
|
||||
setTimeout(() => {
|
||||
if (this.value.trim().length > 50) {
|
||||
decrypt();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user