@tiktool/live 1.2.0 → 1.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/dist/index.d.mts CHANGED
@@ -56,6 +56,7 @@ interface BattleTeamUser {
56
56
  }
57
57
  interface BattleTeam {
58
58
  hostUserId: string;
59
+ hostUser?: TikTokUser;
59
60
  score: number;
60
61
  users: BattleTeamUser[];
61
62
  }
@@ -69,6 +70,7 @@ interface BattleEvent extends BaseEvent {
69
70
  interface BattleArmiesEvent extends BaseEvent {
70
71
  type: 'battleArmies';
71
72
  battleId: string;
73
+ status: number;
72
74
  teams: BattleTeam[];
73
75
  }
74
76
  interface SubscribeEvent extends BaseEvent {
@@ -173,6 +175,7 @@ declare class TikTokLive extends EventEmitter {
173
175
  private _connected;
174
176
  private _eventCount;
175
177
  private _roomId;
178
+ private _battleHosts;
176
179
  private readonly uniqueId;
177
180
  private readonly signServerUrl;
178
181
  private readonly apiKey;
package/dist/index.d.ts CHANGED
@@ -56,6 +56,7 @@ interface BattleTeamUser {
56
56
  }
57
57
  interface BattleTeam {
58
58
  hostUserId: string;
59
+ hostUser?: TikTokUser;
59
60
  score: number;
60
61
  users: BattleTeamUser[];
61
62
  }
@@ -69,6 +70,7 @@ interface BattleEvent extends BaseEvent {
69
70
  interface BattleArmiesEvent extends BaseEvent {
70
71
  type: 'battleArmies';
71
72
  battleId: string;
73
+ status: number;
72
74
  teams: BattleTeam[];
73
75
  }
74
76
  interface SubscribeEvent extends BaseEvent {
@@ -173,6 +175,7 @@ declare class TikTokLive extends EventEmitter {
173
175
  private _connected;
174
176
  private _eventCount;
175
177
  private _roomId;
178
+ private _battleHosts;
176
179
  private readonly uniqueId;
177
180
  private readonly signServerUrl;
178
181
  private readonly apiKey;
package/dist/index.js CHANGED
@@ -159,80 +159,147 @@ function buildAck(id) {
159
159
  function parseUser(data) {
160
160
  const f = decodeProto(data);
161
161
  const id = String(getInt(f, 1) || getStr(f, 1));
162
- const nickname = getStr(f, 3) || getStr(f, 5);
163
- const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2);
162
+ const nickname = getStr(f, 3) || getStr(f, 2);
163
+ const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2) || "";
164
164
  let profilePicture;
165
- const avatarBuf = getBytes(f, 9);
165
+ const avatarBuf = getBytes(f, 9) || getBytes(f, 3);
166
166
  if (avatarBuf) {
167
167
  try {
168
168
  const avatarFields = decodeProto(avatarBuf);
169
- const urlBuf = getBytes(avatarFields, 1);
170
- if (urlBuf) profilePicture = urlBuf.toString("utf-8");
169
+ const urlBufs = getAllBytes(avatarFields, 1);
170
+ if (urlBufs.length > 0) profilePicture = urlBufs[0].toString("utf-8");
171
171
  } catch {
172
172
  }
173
173
  }
174
- return { id, nickname, uniqueId: uniqueId || nickname || id, profilePicture };
174
+ const badges = [];
175
+ const badgeBuf = getBytes(f, 64);
176
+ if (badgeBuf) {
177
+ try {
178
+ const badgeFields = decodeProto(badgeBuf);
179
+ const badgeItems = getAllBytes(badgeFields, 21);
180
+ for (const bi of badgeItems) {
181
+ const bf = decodeProto(bi);
182
+ const name = getStr(bf, 3) || getStr(bf, 2);
183
+ if (name) badges.push(name);
184
+ }
185
+ } catch {
186
+ }
187
+ }
188
+ return {
189
+ id,
190
+ nickname,
191
+ uniqueId: uniqueId || nickname || id,
192
+ profilePicture,
193
+ badges: badges.length > 0 ? badges : void 0
194
+ };
175
195
  }
176
- function parseBattleTeam(teamBuf) {
177
- const fields = decodeProto(teamBuf);
178
- const hostUserId = String(getInt(fields, 1));
179
- const score = getInt(fields, 2);
196
+ function parseBattleTeamFromArmies(itemBuf) {
197
+ const f = decodeProto(itemBuf);
198
+ const hostUserId = String(getInt(f, 1));
199
+ let teamScore = 0;
180
200
  const users = [];
181
- const userFields = getAllBytes(fields, 3);
182
- for (const uf of userFields) {
201
+ const groups = getAllBytes(f, 2);
202
+ for (const gb of groups) {
183
203
  try {
184
- const uFields = decodeProto(uf);
185
- const userBuf = getBytes(uFields, 1);
186
- const userScore = getInt(uFields, 2);
187
- const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
188
- users.push({ user, score: userScore });
204
+ const gf = decodeProto(gb);
205
+ const points = getInt(gf, 2);
206
+ teamScore += points;
207
+ const userBufs = getAllBytes(gf, 1);
208
+ for (const ub of userBufs) {
209
+ try {
210
+ const user = parseUser(ub);
211
+ users.push({ user, score: points });
212
+ } catch {
213
+ }
214
+ }
189
215
  } catch {
190
216
  }
191
217
  }
192
- return { hostUserId, score, users };
218
+ return { hostUserId, score: teamScore, users };
193
219
  }
194
220
  function parseWebcastMessage(method, payload) {
195
221
  const f = decodeProto(payload);
196
- const base = { timestamp: Date.now(), msgId: String(getInt(f, 1) || "") };
222
+ const base = { timestamp: Date.now(), msgId: "" };
223
+ const typeBuf = getBytes(f, 1);
224
+ if (typeBuf) {
225
+ try {
226
+ const tf = decodeProto(typeBuf);
227
+ const ts = getInt(tf, 4);
228
+ if (ts) base.timestamp = ts;
229
+ } catch {
230
+ }
231
+ }
197
232
  switch (method) {
233
+ // Proto: WebcastChatMessage { MessageType type=1, User user=2, string comment=3 }
198
234
  case "WebcastChatMessage": {
199
235
  const userBuf = getBytes(f, 2);
200
236
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
201
237
  return { ...base, type: "chat", user, comment: getStr(f, 3) };
202
238
  }
239
+ // Proto: WebcastMemberMessage { User user=2, WebcastMessageEvent event=1 }
203
240
  case "WebcastMemberMessage": {
204
241
  const userBuf = getBytes(f, 2);
205
242
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
206
- return { ...base, type: "member", user, action: getInt(f, 1) };
243
+ let action = 1;
244
+ const eventBuf = getBytes(f, 1);
245
+ if (eventBuf) {
246
+ const ef = decodeProto(eventBuf);
247
+ const detailBuf = getBytes(ef, 8);
248
+ if (detailBuf) {
249
+ const df = decodeProto(detailBuf);
250
+ const label = getStr(df, 2);
251
+ if (label.includes("followed")) action = 2;
252
+ else if (label.includes("share")) action = 3;
253
+ }
254
+ }
255
+ return { ...base, type: "member", user, action };
207
256
  }
257
+ // Proto: WebcastLikeMessage { User user=5, WebcastMessageEvent event=1, int32 likeCount=2, int32 totalLikeCount=3 }
208
258
  case "WebcastLikeMessage": {
209
259
  const userBuf = getBytes(f, 5);
210
260
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
211
- return { ...base, type: "like", user, likeCount: getInt(f, 1), totalLikes: getInt(f, 2) || getInt(f, 7) };
261
+ return {
262
+ ...base,
263
+ type: "like",
264
+ user,
265
+ likeCount: getInt(f, 2),
266
+ totalLikes: getInt(f, 3)
267
+ };
212
268
  }
269
+ // Proto: WebcastGiftMessage {
270
+ // User user=7, int32 giftId=2, int32 repeatCount=5, int32 repeatEnd=9,
271
+ // GiftDetails giftDetails=15, GiftExtra giftExtra=23
272
+ // }
273
+ // GiftDetails { GiftImage giftImage=1, string giftName=16, string describe=2,
274
+ // int32 giftType=11, int32 diamondCount=12 }
275
+ // GiftExtra { uint64 timestamp=6, uint64 toUserId=8 }
213
276
  case "WebcastGiftMessage": {
214
277
  const userBuf = getBytes(f, 7);
215
278
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
216
- const giftId = getInt(f, 1);
279
+ const giftId = getInt(f, 2);
217
280
  const repeatCount = getInt(f, 5);
218
281
  const repeatEnd = getInt(f, 9) === 1;
219
- const giftType = getInt(f, 6);
220
- const groupId = getStr(f, 11);
221
- let giftName = "", diamondCount = 0;
222
- const giftInfoBuf = getBytes(f, 15);
223
- if (giftInfoBuf) {
224
- const gf = decodeProto(giftInfoBuf);
225
- giftName = getStr(gf, 1);
226
- diamondCount = getInt(gf, 5) || getInt(gf, 2);
227
- }
228
- if (!giftName) {
229
- const giftBuf3 = getBytes(f, 3);
230
- if (giftBuf3) {
231
- const gf3 = decodeProto(giftBuf3);
232
- if (!giftName) giftName = getStr(gf3, 2);
233
- if (!diamondCount) diamondCount = getInt(gf3, 5);
282
+ let giftName = "", diamondCount = 0, giftType = 0;
283
+ let giftImageUrl = "";
284
+ const detailsBuf = getBytes(f, 15);
285
+ if (detailsBuf) {
286
+ const df = decodeProto(detailsBuf);
287
+ giftName = getStr(df, 16) || getStr(df, 2);
288
+ diamondCount = getInt(df, 12);
289
+ giftType = getInt(df, 11);
290
+ const imgBuf = getBytes(df, 1);
291
+ if (imgBuf) {
292
+ const imgf = decodeProto(imgBuf);
293
+ giftImageUrl = getStr(imgf, 1);
234
294
  }
235
295
  }
296
+ let toUserId = "";
297
+ const extraBuf = getBytes(f, 23);
298
+ if (extraBuf) {
299
+ const ef = decodeProto(extraBuf);
300
+ toUserId = String(getInt(ef, 8));
301
+ }
302
+ const groupId = toUserId || getStr(f, 11);
236
303
  return {
237
304
  ...base,
238
305
  type: "gift",
@@ -247,46 +314,93 @@ function parseWebcastMessage(method, payload) {
247
314
  groupId
248
315
  };
249
316
  }
317
+ // Proto: WebcastSocialMessage { User user=2, WebcastMessageEvent event=1 }
250
318
  case "WebcastSocialMessage": {
251
319
  const userBuf = getBytes(f, 2);
252
320
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
253
- const actionInt = getInt(f, 1);
254
- const actionMap = { 1: "follow", 2: "share", 3: "like" };
255
- return { ...base, type: "social", user, action: actionMap[actionInt] || `action_${actionInt}` };
321
+ let action = "follow";
322
+ const eventBuf = getBytes(f, 1);
323
+ if (eventBuf) {
324
+ const ef = decodeProto(eventBuf);
325
+ const detailBuf = getBytes(ef, 8);
326
+ if (detailBuf) {
327
+ const df = decodeProto(detailBuf);
328
+ const label = getStr(df, 2);
329
+ if (label.includes("share")) action = "share";
330
+ else if (label.includes("follow")) action = "follow";
331
+ const displayType = getStr(df, 1);
332
+ if (displayType === "pm_mt_msg_viewer_share") action = "share";
333
+ }
334
+ }
335
+ return { ...base, type: "social", user, action };
256
336
  }
257
- case "WebcastRoomUserSeqMessage":
258
- return {
259
- ...base,
260
- type: "roomUserSeq",
261
- totalViewers: getInt(f, 1) || getInt(f, 3),
262
- viewerCount: getInt(f, 2) || getInt(f, 4)
263
- };
337
+ // Proto: WebcastRoomUserSeqMessage { int32 viewerCount=3 }
338
+ case "WebcastRoomUserSeqMessage": {
339
+ const viewerCount = getInt(f, 3) || getInt(f, 2);
340
+ const totalViewers = getInt(f, 1) || viewerCount;
341
+ return { ...base, type: "roomUserSeq", totalViewers, viewerCount };
342
+ }
343
+ // Proto: WebcastLinkMicBattle { repeated WebcastLinkMicBattleItems battleUsers=10 }
344
+ // battleUsers → { battleGroup=2 → { LinkUser user=1 } }
264
345
  case "WebcastLinkMicBattle": {
265
- const battleId = String(getInt(f, 1) || getStr(f, 1));
266
- const status = getInt(f, 2);
346
+ const battleId = String(getInt(f, 1) || "");
347
+ const status = getInt(f, 2) || 1;
267
348
  const battleDuration = getInt(f, 3);
268
349
  const teams = [];
269
- const teamBufs = getAllBytes(f, 7);
270
- for (const tb of teamBufs) {
350
+ const battleUserBufs = getAllBytes(f, 10);
351
+ for (const bub of battleUserBufs) {
271
352
  try {
272
- teams.push(parseBattleTeam(tb));
353
+ const bf = decodeProto(bub);
354
+ const groupBuf = getBytes(bf, 2);
355
+ if (groupBuf) {
356
+ const gf = decodeProto(groupBuf);
357
+ const linkUserBuf = getBytes(gf, 1);
358
+ if (linkUserBuf) {
359
+ const user = parseUser(linkUserBuf);
360
+ teams.push({
361
+ hostUserId: user.id,
362
+ score: 0,
363
+ users: [{ user, score: 0 }]
364
+ });
365
+ }
366
+ }
273
367
  } catch {
274
368
  }
275
369
  }
370
+ if (teams.length === 0) {
371
+ const teamBufs7 = getAllBytes(f, 7);
372
+ for (const tb of teamBufs7) {
373
+ try {
374
+ teams.push(parseBattleTeamFromArmies(tb));
375
+ } catch {
376
+ }
377
+ }
378
+ }
276
379
  return { ...base, type: "battle", battleId, status, battleDuration, teams };
277
380
  }
381
+ // Proto: WebcastLinkMicArmies { repeated battleItems=3, int32 battleStatus=7 }
382
+ // battleItems → { hostUserId=1, repeated battleGroups=2 }
383
+ // battleGroups → { repeated users=1, int32 points=2 }
278
384
  case "WebcastLinkMicArmies": {
279
- const battleId = String(getInt(f, 1) || getStr(f, 1));
385
+ const battleId = String(getInt(f, 1) || "");
386
+ const battleStatus = getInt(f, 7);
280
387
  const teams = [];
281
- const teamBufs = getAllBytes(f, 3);
282
- for (const tb of teamBufs) {
388
+ const itemBufs = getAllBytes(f, 3);
389
+ for (const ib of itemBufs) {
283
390
  try {
284
- teams.push(parseBattleTeam(tb));
391
+ teams.push(parseBattleTeamFromArmies(ib));
285
392
  } catch {
286
393
  }
287
394
  }
288
- return { ...base, type: "battleArmies", battleId, teams };
395
+ return {
396
+ ...base,
397
+ type: "battleArmies",
398
+ battleId,
399
+ teams,
400
+ status: battleStatus
401
+ };
289
402
  }
403
+ // Proto: WebcastSubNotifyMessage { User user=2, ... subMonth=3 }
290
404
  case "WebcastSubNotifyMessage": {
291
405
  const userBuf = getBytes(f, 2);
292
406
  const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
@@ -312,10 +426,18 @@ function parseWebcastMessage(method, payload) {
312
426
  const envelopeId = String(getInt(f, 1) || getStr(f, 1));
313
427
  return { ...base, type: "envelope", envelopeId, diamondCount: getInt(f, 3) };
314
428
  }
429
+ // Proto: WebcastQuestionNewMessage { MessageType type=1, QuestionDetails questionDetails=2 }
430
+ // QuestionDetails { string questionText=2, User user=5 }
315
431
  case "WebcastQuestionNewMessage": {
316
- const userBuf = getBytes(f, 2);
317
- const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
318
- return { ...base, type: "question", user, questionText: getStr(f, 3) || getStr(f, 4) };
432
+ let questionText = "", user = { id: "0", nickname: "", uniqueId: "" };
433
+ const detailBuf = getBytes(f, 2);
434
+ if (detailBuf) {
435
+ const df = decodeProto(detailBuf);
436
+ questionText = getStr(df, 2);
437
+ const userBuf = getBytes(df, 5);
438
+ if (userBuf) user = parseUser(userBuf);
439
+ }
440
+ return { ...base, type: "question", user, questionText };
319
441
  }
320
442
  case "WebcastRankUpdateMessage":
321
443
  case "WebcastHourlyRankMessage": {
@@ -335,14 +457,17 @@ function parseWebcastMessage(method, payload) {
335
457
  }
336
458
  return { ...base, type: "rankUpdate", rankType, rankList };
337
459
  }
460
+ // Proto: WebcastControlMessage { int32 action=2 }
338
461
  case "WebcastControlMessage":
339
462
  return { ...base, type: "control", action: getInt(f, 2) || getInt(f, 1) };
340
463
  case "WebcastRoomMessage":
341
464
  case "RoomMessage":
342
465
  return { ...base, type: "room", status: getInt(f, 2) };
466
+ // Proto: WebcastLiveIntroMessage { uint64 id=2, string description=4, User user=5 }
343
467
  case "WebcastLiveIntroMessage": {
344
- const roomId = String(getInt(f, 1));
345
- return { ...base, type: "liveIntro", roomId, title: getStr(f, 4) || getStr(f, 2) };
468
+ const roomId = String(getInt(f, 2));
469
+ const title = getStr(f, 4) || getStr(f, 2);
470
+ return { ...base, type: "liveIntro", roomId, title };
346
471
  }
347
472
  case "WebcastLinkMicMethod":
348
473
  case "WebcastLinkmicBattleTaskMessage": {
@@ -418,6 +543,8 @@ var TikTokLive = class extends import_events.EventEmitter {
418
543
  _connected = false;
419
544
  _eventCount = 0;
420
545
  _roomId = "";
546
+ // Cache host identities from battle events for enriching battleArmies
547
+ _battleHosts = /* @__PURE__ */ new Map();
421
548
  uniqueId;
422
549
  signServerUrl;
423
550
  apiKey;
@@ -615,6 +742,27 @@ var TikTokLive = class extends import_events.EventEmitter {
615
742
  const events = parseWebcastResponse(inner);
616
743
  for (const evt of events) {
617
744
  this._eventCount++;
745
+ if (evt.type === "battle") {
746
+ for (const team of evt.teams) {
747
+ const host = team.users.find((u) => u.user.id === team.hostUserId);
748
+ if (host) {
749
+ this._battleHosts.set(team.hostUserId, host.user);
750
+ team.hostUser = host.user;
751
+ }
752
+ }
753
+ }
754
+ if (evt.type === "battleArmies") {
755
+ for (const team of evt.teams) {
756
+ const host = team.users.find((u) => u.user.id === team.hostUserId);
757
+ if (host) {
758
+ team.hostUser = host.user;
759
+ this._battleHosts.set(team.hostUserId, host.user);
760
+ } else {
761
+ const cached = this._battleHosts.get(team.hostUserId);
762
+ if (cached) team.hostUser = cached;
763
+ }
764
+ }
765
+ }
618
766
  this.emit("event", evt);
619
767
  this.emit(evt.type, evt);
620
768
  }