tca-js 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -100
- package/dist/index.d.mts +369 -0
- package/dist/index.d.ts +362 -26
- package/dist/index.js +1304 -52
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1262 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +56 -10
- package/dist/api.d.ts +0 -75
- package/dist/api.js +0 -277
- package/dist/constants.d.ts +0 -86
- package/dist/constants.js +0 -77
- package/dist/context.d.ts +0 -32
- package/dist/context.js +0 -129
- package/dist/errors.d.ts +0 -18
- package/dist/errors.js +0 -17
- package/dist/listener.d.ts +0 -43
- package/dist/listener.js +0 -181
- package/dist/lz4.d.ts +0 -5
- package/dist/lz4.js +0 -57
- package/dist/media.d.ts +0 -27
- package/dist/media.js +0 -152
- package/dist/message.d.ts +0 -6
- package/dist/message.js +0 -113
- package/dist/proto.d.ts +0 -43
- package/dist/proto.js +0 -162
- package/dist/qr-login.d.ts +0 -41
- package/dist/qr-login.js +0 -212
- package/dist/types.d.ts +0 -109
- package/dist/types.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/api.ts","../src/protocol/constants.ts","../src/protocol/proto.ts","../src/errors.ts","../src/listener/listener.ts","../src/models/message.ts","../src/listener/frame.ts","../src/protocol/lz4.ts","../src/api/account.ts","../src/api/media.ts","../src/api/requests.ts","../src/protocol/context.ts","../src/client.ts","../src/auth/qr-login.ts","../src/auth/cdp.ts","../src/auth/chrome.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { Cmd, MARK_READ_BODY_FIELD, MessageType, WEB } from '../protocol/constants';\nimport type { Context } from '../protocol/context';\nimport { Writer } from '../protocol/proto';\nimport { TikTokError } from '../errors';\nimport { Listener } from '../listener/listener';\nimport { parseConversation, parseMessage } from '../models/message';\nimport type {\n ConversationPage,\n ConversationRef,\n ImageInput,\n MessagePage,\n TikTokAccount,\n TikTokImage,\n TikTokMessage,\n TikTokSticker,\n TikTokUser,\n} from '../models/types';\nimport { avatarLinks, fetchAccount } from './account';\nimport { MediaClient } from './media';\nimport {\n getMessagesBody,\n imageMessageExtra,\n markReadBody,\n reactionOps,\n sendMessageBody,\n setPropertyBody,\n} from './requests';\n\n/** Send result: client-side message id (TikTok echoes it back). The server id arrives via the listener (`isSelf`). */\nexport interface SendResult {\n clientMessageId: string;\n}\n\n/**\n * A logged-in TikTok session — create with `new TikTok().login(credentials)`.\n * All calls go directly over HTTP/WS to TikTok using cookies, no browser needed.\n */\nexport class API {\n readonly listener: Listener;\n private readonly media: MediaClient;\n\n constructor(\n private readonly ctx: Context,\n readonly account: TikTokAccount,\n ) {\n this.listener = new Listener(ctx);\n this.media = new MediaClient(ctx);\n }\n\n getOwnId(): string {\n return this.ctx.ownId;\n }\n\n /** Current cookies — save them so the next `login` needs no QR scan. */\n getCookies(): { name: string; value: string }[] {\n return [...this.ctx.cookies].map(([name, value]) => ({ name, value }));\n }\n\n getContext(): { deviceId: string; userAgent: string } {\n return { deviceId: this.ctx.deviceId, userAgent: this.ctx.userAgent };\n }\n\n /** Whether the session is still alive — call periodically to detect expiry early. */\n async isAlive(): Promise<boolean> {\n return (await fetchAccount(this.ctx).catch(() => null)) !== null;\n }\n\n // ─── Inbox ─────────────────────────────────────────────────────────────────\n\n /** Recent conversations + their latest messages, paginated with `cursor`. */\n async getConversations(cursor = '0'): Promise<ConversationPage> {\n const body = await this.ctx.im(\n '/v2/message/get_by_user_init',\n Cmd.GetMessagesByUserInit,\n new Writer().int(1, BigInt(cursor)).finish(),\n );\n return {\n messages: body.msgs(1).map((m) => parseMessage(m, this.ctx.ownId)),\n conversations: body.msgs(2).map((m) => parseConversation(m, this.ctx.ownId)),\n // Cursor = number of conversations fetched so far (0, 10, 20…) — field 4; field 3 is a timestamp, not used for paging.\n cursor: body.id(4),\n hasMore: body.num(5) === 1,\n };\n }\n\n /** Messages of a conversation, newest first. For older messages: `before = <index of oldest message>`. */\n async getMessages(conv: ConversationRef, opts: { limit?: number; before?: string } = {}): Promise<MessagePage> {\n const limit = Math.min(Math.max(opts.limit ?? 20, 1), 50);\n const body = await this.ctx.im(\n '/v1/message/get_by_conversation',\n Cmd.GetMessagesByConversation,\n getMessagesBody(conv, limit, opts.before),\n );\n const messages = body.msgs(1).map((m) => parseMessage(m, this.ctx.ownId));\n return { messages, hasMore: messages.length >= limit };\n }\n\n /** User profiles by uid (display name, @username, avatar). */\n async getUsers(uids: string[]): Promise<TikTokUser[]> {\n if (uids.length === 0) return [];\n const q = new URLSearchParams({\n aid: '1988',\n app_name: 'tiktok_web',\n device_platform: 'web_pc',\n source: 'im_chat',\n from_page: 'message',\n user_ids: JSON.stringify(uids),\n });\n type Res = {\n status_code?: number;\n users?: {\n im_user_profile?: {\n user_id_str?: string;\n unique_id?: string;\n nick_name?: string;\n avatars?: unknown;\n };\n }[];\n };\n const res = await this.ctx.getJson<Res>(`${WEB}/tiktok/v1/im/user/profile/?${q}`);\n if (res.status_code) throw new TikTokError('IM_ERROR', `user/profile: ${res.status_code}`, res.status_code);\n return (res.users ?? []).map(({ im_user_profile: p = {} }) => ({\n uid: p.user_id_str ?? '',\n uniqueId: p.unique_id ?? '',\n nickname: p.nick_name ?? '',\n ...avatarLinks(p.avatars),\n }));\n }\n\n /**\n * Download an avatar (tries each URL in turn, skips heic). No cookie / Referer sent: the image CDN is cross-origin and\n * URLs are already signed. `null` if no URL could be downloaded.\n */\n async downloadAvatar(user: Pick<TikTokUser, 'avatars'>): Promise<{ data: Buffer; type: string } | null> {\n for (const url of user.avatars) {\n const res = await fetch(url, {\n headers: { 'User-Agent': this.ctx.userAgent },\n referrerPolicy: 'no-referrer',\n signal: AbortSignal.timeout(this.ctx.options.timeoutMs ?? 20_000),\n }).catch(() => null);\n const type = res?.headers.get('content-type') ?? '';\n if (!res?.ok || !/^image\\/(jpeg|png|webp|gif)/.test(type)) continue;\n return { data: Buffer.from(await res.arrayBuffer()), type };\n }\n return null;\n }\n\n /** Mark as read up to message `msg`. */\n async markRead(msg: ConversationRef & { index: string }): Promise<void> {\n await this.ctx.im('/v3/conversation/mark_read', Cmd.MarkRead, markReadBody(msg), MARK_READ_BODY_FIELD);\n }\n\n // ─── Send ─────────────────────────────────────────────────────────────────\n\n /** Send a text message to a conversation (usually the just-received `msg` — it carries the conversation ids). */\n async sendMessage(conv: ConversationRef, text: string): Promise<SendResult> {\n const cid = randomUUID();\n await this.send(conv, cid, MessageType.Text, JSON.stringify({ aweType: 0, text }), null);\n return { clientMessageId: cid };\n }\n\n /** Send an image: upload to the private (encrypted) store, then send an image message. */\n async sendImage(conv: ConversationRef, image: ImageInput): Promise<SendResult> {\n const up = await this.media.upload(image);\n const cid = randomUUID();\n await this.send(conv, cid, MessageType.PrivateImage, '', imageMessageExtra(up));\n return { clientMessageId: cid };\n }\n\n /**\n * Set YOUR reaction on a message — like TikTok Web, one reaction per user: a new `emoji` replaces the old one,\n * `null` = remove. Pass the whole `msg` just read (current `reactions` are needed to know which key to remove).\n * Sends `ModifyMessagePropertyRequestBody` (command 705) — see README.\n */\n async setReaction(msg: TikTokMessage, emoji: string | null): Promise<void> {\n const own = this.ctx.ownId;\n const ops = reactionOps(msg, own, emoji);\n if (ops.length === 0) return;\n const body = await this.ctx.im('/v1/message/set_property', Cmd.SetMessageProperty, setPropertyBody(msg, ops, own));\n // status: 0 success, 1 duplicate (also done); anything else means TikTok rejected it.\n const status = body.num(1);\n if (status > 1) throw new TikTokError('IM_ERROR', `set_property: status ${status}`, status);\n }\n\n // ─── Media ─────────────────────────────────────────────────────────────────\n\n /** Download a sticker image (public CDN, unencrypted, no cookie needed). */\n async downloadSticker(sticker: Pick<TikTokSticker, 'url'>): Promise<{ data: Buffer; type: string }> {\n const res = await fetch(sticker.url, {\n headers: { 'User-Agent': this.ctx.userAgent },\n referrerPolicy: 'no-referrer',\n signal: AbortSignal.timeout(this.ctx.options.timeoutMs ?? 20_000),\n }).catch(() => null);\n if (!res?.ok) throw new TikTokError('DOWNLOAD_FAILED', `Sticker download failed: ${res?.status ?? 'network'}`);\n return { data: Buffer.from(await res.arrayBuffer()), type: res.headers.get('content-type') ?? 'image/webp' };\n }\n\n /** Download + decrypt a message's image (pass the whole `msg` — listener messages lack the URL, this looks it up). */\n async downloadImage(source: TikTokMessage | TikTokImage, which: 'origin' | 'thumb' = 'origin'): Promise<Buffer> {\n let image = 'serverMessageId' in source ? source.image : source;\n // Websocket-pushed messages only have uri + key, no download URL → look up history by message id.\n if ('serverMessageId' in source && image && !image.url) {\n const { messages } = await this.getMessages(source, { limit: 5, before: (BigInt(source.index) + 1n).toString() });\n image = messages.find((m) => m.serverMessageId === source.serverMessageId)?.image ?? image;\n }\n if (!image) throw new TikTokError('DOWNLOAD_FAILED', 'Message has no image');\n return this.media.download(image, which);\n }\n\n private async send(\n conv: ConversationRef,\n cid: string,\n type: number,\n content: string,\n extra: Writer | null,\n ): Promise<void> {\n if (!conv.conversationId || !conv.conversationShortId) {\n throw new TikTokError('IM_ERROR', 'Missing conversationId / conversationShortId');\n }\n await this.ctx.im('/v1/message/send', Cmd.SendMessage, sendMessageBody(conv, cid, type, content, extra));\n }\n}\n","/**\n * TikTok Web IM protocol constants — reconstructed from `tiktok.com/business-suite/messages` traffic\n * (09/11/2026). If TikTok changes things on their side, fix them here; how they were found is in the README.\n */\n\nexport const IM_API = 'https://im-api-sg.tiktok.com';\nexport const IM_WS = 'wss://im-ws-sg.tiktok.com/ws/v2';\nexport const WEB = 'https://www.tiktok.com';\n\nexport const DEFAULT_USER_AGENT =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/156.0.0.0 Safari/537.36';\n\n/** TikTok Web app id (HTTP IM). */\nexport const AID_WEB = '1988';\n/** \"frontier\" channel app id — websocket pushing new messages. */\nexport const AID_FRONTIER = '1459';\nexport const SDK_VERSION = '1.8.5';\nexport const BUILD_NUMBER = 'feffaa1:master';\n\n/** Websocket `access_key` = md5(FPID + APP_KEY + device_id + SALT). */\nexport const FRONTIER_FPID = '9';\nexport const FRONTIER_APP_KEY = 'e1bd35ec9db7b8d846de66ed140b1ad9';\nexport const FRONTIER_SALT = 'f8a69f1719916z';\n\n/** IM command (`Request.cmd`) — also the body's field number in `RequestBody`. */\nexport const Cmd = {\n SendMessage: 100,\n GetMessagesByUserInit: 203,\n GetMessagesByConversation: 301,\n /** Websocket push of new messages. */\n NewMessage: 500,\n /** Add / remove a reaction (message `property`) — `/v1/message/set_property`. */\n SetMessageProperty: 705,\n GetConversationInfo: 610,\n MarkRead: 2002,\n UploadConfig: 2059,\n} as const;\n\n/** MarkRead's body lives in field 604, not the command number. */\nexport const MARK_READ_BODY_FIELD = 604;\n\n/** `MessageBody.message_type`. */\nexport const MessageType = {\n System: 1,\n Text: 7,\n ShareVideo: 8,\n PrivateImage: 1802,\n /** Sticker — public image on the sticker CDN, not encrypted. */\n Sticker: 1805,\n /** Hidden command (read receipt…) — not shown in the conversation, but still pushed over websocket. */\n Command: 50001,\n} as const;\n\nexport const ConversationType = { OneToOne: 1 } as const;\n\n/** `Response` fields (both HTTP and websocket payload). */\nexport const Resp = { cmd: 1, seq: 2, status: 3, error: 4, body: 6, logId: 7 } as const;\n\n/** `MessageBody` fields. */\nexport const MsgF = {\n conversationId: 1,\n conversationType: 2,\n serverMessageId: 3,\n indexInConversation: 4,\n conversationShortId: 5,\n type: 6,\n sender: 7,\n content: 8,\n ext: 9,\n createTime: 10,\n /** `property_list` — reaction: `{1 key \"e:<emoji>\", 2 {1 PropertyItem[]}}`. */\n propertyList: 15,\n attachments: 20,\n tag: 21,\n} as const;\n\n/** Operation on a message `property` (`ModifyPropertyContent.operation`). */\nexport const PropertyOp = { Add: 0, Remove: 1 } as const;\n\n/** Reaction key = `e:<emoji>`. The mobile app reacting ❤️ also writes the legacy key `e:love` — treated as ❤️. */\nexport const REACTION_PREFIX = 'e:';\nexport const REACTION_LOVE_KEY = 'e:love';\nexport const REACTION_HEART = '\\u2764\\uFE0F';\n\n/** ImageX — image upload endpoint (AWS SigV4-style signing). */\nexport const IMAGEX_REGION = 'cn-north-1';\nexport const IMAGEX_SERVICE = 'imagex';\nexport const IMAGEX_VERSION = '2018-08-01';\n","/**\n * Minimal protobuf, no `.proto` file needed.\n *\n * TikTok doesn't publish its IM `.proto` — field numbers in `constants.ts` were reconstructed from bytes captured\n * in the browser (09/11/2026). Only three wire types are needed: varint (0), length-delimited (2), and skipping 1/5.\n */\n\nexport interface Field {\n n: number;\n wire: number;\n int: bigint;\n bytes: Buffer;\n}\n\nfunction readVarint(b: Buffer, i: number): [bigint, number] {\n let x = 0n;\n let shift = 0n;\n let c: number;\n do {\n if (i >= b.length) throw new Error('varint runs past end of buffer');\n c = b[i++];\n x |= BigInt(c & 127) << shift;\n shift += 7n;\n } while (c & 128);\n return [x, i];\n}\n\n/** Split a message into a list of fields. `null` if the bytes are not valid protobuf. */\nexport function decodeFields(b: Buffer): Field[] | null {\n const out: Field[] = [];\n let i = 0;\n try {\n while (i < b.length) {\n const [key, j] = readVarint(b, i);\n i = j;\n const wire = Number(key & 7n);\n const n = Number(key >> 3n);\n if (n === 0) return null;\n if (wire === 0) {\n const [v, k] = readVarint(b, i);\n out.push({ n, wire, int: v, bytes: Buffer.alloc(0) });\n i = k;\n } else if (wire === 2) {\n const [len, k] = readVarint(b, i);\n const end = k + Number(len);\n if (end > b.length) return null;\n out.push({ n, wire, int: 0n, bytes: b.subarray(k, end) });\n i = end;\n } else if (wire === 1 || wire === 5) {\n const size = wire === 1 ? 8 : 4;\n if (i + size > b.length) return null;\n out.push({ n, wire, int: 0n, bytes: b.subarray(i, i + size) });\n i += size;\n } else {\n return null;\n }\n }\n } catch {\n return null;\n }\n return out;\n}\n\n/** Decoded message — read by field number. */\nexport class Msg {\n private constructor(readonly fields: Field[]) {}\n\n static parse(b: Buffer): Msg {\n const f = decodeFields(b);\n if (!f) throw new Error('Failed to decode protobuf');\n return new Msg(f);\n }\n\n static tryParse(b: Buffer | undefined): Msg | null {\n if (!b) return null;\n const f = decodeFields(b);\n return f ? new Msg(f) : null;\n }\n\n has(n: number): boolean {\n return this.fields.some((f) => f.n === n);\n }\n\n int(n: number): bigint {\n return this.fields.find((f) => f.n === n)?.int ?? 0n;\n }\n\n num(n: number): number {\n return Number(this.int(n));\n }\n\n /** 64-bit integer as a string — uids / message IDs exceed `Number.MAX_SAFE_INTEGER`. */\n id(n: number): string {\n return this.int(n).toString();\n }\n\n bytes(n: number): Buffer {\n return this.fields.find((f) => f.n === n)?.bytes ?? Buffer.alloc(0);\n }\n\n str(n: number): string {\n return this.bytes(n).toString('utf8');\n }\n\n strs(n: number): string[] {\n return this.fields.filter((f) => f.n === n).map((f) => f.bytes.toString('utf8'));\n }\n\n msg(n: number): Msg | null {\n return Msg.tryParse(this.fields.find((f) => f.n === n)?.bytes);\n }\n\n msgs(n: number): Msg[] {\n return this.fields\n .filter((f) => f.n === n)\n .map((f) => Msg.tryParse(f.bytes))\n .filter((m): m is Msg => m !== null);\n }\n\n /** `map<string,string>` — each entry is a message `{1: key, 2: value}`. */\n map(n: number): Record<string, string> {\n const out: Record<string, string> = {};\n for (const m of this.msgs(n)) out[m.str(1)] = m.str(2);\n return out;\n }\n}\n\nfunction varint(x: bigint | number): Buffer {\n let v = BigInt(x);\n if (v < 0n) v = BigInt.asUintN(64, v);\n const out: number[] = [];\n do {\n const c = Number(v & 127n);\n v >>= 7n;\n out.push(v ? c | 128 : c);\n } while (v);\n return Buffer.from(out);\n}\n\nconst key = (n: number, wire: number): Buffer => varint((BigInt(n) << 3n) | BigInt(wire));\n\n/** Build a message in call order. */\nexport class Writer {\n private readonly parts: Buffer[] = [];\n\n int(n: number, v: bigint | number | string): this {\n this.parts.push(key(n, 0), varint(typeof v === 'string' ? BigInt(v) : v));\n return this;\n }\n\n bytes(n: number, v: Buffer | string): this {\n const b = typeof v === 'string' ? Buffer.from(v, 'utf8') : v;\n this.parts.push(key(n, 2), varint(b.length), b);\n return this;\n }\n\n msg(n: number, w: Writer): this {\n return this.bytes(n, w.finish());\n }\n\n /** `map<string,string>` in the object's key order. */\n map(n: number, obj: Record<string, string>): this {\n for (const [k, v] of Object.entries(obj)) this.msg(n, new Writer().bytes(1, k).bytes(2, v));\n return this;\n }\n\n finish(): Buffer {\n return Buffer.concat(this.parts);\n }\n}\n","/** Error from TikTok or from the library itself. `code` lets callers branch (e.g. session expired → rescan). */\nexport class TikTokError extends Error {\n constructor(\n readonly code: TikTokErrorCode,\n message: string,\n /** Original TikTok code (status_code / error_code), if any. */\n readonly upstream?: number | string,\n ) {\n super(message);\n this.name = 'TikTokError';\n }\n}\n\nexport type TikTokErrorCode =\n /** Cookie is no longer logged in — a new session is required. */\n | 'SESSION_EXPIRED'\n | 'HTTP_ERROR'\n /** IM returned a non-zero `status_code`. */\n | 'IM_ERROR'\n | 'UPLOAD_FAILED'\n | 'DOWNLOAD_FAILED'\n | 'BAD_RESPONSE'\n /** QR login failed (Chrome failed to launch, QR expired, no cookie found…). */\n | 'LOGIN_FAILED'\n /** Caller aborted QR login (`signal`). */\n | 'LOGIN_ABORTED';\n","import { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport { AID_FRONTIER, FRONTIER_FPID, IM_WS, WEB } from '../protocol/constants';\nimport type { Context } from '../protocol/context';\nimport { parseMessage } from '../models/message';\nimport type { TikTokMessage } from '../models/types';\nimport { accessKey, buildAck, newMessageBody, parseFrame } from './frame';\n\n/** The browser sends \"hi\" every 15–30 seconds; use the lower bound. */\nconst HEARTBEAT_MS = 15_000;\n/** If nothing (not even a \"hi\" reply) arrives for this long, treat it as silently dead and reconnect. */\nconst IDLE_TIMEOUT_MS = 75_000;\nconst RECONNECT_MIN_MS = 2_000;\nconst RECONNECT_MAX_MS = 60_000;\n/** The server pushes the same message several times (one frame per device) — remember this many message IDs to dedupe. */\nconst DEDUPE_KEEP = 500;\n\n/** Close code used when we call `stop()` ourselves. */\nexport const MANUAL_CLOSE = 4000;\n\nexport interface ListenerEvents {\n connected: [];\n /** A real message in a conversation (including our own — see `isSelf`). */\n message: [TikTokMessage];\n /** Hidden command: mark as read… (`content.command_type`). */\n command: [TikTokMessage];\n /** Close code + reason. Unless `MANUAL_CLOSE`, the listener reconnects automatically (if enabled). */\n closed: [number, string];\n error: [Error];\n}\n\n/**\n * Listen for new messages via the `im-ws-sg` websocket (aid 1459) — cookies only.\n * Events: `connected`, `message`, `closed`, `error`.\n */\nexport class Listener extends EventEmitter<ListenerEvents> {\n private ws: WebSocket | null = null;\n private heartbeat: NodeJS.Timeout | null = null;\n private idle: NodeJS.Timeout | null = null;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private backoff = RECONNECT_MIN_MS;\n private retry = true;\n private readonly seen = new Set<string>();\n\n constructor(private readonly ctx: Context) {\n super();\n }\n\n get connected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n url(): string {\n const q = new URLSearchParams({\n aid: AID_FRONTIER,\n fpid: FRONTIER_FPID,\n device_platform: 'web',\n version_code: 'fws_1.0.0',\n access_key: accessKey(this.ctx.deviceId),\n device_id: this.ctx.deviceId,\n // Cookie value is already URL-encoded (`1%7C…`) — decode it so URLSearchParams doesn't double-encode.\n ttwid: decodeURIComponent(this.ctx.cookies.get('ttwid') ?? ''),\n xsack: '1',\n xaack: '1',\n xsqos: '0',\n });\n return `${IM_WS}?${q}`;\n }\n\n /** Open the connection. `retryOnClose` (on by default): reconnect on drop, backing off up to 1 minute. */\n start(opts: { retryOnClose?: boolean } = {}): void {\n this.retry = opts.retryOnClose ?? true;\n this.connect();\n }\n\n stop(): void {\n this.retry = false;\n this.clearTimers();\n if (this.reconnectTimer) clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n const ws = this.ws;\n this.ws = null;\n if (ws && ws.readyState <= WebSocket.OPEN) ws.close(MANUAL_CLOSE, 'manual');\n }\n\n private connect(): void {\n const ws = new WebSocket(this.url(), {\n headers: { Cookie: this.ctx.cookieHeader(), 'User-Agent': this.ctx.userAgent, Origin: WEB },\n });\n this.ws = ws;\n ws.binaryType = 'nodebuffer';\n ws.on('open', () => {\n this.backoff = RECONNECT_MIN_MS;\n this.heartbeat = setInterval(() => ws.readyState === WebSocket.OPEN && ws.send('hi'), HEARTBEAT_MS);\n this.touch();\n this.ctx.log('debug', 'tca: websocket connected');\n this.emit('connected');\n });\n ws.on('message', (data: Buffer, isBinary: boolean) => {\n this.touch();\n if (!isBinary) return; // \"hi\" reply\n try {\n this.onFrame(ws, data);\n } catch (err) {\n this.emit('error', err instanceof Error ? err : new Error(String(err)));\n }\n });\n ws.on('error', (err) => this.emit('error', err));\n ws.on('close', (code, reason) => {\n if (this.ws !== ws) return; // old connection was replaced\n this.ws = null;\n this.clearTimers();\n this.emit('closed', code, reason.toString());\n if (!this.retry || code === MANUAL_CLOSE) return;\n const wait = this.backoff;\n this.backoff = Math.min(this.backoff * 2, RECONNECT_MAX_MS);\n this.ctx.log('warn', `tca: websocket closed (${code}) — reconnecting in ${wait / 1000}s`);\n this.reconnectTimer = setTimeout(() => this.connect(), wait);\n });\n }\n\n private touch(): void {\n if (this.idle) clearTimeout(this.idle);\n this.idle = setTimeout(() => {\n this.ctx.log('warn', 'tca: websocket silent too long — reconnecting');\n this.ws?.terminate();\n }, IDLE_TIMEOUT_MS);\n }\n\n private clearTimers(): void {\n if (this.heartbeat) clearInterval(this.heartbeat);\n if (this.idle) clearTimeout(this.idle);\n this.heartbeat = null;\n this.idle = null;\n }\n\n private onFrame(ws: WebSocket, data: Buffer): void {\n const frame = parseFrame(data);\n if (frame.headers.need_ack === '1' && ws.readyState === WebSocket.OPEN) ws.send(buildAck(frame));\n const body = newMessageBody(frame);\n if (!body) return;\n const msg = parseMessage(body, this.ctx.ownId);\n if (!msg.serverMessageId || this.seen.has(msg.serverMessageId)) return;\n this.seen.add(msg.serverMessageId);\n if (this.seen.size > DEDUPE_KEEP) this.seen.delete(this.seen.values().next().value as string);\n if (msg.kind === 'command') this.emit('command', msg);\n else this.emit('message', msg);\n }\n}\n","import { MessageType, MsgF, REACTION_HEART, REACTION_LOVE_KEY, REACTION_PREFIX } from '../protocol/constants';\nimport { Msg } from '../protocol/proto';\nimport type { TikTokConversation, TikTokImage, TikTokMessage, TikTokReaction, TikTokSticker } from './types';\n\nfunction parseJson(s: string): unknown {\n try {\n return JSON.parse(s);\n } catch {\n return s;\n }\n}\n\n/** Image in `attachments` (field 20 → 4): 1 = original, 2 = thumbnail; each {1 uri, 2 url…, 5 {w,h}, 6 key}. */\nfunction parseImage(m: Msg): TikTokImage | null {\n const media = m.msg(MsgF.attachments)?.msg(4);\n const origin = media?.msg(1);\n if (!media || !origin) return null;\n const size = origin.msg(5);\n return {\n uri: origin.str(1),\n url: origin.strs(2)[0] ?? '',\n thumbUrl: media.msg(2)?.strs(2)[0] ?? '',\n width: size?.num(1) ?? 0,\n height: size?.num(2) ?? 0,\n decryptKey: origin.str(6),\n };\n}\n\n/**\n * Sticker (field 20 → 7): `{1 {1 uri, 2 url, 5 {w,h}}, 2 hint, 200 {1 {2 map a:sticker_id, a:sticker_type…}}}`.\n * Links point to the public sticker CDN (`…-dm-sticker-sign-…`) — not encrypted, no cookies needed.\n */\nfunction parseSticker(m: Msg): TikTokSticker | null {\n const st = m.msg(MsgF.attachments)?.msg(7);\n const img = st?.msg(1);\n if (!st || !img) return null;\n const size = img.msg(5);\n const ext = st.msg(200)?.msg(1)?.map(2) ?? {};\n return {\n stickerId: ext['a:sticker_id'] ?? '',\n stickerType: Number(ext['a:sticker_type'] ?? 0),\n url: img.str(2),\n width: size?.num(1) ?? 0,\n height: size?.num(2) ?? 0,\n };\n}\n\n/**\n * Reaction (field 15, `property_list`): each entry `{1 key, 2 {1 PropertyItem[]}}`, key `e:<emoji>`,\n * `PropertyItem {1 uid, 3 sec, 4 idempotent_id, 6 µs}`. The mobile app writes ❤️ as both `e:❤️` and `e:love` → merged.\n */\nfunction parseReactions(m: Msg): TikTokReaction[] {\n const byEmoji = new Map<string, Set<string>>();\n for (const p of m.msgs(MsgF.propertyList)) {\n const key = p.str(1);\n if (!key.startsWith(REACTION_PREFIX)) continue;\n const emoji = key === REACTION_LOVE_KEY ? REACTION_HEART : key.slice(REACTION_PREFIX.length);\n const uids = byEmoji.get(emoji) ?? new Set<string>();\n for (const item of p.msg(2)?.msgs(1) ?? []) uids.add(item.id(1));\n if (uids.size) byEmoji.set(emoji, uids);\n }\n return [...byEmoji].map(([emoji, uids]) => ({ emoji, userIds: [...uids] }));\n}\n\nconst KIND: Record<number, TikTokMessage['kind']> = {\n [MessageType.Text]: 'text',\n [MessageType.PrivateImage]: 'image',\n [MessageType.Sticker]: 'sticker',\n [MessageType.ShareVideo]: 'video',\n [MessageType.System]: 'system',\n [MessageType.Command]: 'command',\n};\n\n/** `MessageBody` → `TikTokMessage`. */\nexport function parseMessage(m: Msg, ownId: string): TikTokMessage {\n const type = m.num(MsgF.type);\n const content = parseJson(m.str(MsgF.content));\n const ext = m.map(MsgF.ext);\n const senderId = m.id(MsgF.sender);\n const text =\n type === MessageType.Text && content && typeof content === 'object' && 'text' in content\n ? String((content as { text: unknown }).text ?? '')\n : '';\n return {\n conversationId: m.str(MsgF.conversationId),\n conversationShortId: m.id(MsgF.conversationShortId),\n serverMessageId: m.id(MsgF.serverMessageId),\n index: m.id(MsgF.indexInConversation),\n type,\n kind: KIND[type] ?? 'other',\n senderId,\n isSelf: senderId === ownId,\n text,\n image: type === MessageType.PrivateImage ? parseImage(m) : null,\n sticker: type === MessageType.Sticker ? parseSticker(m) : null,\n reactions: parseReactions(m),\n content,\n createdAt: m.num(MsgF.createTime),\n clientMessageId: ext['s:client_message_id'] ?? '',\n ext,\n };\n}\n\n/** `ConversationInfo` (field 2 of get_by_user_init): {1 id, 2 shortId, 3 type, 6 {1 {1 uid}}…}. */\nexport function parseConversation(m: Msg, ownId: string): TikTokConversation {\n const participants = (m.msg(6)?.msgs(1) ?? []).map((p) => p.id(1));\n return {\n conversationId: m.str(1),\n conversationShortId: m.id(2),\n participants,\n peerId: participants.find((p) => p !== ownId) ?? '',\n };\n}\n","/**\n * \"Frontier\" websocket framing — pure helpers (no socket), shared by the listener and unit tests.\n */\nimport { createHash } from 'node:crypto';\nimport { Cmd, FRONTIER_APP_KEY, FRONTIER_FPID, FRONTIER_SALT, Resp } from '../protocol/constants';\nimport { lz4Decompress } from '../protocol/lz4';\nimport { Msg, Writer } from '../protocol/proto';\n\n/** \"Frontier\" websocket frame: {1 seqid, 2 logid, 3 service, 4 method, 5 headers, 6 encoding, 7 type, 8 payload}. */\nexport interface Frame {\n seq: bigint;\n logId: bigint;\n headers: Record<string, string>;\n encoding: string;\n payload: Buffer;\n}\n\n/** Websocket `access_key` = md5(FPID + APP_KEY + device_id + SALT). */\nexport function accessKey(deviceId: string): string {\n return createHash('md5')\n .update(FRONTIER_FPID + FRONTIER_APP_KEY + deviceId + FRONTIER_SALT)\n .digest('hex');\n}\n\nexport function parseFrame(data: Buffer): Frame {\n const m = Msg.parse(data);\n return {\n seq: m.int(1),\n logId: m.int(2),\n headers: m.map(5),\n encoding: m.str(6),\n payload: m.bytes(8),\n };\n}\n\n/** The `MessageBody` of a `NewMessage` push inside a frame, or `null` if the frame carries something else. */\nexport function newMessageBody(frame: Frame): Msg | null {\n if (frame.payload.length === 0) return null;\n const payload = frame.encoding === '__lz4' ? lz4Decompress(frame.payload) : frame.payload;\n const resp = Msg.tryParse(payload);\n if (!resp || resp.num(Resp.cmd) !== Cmd.NewMessage) return null;\n // NewMessageNotify: {2 conversationId, 3 type, 4 ?, 5 MessageBody}\n return resp.msg(Resp.body)?.msg(Cmd.NewMessage)?.msg(5) ?? null;\n}\n\n/** Acknowledge receipt — `ack_id = <base36 time>:<x_frontier_traceid>`, echoing the frame's seq/logid. */\nexport function buildAck(f: Frame, now: number = Date.now()): Buffer {\n const ackId = `${now.toString(36)}:${f.headers.x_frontier_traceid ?? ''}`;\n return new Writer()\n .int(1, f.seq)\n .int(2, f.logId)\n .int(3, 5)\n .int(4, 1)\n .map(5, { is_ack: '1', ack_id: ackId, ack_code: '0' })\n .bytes(9, ackId)\n .finish();\n}\n","/**\n * LZ4 block decompression (no frame header) — websocket frames with\n * `payload_encoding = \"__lz4\"` compress their payload this way. No extra dependency for ~30 lines.\n */\nexport function lz4Decompress(src: Buffer): Buffer {\n let out = Buffer.alloc(Math.max(src.length * 4, 1024));\n let o = 0;\n const ensure = (need: number) => {\n if (o + need <= out.length) return;\n let size = out.length * 2;\n while (o + need > size) size *= 2;\n const bigger = Buffer.alloc(size);\n out.copy(bigger, 0, 0, o);\n out = bigger;\n };\n let i = 0;\n while (i < src.length) {\n const token = src[i++];\n let lit = token >> 4;\n if (lit === 15) {\n let b: number;\n do {\n b = src[i++];\n lit += b;\n } while (b === 255);\n }\n ensure(lit);\n src.copy(out, o, i, i + lit);\n i += lit;\n o += lit;\n if (i >= src.length) break;\n const offset = src[i] | (src[i + 1] << 8);\n i += 2;\n if (offset === 0 || offset > o) throw new Error('lz4: invalid offset');\n let len = token & 15;\n if (len === 15) {\n let b: number;\n do {\n b = src[i++];\n len += b;\n } while (b === 255);\n }\n len += 4;\n ensure(len);\n // Copy byte by byte: source and destination ranges may overlap.\n for (let k = 0; k < len; k++, o++) out[o] = out[o - offset];\n }\n return out.subarray(0, o);\n}\n","import { AID_FRONTIER, WEB } from '../protocol/constants';\nimport type { Context } from '../protocol/context';\nimport type { TikTokAccount } from '../models/types';\n\n/** Account for the cookie — `null` if the cookie is no longer logged in. */\nexport async function fetchAccount(ctx: Context): Promise<TikTokAccount | null> {\n type Res = {\n message?: string;\n data?: { user_id_str?: string; username?: string; screen_name?: string; avatar_url?: string; error_code?: number };\n };\n const res = await ctx.getJson<Res>(`${WEB}/passport/web/account/info/?aid=${AID_FRONTIER}`);\n if (res.message !== 'success' || !res.data?.user_id_str) return null;\n return {\n uid: res.data.user_id_str,\n username: res.data.username ?? '',\n screenName: res.data.screen_name ?? '',\n avatar: res.data.avatar_url ?? '',\n };\n}\n\n/** `wid` — device id TikTok issues per `ttwid` cookie; IM + websocket use it as `device_id`. */\nexport async function fetchWebId(ctx: Context): Promise<string> {\n const res = await ctx.fetch(`${WEB}/api/v1/web-cookie-privacy/config?locale=vi-VN&appId=1988&theme=default&tea=1`);\n if (!res.ok) return '';\n return /\"wid\":\"(\\d+)\"/.exec(await res.text())?.[1] ?? '';\n}\n\n/**\n * Avatar URLs from the IM profile `avatars` — collects every `https://…` string (thumb → medium → larger, whatever\n * TikTok returns), then puts browser-readable ones (jpeg/webp/png) first and heic last.\n */\nexport function avatarLinks(avatars: unknown): { avatar: string; avatars: string[] } {\n const urls: string[] = [];\n const walk = (v: unknown) => {\n if (typeof v === 'string') {\n if (/^https?:\\/\\//.test(v) && !urls.includes(v)) urls.push(v);\n } else if (Array.isArray(v)) v.forEach(walk);\n else if (v && typeof v === 'object') Object.values(v).forEach(walk);\n };\n walk(avatars);\n const heic = (u: string) => /\\.heic(\\?|$)/i.test(u.split('?')[0] + '?');\n const ranked = [...urls.filter((u) => !heic(u)), ...urls.filter(heic)];\n return { avatar: ranked[0] ?? '', avatars: ranked };\n}\n","import { createDecipheriv, createHash, createHmac, randomBytes } from 'node:crypto';\nimport { crc32 } from 'node:zlib';\nimport { Cmd, IMAGEX_REGION, IMAGEX_SERVICE, IMAGEX_VERSION } from '../protocol/constants';\nimport type { Context } from '../protocol/context';\nimport { TikTokError } from '../errors';\nimport type { ImageInput, TikTokImage } from '../models/types';\n\n/** Temporary STS credentials for the image store — obtained via IM command `UploadConfig`, valid ~10 minutes. */\ninterface UploadCredential {\n serviceId: string;\n host: string;\n accessKeyId: string;\n sessionToken: string;\n secretAccessKey: string;\n /** ms epoch */\n expiresAt: number;\n}\n\n/** Uploaded image, encrypted by the store — enough to send an image message. */\nexport interface UploadedImage {\n uri: string;\n secretKey: string;\n width: number;\n height: number;\n size: number;\n filename: string;\n}\n\nconst sha256 = (s: string | Buffer) => createHash('sha256').update(s).digest('hex');\nconst hmac = (k: string | Buffer, s: string) => createHmac('sha256', k).update(s).digest();\nconst rfc3986 = (s: string) =>\n encodeURIComponent(s).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);\n\nexport class MediaClient {\n private cred: UploadCredential | null = null;\n\n constructor(private readonly ctx: Context) {}\n\n /** `UploadConfig` returns two credential sets: type 1 = ImageX (images), type 2 = VOD (video). Use type 1. */\n private async credential(): Promise<UploadCredential> {\n if (this.cred && this.cred.expiresAt - Date.now() > 60_000) return this.cred;\n const body = await this.ctx.im('/v1/media/upload_config', Cmd.UploadConfig, Buffer.alloc(0));\n const c = body.msgs(1).find((m) => m.num(1) === 1);\n if (!c) throw new TikTokError('UPLOAD_FAILED', 'upload_config has no ImageX credentials');\n this.cred = {\n serviceId: c.str(2),\n host: c.str(3),\n expiresAt: c.num(4),\n accessKeyId: c.str(6),\n sessionToken: c.str(7),\n secretAccessKey: c.str(8),\n };\n return this.cred;\n }\n\n /** Call ImageX signed with AWS SigV4 (region cn-north-1, service imagex). */\n private async imagex<T>(method: 'GET' | 'POST', params: Record<string, string>, body?: string): Promise<T> {\n const c = await this.credential();\n const amzDate = new Date().toISOString().replace(/[-:]/g, '').replace(/\\.\\d+/, '');\n const date = amzDate.slice(0, 8);\n const query = Object.keys(params)\n .sort()\n .map((k) => `${rfc3986(k)}=${rfc3986(params[k])}`)\n .join('&');\n const headers: Record<string, string> = { 'x-amz-date': amzDate, 'x-amz-security-token': c.sessionToken };\n if (body !== undefined) headers['x-amz-content-sha256'] = sha256(body);\n const signed = Object.keys(headers).sort();\n const canonical = [\n method,\n '/',\n query,\n signed.map((k) => `${k}:${headers[k]}\\n`).join(''),\n signed.join(';'),\n body !== undefined ? headers['x-amz-content-sha256'] : sha256(''),\n ].join('\\n');\n const scope = `${date}/${IMAGEX_REGION}/${IMAGEX_SERVICE}/aws4_request`;\n const toSign = ['AWS4-HMAC-SHA256', amzDate, scope, sha256(canonical)].join('\\n');\n let k = hmac(`AWS4${c.secretAccessKey}`, date);\n k = hmac(k, IMAGEX_REGION);\n k = hmac(k, IMAGEX_SERVICE);\n k = hmac(k, 'aws4_request');\n const signature = createHmac('sha256', k).update(toSign).digest('hex');\n const res = await fetch(`https://${c.host}/?${query}`, {\n method,\n headers: {\n ...headers,\n Authorization: `AWS4-HMAC-SHA256 Credential=${c.accessKeyId}/${scope}, SignedHeaders=${signed.join(';')}, Signature=${signature}`,\n ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n },\n body,\n signal: AbortSignal.timeout(this.ctx.options.timeoutMs ?? 20_000),\n });\n const json = (await res.json().catch(() => null)) as {\n ResponseMetadata?: { Error?: { Message?: string; CodeN?: number } };\n } | null;\n const err = json?.ResponseMetadata?.Error;\n if (!res.ok || !json || err) {\n throw new TikTokError(\n 'UPLOAD_FAILED',\n `imagex ${params.Action}: ${err?.Message ?? `HTTP ${res.status}`}`,\n err?.CodeN ?? res.status,\n );\n }\n return json as T;\n }\n\n /**\n * Upload an image to the private message store: Apply → POST file → Commit (the store encrypts with AES-GCM\n * and returns the key). Image dimensions come from the Commit result — no need to decode the image.\n */\n async upload(image: ImageInput): Promise<UploadedImage> {\n const c = await this.credential();\n const ext = (/\\.([a-z0-9]{2,5})$/i.exec(image.filename)?.[1] ?? 'jpg').toLowerCase();\n type Apply = {\n Result: {\n UploadAddress: { StoreInfos: { StoreUri: string; Auth: string }[]; UploadHosts: string[]; SessionKey: string };\n };\n };\n const apply = await this.imagex<Apply>('GET', {\n Action: 'ApplyImageUpload',\n Version: IMAGEX_VERSION,\n ServiceId: c.serviceId,\n FileSize: String(image.data.length),\n FileExtension: `.${ext}`,\n s: randomBytes(6).toString('hex').slice(0, 10),\n });\n const addr = apply.Result.UploadAddress;\n const store = addr.StoreInfos[0];\n const host = addr.UploadHosts[0];\n if (!store || !host) throw new TikTokError('UPLOAD_FAILED', 'ApplyImageUpload returned no upload target');\n\n const up = await fetch(`https://${host}/upload/v1/${store.StoreUri}`, {\n method: 'POST',\n headers: {\n Authorization: store.Auth,\n 'Content-CRC32': (crc32(image.data) >>> 0).toString(16).padStart(8, '0'),\n // Without this header the store returns 4004 Not Found.\n 'X-Storage-U': this.ctx.ownId,\n 'Content-Type': 'application/octet-stream',\n 'Content-Disposition': 'attachment; filename=\"undefined\"',\n },\n body: new Uint8Array(image.data),\n signal: AbortSignal.timeout(this.ctx.options.timeoutMs ?? 60_000),\n });\n const upJson = (await up.json().catch(() => null)) as { code?: number; message?: string } | null;\n if (!up.ok || upJson?.code !== 2000) {\n throw new TikTokError(\n 'UPLOAD_FAILED',\n `upload: ${upJson?.message ?? `HTTP ${up.status}`}`,\n upJson?.code ?? up.status,\n );\n }\n\n type Commit = {\n Result: {\n Results: { Encryption?: { Uri: string; SecretKey: string; Extra?: Record<string, string> } }[];\n PluginResult?: { ImageWidth?: number; ImageHeight?: number; ImageSize?: number }[];\n };\n };\n const commit = await this.imagex<Commit>(\n 'POST',\n { Action: 'CommitImageUpload', Version: IMAGEX_VERSION, ServiceId: c.serviceId },\n JSON.stringify({\n SessionKey: addr.SessionKey,\n Functions: [{ name: 'Encryption', input: { Config: { copies: 'cipher_v2' } } }],\n }),\n );\n const enc = commit.Result.Results?.[0]?.Encryption;\n const plugin = commit.Result.PluginResult?.[0];\n if (!enc?.Uri || !enc.SecretKey)\n throw new TikTokError('UPLOAD_FAILED', 'CommitImageUpload returned no encrypted result');\n return {\n uri: enc.Uri,\n secretKey: enc.SecretKey,\n width: plugin?.ImageWidth ?? Number(enc.Extra?.img_width ?? 0),\n height: plugin?.ImageHeight ?? Number(enc.Extra?.img_height ?? 0),\n size: plugin?.ImageSize ?? image.data.length,\n filename: image.filename,\n };\n }\n\n /** Download and decrypt a message image. Layout: 12-byte IV ‖ ciphertext ‖ 16-byte tag. */\n async download(image: TikTokImage, which: 'origin' | 'thumb' = 'origin'): Promise<Buffer> {\n const url = which === 'thumb' && image.thumbUrl ? image.thumbUrl : image.url;\n if (!url) throw new TikTokError('DOWNLOAD_FAILED', 'Image has no URL');\n const res = await this.ctx.fetch(url);\n if (!res.ok) throw new TikTokError('DOWNLOAD_FAILED', `Image download: HTTP ${res.status}`, res.status);\n const enc = Buffer.from(await res.arrayBuffer());\n if (!image.decryptKey) return enc;\n try {\n const d = createDecipheriv('aes-256-gcm', Buffer.from(image.decryptKey, 'hex'), enc.subarray(0, 12));\n d.setAuthTag(enc.subarray(enc.length - 16));\n return Buffer.concat([d.update(enc.subarray(12, enc.length - 16)), d.final()]);\n } catch (err) {\n throw new TikTokError('DOWNLOAD_FAILED', `Image decryption: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n}\n","/**\n * Pure builders for IM request bodies (protobuf) — no I/O, so they can be unit-tested byte for byte.\n */\nimport {\n ConversationType,\n PropertyOp,\n REACTION_HEART,\n REACTION_LOVE_KEY,\n REACTION_PREFIX,\n} from '../protocol/constants';\nimport { Writer } from '../protocol/proto';\nimport type { ConversationRef, TikTokMessage } from '../models/types';\nimport type { UploadedImage } from './media';\n\n/** \"Latest\" cursor for reading history — greater than any `index` (µs). */\nexport const NEWEST = 9_999_999_999_999_999n;\n\n/** `GetMessagesByConversationRequestBody` — messages older than `before` (or the newest ones). */\nexport function getMessagesBody(conv: ConversationRef, limit: number, before?: string): Buffer {\n return new Writer()\n .bytes(1, conv.conversationId)\n .int(2, ConversationType.OneToOne)\n .int(3, BigInt(conv.conversationShortId))\n .int(4, 1) // direction: older than cursor\n .int(5, before ? BigInt(before) : NEWEST)\n .int(6, limit)\n .finish();\n}\n\n/** `MarkConversationReadRequestBody` — read up to message `index`. */\nexport function markReadBody(msg: ConversationRef & { index: string }): Buffer {\n return new Writer()\n .bytes(1, msg.conversationId)\n .int(2, BigInt(msg.conversationShortId))\n .int(3, ConversationType.OneToOne)\n .int(4, BigInt(msg.index))\n .int(11, 4)\n .finish();\n}\n\n/**\n * `SendMessageRequestBody`: {1 convId, 2 conversation type, 3 shortId, 4 content, 5 ext, 6 message type, 7 ticket,\n * 8 client id, …}. `extra` is appended verbatim (image attachments).\n */\nexport function sendMessageBody(\n conv: ConversationRef,\n cid: string,\n type: number,\n content: string,\n extra: Writer | null,\n): Buffer {\n const w = new Writer()\n .bytes(1, conv.conversationId)\n .int(2, ConversationType.OneToOne)\n .int(3, BigInt(conv.conversationShortId))\n .bytes(4, content)\n .map(5, { 's:mentioned_users': '', 's:client_message_id': cid })\n .int(6, type)\n .bytes(7, 'deprecated')\n .bytes(8, cid);\n return extra ? Buffer.concat([w.finish(), extra.finish()]) : w.finish();\n}\n\n/** Extra `SendMessageRequestBody` fields (13, 14, 17) describing an uploaded private image. */\nexport function imageMessageExtra(up: UploadedImage): Writer {\n const w = String(up.width);\n const h = String(up.height);\n const media = new Writer()\n .msg(\n 1,\n new Writer()\n .bytes(1, up.uri)\n .msg(5, new Writer().int(1, up.width).int(2, up.height))\n .bytes(6, up.secretKey)\n .int(7, 0),\n )\n .msg(\n 3,\n new Writer()\n .msg(1, new Writer().bytes(1, 'You sent a 📷'))\n .msg(2, new Writer().bytes(1, 'sent a 📷'))\n .msg(3, new Writer().bytes(1, '[Photo]')),\n )\n .msg(\n 200,\n new Writer().msg(\n 1,\n new Writer().bytes(1, up.uri).map(2, {\n image_width: w,\n image_height: h,\n decrypt_key: up.secretKey,\n quote_preview: 'dm_cam_preview_photo',\n sender_preview: 'sender_preview',\n }),\n ),\n );\n return new Writer()\n .msg(13, new Writer().msg(4, media))\n .bytes(14, 'private_image')\n .msg(\n 17,\n new Writer()\n .int(1, 1)\n .bytes(2, up.uri)\n .bytes(3, up.secretKey)\n .msg(10, new Writer().int(1, up.width).int(2, up.height).int(4, up.size).bytes(5, up.filename)),\n );\n}\n\nexport interface PropertyChange {\n op: number;\n key: string;\n}\n\n/**\n * Property operations that make `own`'s reaction on `msg` equal `emoji` (one reaction per user; `null` = remove).\n * Empty when nothing needs to change.\n */\nexport function reactionOps(\n msg: Pick<TikTokMessage, 'reactions'>,\n own: string,\n emoji: string | null,\n): PropertyChange[] {\n const mine = msg.reactions.filter((r) => r.userIds.includes(own)).map((r) => r.emoji);\n const ops: PropertyChange[] = [];\n if (emoji && !mine.includes(emoji)) ops.push({ op: PropertyOp.Add, key: REACTION_PREFIX + emoji });\n for (const e of mine) {\n if (e === emoji) continue;\n ops.push({ op: PropertyOp.Remove, key: REACTION_PREFIX + e });\n // ❤️ sent from the mobile app still uses the legacy key `e:love` — remove it too or the app keeps showing the heart.\n if (e === REACTION_HEART) ops.push({ op: PropertyOp.Remove, key: REACTION_LOVE_KEY });\n }\n return ops;\n}\n\n/** `ModifyMessagePropertyRequestBody` (command 705) applying `ops` to `msg` as user `own`. */\nexport function setPropertyBody(msg: TikTokMessage, ops: PropertyChange[], own: string): Buffer {\n const target = new Writer()\n .bytes(1, msg.conversationId)\n .int(2, ConversationType.OneToOne)\n .int(3, BigInt(msg.conversationShortId))\n .int(4, BigInt(msg.serverMessageId))\n .bytes(5, msg.clientMessageId);\n for (const o of ops) target.msg(6, new Writer().int(1, o.op).bytes(2, o.key).bytes(4, own));\n return new Writer().msg(1, target).bytes(2, 'deprecated').finish();\n}\n","import { AID_WEB, BUILD_NUMBER, DEFAULT_USER_AGENT, IM_API, Resp, SDK_VERSION, WEB } from './constants';\nimport { TikTokError } from '../errors';\nimport { Msg, Writer } from './proto';\nimport type { CookieInput, Credentials, TikTokOptions } from '../models/types';\n\n/** `name → value` cookies from any input shape of `Credentials.cookies`. */\nexport function normalizeCookies(input: Credentials['cookies']): Map<string, string> {\n const jar = new Map<string, string>();\n if (typeof input === 'string') {\n for (const part of input.split(';')) {\n const i = part.indexOf('=');\n if (i > 0) jar.set(part.slice(0, i).trim(), part.slice(i + 1).trim());\n }\n } else if (Array.isArray(input)) {\n for (const c of input as CookieInput[]) if (c?.name) jar.set(c.name, String(c.value ?? ''));\n } else {\n for (const [k, v] of Object.entries(input ?? {})) jar.set(k, String(v));\n }\n return jar;\n}\n\n/** Shared state of a session: cookies, device id, uid, HTTP calls. */\nexport class Context {\n readonly cookies: Map<string, string>;\n /**\n * IM device id = the `wid` TikTok issues for the `ttwid` cookie (`login` fetches it). The websocket only\n * accepts this id — a made-up id gets 400 (HTTP IM isn't picky).\n */\n deviceId: string;\n readonly userAgent: string;\n ownId = '';\n private seq = 10_000;\n\n constructor(\n creds: Credentials,\n readonly options: TikTokOptions = {},\n ) {\n this.cookies = normalizeCookies(creds.cookies);\n this.deviceId = creds.deviceId ?? '';\n this.userAgent = creds.userAgent || DEFAULT_USER_AGENT;\n }\n\n cookieHeader(): string {\n return [...this.cookies].map(([k, v]) => `${k}=${v}`).join('; ');\n }\n\n log(level: 'debug' | 'warn', msg: string): void {\n this.options.logger?.(level, msg);\n }\n\n /** `fetch` with cookies + UA + timeout. */\n async fetch(url: string, init: RequestInit & { headers?: Record<string, string> } = {}): Promise<Response> {\n try {\n return await fetch(url, {\n ...init,\n headers: {\n 'User-Agent': this.userAgent,\n Referer: `${WEB}/`,\n Origin: WEB,\n Cookie: this.cookieHeader(),\n ...init.headers,\n },\n signal: AbortSignal.timeout(this.options.timeoutMs ?? 20_000),\n });\n } catch (err) {\n throw new TikTokError('HTTP_ERROR', `${url.split('?')[0]}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n async getJson<T>(url: string): Promise<T> {\n const res = await this.fetch(url);\n if (!res.ok) throw new TikTokError('HTTP_ERROR', `${url.split('?')[0]}: HTTP ${res.status}`, res.status);\n return (await res.json()) as T;\n }\n\n /** IM `Request` envelope: command + body + common headers. */\n buildRequest(cmd: number, bodyField: number, body: Buffer): Buffer {\n return new Writer()\n .int(1, cmd)\n .int(2, this.seq++)\n .bytes(3, SDK_VERSION)\n .bytes(4, '')\n .int(5, 3)\n .int(6, 0)\n .bytes(7, BUILD_NUMBER)\n .msg(8, new Writer().bytes(bodyField, body))\n .bytes(9, this.deviceId)\n .bytes(11, 'web')\n .map(15, {\n aid: AID_WEB,\n app_name: 'tiktok_web',\n channel: 'web',\n device_platform: 'web_pc',\n device_id: this.deviceId,\n user_is_login: 'true',\n user_agent: this.userAgent,\n })\n .int(18, 1)\n .finish();\n }\n\n /**\n * Call an IM command over HTTP. Returns the unwrapped body (`ResponseBody[cmd]`).\n * NO X-Gnarly/X-Bogus/msToken signature needed — the IM server only checks cookies (tested 09/11/2026).\n */\n async im(path: string, cmd: number, body: Buffer, bodyField: number = cmd): Promise<Msg> {\n const url = `${IM_API}${path}?aid=${AID_WEB}&version_code=1.0.0&app_name=tiktok_web&device_platform=web_pc`;\n const res = await this.fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-protobuf', Accept: 'application/x-protobuf' },\n body: new Uint8Array(this.buildRequest(cmd, bodyField, body)),\n });\n if (!res.ok) throw new TikTokError('HTTP_ERROR', `${path}: HTTP ${res.status}`, res.status);\n const resp = Msg.tryParse(Buffer.from(await res.arrayBuffer()));\n if (!resp) throw new TikTokError('BAD_RESPONSE', `${path}: response is not protobuf`);\n const status = resp.num(Resp.status);\n if (status !== 0) {\n const desc = resp.str(Resp.error);\n // Not logged in: 200005 (tested with a cookieless request).\n const code = status === 200005 ? 'SESSION_EXPIRED' : 'IM_ERROR';\n throw new TikTokError(code, `${path}: ${status} ${desc}`.trim(), status);\n }\n return resp.msg(Resp.body)?.msg(cmd) ?? resp.msg(Resp.body)?.msg(bodyField) ?? Msg.parse(Buffer.alloc(0));\n }\n}\n","import { API } from './api/api';\nimport { fetchAccount, fetchWebId } from './api/account';\nimport { Context } from './protocol/context';\nimport { TikTokError } from './errors';\nimport type { Credentials, TikTokOptions } from './models/types';\n\nexport class TikTok {\n constructor(private readonly options: TikTokOptions = {}) {}\n\n /** Log in with existing cookies. Expired cookies → `TikTokError('SESSION_EXPIRED')`. */\n async login(credentials: Credentials): Promise<API> {\n const ctx = new Context(credentials, this.options);\n if (!ctx.cookies.get('sessionid') && !ctx.cookies.get('sid_tt')) {\n throw new TikTokError('SESSION_EXPIRED', 'Cookie has no sessionid');\n }\n const account = await fetchAccount(ctx);\n if (!account) throw new TikTokError('SESSION_EXPIRED', 'TikTok cookie is no longer logged in');\n ctx.ownId = account.uid;\n ctx.deviceId = (await fetchWebId(ctx).catch(() => '')) || ctx.deviceId;\n if (!ctx.deviceId) throw new TikTokError('BAD_RESPONSE', 'Could not get wid (device_id) — cookie missing ttwid?');\n return new API(ctx, account);\n }\n}\n","import { rm } from 'node:fs/promises';\nimport { DEFAULT_USER_AGENT, WEB } from '../protocol/constants';\nimport { TikTokError } from '../errors';\nimport type { CookieInput, Credentials } from '../models/types';\nimport { openTab, type Cdp } from './cdp';\nimport { launchChrome, resolveChromePath } from './chrome';\n\n/**\n * QR login via headless Chrome.\n *\n * Why a browser: TikTok's QR API (`passport/web/check_qrconnect`) requires a JS signature\n * (X-Gnarly / msToken) — calling it bare from Node yields `error_code 7`. So we open the real\n * `tiktok.com/login/qrcode` page in headless Chrome and let it sign itself; we only LISTEN to\n * responses via CDP: the QR image from the `get_qrcode` body, the status from `check_qrconnect`.\n * After login we grab the cookies and close Chrome — everything after (`login`, chat) needs no browser.\n */\n\nexport type QrLoginEvent =\n /** New QR image (`data:image/png;base64,…`) — show it to the user to scan. A new one comes on expiry. */\n | { type: 'qr'; image: string; expiresAt: number }\n /** Scanned, waiting for confirmation on the phone. */\n | { type: 'scanned' }\n /** Confirmed — fetching cookies. */\n | { type: 'confirmed' };\n\nexport interface QrLoginOptions {\n /**\n * Path to Chrome / Chromium (e.g. `/usr/bin/chromium-browser`). Optional — when omitted, `CHROME_PATH` and the\n * usual install locations are searched.\n */\n chromePath?: string;\n onEvent?: (e: QrLoginEvent) => void;\n signal?: AbortSignal;\n /** Total time to wait for a scan (ms) — default 5 minutes. */\n timeoutMs?: number;\n /** When the QR expires (~1 minute), regenerate it at most this many times. */\n maxRefresh?: number;\n userAgent?: string;\n}\n\n/**\n * Log in to TikTok via QR. Returns `Credentials` (`.tiktok.com` cookies + UA) — pass them to\n * `new TikTok().login(creds)` and SAVE them so you don't need to scan next time.\n */\nexport async function loginQR(opts: QrLoginOptions): Promise<Credentials> {\n const userAgent = opts.userAgent || DEFAULT_USER_AGENT;\n const maxRefresh = opts.maxRefresh ?? 3;\n const { proc, ws, dir } = await launchChrome(resolveChromePath(opts.chromePath), userAgent);\n let cdp: Cdp | null = null;\n try {\n cdp = await openTab(ws);\n const tab = cdp;\n await tab.send('Network.enable');\n await tab.send('Page.enable');\n\n const result = new Promise<Credentials>((resolve, reject) => {\n const urlById = new Map<string, string>();\n let refreshes = 0;\n let scanned = false;\n let done = false;\n const finish = (err: Error | null, creds?: Credentials) => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n opts.signal?.removeEventListener('abort', onAbort);\n if (err) reject(err);\n else resolve(creds!);\n };\n const timer = setTimeout(\n () => finish(new TikTokError('LOGIN_FAILED', 'Timed out waiting for QR scan')),\n opts.timeoutMs ?? 5 * 60_000,\n );\n const onAbort = () => finish(new TikTokError('LOGIN_ABORTED', 'QR login aborted'));\n opts.signal?.addEventListener('abort', onAbort);\n\n const body = async (requestId: string): Promise<{ data?: Record<string, unknown> } | null> => {\n const r = await tab\n .send<{ body: string; base64Encoded: boolean }>('Network.getResponseBody', { requestId })\n .catch(() => null);\n if (!r) return null;\n try {\n return JSON.parse(r.base64Encoded ? Buffer.from(r.body, 'base64').toString() : r.body) as {\n data?: Record<string, unknown>;\n };\n } catch {\n return null;\n }\n };\n\n /** After `confirmed`: the page redirects and sets session cookies — wait for `sessionid`. */\n const collectCookies = async () => {\n for (let i = 0; i < 30 && !done; i++) {\n const { cookies } = await tab.send<{ cookies: (CookieInput & { domain: string })[] }>(\n 'Network.getAllCookies',\n );\n const mine = cookies.filter((c) => c.domain.endsWith('tiktok.com'));\n if (mine.some((c) => c.name === 'sessionid' && c.value) && mine.some((c) => c.name === 'ttwid')) {\n // Same name on `.tiktok.com` and `www.tiktok.com` → keep the `.tiktok.com` one.\n const byName = new Map<string, CookieInput>();\n for (const c of mine.sort(\n (a, b) => (a.domain === '.tiktok.com' ? 1 : 0) - (b.domain === '.tiktok.com' ? 1 : 0),\n )) {\n byName.set(c.name, { name: c.name, value: c.value });\n }\n return finish(null, { cookies: [...byName.values()], userAgent });\n }\n await new Promise((r) => setTimeout(r, 1000));\n }\n finish(new TikTokError('LOGIN_FAILED', 'Confirmed but no sessionid cookie found'));\n };\n\n tab.on((method, p) => {\n if (done) return;\n if (method === 'Network.responseReceived') {\n const res = p.response as { url: string };\n urlById.set(p.requestId as string, res.url);\n return;\n }\n if (method !== 'Network.loadingFinished') return;\n const url = urlById.get(p.requestId as string) ?? '';\n if (url.includes('/passport/web/get_qrcode/')) {\n void body(p.requestId as string).then((j) => {\n const qr = j?.data?.qrcode;\n if (typeof qr === 'string' && qr) {\n opts.onEvent?.({\n type: 'qr',\n image: `data:image/png;base64,${qr}`,\n expiresAt: Number(j?.data?.expire_time ?? 0) * 1000 || Date.now() + 60_000,\n });\n }\n });\n } else if (url.includes('/passport/web/check_qrconnect/')) {\n // The page polls 3 regions in parallel (sg/us/eu) — only the account's region changes status, the rest stay \"new\".\n void body(p.requestId as string).then((j) => {\n const status = String(j?.data?.status ?? '');\n if (status === 'scanned' && !scanned) {\n scanned = true;\n opts.onEvent?.({ type: 'scanned' });\n } else if (status === 'confirmed') {\n opts.onEvent?.({ type: 'confirmed' });\n void collectCookies();\n } else if (status === 'expired' && !scanned) {\n if (refreshes++ >= maxRefresh)\n finish(new TikTokError('LOGIN_FAILED', 'QR expired without being scanned'));\n else void tab.send('Page.reload');\n }\n });\n }\n });\n });\n\n await tab.send('Page.navigate', { url: `${WEB}/login/qrcode` });\n return await result;\n } finally {\n cdp?.close();\n proc.kill('SIGKILL');\n await rm(dir, { recursive: true, force: true }).catch(() => undefined);\n }\n}\n","import WebSocket from 'ws';\n\n/** Minimal Chrome DevTools Protocol client bound to one tab. */\nexport interface Cdp {\n send<T = Record<string, unknown>>(method: string, params?: object): Promise<T>;\n on(handler: (method: string, params: Record<string, unknown>) => void): void;\n close(): void;\n}\n\n/** Attach CDP to a new tab (a flat session on the browser websocket). */\nexport async function openTab(browserWs: string): Promise<Cdp> {\n const ws = new WebSocket(browserWs, { perMessageDeflate: false });\n await new Promise<void>((resolve, reject) => {\n ws.once('open', () => resolve());\n ws.once('error', reject);\n });\n let id = 0;\n // eslint-disable-next-line prefer-const -- the ws message handler reads it before Target.attachToTarget assigns it\n let sessionId: string | undefined;\n const pending = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();\n const handlers: ((m: string, p: Record<string, unknown>) => void)[] = [];\n ws.on('message', (raw: Buffer) => {\n const m = JSON.parse(raw.toString()) as {\n id?: number;\n method?: string;\n params?: Record<string, unknown>;\n result?: unknown;\n error?: { message: string };\n sessionId?: string;\n };\n if (m.id !== undefined && pending.has(m.id)) {\n const p = pending.get(m.id)!;\n pending.delete(m.id);\n if (m.error) p.reject(new Error(m.error.message));\n else p.resolve(m.result ?? {});\n return;\n }\n if (m.method && (!m.sessionId || m.sessionId === sessionId)) for (const h of handlers) h(m.method, m.params ?? {});\n });\n const raw = <T>(method: string, params: object = {}, sid?: string): Promise<T> =>\n new Promise((resolve, reject) => {\n const i = ++id;\n pending.set(i, { resolve: resolve as (v: unknown) => void, reject });\n ws.send(JSON.stringify({ id: i, method, params, ...(sid ? { sessionId: sid } : {}) }));\n });\n const target = await raw<{ targetId: string }>('Target.createTarget', { url: 'about:blank' });\n sessionId = (await raw<{ sessionId: string }>('Target.attachToTarget', { targetId: target.targetId, flatten: true }))\n .sessionId;\n return {\n send: (method, params) => raw(method, params, sessionId),\n on: (h) => handlers.push(h),\n close: () => ws.close(),\n };\n}\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { mkdtemp, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { delimiter, join } from 'node:path';\nimport { TikTokError } from '../errors';\n\n/** Executable names looked up on `PATH` (Linux / BSD). */\nconst PATH_NAMES = ['google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser', 'chrome'];\n\n/** Well-known install locations per platform. */\nfunction knownPaths(platform: NodeJS.Platform, env: NodeJS.ProcessEnv): string[] {\n if (platform === 'darwin') {\n const apps = [\n 'Google Chrome.app/Contents/MacOS/Google Chrome',\n 'Chromium.app/Contents/MacOS/Chromium',\n 'Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',\n ];\n const roots = ['/Applications', ...(env.HOME ? [join(env.HOME, 'Applications')] : [])];\n return roots.flatMap((r) => apps.map((a) => join(r, a)));\n }\n if (platform === 'win32') {\n const roots = [env.PROGRAMFILES, env['PROGRAMFILES(X86)'], env.LOCALAPPDATA].filter((r): r is string => !!r);\n return roots.flatMap((r) => [\n join(r, 'Google', 'Chrome', 'Application', 'chrome.exe'),\n join(r, 'Chromium', 'Application', 'chrome.exe'),\n ]);\n }\n return [\n '/usr/bin/google-chrome',\n '/usr/bin/google-chrome-stable',\n '/usr/bin/chromium',\n '/usr/bin/chromium-browser',\n '/snap/bin/chromium',\n '/opt/google/chrome/chrome',\n ];\n}\n\n/**\n * Locate a Chrome / Chromium executable: `CHROME_PATH` env, then well-known install paths for the platform, then\n * `PATH`. Returns `null` when none is found.\n */\nexport function findChrome(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n exists: (path: string) => boolean = existsSync,\n): string | null {\n if (env.CHROME_PATH && exists(env.CHROME_PATH)) return env.CHROME_PATH;\n for (const p of knownPaths(platform, env)) if (exists(p)) return p;\n if (platform !== 'win32') {\n for (const dir of (env.PATH ?? '').split(delimiter).filter(Boolean)) {\n for (const name of PATH_NAMES) if (exists(join(dir, name))) return join(dir, name);\n }\n }\n return null;\n}\n\n/** `chromePath` if given, otherwise auto-detect — throws `LOGIN_FAILED` when nothing is found. */\nexport function resolveChromePath(chromePath?: string): string {\n if (chromePath) return chromePath;\n const found = findChrome();\n if (!found) {\n throw new TikTokError(\n 'LOGIN_FAILED',\n 'Chrome / Chromium not found — install it, set the CHROME_PATH environment variable, or pass `chromePath`',\n );\n }\n return found;\n}\n\n/** Launch headless Chrome and wait for the \"DevTools listening on ws://…\" line. */\nexport async function launchChrome(\n chromePath: string,\n userAgent: string,\n): Promise<{ proc: ChildProcess; ws: string; dir: string }> {\n const dir = await mkdtemp(join(tmpdir(), 'tca-chrome-'));\n const proc = spawn(\n chromePath,\n [\n '--headless=new',\n '--remote-debugging-port=0',\n `--user-data-dir=${dir}`,\n '--no-first-run',\n '--no-default-browser-check',\n '--disable-gpu',\n '--disable-dev-shm-usage',\n // Containers run as root — Chromium requires disabling the sandbox.\n '--no-sandbox',\n '--window-size=1280,900',\n '--lang=vi-VN',\n // The default headless UA contains \"HeadlessChrome\" — replace it with a normal UA.\n `--user-agent=${userAgent}`,\n 'about:blank',\n ],\n { stdio: ['ignore', 'ignore', 'pipe'] },\n );\n const ws = await new Promise<string>((resolve, reject) => {\n let buf = '';\n const timer = setTimeout(() => reject(new Error('Chrome did not start within 20s')), 20_000);\n proc.stderr!.on('data', (d: Buffer) => {\n buf += d.toString();\n const m = /DevTools listening on (ws:\\/\\/\\S+)/.exec(buf);\n if (m) {\n clearTimeout(timer);\n resolve(m[1]);\n }\n });\n proc.once('error', (e) => {\n clearTimeout(timer);\n reject(e);\n });\n proc.once('exit', (code) => {\n clearTimeout(timer);\n reject(new Error(`Chrome exited (${code}) before it was ready`));\n });\n }).catch(async (err: unknown) => {\n proc.kill('SIGKILL');\n await rm(dir, { recursive: true, force: true });\n throw new TikTokError(\n 'LOGIN_FAILED',\n `Failed to launch Chrome: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n return { proc, ws, dir };\n}\n"],"mappings":"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","names":["key","key","createHash","createHash","k","rm","WebSocket","raw","rm"]}
|
package/package.json
CHANGED
|
@@ -1,19 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tca-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Unofficial TikTok Chat API for JavaScript — read/send TikTok direct messages (text, images, reactions) and listen over websocket using session cookies, no browser needed.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "rice2411",
|
|
7
|
-
"repository": {
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/rice2411/tca-js.git"
|
|
10
|
+
},
|
|
8
11
|
"homepage": "https://github.com/rice2411/tca-js#readme",
|
|
9
|
-
"bugs": {
|
|
10
|
-
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/rice2411/tca-js/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"tiktok",
|
|
17
|
+
"tiktok-api",
|
|
18
|
+
"tiktok-dm",
|
|
19
|
+
"tiktok-chat",
|
|
20
|
+
"direct-message",
|
|
21
|
+
"unofficial",
|
|
22
|
+
"chat",
|
|
23
|
+
"websocket"
|
|
24
|
+
],
|
|
11
25
|
"main": "./dist/index.js",
|
|
26
|
+
"module": "./dist/index.mjs",
|
|
12
27
|
"types": "./dist/index.d.ts",
|
|
13
|
-
"
|
|
14
|
-
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/index.d.mts",
|
|
32
|
+
"default": "./dist/index.mjs"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=22"
|
|
47
|
+
},
|
|
15
48
|
"scripts": {
|
|
16
|
-
"build": "
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"dev": "tsup --watch",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"lint": "eslint .",
|
|
54
|
+
"format": "prettier --write .",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
17
56
|
"prepublishOnly": "npm run build",
|
|
18
57
|
"smoke": "node scripts/smoke.mjs"
|
|
19
58
|
},
|
|
@@ -21,8 +60,15 @@
|
|
|
21
60
|
"ws": "^8.18.0"
|
|
22
61
|
},
|
|
23
62
|
"devDependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@types/
|
|
26
|
-
"
|
|
63
|
+
"@eslint/js": "^10.0.1",
|
|
64
|
+
"@types/node": "^22.20.5",
|
|
65
|
+
"@types/ws": "^8.18.2",
|
|
66
|
+
"eslint": "^10.14.0",
|
|
67
|
+
"globals": "^17.14.0",
|
|
68
|
+
"prettier": "^3.10.0",
|
|
69
|
+
"tsup": "^9.0.0",
|
|
70
|
+
"typescript": "5.9.3",
|
|
71
|
+
"typescript-eslint": "^8.72.2",
|
|
72
|
+
"vitest": "^5.1.0"
|
|
27
73
|
}
|
|
28
74
|
}
|
package/dist/api.d.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { Context } from './context';
|
|
2
|
-
import { Listener } from './listener';
|
|
3
|
-
import type { ConversationPage, ConversationRef, ImageInput, MessagePage, TikTokAccount, TikTokImage, TikTokMessage, TikTokSticker, TikTokUser } from './types';
|
|
4
|
-
/** Kết quả gửi: mã tin phía client (TikTok trả lại đúng mã này). Mã server tới qua listener (`isSelf`). */
|
|
5
|
-
export interface SendResult {
|
|
6
|
-
clientMessageId: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Một phiên TikTok đã đăng nhập — tạo bằng `new TikTok().login(credentials)`.
|
|
10
|
-
* Mọi lệnh đi thẳng HTTP/WS tới TikTok bằng cookie, không cần trình duyệt.
|
|
11
|
-
*/
|
|
12
|
-
export declare class API {
|
|
13
|
-
private readonly ctx;
|
|
14
|
-
readonly account: TikTokAccount;
|
|
15
|
-
readonly listener: Listener;
|
|
16
|
-
private readonly media;
|
|
17
|
-
constructor(ctx: Context, account: TikTokAccount);
|
|
18
|
-
getOwnId(): string;
|
|
19
|
-
/** Cookie hiện tại — lưu lại để lần sau `login` không cần quét QR. */
|
|
20
|
-
getCookies(): {
|
|
21
|
-
name: string;
|
|
22
|
-
value: string;
|
|
23
|
-
}[];
|
|
24
|
-
getContext(): {
|
|
25
|
-
deviceId: string;
|
|
26
|
-
userAgent: string;
|
|
27
|
-
};
|
|
28
|
-
/** Phiên còn sống không — gọi định kỳ để phát hiện hết hạn sớm. */
|
|
29
|
-
isAlive(): Promise<boolean>;
|
|
30
|
-
/** Hội thoại gần đây + tin mới nhất của chúng, lật trang bằng `cursor`. */
|
|
31
|
-
getConversations(cursor?: string): Promise<ConversationPage>;
|
|
32
|
-
/** Tin của một hội thoại, mới nhất trước. Đọc tiếp tin cũ hơn: `before = <index của tin cũ nhất>`. */
|
|
33
|
-
getMessages(conv: ConversationRef, opts?: {
|
|
34
|
-
limit?: number;
|
|
35
|
-
before?: string;
|
|
36
|
-
}): Promise<MessagePage>;
|
|
37
|
-
/** Hồ sơ người dùng theo uid (tên hiển thị, @username, ảnh đại diện). */
|
|
38
|
-
getUsers(uids: string[]): Promise<TikTokUser[]>;
|
|
39
|
-
/**
|
|
40
|
-
* Tải ảnh đại diện (thử lần lượt các link, bỏ heic). Không gửi cookie / Referer: CDN ảnh khác miền và
|
|
41
|
-
* link đã có chữ ký. `null` nếu không link nào tải được.
|
|
42
|
-
*/
|
|
43
|
-
downloadAvatar(user: Pick<TikTokUser, 'avatars'>): Promise<{
|
|
44
|
-
data: Buffer;
|
|
45
|
-
type: string;
|
|
46
|
-
} | null>;
|
|
47
|
-
/** Đánh dấu đã đọc tới tin `msg`. */
|
|
48
|
-
markRead(msg: ConversationRef & {
|
|
49
|
-
index: string;
|
|
50
|
-
}): Promise<void>;
|
|
51
|
-
/** Gửi tin chữ vào hội thoại (thường là `msg` vừa nhận — nó mang sẵn mã hội thoại). */
|
|
52
|
-
sendMessage(conv: ConversationRef, text: string): Promise<SendResult>;
|
|
53
|
-
/** Gửi một ảnh: tải lên kho riêng tư (được mã hoá) rồi gửi tin ảnh. */
|
|
54
|
-
sendImage(conv: ConversationRef, image: ImageInput): Promise<SendResult>;
|
|
55
|
-
/**
|
|
56
|
-
* Đặt reaction CỦA MÌNH trên một tin — như TikTok Web, mỗi người một reaction: `emoji` mới thay cái cũ,
|
|
57
|
-
* `null` = gỡ. Truyền nguyên `msg` vừa đọc (cần `reactions` hiện tại để biết gỡ khoá nào).
|
|
58
|
-
* Gửi `ModifyMessagePropertyRequestBody` (lệnh 705) — xem README.
|
|
59
|
-
*/
|
|
60
|
-
setReaction(msg: TikTokMessage, emoji: string | null): Promise<void>;
|
|
61
|
-
/** Tải ảnh sticker (CDN công khai, không mã hoá, không cần cookie). */
|
|
62
|
-
downloadSticker(sticker: Pick<TikTokSticker, 'url'>): Promise<{
|
|
63
|
-
data: Buffer;
|
|
64
|
-
type: string;
|
|
65
|
-
}>;
|
|
66
|
-
/** Tải + giải mã ảnh của một tin (truyền cả `msg` — tin từ listener thiếu link, hàm tự tra lại). */
|
|
67
|
-
downloadImage(source: TikTokMessage | TikTokImage, which?: 'origin' | 'thumb'): Promise<Buffer>;
|
|
68
|
-
/** `SendMessageRequestBody`: {1 convId, 2 loại hội thoại, 3 shortId, 4 nội dung, 5 ext, 6 loại tin, 7 ticket, 8 client id, …}. */
|
|
69
|
-
private send;
|
|
70
|
-
}
|
|
71
|
-
/** Tài khoản của cookie — `null` nếu cookie không còn đăng nhập. */
|
|
72
|
-
export declare function fetchAccount(ctx: Context): Promise<TikTokAccount | null>;
|
|
73
|
-
/** `wid` — mã máy TikTok cấp theo cookie `ttwid`; IM + websocket dùng làm `device_id`. */
|
|
74
|
-
export declare function fetchWebId(ctx: Context): Promise<string>;
|
|
75
|
-
export type { TikTokMessage };
|