// 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);