probe-dy-user-search2.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. #!/usr/bin/env node
  2. /**
  3. * 探测 TikHub 的抖音用户搜索正确 API 路径
  4. */
  5. const fs = require('fs');
  6. const path = require('path');
  7. const os = require('os');
  8. const https = require('https');
  9. const TIKHUB_TOKEN = JSON.parse(
  10. fs.readFileSync(
  11. path.join(os.homedir(), '.openclaw', 'skills', 'xiaohongshu-search-notes', 'api-config.json'),
  12. 'utf-8'
  13. )
  14. ).endpoint.headers.Authorization.replace(/^Bearer\s+/, '');
  15. const COOKIE = fs.readFileSync(path.resolve('data/douyin-cookie.txt'), 'utf-8').trim();
  16. const keyword = '葵花药业';
  17. const paths = [
  18. // app v3
  19. '/api/v1/douyin/app/v3/fetch_user_search_result',
  20. '/api/v1/douyin/app/v3/search_user',
  21. '/api/v1/douyin/app/v3/fetch_general_search_result',
  22. // web
  23. '/api/v1/douyin/web/fetch_user_search_result_v2',
  24. '/api/v1/douyin/web/fetch_user_search_result',
  25. '/api/v1/douyin/web/search_user',
  26. '/api/v1/douyin/web/fetch_general_search_result',
  27. '/api/v1/douyin/web/fetch_general_search_result_v2',
  28. // app v1/v2
  29. '/api/v1/douyin/app/v1/fetch_user_search_result',
  30. '/api/v1/douyin/app/v2/fetch_user_search_result',
  31. ];
  32. function get(apiPath, params) {
  33. const qs = Object.entries(params)
  34. .filter(([, v]) => v !== undefined && v !== null && v !== '')
  35. .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
  36. .join('&');
  37. return new Promise((resolve) => {
  38. const opts = {
  39. hostname: 'api.tikhub.io',
  40. path: `${apiPath}?${qs}`,
  41. method: 'GET',
  42. headers: { Authorization: `Bearer ${TIKHUB_TOKEN}`, Accept: 'application/json' },
  43. };
  44. const req = https.request(opts, (res) => {
  45. const chunks = [];
  46. res.on('data', (c) => chunks.push(c));
  47. res.on('end', () => {
  48. const body = Buffer.concat(chunks).toString('utf-8');
  49. try {
  50. resolve({ status: res.statusCode, json: JSON.parse(body) });
  51. } catch (e) {
  52. resolve({ status: res.statusCode, json: { _raw: body.slice(0, 200) } });
  53. }
  54. });
  55. });
  56. req.on('error', (e) => resolve({ status: 0, json: { _error: e.message } }));
  57. req.setTimeout(20000, () => { req.destroy(); resolve({ status: 0, json: { _error: 'timeout' } }); });
  58. req.end();
  59. });
  60. }
  61. const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
  62. (async () => {
  63. const out = [];
  64. for (const p of paths) {
  65. await sleep(800);
  66. const r = await get(p, { keyword, cookie: COOKIE, offset: 0, count: 10 });
  67. const status = r.status;
  68. const code = r.json?.code;
  69. const msg = r.json?.message_zh || r.json?.message || r.json?.detail?.message_zh || r.json?.detail?.message;
  70. const data = r.json?.data;
  71. const hasData = data != null && !Array.isArray(data) ? Object.keys(data).length : (Array.isArray(data) ? data.length : 0);
  72. const dataKeys = data && typeof data === 'object' && !Array.isArray(data) ? Object.keys(data).slice(0, 8).join(',') : '';
  73. const line = { path: p, status, code, msg: (msg || '').slice(0, 80), hasData, dataKeys };
  74. out.push(line);
  75. }
  76. fs.writeFileSync('data/dy-search-paths.json', JSON.stringify(out, null, 2), 'utf-8');
  77. console.log(JSON.stringify(out, null, 2));
  78. })();