// index.ts import Signer from "tiktok-signature"; import axios, { AxiosResponse } from "axios"; import querystring from "querystring"; import { Browser, Page, BrowserContext } from "playwright"; import { exec } from 'child_process'; import { promisify } from 'util'; // import {DateTime} from 'luxon'; // Promisify exec for easier async/await usage const execPromise = promisify(exec); const TT_REQ_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.56"; const queryParams = { WebIdLastTime: "1739192062", aid: "1988", app_language: "en", app_name: "tiktok_web", browser_language: "en-US", browser_name: "Mozilla", browser_online: "true", browser_platform: "Win32", // Notice we use the decoded version for readability. browser_version: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0", channel: "tiktok_web", cookie_enabled: "true", count: "35", coverFormat: "2", cursor: "0", device_id: "7469772981536687624", device_platform: "web_pc", focus_state: "true", from_page: "user", history_len: "8", is_fullscreen: "false", is_page_visible: "true", language: "en", os: "windows", priority_region: "RO", referer: "https://www.tiktok.com/business-suite/messages?from=homepage&lang=en", region: "RO", root_referer: "https://www.tiktok.com/business-suite/messages?from=homepage&lang=en", screen_height: "1080", screen_width: "1920", secUid: "MS4wLjABAAAAAbt3ELTXRAMA0RVzgFjvOPEhWIKyDmY9pxPKUj5nJGIatb9xH7lTeIsU_322Sc0p", tz_name: "Europe/Bucharest", verifyFp: "verify_m6z21ydc_1d2kIOvr_Re3f_4Fit_AmQz_HL2g9Rui8xZp", webcast_language: "en", msToken: "Gv-tA8UBoR9MrpShqSMLW5jqxKbjgqn1NJx3h2AWw3AYNZl3eKUG3855040l0RcW0Ap6uoknQRj5Iq7YuaSFeDqwAh0dubDNHQSaoYgziHNyXVdnDqIAz1WdoQx_mXEM860DcE64CXvr9nzcrBaiimxfwA==", "X-Bogus": "DFSzswVuXEvANePAtkyrUO0EsCa-", _signature: "_02B4Z6wo000019pWL7AAAIDCIaUS-UMYoj.aVisAAJFF2a" }; const TT_REQ_PERM_URL = `https://www.tiktok.com/api/post/item_list/?${new URLSearchParams(queryParams).toString()}`; const parsedUrl = new URL(TT_REQ_PERM_URL); const parsedQuery = querystring.parse(parsedUrl.search.slice(1)); interface SignerInterface { init(): Promise; sign(url: string): Promise<{ "x-tt-params": string; signed_url: string }>; navigator(): Promise<{ user_agent: string }>; browser: Browser; context: BrowserContext; } // Create a signer instance. const signer: SignerInterface = new Signer(null, TT_REQ_USER_AGENT); // Initialize the signer. (async function () { await signer.init(); })(); async function main(username: string): Promise { try { const options = { method: "GET", url: "https://www.tiktok.com/@" + username, }; const response = await axios(options); const data: string = response.data; const secUidMatch = data.match(/"secUid":"(.*?)"/); const SEC_UID = secUidMatch ? secUidMatch[1] : "No secUid found"; console.log(SEC_UID) const PARAMS = { count: 30, device_id: "7165118680723998211", secUid: SEC_UID, cursor: 0, }; // Merge parsedQuery with PARAMS. const mergedParams = { ...parsedQuery, ...PARAMS }; const qsObject = new URLSearchParams(mergedParams as any); const qs = qsObject.toString(); const unsignedUrl = `https://www.tiktok.com/api/post/item_list/?${qs}`; const signature = await signer.sign(unsignedUrl); const navigatorData = await signer.navigator(); const { "x-tt-params": xTtParams, signed_url } = signature; const { user_agent: userAgent } = navigatorData; const res = await testApiReq({ userAgent, xTtParams, signed_url }); if(res.data.length == 0) { throw new Error("No data") } console.log(JSON.stringify({...res.data, error: false})); } catch (e) { console.log( JSON.stringify({ error: true, errorlog: String(e).trim() }) // return ); } } async function twitch(fun: string): Promise { try { if (!signer.context) { console.log( JSON.stringify({ error: true, online: false, errorlog: "Browser not initialized, please wait a moment", }) ); return; } const p: Page = await signer.context.newPage(); await p.goto("https://www.twitch.tv/" + fun); await p.waitForLoadState('networkidle') const CheckUserStatus = await p.evaluate(() => { try { document.getElementsByClassName("home__lower-content")[0].remove() const OfflineCheck = document.getElementsByClassName( "channel-status-info channel-status-info--offline" )[0]?.getElementsByTagName("p")[0].textContent; const OnlineCheck = document.getElementsByClassName( "tw-channel-status-text-indicator" )[0]; console.log(OnlineCheck, OfflineCheck); if (!OfflineCheck && !OnlineCheck) { return { online: false, error: true, errorlog: "User not Found" }; } else if (OfflineCheck) { return { online: false, error: false }; } else if (OnlineCheck) { const getText = (selector: string): string => { const element = document.querySelector(selector); return element instanceof HTMLElement ? element.innerText : ""; }; const viewers = getText( "p[data-a-target='animated-channel-viewers-count']" ); const streamTime = getText(".live-time").split("\n")[0]; const title = getText('[data-a-target="stream-title"]'); const game = getText("a[data-a-target=stream-game-link] span"); if (!viewers && !streamTime && !title && !game) { return { online: false, error: true }; } return { online: true, viewers, streamTime, title, game, error: false, }; } } catch (e) { return { online: false, error: true, errorlog: String(e) }; } }); await p.close(); console.log(JSON.stringify(CheckUserStatus)); } catch (e) { console.log( JSON.stringify({ error: true, online: false, errorlog: String(e).trim() }) ); } } async function kick(fun: string): Promise { try { const curlCommand = `curl_chrome116 'https://kick.com/api/v1/channels/${encodeURIComponent(fun)}'`; const v = await execPromise(curlCommand.trim()); let k = (JSON.parse(v.stdout)); let u = k.livestream if(!u) { console.log(JSON.stringify({error: false, online: false})) return } console.log(JSON.stringify({error: false, online: true, ...u})) return; } catch (e) { console.log( JSON.stringify({ error: true, online: false, errorlog: String(e).trim() }) ); } } interface TestApiReqParams { userAgent: string; xTtParams: string; signed_url: string; } /** * Makes an API request using axios. */ async function testApiReq({ userAgent, xTtParams, signed_url, }: TestApiReqParams): Promise> { const options = { method: "GET", headers: { "user-agent": userAgent, "x-tt-params": xTtParams, }, url: signed_url, }; return axios(options); } // if(true) { // kick("akibell") // // process.exit(0) // } process.stdin.on("data", (data: Buffer) => { // return; try { const fun = data.toString().trim().split(" "); switch (fun[0]) { case "twitch": twitch(fun.slice(1).join(" ")); break; case "kick": kick(fun.slice(1).join(" ")); break; default: // if (fun.length > 1) { main(fun.join(" ")); // } } } catch (e) { console.log(JSON.stringify({ error: true, errorlog: String(e) })); } });