@floating-live/bilibili 0.15.3 → 0.16.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/LICENSE +21 -0
- package/index.d.ts +4 -5
- package/index.js +4 -21
- package/package.json +13 -9
- package/parser.d.ts +10 -6
- package/parser.js +472 -422
- package/plugin.d.ts +42 -29
- package/plugin.js +163 -67
- package/room.d.ts +105 -74
- package/room.js +294 -230
- package/types.d.ts +39 -484
- package/types.js +1 -2
- package/utils/crypto.d.ts +1 -0
- package/utils/crypto.js +10 -0
- package/utils/request.d.ts +177 -0
- package/utils/request.js +96 -0
- package/index.ts +0 -5
- package/parser.ts +0 -433
- package/plugin.ts +0 -103
- package/room.ts +0 -272
- package/tsconfig.json +0 -14
- package/types.ts +0 -503
- package/utils.d.ts +0 -18
- package/utils.js +0 -138
- package/utils.ts +0 -145
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { BilibiliApiClient } from "bilibili-live-danmaku";
|
|
2
|
+
export interface DataXpassportQrcodeGenerate {
|
|
3
|
+
url: string;
|
|
4
|
+
qrcode_key: string;
|
|
5
|
+
}
|
|
6
|
+
export interface DataXpassportQrcodePoll {
|
|
7
|
+
url: string;
|
|
8
|
+
refresh_token: string;
|
|
9
|
+
/** 登录时间 */
|
|
10
|
+
timestamp: number;
|
|
11
|
+
/** 扫码状态
|
|
12
|
+
* > 0:扫码登录成功
|
|
13
|
+
* > 86038:二维码已失效
|
|
14
|
+
* > 86090:二维码已扫码未确认
|
|
15
|
+
* > 86101:未扫码
|
|
16
|
+
*/
|
|
17
|
+
code: number;
|
|
18
|
+
/** 扫码状态信息 */
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
export interface DataXapiNav {
|
|
22
|
+
isLogin: boolean;
|
|
23
|
+
email_verified: number;
|
|
24
|
+
face: string;
|
|
25
|
+
face_nft: number;
|
|
26
|
+
face_nft_type: number;
|
|
27
|
+
level_info: {
|
|
28
|
+
current_level: number;
|
|
29
|
+
current_min: number;
|
|
30
|
+
current_exp: number;
|
|
31
|
+
next_exp: string;
|
|
32
|
+
};
|
|
33
|
+
mid: number;
|
|
34
|
+
mobile_verified: number;
|
|
35
|
+
money: number;
|
|
36
|
+
moral: number;
|
|
37
|
+
official: {
|
|
38
|
+
role: number;
|
|
39
|
+
title: string;
|
|
40
|
+
desc: string;
|
|
41
|
+
type: number;
|
|
42
|
+
};
|
|
43
|
+
officialVerify: {
|
|
44
|
+
type: number;
|
|
45
|
+
desc: string;
|
|
46
|
+
};
|
|
47
|
+
pendant: {
|
|
48
|
+
pid: number;
|
|
49
|
+
name: string;
|
|
50
|
+
image: string;
|
|
51
|
+
expire: number;
|
|
52
|
+
image_enhance: string;
|
|
53
|
+
image_enhance_frame: string;
|
|
54
|
+
n_pid: number;
|
|
55
|
+
};
|
|
56
|
+
scores: number;
|
|
57
|
+
uname: string;
|
|
58
|
+
vipDueDate: number;
|
|
59
|
+
vipStatus: number;
|
|
60
|
+
vipType: number;
|
|
61
|
+
vip_pay_type: number;
|
|
62
|
+
vip_theme_type: number;
|
|
63
|
+
vip_label: {
|
|
64
|
+
path: string;
|
|
65
|
+
text: number;
|
|
66
|
+
label_theme: string;
|
|
67
|
+
text_color: string;
|
|
68
|
+
bg_style: number;
|
|
69
|
+
bg_color: string;
|
|
70
|
+
border_color: string;
|
|
71
|
+
use_img_label: boolean;
|
|
72
|
+
img_label_uri_hans: string;
|
|
73
|
+
img_label_uri_hant: string;
|
|
74
|
+
img_label_uri_hans_static: string;
|
|
75
|
+
img_label_uri_hant_static: string;
|
|
76
|
+
label_id: number;
|
|
77
|
+
label_goto: {
|
|
78
|
+
mobile: string;
|
|
79
|
+
pc_web: string;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
vip_avatar_subscript: number;
|
|
83
|
+
vip_nickname_color: string;
|
|
84
|
+
vip: {
|
|
85
|
+
type: number;
|
|
86
|
+
status: number;
|
|
87
|
+
due_date: number;
|
|
88
|
+
vip_pay_type: number;
|
|
89
|
+
theme_type: number;
|
|
90
|
+
label: {
|
|
91
|
+
path: string;
|
|
92
|
+
text: string;
|
|
93
|
+
label_theme: string;
|
|
94
|
+
text_color: string;
|
|
95
|
+
bg_style: number;
|
|
96
|
+
bg_color: string;
|
|
97
|
+
border_color: string;
|
|
98
|
+
use_img_label: boolean;
|
|
99
|
+
img_label_uri_hans: string;
|
|
100
|
+
img_label_uri_hant: string;
|
|
101
|
+
img_label_uri_hans_static: string;
|
|
102
|
+
img_label_uri_hant_static: string;
|
|
103
|
+
label_id: number;
|
|
104
|
+
label_goto: {
|
|
105
|
+
mobile: string;
|
|
106
|
+
pc_web: string;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
avatar_subscript: number;
|
|
110
|
+
nickname_color: string;
|
|
111
|
+
role: number;
|
|
112
|
+
avatar_subscript_url: string;
|
|
113
|
+
tv_vip_status: number;
|
|
114
|
+
tv_vip_pay_type: number;
|
|
115
|
+
tv_due_date: number;
|
|
116
|
+
avatar_icon: {
|
|
117
|
+
icon_type: number;
|
|
118
|
+
icon_resource: {};
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
wallet: {
|
|
122
|
+
mid: number;
|
|
123
|
+
bcoin_balance: number;
|
|
124
|
+
coupon_balance: number;
|
|
125
|
+
coupon_due_time: number;
|
|
126
|
+
};
|
|
127
|
+
has_shop: boolean;
|
|
128
|
+
shop_url: "";
|
|
129
|
+
answer_status: number;
|
|
130
|
+
is_senior_member: number;
|
|
131
|
+
wbi_img: {
|
|
132
|
+
img_url: string;
|
|
133
|
+
sub_url: string;
|
|
134
|
+
};
|
|
135
|
+
is_jury: boolean;
|
|
136
|
+
name_render: null;
|
|
137
|
+
}
|
|
138
|
+
export interface DataXpassportCookieInfo {
|
|
139
|
+
refresh: boolean;
|
|
140
|
+
timestamp: number;
|
|
141
|
+
}
|
|
142
|
+
export interface DataXpassportCookieRefresh {
|
|
143
|
+
status: number;
|
|
144
|
+
message: string;
|
|
145
|
+
refresh_token: string;
|
|
146
|
+
}
|
|
147
|
+
export declare class BilibiliApiClientEx extends BilibiliApiClient {
|
|
148
|
+
/** 生成登录二维码 */
|
|
149
|
+
xpassportQrcodeGenerate(): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportQrcodeGenerate>>;
|
|
150
|
+
/** 轮询登录二维码 */
|
|
151
|
+
xpassportQrcodePoll(params: {
|
|
152
|
+
qrcode_key: string;
|
|
153
|
+
}): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportQrcodePoll>>;
|
|
154
|
+
/** 获取当前登录信息 */
|
|
155
|
+
xapiNav(): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXapiNav>>;
|
|
156
|
+
xpassportCookieInfo(): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportCookieInfo>>;
|
|
157
|
+
wwwCorrespond(correspondPath: string): Promise<Response>;
|
|
158
|
+
xpassportCookieRefresh(params: {
|
|
159
|
+
csrf: string;
|
|
160
|
+
refresh_csrf: string;
|
|
161
|
+
source?: string;
|
|
162
|
+
refresh_token: string;
|
|
163
|
+
}): Promise<import("bilibili-live-danmaku").ResponseDataRoot<DataXpassportCookieRefresh>>;
|
|
164
|
+
xpassportConfirmRefresh(params: {
|
|
165
|
+
csrf: string;
|
|
166
|
+
refresh_token: string;
|
|
167
|
+
}): Promise<import("bilibili-live-danmaku").ResponseDataRoot<unknown>>;
|
|
168
|
+
refreshCookie({ timestamp, refresh_token, }: {
|
|
169
|
+
timestamp: number;
|
|
170
|
+
refresh_token: string;
|
|
171
|
+
}): Promise<{
|
|
172
|
+
cookie: string;
|
|
173
|
+
refresh_token: string;
|
|
174
|
+
}>;
|
|
175
|
+
}
|
|
176
|
+
export declare function correspondRefreshCsrf(html: string): Promise<string>;
|
|
177
|
+
export declare function fetchLiveRoomData(client: BilibiliApiClient, id: number): Promise<import("..").BilibiliRoomData>;
|
package/utils/request.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { assertRequestOk, BilibiliApiClient, unwrapRequestData, } from "bilibili-live-danmaku";
|
|
2
|
+
import { parseGetInfoByRoom, parseRoomBaseInfo } from "../parser";
|
|
3
|
+
import { getCorrespondPath } from "./crypto";
|
|
4
|
+
export class BilibiliApiClientEx extends BilibiliApiClient {
|
|
5
|
+
/** 生成登录二维码 */
|
|
6
|
+
async xpassportQrcodeGenerate() {
|
|
7
|
+
const res = await this.request("https://passport.bilibili.com/x/passport-login/web/qrcode/generate");
|
|
8
|
+
return unwrapRequestData(res);
|
|
9
|
+
}
|
|
10
|
+
/** 轮询登录二维码 */
|
|
11
|
+
async xpassportQrcodePoll(params) {
|
|
12
|
+
const url = new URL("https://passport.bilibili.com/x/passport-login/web/qrcode/poll");
|
|
13
|
+
url.searchParams.set("qrcode_key", params.qrcode_key);
|
|
14
|
+
const res = await this.request("https://passport.bilibili.com/x/passport-login/web/qrcode/generate");
|
|
15
|
+
const data = unwrapRequestData(res);
|
|
16
|
+
this.cookies.setFromHeaders(res.headers);
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
/** 获取当前登录信息 */
|
|
20
|
+
async xapiNav() {
|
|
21
|
+
const res = await this.request("https://api.bilibili.com/x/web-interface/nav");
|
|
22
|
+
return unwrapRequestData(res);
|
|
23
|
+
}
|
|
24
|
+
async xpassportCookieInfo() {
|
|
25
|
+
const res = await this.request("https://passport.bilibili.com/x/passport-login/web/cookie/info");
|
|
26
|
+
return unwrapRequestData(res);
|
|
27
|
+
}
|
|
28
|
+
async wwwCorrespond(correspondPath) {
|
|
29
|
+
const res = await this.request(`https://www.bilibili.com/correspond/1/${correspondPath}`);
|
|
30
|
+
assertRequestOk(res);
|
|
31
|
+
return res;
|
|
32
|
+
}
|
|
33
|
+
async xpassportCookieRefresh(params) {
|
|
34
|
+
const url = new URL("https://passport.bilibili.com/x/passport-login/web/cookie/refresh");
|
|
35
|
+
url.searchParams.set("csrf", params.csrf);
|
|
36
|
+
url.searchParams.set("refresh_csrf", params.refresh_csrf);
|
|
37
|
+
url.searchParams.set("source", params.source || "main_web");
|
|
38
|
+
url.searchParams.set("refresh_token", params.refresh_token);
|
|
39
|
+
const res = await this.request(url);
|
|
40
|
+
const data = unwrapRequestData(res);
|
|
41
|
+
this.cookies.setFromHeaders(res.headers);
|
|
42
|
+
return data;
|
|
43
|
+
}
|
|
44
|
+
async xpassportConfirmRefresh(params) {
|
|
45
|
+
const url = new URL("https://passport.bilibili.com/x/passport-login/web/confirm/refresh");
|
|
46
|
+
url.searchParams.set("csrf", params.csrf);
|
|
47
|
+
url.searchParams.set("refresh_token", params.refresh_token);
|
|
48
|
+
const res = await this.request(url);
|
|
49
|
+
return unwrapRequestData(res);
|
|
50
|
+
}
|
|
51
|
+
async refreshCookie({ timestamp, refresh_token, }) {
|
|
52
|
+
// 生成CorrespondPath
|
|
53
|
+
const correspondPath = await getCorrespondPath(timestamp);
|
|
54
|
+
// 获取refresh_csrf
|
|
55
|
+
const correspondRes = await this.wwwCorrespond(correspondPath);
|
|
56
|
+
const correspondHtmlText = await correspondRes.text();
|
|
57
|
+
const refresh_csrf = await correspondRefreshCsrf(correspondHtmlText);
|
|
58
|
+
// 刷新cookie
|
|
59
|
+
const { refresh_token: new_refresh_token } = (await this.xpassportCookieRefresh({
|
|
60
|
+
csrf: this.cookies.get("bili_jct"),
|
|
61
|
+
refresh_csrf,
|
|
62
|
+
refresh_token,
|
|
63
|
+
})).data;
|
|
64
|
+
// 确认更新
|
|
65
|
+
await this.xpassportConfirmRefresh({
|
|
66
|
+
csrf: this.cookies.get("bili_jct"),
|
|
67
|
+
refresh_token,
|
|
68
|
+
});
|
|
69
|
+
return { cookie: this.cookie, refresh_token: new_refresh_token };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const correspondRefreshCsrfRegex = /\<div id="1-name"\>([0-9a-zA-Z]+)\<\/div\>/;
|
|
73
|
+
export async function correspondRefreshCsrf(html) {
|
|
74
|
+
const csrf = html.match(correspondRefreshCsrfRegex)?.[1];
|
|
75
|
+
if (!csrf)
|
|
76
|
+
throw new Error("无法提取refresh_csrf");
|
|
77
|
+
return csrf;
|
|
78
|
+
}
|
|
79
|
+
export async function fetchLiveRoomData(client, id) {
|
|
80
|
+
try {
|
|
81
|
+
const res = await client.xliveGetInfoByRoom({
|
|
82
|
+
room_id: id,
|
|
83
|
+
});
|
|
84
|
+
return parseGetInfoByRoom(res.data);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
// fallback
|
|
88
|
+
const res = await client.xliveGetRoomBaseInfo({
|
|
89
|
+
room_ids: [id],
|
|
90
|
+
});
|
|
91
|
+
for (const r in res.data.by_room_ids) {
|
|
92
|
+
return parseRoomBaseInfo(res.data.by_room_ids[r]);
|
|
93
|
+
}
|
|
94
|
+
throw new Error("房间不存在");
|
|
95
|
+
}
|
|
96
|
+
}
|
package/index.ts
DELETED
package/parser.ts
DELETED
|
@@ -1,433 +0,0 @@
|
|
|
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
|
-
}
|