#!/usr/bin/env node /** * 暴力探测 TikHub Douyin 搜索 endpoint 命名 * 目标:找到一个能按关键词返回视频列表的 cookie-free 接口 */ 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 req(hostname, pathUrl, method = 'GET', body = null) { return new Promise((resolve) => { const headers = { Authorization: `Bearer ${TOKEN}`, Accept: 'application/json' }; if (body) { headers['Content-Type'] = 'application/json'; headers['Content-Length'] = Buffer.byteLength(body); } const r = https.request({ hostname, path: pathUrl, method, headers }, (res) => { const chunks = []; res.on('data', (c) => chunks.push(c)); res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString('utf-8') })); }); r.on('error', (e) => resolve({ status: 0, body: e.message })); r.setTimeout(15000, () => { r.destroy(); resolve({ status: 0, body: 'timeout' }); }); if (body) r.write(body); r.end(); }); } const kw = '猴菇饮'; const kwE = encodeURIComponent(kw); // 根据 TikHub 抖音 endpoint 命名模式(app/v3 + web) const candidates = [ // 直连 api.tikhub.io(官方域名) { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_general_search_result?keyword=${kwE}&offset=0&count=20&sort_type=0&publish_time=0` }, { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_general_search_result?keyword=${kwE}&offset=0&count=20` }, { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_video_search?keyword=${kwE}&offset=0&count=20` }, { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_user_search?keyword=${kwE}&offset=0&count=20` }, { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_hot_search_list` }, { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_hot_search_list` }, { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_word_hot_search_list?keyword=${kwE}` }, // fmode 网关(已知工作) { host: 'server.fmode.cn', path: `/thapi/v1/douyin/app/v3/fetch_general_search_result?keyword=${kwE}&offset=0&count=20&sort_type=0&publish_time=0` }, { host: 'server.fmode.cn', path: `/thapi/v1/douyin/web/fetch_general_search_result?keyword=${kwE}&offset=0&count=20` }, { host: 'server.fmode.cn', path: `/thapi/v1/douyin/app/v3/fetch_video_search?keyword=${kwE}&offset=0&count=20` }, { host: 'server.fmode.cn', path: `/thapi/v1/douyin/web/fetch_word_hot_search_list?keyword=${kwE}` }, { host: 'server.fmode.cn', path: `/thapi/v1/douyin/web/fetch_hot_search_list` }, // 关键词热度趋势 { host: 'api.tikhub.io', path: `/api/v1/douyin/web/fetch_keyword_hot_trend?keyword=${kwE}` }, { host: 'api.tikhub.io', path: `/api/v1/douyin/app/v3/fetch_keyword_hot_trend?keyword=${kwE}` }, ]; (async () => { console.log(`🔎 keyword = "${kw}"`); console.log(`🔑 token = ${TOKEN.slice(0, 10)}...\n`); for (const c of candidates) { const r = await req(c.host, c.path); let summary; try { const j = JSON.parse(r.body); const d = j.data ?? j; if (Array.isArray(d?.data)) summary = `data=Array(${d.data.length})`; else if (d?.aweme_list) summary = `aweme_list=${d.aweme_list.length}`; else if (d?.business_data) summary = `business_data=${Array.isArray(d.business_data) ? d.business_data.length : 'obj'}`; else if (d?.word_list) summary = `word_list=${d.word_list.length}`; else if (d?.hot_search_list) summary = `hot_search=${d.hot_search_list.length}`; else if (d?.detail) summary = `detail="${String(d.detail).slice(0, 80)}"`; else if (j.code) summary = `code=${j.code} msg=${(j.message || j.msg || '').slice(0, 60)}`; else summary = 'keys=' + Object.keys(d || j).slice(0, 5).join(','); } catch (e) { summary = r.body.slice(0, 100); } const flag = r.status === 200 ? '✅' : (r.status === 404 ? '✗' : '⚠'); console.log(`${flag} [${r.status}] ${c.host}${c.path.slice(0, 75)}`); console.log(` → ${summary}\n`); await new Promise((r) => setTimeout(r, 500)); } })();