blueddd commited on
Commit
0144988
·
verified ·
1 Parent(s): 4cbca2a

Update api.js

Browse files
Files changed (1) hide show
  1. api.js +349 -6
api.js CHANGED
@@ -1,7 +1,7 @@
1
  const express = require('express');
2
  const crypto = require('crypto');
3
  const axios = require('axios');
4
- const cors = require('cors');
5
  const app = express();
6
 
7
  app.use((req, res, next) => {
@@ -14,13 +14,13 @@ app.use(express.static('public'));
14
 
15
 
16
 
17
- app.get('/blued', async (req, res) => {
18
  const { name } = req.query;
19
 
20
  const input = `16566580507931f7c79f67099ddad3a4f0e4ed29a6name${name}page1`;
21
 
22
  const sign = crypto.createHash('md5').update(input).digest('hex').toUpperCase();
23
-
24
  const url = 'https://app.blued.cn/home/web-recharge/getUserInfoByNickname';
25
 
26
  const params = {
@@ -31,13 +31,356 @@ app.get('/blued', async (req, res) => {
31
 
32
  try {
33
  const response = await axios.get(url, { params });
34
- res.send(response.data);
35
  } catch (error) {
36
  console.error(error);
37
- res.status(500).send('Error getting data from Blued');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
  });
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  app.listen(3000, '0.0.0.0', () => {
42
- console.log('Server listening on port 80');
43
  });
 
1
  const express = require('express');
2
  const crypto = require('crypto');
3
  const axios = require('axios');
4
+ const cors = require('cors');
5
  const app = express();
6
 
7
  app.use((req, res, next) => {
 
14
 
15
 
16
 
17
+ app.get('/search', async (req, res) => {
18
  const { name } = req.query;
19
 
20
  const input = `16566580507931f7c79f67099ddad3a4f0e4ed29a6name${name}page1`;
21
 
22
  const sign = crypto.createHash('md5').update(input).digest('hex').toUpperCase();
23
+
24
  const url = 'https://app.blued.cn/home/web-recharge/getUserInfoByNickname';
25
 
26
  const params = {
 
31
 
32
  try {
33
  const response = await axios.get(url, { params });
34
+ res.send(processResponse(response.data));
35
  } catch (error) {
36
  console.error(error);
37
+ res.status(500).send('Error getting data from Blued');
38
+ }
39
+ });
40
+
41
+ app.get('/viewPhone', async (req, res) => {
42
+ const { id } = req.query;
43
+
44
+ const url = 'https://app.blued.cn/msg/phone/list';
45
+
46
+ const headers = {
47
+ 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
48
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
49
+ 'Accept-Encoding': 'gzip, deflate, br',
50
+ 'Connection': 'keep-alive',
51
+ 'Cookie': 'uid=' + id
52
+ };
53
+
54
+ try {
55
+ const response = await axios({
56
+ url,
57
+ headers
58
+ });
59
+ res.send((response.data));
60
+ } catch (error) {
61
+ console.error(error);
62
+ res.status(500).send('Error getting data from Blued');
63
+ }
64
+ });
65
+
66
+
67
+ // 新增路由:代理用户主页
68
+ app.get('/getDetail', async (req, res) => {
69
+ const { id } = req.query;
70
+
71
+ if (!id) {
72
+ return res.status(400).send('Missing id parameter');
73
+ }
74
+
75
+ const targetUrl = `https://app.blued.cn/user`;
76
+ const params = { id };
77
+
78
+ // 模拟真实浏览器请求头(关键!)
79
+ const headers = {
80
+ 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
81
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
82
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
83
+ 'Accept-Encoding': 'gzip, deflate, br',
84
+ 'Connection': 'keep-alive',
85
+ 'Upgrade-Insecure-Requests': '1',
86
+ 'Sec-Fetch-Dest': 'document',
87
+ 'Sec-Fetch-Mode': 'navigate',
88
+ 'Sec-Fetch-Site': 'none',
89
+ 'Sec-Fetch-User': '?1',
90
+ };
91
+
92
+ try {
93
+ const response = await axios({
94
+ method: 'GET',
95
+ url: targetUrl,
96
+ params: params,
97
+ headers: headers,
98
+ responseType: 'text', // 确保返回文本
99
+ timeout: 10000,
100
+ // 如果 Blued 使用 gzip,axios 会自动解压
101
+ });
102
+
103
+ // 设置正确的内容类型
104
+ res.set('Content-Type', 'text/html; charset=utf-8');
105
+ // 可选:移除或修改 CSP/X-Frame-Options(但通常在 HTML meta 或响应头中)
106
+ // 注意:响应头由 Blued 控制,我们只能改 HTML 内容
107
+
108
+ // (可选)尝试移除 HTML 中的 X-Frame-Options meta(如果存在)
109
+ let html = response.data;
110
+ // 移除可能的 <meta http-equiv="X-Frame-Options"> 标签
111
+ html = html.replace(/<meta[^>]*http-equiv=["']?X-Frame-Options["']?[^>]*>/gi, '');
112
+ console.log(extractConfigFromHtml(html))
113
+ // 返回修改后的 HTML
114
+ res.json(extractConfigFromHtml(html));
115
+ } catch (error) {
116
+ console.error('Proxy error:', error.message);
117
+ if (error.response) {
118
+ console.error('Blued responded with status:', error.response.status);
119
+ }
120
+ res.status(500).send('Failed to fetch user page');
121
  }
122
  });
123
 
124
+
125
+
126
+
127
+
128
+ function extractConfigFromHtml(htmlString) {
129
+ // 匹配 window.CONFIG = { ... };
130
+ const match = htmlString.match(/window\.CONFIG\s*=\s*(\{[\s\S]*?\});\s*</);
131
+ if (match) {
132
+ try {
133
+ // 安全地解析 JSON(注意:这里其实是 JS 对象字面量,不是标准 JSON)
134
+ // 但由于 Blued 的 CONFIG 是合法 JS 对象,且无函数,可用 eval(谨慎!)或更安全方式
135
+ // 推荐:先替换掉末尾分号,再用 Function 构造器(比 eval 稍安全)
136
+ const configStr = match[1];
137
+ // 使用 Function 构造器解析(避免污染作用域)
138
+ const config = new Function(`return (${configStr})`)();
139
+ return config;
140
+ } catch (e) {
141
+ console.error('解析 CONFIG 失败:', e);
142
+ return null;
143
+ }
144
+ }
145
+ return null;
146
+ }
147
+
148
+ // 示例用法(假设你已通过 fetch 或 axios 获取到 html 字符串)
149
+ // const html = await response.text();
150
+ // const config = extractConfigFromHtml(html);
151
+
152
+ function processResponse(response) {
153
+ // 检查结构是否合法
154
+ if (!response || !response.data || !Array.isArray(response.data.list)) {
155
+ return response; // 结构不符合,直接返回原数据
156
+ }
157
+
158
+ // 深拷贝并处理 list 中的每个对象
159
+ const processedList = response.data.list.map(item => {
160
+ if (item && typeof item === 'object' && item.hasOwnProperty('uid')) {
161
+ return {
162
+ ...item,
163
+ uuid: decrypt(item.uid)
164
+ };
165
+ }
166
+ // 如果没有 uid,保留原对象
167
+ return { ...item };
168
+ });
169
+
170
+ // 返回新结构
171
+ return {
172
+ ...response,
173
+ data: {
174
+ ...response.data,
175
+ list: processedList
176
+ }
177
+ };
178
+ }
179
+
180
+
181
+ class Hashids {
182
+ constructor(salt = '', minLength = 0, alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123567890') {
183
+ this.salt = salt + '';
184
+ this.minLength = Math.max(0, parseInt(minLength, 10) || 0);
185
+ this.alphabet = '';
186
+ // deduplicate alphabet chars (preserve order)
187
+ for (let ch of alphabet) if (!this.alphabet.includes(ch)) this.alphabet += ch;
188
+ if (this.alphabet.length < 16) throw new Error('alphabet must contain at least 16 unique characters');
189
+ if (this.alphabet.includes(' ')) throw new Error('alphabet cannot contains spaces');
190
+
191
+ this.seps = 'cfhistuCFHISTU';
192
+ // remove seps from alphabet (mirror Java logic)
193
+ let a = this.alphabet;
194
+ for (let i = 0; i < this.seps.length; i++) {
195
+ const idx = a.indexOf(this.seps.charAt(i));
196
+ if (idx === -1) {
197
+ // if not found, replace that position in seps with space (same effect as Java)
198
+ this.seps = this.seps.substring(0, i) + ' ' + this.seps.substring(i + 1);
199
+ } else {
200
+ a = a.substring(0, idx) + ' ' + a.substring(idx + 1);
201
+ }
202
+ }
203
+ a = a.replace(/\s+/g, '');
204
+ this.seps = this.seps.replace(/\s+/g, '');
205
+
206
+ // m21293b(strReplaceAll, this.f22838a);
207
+ let shuffledSeps = this._consistentShuffle(this.seps, this.salt);
208
+ this.seps = shuffledSeps;
209
+
210
+ // adjust seps/alphabet according to Java logic
211
+ if (!this.seps || (a.length / this.seps.length) > 3.5) {
212
+ let ceil = Math.ceil(a.length / 3.5);
213
+ if (ceil === 1) ceil = ceil + 1;
214
+ if (ceil > this.seps.length) {
215
+ const diff = ceil - this.seps.length;
216
+ this.seps += a.substring(0, diff);
217
+ a = a.substring(diff);
218
+ } else {
219
+ this.seps = this.seps.substring(0, ceil);
220
+ }
221
+ }
222
+
223
+ // now shuffle alphabet with salt
224
+ a = this._consistentShuffle(a, this.salt);
225
+
226
+ const guardCount = Math.ceil(a.length / 12);
227
+ if (a.length < 3) {
228
+ this.guards = this.seps.substring(0, guardCount);
229
+ this.seps = this.seps.substring(guardCount);
230
+ } else {
231
+ this.guards = a.substring(0, guardCount);
232
+ a = a.substring(guardCount);
233
+ }
234
+
235
+ this.alphabet = a;
236
+ }
237
+
238
+ // consistent shuffle m21293b
239
+ _consistentShuffle(alphabet, salt) {
240
+ if (!salt || salt.length === 0) return alphabet;
241
+ const arr = alphabet.split('');
242
+ const saltChars = salt.split('').map(c => c.charCodeAt(0));
243
+ let i = arr.length - 1;
244
+ let v = 0;
245
+ let p = 0;
246
+ while (i > 0) {
247
+ const idx = v % saltChars.length;
248
+ const c = saltChars[idx];
249
+ p += c;
250
+ const j = (c + idx + p) % i;
251
+ // swap arr[j] and arr[i]
252
+ const tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp;
253
+ i--;
254
+ v = idx + 1;
255
+ }
256
+ return arr.join('');
257
+ }
258
+
259
+ // encode numbers -> hash (m21296a -> m21294b)
260
+ encode(...numbers) {
261
+ if (!numbers || numbers.length === 0) return '';
262
+ // check max like Java: > 9007199254740992L
263
+ const MAX_ALLOWED = BigInt('9007199254740992');
264
+ for (let n of numbers) {
265
+ // accept BigInt or Number or numeric string
266
+ let bn = (typeof n === 'bigint') ? n : BigInt(n);
267
+ if (bn > MAX_ALLOWED) throw new Error('number can not be greater than 9007199254740992');
268
+ }
269
+ return this._encode(numbers);
270
+ }
271
+
272
+ _encode(nums) {
273
+ // compute i sum
274
+ let iSum = 0;
275
+ for (let idx = 0; idx < nums.length; idx++) {
276
+ const n = BigInt(nums[idx]);
277
+ iSum = Number(iSum + Number(n % BigInt(idx + 100)));
278
+ }
279
+ let alphabet = this.alphabet;
280
+ const lotteryChar = alphabet.charAt(iSum % alphabet.length);
281
+ let ret = lotteryChar;
282
+ for (let i = 0; i < nums.length; i++) {
283
+ const n = BigInt(nums[i]);
284
+ alphabet = this._consistentShuffle(alphabet, (lotteryChar + this.salt + alphabet).substring(0, alphabet.length));
285
+ ret += this._hashNumber(n, alphabet);
286
+ if (i + 1 < nums.length) {
287
+ const guardIndex = Number((n % BigInt(alphabet.charCodeAt(0) + i)) % BigInt(this.seps.length));
288
+ ret += this.seps.charAt(guardIndex);
289
+ }
290
+ }
291
+
292
+ // pad with guards/guards logic like Java
293
+ if (ret.length < this.minLength) {
294
+ ret = this.guards.charAt((ret.charCodeAt(0) + iSum) % this.guards.length) + ret;
295
+ if (ret.length < this.minLength) {
296
+ ret = ret + this.guards.charAt((iSum + ret.charCodeAt(2 % ret.length)) % this.guards.length);
297
+ }
298
+ }
299
+
300
+ // while still less than minLength, expand by shuffling alphabet etc
301
+ let half = Math.floor(alphabet.length / 2);
302
+ while (ret.length < this.minLength) {
303
+ alphabet = this._consistentShuffle(alphabet, alphabet);
304
+ let str = alphabet.substring(half) + ret + alphabet.substring(0, half);
305
+ const excess = str.length - this.minLength;
306
+ if (excess > 0) {
307
+ const start = Math.floor(excess / 2);
308
+ str = str.substring(start, start + this.minLength);
309
+ }
310
+ ret = str;
311
+ }
312
+
313
+ return ret;
314
+ }
315
+
316
+ // convert number to string using alphabet (m21291a)
317
+ _hashNumber(number, alphabet) {
318
+ let num = BigInt(number);
319
+ const len = alphabet.length;
320
+ let out = '';
321
+ if (num === 0n) return alphabet.charAt(0);
322
+ while (num > 0n) {
323
+ const idx = Number(num % BigInt(len));
324
+ out = alphabet.charAt(idx) + out;
325
+ num = num / BigInt(len);
326
+ }
327
+ return out;
328
+ }
329
+
330
+ // decode hash -> array of numbers (m21297a -> m21292a)
331
+ decode(hash) {
332
+ if (hash === '') return [];
333
+ // remove guards
334
+ const hashSplit = hash.replace(new RegExp('[' + this.guards.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + ']', 'g'), ' ').split(' ').filter(Boolean);
335
+ const hashPart = (hashSplit.length === 3 || hashSplit.length === 2) ? hashSplit[1] : hashSplit[0];
336
+ const lottery = hashPart.charAt(0);
337
+ const parts = hashPart.substring(1).replace(new RegExp('[' + this.seps.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + ']', 'g'), ' ').split(' ').filter(Boolean);
338
+ const result = [];
339
+ for (let i = 0; i < parts.length; i++) {
340
+ const part = parts[i];
341
+ let alphabet = this._consistentShuffle(this.alphabet, (lottery + this.salt + this.alphabet).substring(0, this.alphabet.length));
342
+ result.push(this._unhash(part, alphabet));
343
+ }
344
+
345
+ // verify
346
+ const encoded = this._encode(result);
347
+ // if verification fails, return empty arr
348
+ if (encoded !== hash) return [];
349
+ return result;
350
+ }
351
+
352
+ _unhash(input, alphabet) {
353
+ let num = 0n;
354
+ const len = BigInt(alphabet.length);
355
+ for (let i = 0; i < input.length; i++) {
356
+ const pos = BigInt(alphabet.indexOf(input.charAt(i)));
357
+ num = num * len + pos;
358
+ }
359
+ return num;
360
+ }
361
+ }
362
+
363
+ // --- 对外封装的函数(与 Java 中的 HashidEncryptTool 对应) ---
364
+ // decrypt: Java m22018a(String str) -> 传入 hash,返回拼接的数字字符串
365
+ function decrypt(hashStr) {
366
+ if (!hashStr) return '';
367
+ try {
368
+ const h = new Hashids('1766', 6);
369
+ const arr = h.decode(hashStr);
370
+ // arr contains BigInt values. Concatenate them as decimal strings without separator (same as Java append)
371
+ return arr.map(x => x.toString()).join('');
372
+ } catch (e) {
373
+ console.error(e);
374
+ return '';
375
+ }
376
+ }
377
+
378
+
379
+ module.exports = {
380
+ HashidsJS: Hashids,
381
+ decrypt: decrypt
382
+ };
383
+
384
  app.listen(3000, '0.0.0.0', () => {
385
+ console.log('Server listening on port 80');
386
  });