chore: initial commit - maqt-desktop v0.2
- Phase 1-5: UI framework, auth, weapon schemes, color filters, system optimization - Industrial/tech design style with Chinese localization - Points to gch3n.online/delta backend API - Hardware monitor, filter editor, and all module pages
This commit is contained in:
131
docs/scrape_schemes.js
Normal file
131
docs/scrape_schemes.js
Normal file
@@ -0,0 +1,131 @@
|
||||
// run `node scrape_schemes.js` on Windows
|
||||
// 从 maqt.top 采集方案数据并导入到我们后端
|
||||
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// ========== 配置 ==========
|
||||
const API_BASE = 'maqt.top';
|
||||
const OUR_API = 'http://100.105.17.52:3001'; // 先导入到本地后端
|
||||
const TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDc5MTcsInVzZXJuYW1lIjoic2l4dGVlbnRoIiwidG9rZW5WZXJzaW9uIjozLCJpYXQiOjE3NzgyMTI0MDUsImV4cCI6MjA5Mzc4ODQwNX0.B_J0CDtaiiF2jJ592yKtD4RtQDIR3cDF_EYgn2UM2ko';
|
||||
|
||||
const CATEGORIES = ['AR', 'SMG', 'SR', 'LMG', 'SG', 'Pistol', 'Launcher'];
|
||||
|
||||
// ========== AES 解密 ==========
|
||||
// 从 deobfuscated 代码中找到密钥后填入这里
|
||||
// 格式: Buffer.from(key, 'utf-8') 需要 32 字节
|
||||
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY || '0123456789abcdef0123456789abcdef';
|
||||
|
||||
function aesDecrypt(ivHex, dataHex) {
|
||||
const iv = Buffer.from(ivHex, 'hex');
|
||||
const encrypted = Buffer.from(dataHex, 'hex');
|
||||
const key = Buffer.from(ENCRYPTION_KEY, 'utf-8');
|
||||
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
||||
let decrypted = decipher.update(encrypted, undefined, 'utf-8');
|
||||
decrypted += decipher.final('utf-8');
|
||||
return JSON.parse(decrypted);
|
||||
}
|
||||
|
||||
// ========== HTTP 请求 ==========
|
||||
function fetch(url, postData) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const options = {
|
||||
hostname: API_BASE,
|
||||
path: url,
|
||||
method: postData ? 'POST' : 'GET',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${TOKEN}`,
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) MaQiangTang/7.0.4',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
};
|
||||
if (postData) {
|
||||
options.headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
const req = https.request(options, (res) => {
|
||||
let data = '';
|
||||
res.on('data', chunk => data += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
console.log(` ${url.slice(0, 50)} → ${res.statusCode} ${parsed.encrypted ? '(encrypted)' : '(plain)'}`);
|
||||
resolve(parsed);
|
||||
} catch (e) {
|
||||
resolve({ error: e.message, raw: data.slice(0, 100) });
|
||||
}
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
if (postData) req.write(JSON.stringify(postData));
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// ========== 导入到我们的后端 ==========
|
||||
function importScheme(scheme) {
|
||||
return new Promise((resolve) => {
|
||||
const data = JSON.stringify(scheme);
|
||||
const req = http.request(`${OUR_API}/api/schemes`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(data),
|
||||
'Authorization': `Bearer ${TOKEN}`,
|
||||
},
|
||||
}, (res) => {
|
||||
let d = '';
|
||||
res.on('data', c => d += c);
|
||||
res.on('end', () => resolve(JSON.parse(d)));
|
||||
});
|
||||
req.on('error', (e) => resolve({ error: e.message }));
|
||||
req.write(data);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
// ========== 采集流程 ==========
|
||||
async function scrape() {
|
||||
console.log('=== 开始采集改枪方案数据 ===\n');
|
||||
|
||||
// 1. 先测试分类 API
|
||||
console.log('--- 采集分类 ---');
|
||||
for (const cat of CATEGORIES.slice(0, 3)) {
|
||||
console.log(`\n分类: ${cat}`);
|
||||
const result = await fetch(`/api/category/${cat}`);
|
||||
if (!result.encrypted && Array.isArray(result)) {
|
||||
console.log(` 返回 ${result.length} 条`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 采集方案列表
|
||||
console.log('\n--- 采集方案列表 ---');
|
||||
for (const cat of CATEGORIES.slice(0, 2)) {
|
||||
console.log(`\n分类: ${cat}`);
|
||||
const result = await fetch(`/api/schemes?sort=hot&page=1&limit=12&weaponCategory=${encodeURIComponent(cat)}`);
|
||||
|
||||
if (result.encrypted) {
|
||||
try {
|
||||
const decrypted = aesDecrypt(result.iv, result.data);
|
||||
console.log(` ✅ 解密成功! 数据:\n`, JSON.stringify(decrypted, null, 2).slice(0, 2000));
|
||||
|
||||
// 保存解密结果
|
||||
fs.writeFileSync(`schemes_${cat}.json`, JSON.stringify(decrypted, null, 2));
|
||||
console.log(` 已保存到 schemes_${cat}.json`);
|
||||
} catch (e) {
|
||||
console.log(` ❌ 解密失败: ${e.message}`);
|
||||
console.log(` 密钥可能不正确,需要从 deobfuscated 代码中查找`);
|
||||
console.log(` 密钥长度: ${Buffer.from(ENCRYPTION_KEY).length} bytes`);
|
||||
console.log(` 加密数据 iv=${result.iv} data=${result.data.slice(0, 50)}...`);
|
||||
}
|
||||
} else {
|
||||
console.log(` 明文响应:\n`, JSON.stringify(result).slice(0, 500));
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== 采集完成 ===');
|
||||
}
|
||||
|
||||
scrape().catch(console.error);
|
||||
Reference in New Issue
Block a user