#!/usr/bin/env node /** * 探测 TikHub TikTok 端点的可用性 * 关键:TikTok 不需要 cookie 登录态(海外版) * 产品目标: * - liver → milk thistle * - probiotic → kids probiotics * - monkey → lions mane */ const fs = require('fs'); const path = require('path'); const os = require('os'); const https = require('https'); const TOKEN = JSON.parse( fs.readFileSync( path.join(os.homedir(), '.openclaw', 'skills', 'xiaohongshu-search-notes', 'api-config.json'), 'utf-8' ) ).endpoint.headers.Authorization.replace(/^Bearer\s+/, ''); function get(apiPath) { return new Promise((resolve) => { const opts = { hostname: 'api.tikhub.io', path: apiPath, method: 'GET', headers: { Authorization: `Bearer ${TOKEN}`, Accept: 'application/json' }, }; const req = https.request(opts, (res) => { const chunks = []; res.on('data', (c) => chunks.push(c)); res.on('end', () => { const body = Buffer.concat(chunks).toString('utf-8'); try { resolve({ status: res.statusCode, json: JSON.parse(body) }); } catch (e) { resolve({ status: res.statusCode, text: body.slice(0, 300) }); } }); }); req.on('error', (e) => resolve({ status: 0, error: e.message })); req.setTimeout(45000, () => { req.destroy(); resolve({ status: 0, error: 'timeout' }); }); req.end(); }); } const kw = 'milk thistle'; const kwE = encodeURIComponent(kw); // 测试候选路径:视频搜 + 用户搜 + 关键词相关 const paths = [ // app v3 系列 `/api/v1/tiktok/app/v3/fetch_video_search_result?keyword=${kwE}&cursor=0&count=10`, `/api/v1/tiktok/app/v3/fetch_general_search_result?keyword=${kwE}&cursor=0&count=10`, `/api/v1/tiktok/app/v3/fetch_user_search_result?keyword=${kwE}&cursor=0&count=10`, // web 系列 `/api/v1/tiktok/web/fetch_video_search_result?keyword=${kwE}&cursor=0&count=10`, `/api/v1/tiktok/web/fetch_general_search_result?keyword=${kwE}&cursor=0&count=10`, `/api/v1/tiktok/web/fetch_user_search_result?keyword=${kwE}&cursor=0&count=10`, `/api/v1/tiktok/web/fetch_search_video?keyword=${kwE}&cursor=0&count=10`, `/api/v1/tiktok/web/fetch_search_user?keyword=${kwE}&cursor=0&count=10`, // V2 `/api/v1/tiktok/app/v3/fetch_video_search_result_v2?keyword=${kwE}&cursor=0&count=10`, // 不带 app/web 前缀 `/api/v1/tiktok/fetch_video_search_result?keyword=${kwE}&cursor=0&count=10`, ]; (async () => { const results = []; for (const p of paths) { const r = await get(p); const code = r.json?.code; const msg = (r.json?.message_zh || r.json?.message || '').slice(0, 60); const d = r.json?.data; let hint = ''; if (d) { const inner = d.data || d.aweme_list || d.user_list || null; if (Array.isArray(d)) hint = `arr[${d.length}]`; else if (inner && Array.isArray(inner)) hint = `arr[${inner.length}]` + (inner[0] ? ` first_keys=${Object.keys(inner[0]).slice(0, 5).join(',')}` : ''); else hint = 'keys=' + Object.keys(d).slice(0, 6).join(','); } console.log(`[${r.status}] ${p.split('?')[0].replace('/api/v1/tiktok/', '')}`); console.log(` code=${code} msg="${msg}" hint=${hint}`); results.push({ path: p, status: r.status, code, msg, hint }); await new Promise((res) => setTimeout(res, 700)); } fs.writeFileSync('data/tiktok-probe.json', JSON.stringify(results, null, 2), 'utf-8'); console.log('\n✓ data/tiktok-probe.json'); })();