tca-js 0.3.0 → 0.3.1

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/dist/api.js CHANGED
@@ -10,11 +10,11 @@ const listener_1 = require("./listener");
10
10
  const media_1 = require("./media");
11
11
  const message_1 = require("./message");
12
12
  const proto_1 = require("./proto");
13
- /** Mốc "mới nhất" khi đọc lịch sử — lớn hơn mọi `index` (µs). */
13
+ /** "Latest" cursor for reading history — greater than any `index` (µs). */
14
14
  const NEWEST = 9999999999999999n;
15
15
  /**
16
- * Một phiên TikTok đã đăng nhập — tạo bằng `new TikTok().login(credentials)`.
17
- * Mọi lệnh đi thẳng HTTP/WS tới TikTok bằng cookie, không cần trình duyệt.
16
+ * A logged-in TikTok session — create with `new TikTok().login(credentials)`.
17
+ * All calls go directly over HTTP/WS to TikTok using cookies, no browser needed.
18
18
  */
19
19
  class API {
20
20
  ctx;
@@ -30,44 +30,44 @@ class API {
30
30
  getOwnId() {
31
31
  return this.ctx.ownId;
32
32
  }
33
- /** Cookie hiện tại — lưu lại để lần sau `login` không cần quét QR. */
33
+ /** Current cookies — save them so the next `login` needs no QR scan. */
34
34
  getCookies() {
35
35
  return [...this.ctx.cookies].map(([name, value]) => ({ name, value }));
36
36
  }
37
37
  getContext() {
38
38
  return { deviceId: this.ctx.deviceId, userAgent: this.ctx.userAgent };
39
39
  }
40
- /** Phiên còn sống không — gọi định kỳ để phát hiện hết hạn sớm. */
40
+ /** Whether the session is still alive — call periodically to detect expiry early. */
41
41
  async isAlive() {
42
42
  return (await fetchAccount(this.ctx).catch(() => null)) !== null;
43
43
  }
44
- // ─── Hộp thư ─────────────────────────────────────────────────────────────
45
- /** Hội thoại gần đây + tin mới nhất của chúng, lật trang bằng `cursor`. */
44
+ // ─── Inbox ─────────────────────────────────────────────────────────────────
45
+ /** Recent conversations + their latest messages, paginated with `cursor`. */
46
46
  async getConversations(cursor = '0') {
47
47
  const body = await this.ctx.im('/v2/message/get_by_user_init', constants_1.Cmd.GetMessagesByUserInit, new proto_1.Writer().int(1, BigInt(cursor)).finish());
48
48
  return {
49
49
  messages: body.msgs(1).map((m) => (0, message_1.parseMessage)(m, this.ctx.ownId)),
50
50
  conversations: body.msgs(2).map((m) => (0, message_1.parseConversation)(m, this.ctx.ownId)),
51
- // Con trỏ = số hội thoại đã lấy (0, 10, 20…) — trường 4; trường 3 là mốc thời gian, không dùng để lật.
51
+ // Cursor = number of conversations fetched so far (0, 10, 20…) — field 4; field 3 is a timestamp, not used for paging.
52
52
  cursor: body.id(4),
53
53
  hasMore: body.num(5) === 1,
54
54
  };
55
55
  }
56
- /** 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>`. */
56
+ /** Messages of a conversation, newest first. For older messages: `before = <index of oldest message>`. */
57
57
  async getMessages(conv, opts = {}) {
58
58
  const limit = Math.min(Math.max(opts.limit ?? 20, 1), 50);
59
59
  const body = await this.ctx.im('/v1/message/get_by_conversation', constants_1.Cmd.GetMessagesByConversation, new proto_1.Writer()
60
60
  .bytes(1, conv.conversationId)
61
61
  .int(2, constants_1.ConversationType.OneToOne)
62
62
  .int(3, BigInt(conv.conversationShortId))
63
- .int(4, 1) // hướng: cũ hơn mốc
63
+ .int(4, 1) // direction: older than cursor
64
64
  .int(5, opts.before ? BigInt(opts.before) : NEWEST)
65
65
  .int(6, limit)
66
66
  .finish());
67
67
  const messages = body.msgs(1).map((m) => (0, message_1.parseMessage)(m, this.ctx.ownId));
68
68
  return { messages, hasMore: messages.length >= limit };
69
69
  }
70
- /** Hồ sơ người dùng theo uid (tên hiển thị, @username, ảnh đại diện). */
70
+ /** User profiles by uid (display name, @username, avatar). */
71
71
  async getUsers(uids) {
72
72
  if (uids.length === 0)
73
73
  return [];
@@ -90,8 +90,8 @@ class API {
90
90
  }));
91
91
  }
92
92
  /**
93
- * 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à
94
- * link đã có chữ ký. `null` nếu không link nào tải được.
93
+ * Download an avatar (tries each URL in turn, skips heic). No cookie / Referer sent: the image CDN is cross-origin and
94
+ * URLs are already signed. `null` if no URL could be downloaded.
95
95
  */
96
96
  async downloadAvatar(user) {
97
97
  for (const url of user.avatars) {
@@ -107,7 +107,7 @@ class API {
107
107
  }
108
108
  return null;
109
109
  }
110
- /** Đánh dấu đã đọc tới tin `msg`. */
110
+ /** Mark as read up to message `msg`. */
111
111
  async markRead(msg) {
112
112
  await this.ctx.im('/v3/conversation/mark_read', constants_1.Cmd.MarkRead, new proto_1.Writer()
113
113
  .bytes(1, msg.conversationId)
@@ -117,14 +117,14 @@ class API {
117
117
  .int(11, 4)
118
118
  .finish(), constants_1.MARK_READ_BODY_FIELD);
119
119
  }
120
- // ─── Gửi ──────────────────────────────────────────────────────────────────
121
- /** 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). */
120
+ // ─── Send ─────────────────────────────────────────────────────────────────
121
+ /** Send a text message to a conversation (usually the just-received `msg` — it carries the conversation ids). */
122
122
  async sendMessage(conv, text) {
123
123
  const cid = (0, node_crypto_1.randomUUID)();
124
124
  await this.send(conv, cid, constants_1.MessageType.Text, JSON.stringify({ aweType: 0, text }), null);
125
125
  return { clientMessageId: cid };
126
126
  }
127
- /** Gửi một ảnh: tải lên kho riêng tư (được mã hoá) rồi gửi tin ảnh. */
127
+ /** Send an image: upload to the private (encrypted) store, then send an image message. */
128
128
  async sendImage(conv, image) {
129
129
  const up = await this.media.upload(image);
130
130
  const cid = (0, node_crypto_1.randomUUID)();
@@ -159,9 +159,9 @@ class API {
159
159
  return { clientMessageId: cid };
160
160
  }
161
161
  /**
162
- * Đặ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ũ,
163
- * `null` = gỡ. Truyền nguyên `msg` vừa đọc (cần `reactions` hiện tại để biết gỡ khoá nào).
164
- * Gửi `ModifyMessagePropertyRequestBody` (lệnh 705) — xem README.
162
+ * Set YOUR reaction on a message — like TikTok Web, one reaction per user: a new `emoji` replaces the old one,
163
+ * `null` = remove. Pass the whole `msg` just read (current `reactions` are needed to know which key to remove).
164
+ * Sends `ModifyMessagePropertyRequestBody` (command 705) — see README.
165
165
  */
166
166
  async setReaction(msg, emoji) {
167
167
  const own = this.ctx.ownId;
@@ -173,7 +173,7 @@ class API {
173
173
  if (e === emoji)
174
174
  continue;
175
175
  ops.push({ op: constants_1.PropertyOp.Remove, key: constants_1.REACTION_PREFIX + e });
176
- // ❤️ thả từ app điện thoại còn nằm ở khoá cũ `e:love` — gỡ luôn kẻo app vẫn hiện tim.
176
+ // ❤️ sent from the mobile app still uses the legacy key `e:love` — remove it too or the app keeps showing the heart.
177
177
  if (e === constants_1.REACTION_HEART)
178
178
  ops.push({ op: constants_1.PropertyOp.Remove, key: constants_1.REACTION_LOVE_KEY });
179
179
  }
@@ -188,12 +188,12 @@ class API {
188
188
  for (const o of ops)
189
189
  target.msg(6, new proto_1.Writer().int(1, o.op).bytes(2, o.key).bytes(4, own));
190
190
  const body = await this.ctx.im('/v1/message/set_property', constants_1.Cmd.SetMessageProperty, new proto_1.Writer().msg(1, target).bytes(2, 'deprecated').finish());
191
- // status: 0 thành công, 1 gửi trùng (cũng là xong); còn lại TikTok từ chối.
191
+ // status: 0 success, 1 duplicate (also done); anything else means TikTok rejected it.
192
192
  const status = body.num(1);
193
193
  if (status > 1)
194
194
  throw new errors_1.TikTokError('IM_ERROR', `set_property: status ${status}`, status);
195
195
  }
196
- /** Tải ảnh sticker (CDN công khai, không mã hoá, không cần cookie). */
196
+ /** Download a sticker image (public CDN, unencrypted, no cookie needed). */
197
197
  async downloadSticker(sticker) {
198
198
  const res = await fetch(sticker.url, {
199
199
  headers: { 'User-Agent': this.ctx.userAgent },
@@ -201,25 +201,25 @@ class API {
201
201
  signal: AbortSignal.timeout(this.ctx.options.timeoutMs ?? 20_000),
202
202
  }).catch(() => null);
203
203
  if (!res?.ok)
204
- throw new errors_1.TikTokError('DOWNLOAD_FAILED', `Tải sticker lỗi: ${res?.status ?? 'mạng'}`);
204
+ throw new errors_1.TikTokError('DOWNLOAD_FAILED', `Sticker download failed: ${res?.status ?? 'network'}`);
205
205
  return { data: Buffer.from(await res.arrayBuffer()), type: res.headers.get('content-type') ?? 'image/webp' };
206
206
  }
207
- /** 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). */
207
+ /** Download + decrypt a message's image (pass the whole `msg` — listener messages lack the URL, this looks it up). */
208
208
  async downloadImage(source, which = 'origin') {
209
209
  let image = 'serverMessageId' in source ? source.image : source;
210
- // Tin đẩy qua websocket chỉ có uri + khoá, không có link tải → tra lại lịch sử theo mã tin.
210
+ // Websocket-pushed messages only have uri + key, no download URL → look up history by message id.
211
211
  if ('serverMessageId' in source && image && !image.url) {
212
212
  const { messages } = await this.getMessages(source, { limit: 5, before: (BigInt(source.index) + 1n).toString() });
213
213
  image = messages.find((m) => m.serverMessageId === source.serverMessageId)?.image ?? image;
214
214
  }
215
215
  if (!image)
216
- throw new errors_1.TikTokError('DOWNLOAD_FAILED', 'Tin không có ảnh');
216
+ throw new errors_1.TikTokError('DOWNLOAD_FAILED', 'Message has no image');
217
217
  return this.media.download(image, which);
218
218
  }
219
- /** `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, …}. */
219
+ /** `SendMessageRequestBody`: {1 convId, 2 conversation type, 3 shortId, 4 content, 5 ext, 6 message type, 7 ticket, 8 client id, …}. */
220
220
  async send(conv, cid, type, content, extra) {
221
221
  if (!conv.conversationId || !conv.conversationShortId) {
222
- throw new errors_1.TikTokError('IM_ERROR', 'Thiếu conversationId / conversationShortId');
222
+ throw new errors_1.TikTokError('IM_ERROR', 'Missing conversationId / conversationShortId');
223
223
  }
224
224
  const w = new proto_1.Writer()
225
225
  .bytes(1, conv.conversationId)
@@ -235,7 +235,7 @@ class API {
235
235
  }
236
236
  }
237
237
  exports.API = API;
238
- /** Tài khoản của cookie — `null` nếu cookie không còn đăng nhập. */
238
+ /** Account for the cookie — `null` if the cookie is no longer logged in. */
239
239
  async function fetchAccount(ctx) {
240
240
  const res = await ctx.getJson(`${constants_1.WEB}/passport/web/account/info/?aid=${constants_1.AID_FRONTIER}`);
241
241
  if (res.message !== 'success' || !res.data?.user_id_str)
@@ -248,8 +248,8 @@ async function fetchAccount(ctx) {
248
248
  };
249
249
  }
250
250
  /**
251
- * Link ảnh đại diện trong `avatars` của hồ sơ IM — gom mọi chuỗi `https://…` (cỡ thumb → medium → larger, tuỳ
252
- * TikTok trả gì), rồi xếp bản trình duyệt đọc được (jpeg/webp/png) lên trước, heic xuống cuối.
251
+ * Avatar URLs from the IM profile `avatars` — collects every `https://…` string (thumb → medium → larger, whatever
252
+ * TikTok returns), then puts browser-readable ones (jpeg/webp/png) first and heic last.
253
253
  */
254
254
  function avatarLinks(avatars) {
255
255
  const urls = [];
@@ -268,7 +268,7 @@ function avatarLinks(avatars) {
268
268
  const ranked = [...urls.filter((u) => !heic(u)), ...urls.filter(heic)];
269
269
  return { avatar: ranked[0] ?? '', avatars: ranked };
270
270
  }
271
- /** `wid` — mã máy TikTok cấp theo cookie `ttwid`; IM + websocket dùng làm `device_id`. */
271
+ /** `wid` — device id TikTok issues per `ttwid` cookie; IM + websocket use it as `device_id`. */
272
272
  async function fetchWebId(ctx) {
273
273
  const res = await ctx.fetch(`${constants_1.WEB}/api/v1/web-cookie-privacy/config?locale=vi-VN&appId=1988&theme=default&tea=1`);
274
274
  if (!res.ok)
@@ -1,35 +1,35 @@
1
1
  /**
2
- * Hằng giao thức IM của TikTok Web — dựng lại từ lưu lượng `tiktok.com/business-suite/messages`
3
- * (09/11/2026). TikTok đổi phía họ thì sửa ở đây; ghi lại cách dò ra ở README.
2
+ * TikTok Web IM protocol constants — reconstructed from `tiktok.com/business-suite/messages` traffic
3
+ * (09/11/2026). If TikTok changes things on their side, fix them here; how they were found is in the README.
4
4
  */
5
5
  export declare const IM_API = "https://im-api-sg.tiktok.com";
6
6
  export declare const IM_WS = "wss://im-ws-sg.tiktok.com/ws/v2";
7
7
  export declare const WEB = "https://www.tiktok.com";
8
8
  export declare const DEFAULT_USER_AGENT = "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";
9
- /** App id của TikTok Web (HTTP IM). */
9
+ /** TikTok Web app id (HTTP IM). */
10
10
  export declare const AID_WEB = "1988";
11
- /** App id kênh "frontier" — websocket đẩy tin mới. */
11
+ /** "frontier" channel app id — websocket pushing new messages. */
12
12
  export declare const AID_FRONTIER = "1459";
13
13
  export declare const SDK_VERSION = "1.8.5";
14
14
  export declare const BUILD_NUMBER = "feffaa1:master";
15
- /** `access_key` của websocket = md5(FPID + APP_KEY + device_id + SALT). */
15
+ /** Websocket `access_key` = md5(FPID + APP_KEY + device_id + SALT). */
16
16
  export declare const FRONTIER_FPID = "9";
17
17
  export declare const FRONTIER_APP_KEY = "e1bd35ec9db7b8d846de66ed140b1ad9";
18
18
  export declare const FRONTIER_SALT = "f8a69f1719916z";
19
- /** Lệnh IM (`Request.cmd`) — đồng thời là số trường của body trong `RequestBody`. */
19
+ /** IM command (`Request.cmd`) — also the body's field number in `RequestBody`. */
20
20
  export declare const Cmd: {
21
21
  readonly SendMessage: 100;
22
22
  readonly GetMessagesByUserInit: 203;
23
23
  readonly GetMessagesByConversation: 301;
24
- /** Websocket đẩy tin mới. */
24
+ /** Websocket push of new messages. */
25
25
  readonly NewMessage: 500;
26
- /** Thả / gỡ reaction (`property` của tin) — `/v1/message/set_property`. */
26
+ /** Add / remove a reaction (message `property`) — `/v1/message/set_property`. */
27
27
  readonly SetMessageProperty: 705;
28
28
  readonly GetConversationInfo: 610;
29
29
  readonly MarkRead: 2002;
30
30
  readonly UploadConfig: 2059;
31
31
  };
32
- /** Body của MarkRead nằm ở trường 604, không trùng số lệnh. */
32
+ /** MarkRead's body lives in field 604, not the command number. */
33
33
  export declare const MARK_READ_BODY_FIELD = 604;
34
34
  /** `MessageBody.message_type`. */
35
35
  export declare const MessageType: {
@@ -37,15 +37,15 @@ export declare const MessageType: {
37
37
  readonly Text: 7;
38
38
  readonly ShareVideo: 8;
39
39
  readonly PrivateImage: 1802;
40
- /** Sticker (nhãn dán) — ảnh công khai trên CDN sticker, không mã hoá. */
40
+ /** Sticker — public image on the sticker CDN, not encrypted. */
41
41
  readonly Sticker: 1805;
42
- /** Lệnh ẩn (đã đọc…) — không hiện trong hội thoại, websocket vẫn đẩy. */
42
+ /** Hidden command (read receipt…) — not shown in the conversation, but still pushed over websocket. */
43
43
  readonly Command: 50001;
44
44
  };
45
45
  export declare const ConversationType: {
46
46
  readonly OneToOne: 1;
47
47
  };
48
- /** Trường của `Response` (cả HTTP lẫn payload websocket). */
48
+ /** `Response` fields (both HTTP and websocket payload). */
49
49
  export declare const Resp: {
50
50
  readonly cmd: 1;
51
51
  readonly seq: 2;
@@ -54,7 +54,7 @@ export declare const Resp: {
54
54
  readonly body: 6;
55
55
  readonly logId: 7;
56
56
  };
57
- /** Trường của `MessageBody`. */
57
+ /** `MessageBody` fields. */
58
58
  export declare const MsgF: {
59
59
  readonly conversationId: 1;
60
60
  readonly conversationType: 2;
@@ -66,21 +66,21 @@ export declare const MsgF: {
66
66
  readonly content: 8;
67
67
  readonly ext: 9;
68
68
  readonly createTime: 10;
69
- /** `property_list` — reaction: `{1 khoá "e:<emoji>", 2 {1 PropertyItem[]}}`. */
69
+ /** `property_list` — reaction: `{1 key "e:<emoji>", 2 {1 PropertyItem[]}}`. */
70
70
  readonly propertyList: 15;
71
71
  readonly attachments: 20;
72
72
  readonly tag: 21;
73
73
  };
74
- /** Thao tác trên `property` của tin (`ModifyPropertyContent.operation`). */
74
+ /** Operation on a message `property` (`ModifyPropertyContent.operation`). */
75
75
  export declare const PropertyOp: {
76
76
  readonly Add: 0;
77
77
  readonly Remove: 1;
78
78
  };
79
- /** Khoá reaction = `e:<emoji>`. App điện thoại thả ❤️ còn ghi thêm khoá cũ `e:love` — coi như ❤️. */
79
+ /** Reaction key = `e:<emoji>`. The mobile app reacting ❤️ also writes the legacy key `e:love` — treated as ❤️. */
80
80
  export declare const REACTION_PREFIX = "e:";
81
81
  export declare const REACTION_LOVE_KEY = "e:love";
82
82
  export declare const REACTION_HEART = "\u2764\uFE0F";
83
- /** ImageX — nơi tải ảnh lên (ký kiểu AWS SigV4). */
83
+ /** ImageX — image upload endpoint (AWS SigV4-style signing). */
84
84
  export declare const IMAGEX_REGION = "cn-north-1";
85
85
  export declare const IMAGEX_SERVICE = "imagex";
86
86
  export declare const IMAGEX_VERSION = "2018-08-01";
package/dist/constants.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
- * Hằng giao thức IM của TikTok Web — dựng lại từ lưu lượng `tiktok.com/business-suite/messages`
4
- * (09/11/2026). TikTok đổi phía họ thì sửa ở đây; ghi lại cách dò ra ở README.
3
+ * TikTok Web IM protocol constants — reconstructed from `tiktok.com/business-suite/messages` traffic
4
+ * (09/11/2026). If TikTok changes things on their side, fix them here; how they were found is in the README.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.IMAGEX_VERSION = exports.IMAGEX_SERVICE = exports.IMAGEX_REGION = exports.REACTION_HEART = exports.REACTION_LOVE_KEY = exports.REACTION_PREFIX = exports.PropertyOp = exports.MsgF = exports.Resp = exports.ConversationType = exports.MessageType = exports.MARK_READ_BODY_FIELD = exports.Cmd = exports.FRONTIER_SALT = exports.FRONTIER_APP_KEY = exports.FRONTIER_FPID = exports.BUILD_NUMBER = exports.SDK_VERSION = exports.AID_FRONTIER = exports.AID_WEB = exports.DEFAULT_USER_AGENT = exports.WEB = exports.IM_WS = exports.IM_API = void 0;
@@ -9,30 +9,30 @@ exports.IM_API = 'https://im-api-sg.tiktok.com';
9
9
  exports.IM_WS = 'wss://im-ws-sg.tiktok.com/ws/v2';
10
10
  exports.WEB = 'https://www.tiktok.com';
11
11
  exports.DEFAULT_USER_AGENT = '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';
12
- /** App id của TikTok Web (HTTP IM). */
12
+ /** TikTok Web app id (HTTP IM). */
13
13
  exports.AID_WEB = '1988';
14
- /** App id kênh "frontier" — websocket đẩy tin mới. */
14
+ /** "frontier" channel app id — websocket pushing new messages. */
15
15
  exports.AID_FRONTIER = '1459';
16
16
  exports.SDK_VERSION = '1.8.5';
17
17
  exports.BUILD_NUMBER = 'feffaa1:master';
18
- /** `access_key` của websocket = md5(FPID + APP_KEY + device_id + SALT). */
18
+ /** Websocket `access_key` = md5(FPID + APP_KEY + device_id + SALT). */
19
19
  exports.FRONTIER_FPID = '9';
20
20
  exports.FRONTIER_APP_KEY = 'e1bd35ec9db7b8d846de66ed140b1ad9';
21
21
  exports.FRONTIER_SALT = 'f8a69f1719916z';
22
- /** Lệnh IM (`Request.cmd`) — đồng thời là số trường của body trong `RequestBody`. */
22
+ /** IM command (`Request.cmd`) — also the body's field number in `RequestBody`. */
23
23
  exports.Cmd = {
24
24
  SendMessage: 100,
25
25
  GetMessagesByUserInit: 203,
26
26
  GetMessagesByConversation: 301,
27
- /** Websocket đẩy tin mới. */
27
+ /** Websocket push of new messages. */
28
28
  NewMessage: 500,
29
- /** Thả / gỡ reaction (`property` của tin) — `/v1/message/set_property`. */
29
+ /** Add / remove a reaction (message `property`) — `/v1/message/set_property`. */
30
30
  SetMessageProperty: 705,
31
31
  GetConversationInfo: 610,
32
32
  MarkRead: 2002,
33
33
  UploadConfig: 2059,
34
34
  };
35
- /** Body của MarkRead nằm ở trường 604, không trùng số lệnh. */
35
+ /** MarkRead's body lives in field 604, not the command number. */
36
36
  exports.MARK_READ_BODY_FIELD = 604;
37
37
  /** `MessageBody.message_type`. */
38
38
  exports.MessageType = {
@@ -40,15 +40,15 @@ exports.MessageType = {
40
40
  Text: 7,
41
41
  ShareVideo: 8,
42
42
  PrivateImage: 1802,
43
- /** Sticker (nhãn dán) — ảnh công khai trên CDN sticker, không mã hoá. */
43
+ /** Sticker — public image on the sticker CDN, not encrypted. */
44
44
  Sticker: 1805,
45
- /** Lệnh ẩn (đã đọc…) — không hiện trong hội thoại, websocket vẫn đẩy. */
45
+ /** Hidden command (read receipt…) — not shown in the conversation, but still pushed over websocket. */
46
46
  Command: 50001,
47
47
  };
48
48
  exports.ConversationType = { OneToOne: 1 };
49
- /** Trường của `Response` (cả HTTP lẫn payload websocket). */
49
+ /** `Response` fields (both HTTP and websocket payload). */
50
50
  exports.Resp = { cmd: 1, seq: 2, status: 3, error: 4, body: 6, logId: 7 };
51
- /** Trường của `MessageBody`. */
51
+ /** `MessageBody` fields. */
52
52
  exports.MsgF = {
53
53
  conversationId: 1,
54
54
  conversationType: 2,
@@ -60,18 +60,18 @@ exports.MsgF = {
60
60
  content: 8,
61
61
  ext: 9,
62
62
  createTime: 10,
63
- /** `property_list` — reaction: `{1 khoá "e:<emoji>", 2 {1 PropertyItem[]}}`. */
63
+ /** `property_list` — reaction: `{1 key "e:<emoji>", 2 {1 PropertyItem[]}}`. */
64
64
  propertyList: 15,
65
65
  attachments: 20,
66
66
  tag: 21,
67
67
  };
68
- /** Thao tác trên `property` của tin (`ModifyPropertyContent.operation`). */
68
+ /** Operation on a message `property` (`ModifyPropertyContent.operation`). */
69
69
  exports.PropertyOp = { Add: 0, Remove: 1 };
70
- /** Khoá reaction = `e:<emoji>`. App điện thoại thả ❤️ còn ghi thêm khoá cũ `e:love` — coi như ❤️. */
70
+ /** Reaction key = `e:<emoji>`. The mobile app reacting ❤️ also writes the legacy key `e:love` — treated as ❤️. */
71
71
  exports.REACTION_PREFIX = 'e:';
72
72
  exports.REACTION_LOVE_KEY = 'e:love';
73
73
  exports.REACTION_HEART = '\u2764\uFE0F';
74
- /** ImageX — nơi tải ảnh lên (ký kiểu AWS SigV4). */
74
+ /** ImageX — image upload endpoint (AWS SigV4-style signing). */
75
75
  exports.IMAGEX_REGION = 'cn-north-1';
76
76
  exports.IMAGEX_SERVICE = 'imagex';
77
77
  exports.IMAGEX_VERSION = '2018-08-01';
package/dist/context.d.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  import { Msg } from './proto';
2
2
  import type { Credentials, TikTokOptions } from './types';
3
- /** Cookie dạng `name → value` từ mọi kiểu đầu vào của `Credentials.cookies`. */
3
+ /** `name → value` cookies from any input shape of `Credentials.cookies`. */
4
4
  export declare function normalizeCookies(input: Credentials['cookies']): Map<string, string>;
5
- /** Trạng thái dùng chung của một phiên: cookie, mã máy, uid, gọi HTTP. */
5
+ /** Shared state of a session: cookies, device id, uid, HTTP calls. */
6
6
  export declare class Context {
7
7
  readonly options: TikTokOptions;
8
8
  readonly cookies: Map<string, string>;
9
9
  /**
10
- * Mã máy IM = `wid` TikTok cấp theo cookie `ttwid` (`login` tự hỏi). Websocket chỉ nhận đúng
11
- * mã này — mã tự bịa bị 400 (HTTP IM thì không kén).
10
+ * IM device id = the `wid` TikTok issues for the `ttwid` cookie (`login` fetches it). The websocket only
11
+ * accepts this id — a made-up id gets 400 (HTTP IM isn't picky).
12
12
  */
13
13
  deviceId: string;
14
14
  readonly userAgent: string;
@@ -17,16 +17,16 @@ export declare class Context {
17
17
  constructor(creds: Credentials, options?: TikTokOptions);
18
18
  cookieHeader(): string;
19
19
  log(level: 'debug' | 'warn', msg: string): void;
20
- /** `fetch` kèm cookie + UA + hạn chờ. */
20
+ /** `fetch` with cookies + UA + timeout. */
21
21
  fetch(url: string, init?: RequestInit & {
22
22
  headers?: Record<string, string>;
23
23
  }): Promise<Response>;
24
24
  getJson<T>(url: string): Promise<T>;
25
- /** Vỏ `Request` của IM: lệnh + body + đầu mục chung. */
25
+ /** IM `Request` envelope: command + body + common headers. */
26
26
  buildRequest(cmd: number, bodyField: number, body: Buffer): Buffer;
27
27
  /**
28
- * Gọi một lệnh IM qua HTTP. Trả body đã bóc (`ResponseBody[cmd]`).
29
- * KHÔNG cần chữ ký X-Gnarly/X-Bogus/msToken — server IM chỉ xét cookie (thử 09/11/2026).
28
+ * Call an IM command over HTTP. Returns the unwrapped body (`ResponseBody[cmd]`).
29
+ * NO X-Gnarly/X-Bogus/msToken signature needed — the IM server only checks cookies (tested 09/11/2026).
30
30
  */
31
31
  im(path: string, cmd: number, body: Buffer, bodyField?: number): Promise<Msg>;
32
32
  }
package/dist/context.js CHANGED
@@ -5,7 +5,7 @@ exports.normalizeCookies = normalizeCookies;
5
5
  const constants_1 = require("./constants");
6
6
  const errors_1 = require("./errors");
7
7
  const proto_1 = require("./proto");
8
- /** Cookie dạng `name → value` từ mọi kiểu đầu vào của `Credentials.cookies`. */
8
+ /** `name → value` cookies from any input shape of `Credentials.cookies`. */
9
9
  function normalizeCookies(input) {
10
10
  const jar = new Map();
11
11
  if (typeof input === 'string') {
@@ -26,13 +26,13 @@ function normalizeCookies(input) {
26
26
  }
27
27
  return jar;
28
28
  }
29
- /** Trạng thái dùng chung của một phiên: cookie, mã máy, uid, gọi HTTP. */
29
+ /** Shared state of a session: cookies, device id, uid, HTTP calls. */
30
30
  class Context {
31
31
  options;
32
32
  cookies;
33
33
  /**
34
- * Mã máy IM = `wid` TikTok cấp theo cookie `ttwid` (`login` tự hỏi). Websocket chỉ nhận đúng
35
- * mã này — mã tự bịa bị 400 (HTTP IM thì không kén).
34
+ * IM device id = the `wid` TikTok issues for the `ttwid` cookie (`login` fetches it). The websocket only
35
+ * accepts this id — a made-up id gets 400 (HTTP IM isn't picky).
36
36
  */
37
37
  deviceId;
38
38
  userAgent;
@@ -50,7 +50,7 @@ class Context {
50
50
  log(level, msg) {
51
51
  this.options.logger?.(level, msg);
52
52
  }
53
- /** `fetch` kèm cookie + UA + hạn chờ. */
53
+ /** `fetch` with cookies + UA + timeout. */
54
54
  async fetch(url, init = {}) {
55
55
  try {
56
56
  return await fetch(url, {
@@ -75,7 +75,7 @@ class Context {
75
75
  throw new errors_1.TikTokError('HTTP_ERROR', `${url.split('?')[0]}: HTTP ${res.status}`, res.status);
76
76
  return (await res.json());
77
77
  }
78
- /** Vỏ `Request` của IM: lệnh + body + đầu mục chung. */
78
+ /** IM `Request` envelope: command + body + common headers. */
79
79
  buildRequest(cmd, bodyField, body) {
80
80
  return new proto_1.Writer()
81
81
  .int(1, cmd)
@@ -101,8 +101,8 @@ class Context {
101
101
  .finish();
102
102
  }
103
103
  /**
104
- * Gọi một lệnh IM qua HTTP. Trả body đã bóc (`ResponseBody[cmd]`).
105
- * KHÔNG cần chữ ký X-Gnarly/X-Bogus/msToken — server IM chỉ xét cookie (thử 09/11/2026).
104
+ * Call an IM command over HTTP. Returns the unwrapped body (`ResponseBody[cmd]`).
105
+ * NO X-Gnarly/X-Bogus/msToken signature needed — the IM server only checks cookies (tested 09/11/2026).
106
106
  */
107
107
  async im(path, cmd, body, bodyField = cmd) {
108
108
  const url = `${constants_1.IM_API}${path}?aid=${constants_1.AID_WEB}&version_code=1.0.0&app_name=tiktok_web&device_platform=web_pc`;
@@ -115,11 +115,11 @@ class Context {
115
115
  throw new errors_1.TikTokError('HTTP_ERROR', `${path}: HTTP ${res.status}`, res.status);
116
116
  const resp = proto_1.Msg.tryParse(Buffer.from(await res.arrayBuffer()));
117
117
  if (!resp)
118
- throw new errors_1.TikTokError('BAD_RESPONSE', `${path}: phản hồi không phải protobuf`);
118
+ throw new errors_1.TikTokError('BAD_RESPONSE', `${path}: response is not protobuf`);
119
119
  const status = resp.num(constants_1.Resp.status);
120
120
  if (status !== 0) {
121
121
  const desc = resp.str(constants_1.Resp.error);
122
- // Chưa đăng nhập: 200005 (thử bằng request không cookie).
122
+ // Not logged in: 200005 (tested with a cookieless request).
123
123
  const code = status === 200005 ? 'SESSION_EXPIRED' : 'IM_ERROR';
124
124
  throw new errors_1.TikTokError(code, `${path}: ${status} ${desc}`.trim(), status);
125
125
  }
package/dist/errors.d.ts CHANGED
@@ -1,18 +1,18 @@
1
- /** Lỗi từ TikTok hoặc từ chính thư viện. `code` để bên dùng phân nhánh (vd phiên hết hạn → quét lại). */
1
+ /** Error from TikTok or from the library itself. `code` lets callers branch (e.g. session expired → rescan). */
2
2
  export declare class TikTokError extends Error {
3
3
  readonly code: TikTokErrorCode;
4
- /** Mã gốc TikTok trả (status_code / error_code) nếu có. */
4
+ /** Original TikTok code (status_code / error_code), if any. */
5
5
  readonly upstream?: number | string | undefined;
6
6
  constructor(code: TikTokErrorCode, message: string,
7
- /** Mã gốc TikTok trả (status_code / error_code) nếu có. */
7
+ /** Original TikTok code (status_code / error_code), if any. */
8
8
  upstream?: number | string | undefined);
9
9
  }
10
10
  export type TikTokErrorCode =
11
- /** Cookie không còn đăng nhập — phải lấy phiên mới. */
11
+ /** Cookie is no longer logged in — a new session is required. */
12
12
  'SESSION_EXPIRED' | 'HTTP_ERROR'
13
- /** IM trả `status_code` khác 0. */
13
+ /** IM returned a non-zero `status_code`. */
14
14
  | 'IM_ERROR' | 'UPLOAD_FAILED' | 'DOWNLOAD_FAILED' | 'BAD_RESPONSE'
15
- /** Đăng nhập QR không thành (Chrome không mở được, QR hết hạn, không thấy cookie…). */
15
+ /** QR login failed (Chrome failed to launch, QR expired, no cookie found…). */
16
16
  | 'LOGIN_FAILED'
17
- /** Bên gọi huỷ đăng nhập QR (`signal`). */
17
+ /** Caller aborted QR login (`signal`). */
18
18
  | 'LOGIN_ABORTED';
package/dist/errors.js CHANGED
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TikTokError = void 0;
4
- /** Lỗi từ TikTok hoặc từ chính thư viện. `code` để bên dùng phân nhánh (vd phiên hết hạn → quét lại). */
4
+ /** Error from TikTok or from the library itself. `code` lets callers branch (e.g. session expired → rescan). */
5
5
  class TikTokError extends Error {
6
6
  code;
7
7
  upstream;
8
8
  constructor(code, message,
9
- /** Mã gốc TikTok trả (status_code / error_code) nếu có. */
9
+ /** Original TikTok code (status_code / error_code), if any. */
10
10
  upstream) {
11
11
  super(message);
12
12
  this.code = code;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tca — TikTok Chat API (giả lập TikTok Web), cùng ý tưởng với zca-js cho Zalo.
2
+ * tca-js — unofficial TikTok Chat API (speaks the TikTok Web protocol).
3
3
  *
4
4
  * ```ts
5
5
  * const api = await new TikTok().login({ cookies, deviceId });
@@ -10,15 +10,15 @@
10
10
  * api.listener.start();
11
11
  * ```
12
12
  *
13
- * Lấy cookie lần đầu: `loginQR({ chromePath, onEvent })` — mở Chrome headless ở trang QR của TikTok
14
- * (API đăng nhập đòi chữ ký JS nên không làm thuần Node được), quét xong trả cookie. Xem README.
13
+ * Getting cookies the first time: `loginQR({ chromePath, onEvent })` — opens headless Chrome on TikTok's QR page
14
+ * (the login API requires a JS signature so it can't be done in pure Node), returns cookies after scanning. See README.
15
15
  */
16
16
  import { API } from './api';
17
17
  import type { Credentials, TikTokOptions } from './types';
18
18
  export declare class TikTok {
19
19
  private readonly options;
20
20
  constructor(options?: TikTokOptions);
21
- /** Đăng nhập bằng cookie đã có. Cookie hết hạn → `TikTokError('SESSION_EXPIRED')`. */
21
+ /** Log in with existing cookies. Expired cookies → `TikTokError('SESSION_EXPIRED')`. */
22
22
  login(credentials: Credentials): Promise<API>;
23
23
  }
24
24
  export { API } from './api';