@tiktool/live 1.1.0 → 1.3.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
@@ -37,6 +37,8 @@ interface GiftEvent extends BaseEvent {
37
37
  repeatCount: number;
38
38
  repeatEnd: boolean;
39
39
  combo: boolean;
40
+ giftType: number;
41
+ groupId: string;
40
42
  }
41
43
  interface SocialEvent extends BaseEvent {
42
44
  type: 'social';
@@ -48,12 +50,27 @@ interface RoomUserSeqEvent extends BaseEvent {
48
50
  viewerCount: number;
49
51
  totalViewers: number;
50
52
  }
53
+ interface BattleTeamUser {
54
+ user: TikTokUser;
55
+ score: number;
56
+ }
57
+ interface BattleTeam {
58
+ hostUserId: string;
59
+ score: number;
60
+ users: BattleTeamUser[];
61
+ }
51
62
  interface BattleEvent extends BaseEvent {
52
63
  type: 'battle';
64
+ battleId: string;
53
65
  status: number;
66
+ battleDuration: number;
67
+ teams: BattleTeam[];
54
68
  }
55
69
  interface BattleArmiesEvent extends BaseEvent {
56
70
  type: 'battleArmies';
71
+ battleId: string;
72
+ status: number;
73
+ teams: BattleTeam[];
57
74
  }
58
75
  interface SubscribeEvent extends BaseEvent {
59
76
  type: 'subscribe';
@@ -64,9 +81,11 @@ interface EmoteChatEvent extends BaseEvent {
64
81
  type: 'emoteChat';
65
82
  user: TikTokUser;
66
83
  emoteId: string;
84
+ emoteUrl: string;
67
85
  }
68
86
  interface EnvelopeEvent extends BaseEvent {
69
87
  type: 'envelope';
88
+ envelopeId: string;
70
89
  diamondCount: number;
71
90
  }
72
91
  interface QuestionEvent extends BaseEvent {
@@ -80,19 +99,26 @@ interface ControlEvent extends BaseEvent {
80
99
  }
81
100
  interface RoomEvent extends BaseEvent {
82
101
  type: 'room';
83
- status: string;
102
+ status: number;
84
103
  }
85
104
  interface LiveIntroEvent extends BaseEvent {
86
105
  type: 'liveIntro';
106
+ roomId: string;
87
107
  title: string;
88
108
  }
89
109
  interface RankUpdateEvent extends BaseEvent {
90
110
  type: 'rankUpdate';
91
111
  rankType: string;
112
+ rankList: Array<{
113
+ user: TikTokUser;
114
+ rank: number;
115
+ score: number;
116
+ }>;
92
117
  }
93
118
  interface LinkMicEvent extends BaseEvent {
94
119
  type: 'linkMic';
95
- action: number;
120
+ action: string;
121
+ users: TikTokUser[];
96
122
  }
97
123
  interface UnknownEvent extends BaseEvent {
98
124
  type: 'unknown';
@@ -170,4 +196,4 @@ declare class TikTokLive extends EventEmitter {
170
196
  private stopHeartbeat;
171
197
  }
172
198
 
173
- export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
199
+ export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTeam, type BattleTeamUser, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
package/dist/index.d.ts CHANGED
@@ -37,6 +37,8 @@ interface GiftEvent extends BaseEvent {
37
37
  repeatCount: number;
38
38
  repeatEnd: boolean;
39
39
  combo: boolean;
40
+ giftType: number;
41
+ groupId: string;
40
42
  }
41
43
  interface SocialEvent extends BaseEvent {
42
44
  type: 'social';
@@ -48,12 +50,27 @@ interface RoomUserSeqEvent extends BaseEvent {
48
50
  viewerCount: number;
49
51
  totalViewers: number;
50
52
  }
53
+ interface BattleTeamUser {
54
+ user: TikTokUser;
55
+ score: number;
56
+ }
57
+ interface BattleTeam {
58
+ hostUserId: string;
59
+ score: number;
60
+ users: BattleTeamUser[];
61
+ }
51
62
  interface BattleEvent extends BaseEvent {
52
63
  type: 'battle';
64
+ battleId: string;
53
65
  status: number;
66
+ battleDuration: number;
67
+ teams: BattleTeam[];
54
68
  }
55
69
  interface BattleArmiesEvent extends BaseEvent {
56
70
  type: 'battleArmies';
71
+ battleId: string;
72
+ status: number;
73
+ teams: BattleTeam[];
57
74
  }
58
75
  interface SubscribeEvent extends BaseEvent {
59
76
  type: 'subscribe';
@@ -64,9 +81,11 @@ interface EmoteChatEvent extends BaseEvent {
64
81
  type: 'emoteChat';
65
82
  user: TikTokUser;
66
83
  emoteId: string;
84
+ emoteUrl: string;
67
85
  }
68
86
  interface EnvelopeEvent extends BaseEvent {
69
87
  type: 'envelope';
88
+ envelopeId: string;
70
89
  diamondCount: number;
71
90
  }
72
91
  interface QuestionEvent extends BaseEvent {
@@ -80,19 +99,26 @@ interface ControlEvent extends BaseEvent {
80
99
  }
81
100
  interface RoomEvent extends BaseEvent {
82
101
  type: 'room';
83
- status: string;
102
+ status: number;
84
103
  }
85
104
  interface LiveIntroEvent extends BaseEvent {
86
105
  type: 'liveIntro';
106
+ roomId: string;
87
107
  title: string;
88
108
  }
89
109
  interface RankUpdateEvent extends BaseEvent {
90
110
  type: 'rankUpdate';
91
111
  rankType: string;
112
+ rankList: Array<{
113
+ user: TikTokUser;
114
+ rank: number;
115
+ score: number;
116
+ }>;
92
117
  }
93
118
  interface LinkMicEvent extends BaseEvent {
94
119
  type: 'linkMic';
95
- action: number;
120
+ action: string;
121
+ users: TikTokUser[];
96
122
  }
97
123
  interface UnknownEvent extends BaseEvent {
98
124
  type: 'unknown';
@@ -170,4 +196,4 @@ declare class TikTokLive extends EventEmitter {
170
196
  private stopHeartbeat;
171
197
  }
172
198
 
173
- export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
199
+ export { type BaseEvent, type BattleArmiesEvent, type BattleEvent, type BattleTeam, type BattleTeamUser, type ChatEvent, type ControlEvent, type EmoteChatEvent, type EnvelopeEvent, type GiftEvent, type LikeEvent, type LinkMicEvent, type LiveEvent, type LiveIntroEvent, type MemberEvent, type QuestionEvent, type RankUpdateEvent, type RoomEvent, type RoomInfo, type RoomUserSeqEvent, type SocialEvent, type SubscribeEvent, TikTokLive, type TikTokLiveEvents, type TikTokLiveOptions, type TikTokUser, type UnknownEvent };
package/dist/index.js CHANGED
@@ -123,6 +123,9 @@ function getInt(fields, fn) {
123
123
  const f = fields.find((x) => x.fn === fn && x.wt === 0);
124
124
  return f ? Number(f.value) : 0;
125
125
  }
126
+ function getAllBytes(fields, fn) {
127
+ return fields.filter((x) => x.fn === fn && x.wt === 2).map((x) => x.value);
128
+ }
126
129
  function buildHeartbeat(roomId) {
127
130
  const payload = encodeField(1, 0, BigInt(roomId));
128
131
  return Buffer.concat([
@@ -156,67 +159,329 @@ function buildAck(id) {
156
159
  function parseUser(data) {
157
160
  const f = decodeProto(data);
158
161
  const id = String(getInt(f, 1) || getStr(f, 1));
159
- const nickname = getStr(f, 3) || getStr(f, 5);
160
- const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2);
161
- return { id, nickname, uniqueId: uniqueId || nickname || id };
162
+ const nickname = getStr(f, 3) || getStr(f, 2);
163
+ const uniqueId = getStr(f, 38) || getStr(f, 4) || getStr(f, 2) || "";
164
+ let profilePicture;
165
+ const avatarBuf = getBytes(f, 9) || getBytes(f, 3);
166
+ if (avatarBuf) {
167
+ try {
168
+ const avatarFields = decodeProto(avatarBuf);
169
+ const urlBufs = getAllBytes(avatarFields, 1);
170
+ if (urlBufs.length > 0) profilePicture = urlBufs[0].toString("utf-8");
171
+ } catch {
172
+ }
173
+ }
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
+ };
195
+ }
196
+ function parseBattleTeamFromArmies(itemBuf) {
197
+ const f = decodeProto(itemBuf);
198
+ const hostUserId = String(getInt(f, 1));
199
+ let teamScore = 0;
200
+ const users = [];
201
+ const groups = getAllBytes(f, 2);
202
+ for (const gb of groups) {
203
+ try {
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
+ }
215
+ } catch {
216
+ }
217
+ }
218
+ return { hostUserId, score: teamScore, users };
162
219
  }
163
220
  function parseWebcastMessage(method, payload) {
164
221
  const f = decodeProto(payload);
165
- const userBuf = getBytes(f, 2);
166
- const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
167
- 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
+ }
168
232
  switch (method) {
169
- case "WebcastChatMessage":
233
+ // Proto: WebcastChatMessage { MessageType type=1, User user=2, string comment=3 }
234
+ case "WebcastChatMessage": {
235
+ const userBuf = getBytes(f, 2);
236
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
170
237
  return { ...base, type: "chat", user, comment: getStr(f, 3) };
171
- case "WebcastMemberMessage":
172
- return { ...base, type: "member", user, action: getInt(f, 3) };
173
- case "WebcastLikeMessage":
174
- return { ...base, type: "like", user, likeCount: getInt(f, 5), totalLikes: getInt(f, 6) || getInt(f, 7) };
175
- case "WebcastGiftMessage": {
176
- const giftBuf = getBytes(f, 3);
177
- let giftName = "", giftId = 0, diamondCount = 0;
178
- if (giftBuf) {
179
- const gf = decodeProto(giftBuf);
180
- giftId = getInt(gf, 1);
181
- giftName = getStr(gf, 2);
182
- diamondCount = getInt(gf, 5);
238
+ }
239
+ // Proto: WebcastMemberMessage { User user=2, WebcastMessageEvent event=1 }
240
+ case "WebcastMemberMessage": {
241
+ const userBuf = getBytes(f, 2);
242
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
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
+ }
183
254
  }
255
+ return { ...base, type: "member", user, action };
256
+ }
257
+ // Proto: WebcastLikeMessage { User user=5, WebcastMessageEvent event=1, int32 likeCount=2, int32 totalLikeCount=3 }
258
+ case "WebcastLikeMessage": {
259
+ const userBuf = getBytes(f, 5);
260
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
261
+ return {
262
+ ...base,
263
+ type: "like",
264
+ user,
265
+ likeCount: getInt(f, 2),
266
+ totalLikes: getInt(f, 3)
267
+ };
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 }
276
+ case "WebcastGiftMessage": {
277
+ const userBuf = getBytes(f, 7);
278
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
279
+ const giftId = getInt(f, 2);
184
280
  const repeatCount = getInt(f, 5);
185
281
  const repeatEnd = getInt(f, 9) === 1;
186
- return { ...base, type: "gift", user, giftId, giftName, diamondCount, repeatCount, repeatEnd, combo: repeatCount > 1 && !repeatEnd };
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);
294
+ }
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);
303
+ return {
304
+ ...base,
305
+ type: "gift",
306
+ user,
307
+ giftId,
308
+ giftName,
309
+ diamondCount,
310
+ repeatCount,
311
+ repeatEnd,
312
+ combo: repeatCount > 1 && !repeatEnd,
313
+ giftType,
314
+ groupId
315
+ };
187
316
  }
317
+ // Proto: WebcastSocialMessage { User user=2, WebcastMessageEvent event=1 }
188
318
  case "WebcastSocialMessage": {
189
- const action = getInt(f, 3);
190
- const actionMap = { 1: "follow", 2: "share", 3: "like" };
191
- return { ...base, type: "social", user, action: actionMap[action] || `action_${action}` };
319
+ const userBuf = getBytes(f, 2);
320
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
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 };
336
+ }
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 } }
345
+ case "WebcastLinkMicBattle": {
346
+ const battleId = String(getInt(f, 1) || "");
347
+ const status = getInt(f, 2) || 1;
348
+ const battleDuration = getInt(f, 3);
349
+ const teams = [];
350
+ const battleUserBufs = getAllBytes(f, 10);
351
+ for (const bub of battleUserBufs) {
352
+ try {
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
+ }
367
+ } catch {
368
+ }
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
+ }
379
+ return { ...base, type: "battle", battleId, status, battleDuration, teams };
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 }
384
+ case "WebcastLinkMicArmies": {
385
+ const battleId = String(getInt(f, 1) || "");
386
+ const battleStatus = getInt(f, 7);
387
+ const teams = [];
388
+ const itemBufs = getAllBytes(f, 3);
389
+ for (const ib of itemBufs) {
390
+ try {
391
+ teams.push(parseBattleTeamFromArmies(ib));
392
+ } catch {
393
+ }
394
+ }
395
+ return {
396
+ ...base,
397
+ type: "battleArmies",
398
+ battleId,
399
+ teams,
400
+ status: battleStatus
401
+ };
402
+ }
403
+ // Proto: WebcastSubNotifyMessage { User user=2, ... subMonth=3 }
404
+ case "WebcastSubNotifyMessage": {
405
+ const userBuf = getBytes(f, 2);
406
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
407
+ return { ...base, type: "subscribe", user, subMonth: getInt(f, 3) };
408
+ }
409
+ case "WebcastEmoteChatMessage": {
410
+ const userBuf = getBytes(f, 2);
411
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
412
+ let emoteId = "", emoteUrl = "";
413
+ const emoteBuf = getBytes(f, 3);
414
+ if (emoteBuf) {
415
+ const ef = decodeProto(emoteBuf);
416
+ emoteId = getStr(ef, 1);
417
+ const imageBuf = getBytes(ef, 2);
418
+ if (imageBuf) {
419
+ const imgFields = decodeProto(imageBuf);
420
+ emoteUrl = getStr(imgFields, 1);
421
+ }
422
+ }
423
+ return { ...base, type: "emoteChat", user, emoteId, emoteUrl };
424
+ }
425
+ case "WebcastEnvelopeMessage": {
426
+ const envelopeId = String(getInt(f, 1) || getStr(f, 1));
427
+ return { ...base, type: "envelope", envelopeId, diamondCount: getInt(f, 3) };
428
+ }
429
+ // Proto: WebcastQuestionNewMessage { MessageType type=1, QuestionDetails questionDetails=2 }
430
+ // QuestionDetails { string questionText=2, User user=5 }
431
+ case "WebcastQuestionNewMessage": {
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 };
192
441
  }
193
- case "WebcastRoomUserSeqMessage":
194
- return { ...base, type: "roomUserSeq", viewerCount: getInt(f, 3), totalViewers: getInt(f, 4) };
195
- case "WebcastLinkMicBattle":
196
- return { ...base, type: "battle", status: getInt(f, 3) };
197
- case "WebcastLinkMicArmies":
198
- return { ...base, type: "battleArmies" };
199
- case "WebcastSubNotifyMessage":
200
- return { ...base, type: "subscribe", user, subMonth: getInt(f, 8) };
201
- case "WebcastEmoteChatMessage":
202
- return { ...base, type: "emoteChat", user, emoteId: getStr(f, 3) };
203
- case "WebcastEnvelopeMessage":
204
- return { ...base, type: "envelope", diamondCount: getInt(f, 3) };
205
- case "WebcastQuestionNewMessage":
206
- return { ...base, type: "question", user, questionText: getStr(f, 3) };
207
442
  case "WebcastRankUpdateMessage":
208
- case "WebcastHourlyRankMessage":
209
- return { ...base, type: "rankUpdate", rankType: getStr(f, 3) };
443
+ case "WebcastHourlyRankMessage": {
444
+ const rankType = getStr(f, 1) || `rank_${getInt(f, 1)}`;
445
+ const rankList = [];
446
+ const listBufs = getAllBytes(f, 2);
447
+ for (const lb of listBufs) {
448
+ try {
449
+ const rf = decodeProto(lb);
450
+ const userBuf = getBytes(rf, 1);
451
+ const rank = getInt(rf, 2);
452
+ const score = getInt(rf, 3);
453
+ const user = userBuf ? parseUser(userBuf) : { id: "0", nickname: "", uniqueId: "" };
454
+ rankList.push({ user, rank, score });
455
+ } catch {
456
+ }
457
+ }
458
+ return { ...base, type: "rankUpdate", rankType, rankList };
459
+ }
460
+ // Proto: WebcastControlMessage { int32 action=2 }
210
461
  case "WebcastControlMessage":
211
- return { ...base, type: "control", action: getInt(f, 2) };
462
+ return { ...base, type: "control", action: getInt(f, 2) || getInt(f, 1) };
212
463
  case "WebcastRoomMessage":
213
464
  case "RoomMessage":
214
- return { ...base, type: "room", status: getStr(f, 3) };
215
- case "WebcastLiveIntroMessage":
216
- return { ...base, type: "liveIntro", title: getStr(f, 3) };
465
+ return { ...base, type: "room", status: getInt(f, 2) };
466
+ // Proto: WebcastLiveIntroMessage { uint64 id=2, string description=4, User user=5 }
467
+ case "WebcastLiveIntroMessage": {
468
+ const roomId = String(getInt(f, 2));
469
+ const title = getStr(f, 4) || getStr(f, 2);
470
+ return { ...base, type: "liveIntro", roomId, title };
471
+ }
217
472
  case "WebcastLinkMicMethod":
218
- case "WebcastLinkmicBattleTaskMessage":
219
- return { ...base, type: "linkMic", action: getInt(f, 3) };
473
+ case "WebcastLinkmicBattleTaskMessage": {
474
+ const action = getStr(f, 1) || `action_${getInt(f, 1)}`;
475
+ const users = [];
476
+ const userBufs = getAllBytes(f, 2);
477
+ for (const ub of userBufs) {
478
+ try {
479
+ users.push(parseUser(ub));
480
+ } catch {
481
+ }
482
+ }
483
+ return { ...base, type: "linkMic", action, users };
484
+ }
220
485
  default:
221
486
  return { ...base, type: "unknown", method };
222
487
  }