@floating-live/bilibili 0.15.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/parser.ts ADDED
@@ -0,0 +1,433 @@
1
+ import { UserType, ImageSize, RoomStatus } from "floating-live/enums";
2
+ import { ImageInfo, Message, RoomInfo } from "floating-live/types";
3
+ import { generateId } from "floating-live/utils";
4
+ import { BilibiliRoomInfo, RawInfo, RawMessage } from "./types";
5
+ type ParsingFunction<T extends RawMessage.All> = {
6
+ [K in T["cmd"]]: (msg: any, room?: RoomInfo) => Message.All | undefined;
7
+ };
8
+
9
+ // utils
10
+
11
+ // 获取当前时间戳,以替代无法从数据中获取时间戳的情况,精度为1s
12
+ function getDateTimestamp() {
13
+ return Math.floor(Date.now() / 1000) * 1000;
14
+ }
15
+
16
+ const parsingFunction: ParsingFunction<RawMessage.All> = {
17
+ DANMU_MSG: (msg: RawMessage.DANMU_MSG, room) => {
18
+ const { info, msg_id } = msg;
19
+ let content = info[1];
20
+ let mode = info[0][1];
21
+ let color = info[0][3];
22
+ let uname = info[2][1];
23
+ let uid = info[2][0];
24
+ let timestamp = info[0][4];
25
+ let guard_level = info[7]; // 舰长级别
26
+ let extra = JSON.parse(info[0][15].extra);
27
+ let medal =
28
+ info[3][0] != null
29
+ ? {
30
+ level: info[3][0],
31
+ name: info[3][1],
32
+ // user: info[3][2],
33
+ id: info[3][12],
34
+ membership: info[3][10] || 0,
35
+ }
36
+ : null;
37
+ let identity: null | UserType = 0;
38
+ let emoticon: { [key: string]: ImageInfo } | undefined = undefined;
39
+ if (extra.emots) {
40
+ emoticon = {};
41
+ for (let key in extra.emots) {
42
+ let emotItem = extra.emots[key];
43
+ emoticon[key] = {
44
+ id: emotItem.emoticon_unique,
45
+ url: emotItem.url,
46
+ name: emotItem.emoji,
47
+ };
48
+ }
49
+ }
50
+ if (info[2][2]) {
51
+ identity = UserType.admin;
52
+ } else if (uid === room?.anchor.id) {
53
+ identity = UserType.anchor;
54
+ }
55
+ let danmaku: Message.Comment = {
56
+ platform: "bilibili",
57
+ room: room?.id || 0,
58
+ type: "comment",
59
+ timestamp: timestamp,
60
+ id: msg_id!,
61
+ info: {
62
+ color: color,
63
+ mode: mode,
64
+ content: content,
65
+ image: info[0][12]
66
+ ? {
67
+ name: content,
68
+ id: info[0][13].emoticon_unique,
69
+ url: info[0][13].url,
70
+ size:
71
+ info[0][13].width == info[0][13].height
72
+ ? ImageSize.large
73
+ : ImageSize.small,
74
+ }
75
+ : undefined,
76
+ emoticon: emoticon,
77
+ user: {
78
+ name: uname,
79
+ id: uid,
80
+ medal: medal,
81
+ membership: guard_level,
82
+ type: identity,
83
+ },
84
+ },
85
+ };
86
+ danmaku.id ??= generateId(danmaku);
87
+ return danmaku;
88
+ },
89
+ INTERACT_WORD: (msg: RawMessage.INTERACT_WORD, room) => {
90
+ const { data, msg_id, send_time } = msg;
91
+ let type: "entry" | "follow" | "share";
92
+ switch (data.msg_type) {
93
+ case 1:
94
+ type = "entry"; // 进入直播间
95
+ break;
96
+ case 2:
97
+ type = "follow"; // 关注直播间
98
+ break;
99
+ case 3:
100
+ type = "share"; // 分享直播间
101
+ break;
102
+ default:
103
+ return;
104
+ }
105
+ let interact: Message.Interact = {
106
+ platform: "bilibili",
107
+ room: room?.id || 0,
108
+ type: type,
109
+ id: msg_id!,
110
+ timestamp: send_time || Math.floor(data.trigger_time / 1000000),
111
+ info: {
112
+ user: {
113
+ name: data.uname,
114
+ id: data.uid,
115
+ medal: data.fans_medal?.medal_level
116
+ ? {
117
+ level: data.fans_medal.medal_level,
118
+ name: data.fans_medal.medal_name,
119
+ id: data.fans_medal.target_id,
120
+ membership: data.fans_medal.guard_level,
121
+ }
122
+ : null,
123
+ },
124
+ },
125
+ };
126
+ interact.id ??= generateId(interact);
127
+ return interact;
128
+ },
129
+ SEND_GIFT: (msg: RawMessage.SEND_GIFT, room) => {
130
+ const { data, msg_id, send_time } = msg;
131
+ let gift: Message.Gift = {
132
+ platform: "bilibili",
133
+ room: room?.id || 0,
134
+ type: "gift",
135
+ id: msg_id || data.combo_id,
136
+ timestamp: send_time || data.timestamp * 1000,
137
+ info: {
138
+ user: {
139
+ name: data.uname,
140
+ id: data.uid,
141
+ medal:
142
+ data.fans_medal && data.fans_medal.medal_level
143
+ ? {
144
+ level: data.fans_medal.medal_level,
145
+ name: data.fans_medal.medal_name,
146
+ id: data.fans_medal.target_id,
147
+ membership: data.fans_medal.guard_level,
148
+ }
149
+ : null,
150
+ membership: data.guard_level,
151
+ avatar: data.face,
152
+ },
153
+ gift: {
154
+ name: data.giftName,
155
+ id: data.giftId,
156
+ num: data.num,
157
+ value: data.total_coin,
158
+ currency: data.coin_type,
159
+ action: data.action,
160
+ comboId: data.batch_combo_id,
161
+ },
162
+ },
163
+ };
164
+ gift.id ??= generateId(gift);
165
+ return gift;
166
+ },
167
+ GUARD_BUY: (msg: RawMessage.GUARD_BUY, room) => {
168
+ const { data, msg_id, send_time } = msg;
169
+ let gift: Message.Membership = {
170
+ platform: "bilibili",
171
+ room: room?.id || 0,
172
+ type: "membership",
173
+ id: msg_id!,
174
+ timestamp: send_time || data.start_time * 1000,
175
+ info: {
176
+ user: {
177
+ name: data.username,
178
+ id: data.uid,
179
+ membership: data.guard_level,
180
+ },
181
+ gift: {
182
+ name: data.gift_name,
183
+ id: data.gift_id,
184
+ num: data.num,
185
+ value: data.price,
186
+ currency: "gold",
187
+ },
188
+ name: data.gift_name,
189
+ level: data.guard_level,
190
+ duration: data.num * 30,
191
+ },
192
+ };
193
+ gift.id ??= generateId(gift);
194
+ return gift;
195
+ },
196
+ SUPER_CHAT_MESSAGE: (msg: RawMessage.SUPER_CHAT_MESSAGE, room) => {
197
+ const { data, msg_id, send_time } = msg;
198
+ let sc: Message.Superchat = {
199
+ platform: "bilibili",
200
+ room: room?.id || 0,
201
+ type: "superchat",
202
+ id: msg_id!,
203
+ timestamp: send_time || data.ts * 1000,
204
+ info: {
205
+ id: data.id,
206
+ user: {
207
+ name: data.user_info.uname,
208
+ id: data.uid,
209
+ avatar: data.user_info.face,
210
+ medal:
211
+ data.medal_info && data.medal_info.medal_level
212
+ ? {
213
+ level: data.medal_info.medal_level,
214
+ name: data.medal_info.medal_name,
215
+ id: data.medal_info.target_id,
216
+ membership: data.medal_info.guard_level,
217
+ }
218
+ : null,
219
+ membership: data.user_info.guard_level,
220
+ },
221
+ content: data.message, // 文字
222
+ color: data.background_bottom_color, // 颜色
223
+ duration: data.time * 1000, // 持续时间(ms)
224
+ gift: {
225
+ id: data.gift.gift_id,
226
+ name: data.gift.gift_name,
227
+ num: data.gift.num,
228
+ value: data.price * 1000, // 花费
229
+ currency: "gold",
230
+ },
231
+ },
232
+ };
233
+ sc.id ??= generateId(sc);
234
+ return sc;
235
+ },
236
+ WATCHED_CHANGE: (msg: RawMessage.WATCHED_CHANGE, room) => {
237
+ const { data, msg_id, send_time } = msg;
238
+ const stats: Message.LiveStats = {
239
+ platform: "bilibili",
240
+ room: room?.id || 0,
241
+ type: "live_stats",
242
+ id: msg_id!,
243
+ timestamp: send_time || getDateTimestamp(),
244
+ info: {
245
+ view: data.num,
246
+ },
247
+ };
248
+ stats.id ??= generateId(stats);
249
+ return stats;
250
+ },
251
+ LIKE_INFO_V3_UPDATE: (msg: RawMessage.LIKE_INFO_V3_UPDATE, room) => {
252
+ const { data, msg_id, send_time } = msg;
253
+ const stats: Message.LiveStats = {
254
+ platform: "bilibili",
255
+ room: room?.id || 0,
256
+ type: "live_stats",
257
+ id: msg_id!,
258
+ timestamp: send_time || getDateTimestamp(),
259
+ info: {
260
+ like: data.click_count,
261
+ },
262
+ };
263
+ stats.id ??= generateId(stats);
264
+ return stats;
265
+ },
266
+ ONLINE_RANK_COUNT: (msg: RawMessage.ONLINE_RANK_COUNT, room) => {
267
+ const { data, msg_id, send_time } = msg;
268
+ if (data.online_count == null) return;
269
+ const stats: Message.LiveStats = {
270
+ platform: "bilibili",
271
+ room: room?.id || 0,
272
+ type: "live_stats",
273
+ id: msg_id!,
274
+ timestamp: send_time || getDateTimestamp(),
275
+ info: {
276
+ online: data.online_count,
277
+ },
278
+ };
279
+ stats.id ??= generateId(stats);
280
+ return stats;
281
+ },
282
+ ROOM_BLOCK_MSG: (msg: RawMessage.ROOM_BLOCK_MSG, room) => {
283
+ const { data, msg_id, send_time } = msg;
284
+ const block: Message.Block = {
285
+ platform: "bilibili",
286
+ room: room?.id || 0,
287
+ type: "block",
288
+ id: msg_id!,
289
+ timestamp: send_time || getDateTimestamp(),
290
+ info: {
291
+ user: {
292
+ id: data.uid,
293
+ name: data.uname,
294
+ },
295
+ operator: {
296
+ type: data.operator == 2 ? UserType.anchor : UserType.admin,
297
+ },
298
+ },
299
+ };
300
+ block.id ??= generateId(block);
301
+ return block;
302
+ },
303
+ LIVE: (msg: RawMessage.LIVE, room) => {
304
+ const { msg_id, send_time } = msg;
305
+ // 直播间开播
306
+ if (msg.live_time) {
307
+ let live: Message.LiveStart = {
308
+ platform: "bilibili",
309
+ room: room?.id || 0,
310
+ type: "live_start",
311
+ id: msg_id!,
312
+ timestamp: send_time || msg.live_time * 1000,
313
+ info: {
314
+ id: msg.live_key.toString(),
315
+ },
316
+ };
317
+ live.id ??= generateId(live);
318
+ return live;
319
+ }
320
+ },
321
+ CUT_OFF: (msg: RawMessage.CUT_OFF, room) => {
322
+ const { msg_id, send_time } = msg;
323
+ // 直播间被切断
324
+ let cut: Message.LiveCut = {
325
+ platform: "bilibili",
326
+ room: room?.id || 0,
327
+ type: "live_cut",
328
+ id: msg_id!,
329
+ timestamp: send_time || getDateTimestamp(),
330
+ info: {
331
+ message: msg.msg,
332
+ },
333
+ };
334
+ cut.id ??= generateId(cut);
335
+ return cut;
336
+ },
337
+ PREPARING: (msg: RawMessage.PREPARING, room) => {
338
+ const { msg_id, send_time } = msg;
339
+ let off: Message.LiveEnd = {
340
+ platform: "bilibili",
341
+ room: room?.id || 0,
342
+ type: "live_end",
343
+ id: msg_id!,
344
+ timestamp: send_time || getDateTimestamp(),
345
+ info: {
346
+ status: msg.round ? RoomStatus.round : RoomStatus.off,
347
+ },
348
+ };
349
+ off.id ??= generateId(off);
350
+ return off;
351
+ },
352
+ ROOM_CHANGE: (msg: RawMessage.ROOM_CHANGE, room) => {
353
+ const { data, msg_id, send_time } = msg;
354
+ let change: Message.LiveDetail = {
355
+ platform: "bilibili",
356
+ room: room?.id || 0,
357
+ type: "live_detail",
358
+ id: msg_id!,
359
+ timestamp: send_time || getDateTimestamp(),
360
+ info: {
361
+ title: data.title,
362
+ area: [data.parent_area_name, data.area_name],
363
+ },
364
+ };
365
+ change.id ??= generateId(change);
366
+ return change;
367
+ },
368
+ ANCHOR_LOT_START: (msg: RawMessage.ANCHOR_LOT_START, room) => {
369
+ const { data, msg_id, send_time } = msg;
370
+ let m: Message.Lottery = {
371
+ platform: "bilibili",
372
+ room: room?.id || 0,
373
+ type: "lottery",
374
+ timestamp: send_time || data.current_time * 1000,
375
+ id: msg_id!,
376
+ info: {
377
+ id: data.id,
378
+ award: {
379
+ name: data.award_name,
380
+ num: data.award_num,
381
+ image: data.award_image,
382
+ id: -1,
383
+ value: 0,
384
+ currency: "",
385
+ },
386
+ },
387
+ };
388
+ return m;
389
+ },
390
+ };
391
+
392
+ /** 转换bilibili直播弹幕为floating通用格式 */
393
+ export function parseMessage(
394
+ data: RawMessage.All,
395
+ room?: BilibiliRoomInfo
396
+ ): Message.All | undefined {
397
+ return parsingFunction[data.cmd]?.(data, room);
398
+ }
399
+
400
+ /** 转换bilibili直播间信息为floating通用格式 */
401
+ export function parseInfo(data: RawInfo): BilibiliRoomInfo {
402
+ const { room_info, anchor_info, like_info_v3, watched_show }: RawInfo = data;
403
+ return {
404
+ platform: "bilibili",
405
+ id: room_info.room_id,
406
+ roomId: room_info.room_id,
407
+ shortId: room_info.short_id,
408
+ key: `bilibili:${room_info.room_id}`,
409
+ detail: {
410
+ title: room_info.title,
411
+ area: [room_info.parent_area_name, room_info.area_name],
412
+ cover: room_info.cover,
413
+ },
414
+ anchor: {
415
+ id: room_info.uid,
416
+ name: anchor_info.base_info.uname,
417
+ avatar: anchor_info.base_info.face,
418
+ },
419
+ stats: {
420
+ view: watched_show.num,
421
+ like: like_info_v3.total_likes,
422
+ },
423
+ liveId: room_info.live_id_str,
424
+ available: room_info.lock_status ? false : true,
425
+ status: room_info.lock_status ? RoomStatus.banned : room_info.live_status,
426
+ timestamp:
427
+ room_info.live_status == RoomStatus.live
428
+ ? room_info.live_start_time * 1000
429
+ : 0,
430
+ opened: false,
431
+ connection: 0,
432
+ };
433
+ }
package/room.d.ts ADDED
@@ -0,0 +1,74 @@
1
+ import { BilibiliRoomInfo, ConnectTokens } from "./types";
2
+ import { LiveRoom } from "floating-live";
3
+ import { UserInfo, RoomStatsInfo, RoomDetail } from "floating-live";
4
+ import { KeepLiveWS } from "bilibili-live-ws";
5
+ /** 配置项 */
6
+ export interface RoomOptions {
7
+ /** 生成房间后是否打开 */
8
+ open?: boolean;
9
+ /** 登录凭据 */
10
+ credentials?: string;
11
+ /** 连接凭证 */
12
+ tokens?: ConnectTokens;
13
+ /** 房间信息 */
14
+ info?: BilibiliRoomInfo;
15
+ /** 断线重连间隔(默认值为100ms) */
16
+ connectInterval?: number;
17
+ }
18
+ export declare class RoomBilibili extends LiveRoom implements BilibiliRoomInfo {
19
+ /** 平台id */
20
+ readonly platform: string;
21
+ /** 直播间号 */
22
+ readonly id: number;
23
+ /** 直播间room_id */
24
+ roomId: number;
25
+ /** 直播间短房号 */
26
+ shortId: number;
27
+ /** 房间信息 */
28
+ detail: RoomDetail;
29
+ stats: RoomStatsInfo;
30
+ /** 房间是否可用 */
31
+ available: boolean;
32
+ /** 是否连接上房间 */
33
+ connected: boolean;
34
+ /** 主播信息 */
35
+ anchor: UserInfo;
36
+ /** 直播间弹幕api模块 */
37
+ client: KeepLiveWS | null;
38
+ /** 断线重连间隔 */
39
+ connectInterval: number;
40
+ /** 连接凭证 */
41
+ private tokens;
42
+ /** 是否处于正在打开的状态 */
43
+ private opening;
44
+ /** 是否处于重连状态 */
45
+ private reconnecting;
46
+ constructor(
47
+ /** 房间id */
48
+ id: number, options?: RoomOptions);
49
+ /** 初始化 */
50
+ protected init(options?: RoomOptions): Promise<void>;
51
+ /** 获取房间信息 */
52
+ getInfo(): Promise<void>;
53
+ /** 请求房间信息 */
54
+ fetchInfo(): Promise<BilibiliRoomInfo>;
55
+ /** 更新直播间信息 */
56
+ updateInfo({ roomId, shortId, status, timestamp, detail, stats, anchor, liveId, available, }: BilibiliRoomInfo): void;
57
+ /** 开启直播间监听 */
58
+ open(): Promise<void>;
59
+ private openWS;
60
+ /** 设置登录凭据 */
61
+ setCredentials(credentials: string, userToken?: ConnectTokens): Promise<void>;
62
+ /** 请求连接tokens */
63
+ fetchTokens(credentials?: string, userToken?: ConnectTokens): Promise<ConnectTokens>;
64
+ /** 设置直播连接tokens */
65
+ setTokens(tokens: ConnectTokens): void;
66
+ /** 重连 */
67
+ reconnect(): void;
68
+ private emitConnention;
69
+ /** 创建直播弹幕客户端 */
70
+ private initClient;
71
+ /** 关闭直播间监听 */
72
+ close(): void;
73
+ }
74
+ export default RoomBilibili;