probe-tiktok-endpoints.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. #!/usr/bin/env node
  2. /**
  3. * 探测 TikHub TikTok 端点的可用性
  4. * 关键:TikTok 不需要 cookie 登录态(海外版)
  5. * 产品目标:
  6. * - liver → milk thistle
  7. * - probiotic → kids probiotics
  8. * - monkey → lions mane
  9. */
  10. const fs = require('fs');
  11. const path = require('path');
  12. const os = require('os');
  13. const https = require('https');
  14. const TOKEN = JSON.parse(
  15. fs.readFileSync(
  16. path.join(os.homedir(), '.openclaw', 'skills', 'xiaohongshu-search-notes', 'api-config.json'),
  17. 'utf-8'
  18. )
  19. ).endpoint.headers.Authorization.replace(/^Bearer\s+/, '');
  20. function get(apiPath) {
  21. return new Promise((resolve) => {
  22. const opts = {
  23. hostname: 'api.tikhub.io',
  24. path: apiPath,
  25. method: 'GET',
  26. headers: { Authorization: `Bearer ${TOKEN}`, Accept: 'application/json' },
  27. };
  28. const req = https.request(opts, (res) => {
  29. const chunks = [];
  30. res.on('data', (c) => chunks.push(c));
  31. res.on('end', () => {
  32. const body = Buffer.concat(chunks).toString('utf-8');
  33. try { resolve({ status: res.statusCode, json: JSON.parse(body) }); }
  34. catch (e) { resolve({ status: res.statusCode, text: body.slice(0, 300) }); }
  35. });
  36. });
  37. req.on('error', (e) => resolve({ status: 0, error: e.message }));
  38. req.setTimeout(45000, () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); });
  39. req.end();
  40. });
  41. }
  42. const kw = 'milk thistle';
  43. const kwE = encodeURIComponent(kw);
  44. // 测试候选路径:视频搜 + 用户搜 + 关键词相关
  45. const paths = [
  46. // app v3 系列
  47. `/api/v1/tiktok/app/v3/fetch_video_search_result?keyword=${kwE}&cursor=0&count=10`,
  48. `/api/v1/tiktok/app/v3/fetch_general_search_result?keyword=${kwE}&cursor=0&count=10`,
  49. `/api/v1/tiktok/app/v3/fetch_user_search_result?keyword=${kwE}&cursor=0&count=10`,
  50. // web 系列
  51. `/api/v1/tiktok/web/fetch_video_search_result?keyword=${kwE}&cursor=0&count=10`,
  52. `/api/v1/tiktok/web/fetch_general_search_result?keyword=${kwE}&cursor=0&count=10`,
  53. `/api/v1/tiktok/web/fetch_user_search_result?keyword=${kwE}&cursor=0&count=10`,
  54. `/api/v1/tiktok/web/fetch_search_video?keyword=${kwE}&cursor=0&count=10`,
  55. `/api/v1/tiktok/web/fetch_search_user?keyword=${kwE}&cursor=0&count=10`,
  56. // V2
  57. `/api/v1/tiktok/app/v3/fetch_video_search_result_v2?keyword=${kwE}&cursor=0&count=10`,
  58. // 不带 app/web 前缀
  59. `/api/v1/tiktok/fetch_video_search_result?keyword=${kwE}&cursor=0&count=10`,
  60. ];
  61. (async () => {
  62. const results = [];
  63. for (const p of paths) {
  64. const r = await get(p);
  65. const code = r.json?.code;
  66. const msg = (r.json?.message_zh || r.json?.message || '').slice(0, 60);
  67. const d = r.json?.data;
  68. let hint = '';
  69. if (d) {
  70. const inner = d.data || d.aweme_list || d.user_list || null;
  71. if (Array.isArray(d)) hint = `arr[${d.length}]`;
  72. else if (inner && Array.isArray(inner)) hint = `arr[${inner.length}]` + (inner[0] ? ` first_keys=${Object.keys(inner[0]).slice(0, 5).join(',')}` : '');
  73. else hint = 'keys=' + Object.keys(d).slice(0, 6).join(',');
  74. }
  75. console.log(`[${r.status}] ${p.split('?')[0].replace('/api/v1/tiktok/', '')}`);
  76. console.log(` code=${code} msg="${msg}" hint=${hint}`);
  77. results.push({ path: p, status: r.status, code, msg, hint });
  78. await new Promise((res) => setTimeout(res, 700));
  79. }
  80. fs.writeFileSync('data/tiktok-probe.json', JSON.stringify(results, null, 2), 'utf-8');
  81. console.log('\n✓ data/tiktok-probe.json');
  82. })();