├── .github └── workflows │ └── release.yml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── biome.json ├── browser.d.ts ├── example ├── .npmrc ├── browser │ ├── index.html │ └── src.ts ├── node │ └── node.ts ├── package.json └── pnpm-lock.yaml ├── mock ├── ANCHOR_LOT_AWARD │ ├── real_award.json │ └── virtual_award.json ├── ANCHOR_LOT_CHECKSTATUS │ └── normal.json ├── ANCHOR_LOT_END │ └── normal.json ├── ANCHOR_LOT_START │ ├── real_award_require_follow.json │ ├── real_award_require_medal.json │ ├── real_award_send_gift_require_no.json │ └── virtual_award_require_follow.json ├── COMBO_SEND │ └── normal.json ├── CUT_OFF │ └── normal.json ├── DANMU_MSG-4-0-2-2-2-0 │ ├── emoticon_normal.json │ └── normal_no_badge.json ├── DANMU_MSG │ ├── emoticon_normal.json │ ├── emoticon_special.json │ ├── guard_jianzhang.json │ ├── guard_jianzhang_not_same_badge.json │ ├── guard_tidu.json │ ├── guard_zongdu.json │ ├── inmessage_emoji.json │ ├── lottery.json │ ├── rank_3.json │ └── room_admin.json ├── ENTRY_EFFECT │ ├── guard_jianzhang.json │ ├── guard_none.json │ └── guard_tidu.json ├── GUARD_BUY │ └── normal.json ├── INTERACT_WORD │ ├── enter.json │ ├── follow.json │ └── share.json ├── LIKE_INFO_V3_CLICK │ └── normal.json ├── LIKE_INFO_V3_UPDATE │ └── normal.json ├── LIVE │ ├── push_stream.json │ └── start_live.json ├── NOTICE_MSG │ └── guard_buy.json ├── ONLINE_RANK_COUNT │ └── normal.json ├── ONLINE_RANK_TOP3 │ └── normal.json ├── ONLINE_RANK_V2 │ └── normal.json ├── POPULARITY_RED_POCKET_NEW │ ├── normal_1.json │ └── normal_2.json ├── POPULARITY_RED_POCKET_START │ ├── normal_1.json │ └── normal_2.json ├── POPULARITY_RED_POCKET_WINNER_LIST │ ├── normal_1.json │ └── normal_2.json ├── PREPARING │ └── normal.json ├── README.md ├── ROOM_ADMINS │ └── normal.json ├── ROOM_ADMIN_REVOKE │ └── normal.json ├── ROOM_BLOCK_MSG │ └── normal.json ├── ROOM_CHANGE │ └── normal.json ├── ROOM_REAL_TIME_MESSAGE_UPDATE │ └── normal.json ├── ROOM_SILENT_OFF │ └── normal.json ├── ROOM_SILENT_ON │ ├── all_member.json │ ├── by_medal.json │ ├── by_user_level.json │ └── by_user_level_unlimited.json ├── SEND_GIFT │ ├── PK票_target_user_no_badge.json │ ├── Super_target_user.json │ ├── 小花花_not_same_badge.json │ ├── 干杯_not_same_badge.json │ ├── 幸运之钥.json │ ├── 泡泡糖_not_same_badge.json │ ├── 粉丝团灯牌.json │ └── 辣条_no_badge.json ├── SUPER_CHAT_MESSAGE │ └── normal.json ├── SUPER_CHAT_MESSAGE_DELETE │ └── normal.json ├── SUPER_CHAT_MESSAGE_JPN │ ├── jpn_translation.json │ └── normal.json ├── USER_TOAST_MSG │ ├── guard_jianzhang_new.json │ └── guard_jianzhang_renew.json ├── WARNING │ └── normal.json ├── WATCHED_CHANGE │ ├── 100.json │ ├── 120k.json │ ├── 1k.json │ ├── 24m.json │ └── 90k.json └── room_admin_entrance │ └── normal.json ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── src ├── browser.ts ├── index.ts ├── listener │ └── index.ts ├── parser │ ├── ANCHOR_LOT_AWARD.ts │ ├── ANCHOR_LOT_START.ts │ ├── DANMU_MSG.ts │ ├── GUARD_BUY.ts │ ├── HEARTBEAT.ts │ ├── INTERACT_WORD_ENTRY_EFFECT.ts │ ├── LIKE_INFO_V3_UPDATE.ts │ ├── LIVE.ts │ ├── ONLINE_RANK_COUNT.ts │ ├── POPULARITY_RED_POCKET_START.ts │ ├── POPULARITY_RED_POCKET_WINNER_LIST.ts │ ├── PREPARING.ts │ ├── ROOM_ADMIN.ts │ ├── ROOM_CHANGE.ts │ ├── ROOM_SILENT.ts │ ├── SEND_GIFT.ts │ ├── SUPER_CHAT_MESSAGE.ts │ ├── WARNING_CUT_OFF.ts │ ├── WATCHED_CHANGE.ts │ └── index.ts ├── types │ ├── app.ts │ ├── const.ts │ └── message.ts └── utils │ ├── color.ts │ └── message.ts ├── tsconfig.json └── tsup.config.ts /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Set node 17 | uses: actions/setup-node@v4 18 | with: 19 | registry-url: https://registry.npmjs.org/ 20 | node-version: lts/* 21 | 22 | - run: npx changelogithub 23 | env: 24 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | example/**/dist.js -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "bilibili", 4 | "Danmu" 5 | ] 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Diu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blive-message-listener 2 | 3 | [![npm](https://img.shields.io/npm/v/blive-message-listener)](https://www.npmjs.com/package/blive-message-listener) 4 | 5 | 类型友好的 Bilibili 直播间弹幕监听库。 6 | 7 | ## Features 8 | 9 | - 将 [原始数据](mock) 转为更友好的格式输出 10 | - Node 环境与浏览器环境支持 11 | - 支持监听与获取原始消息 12 | 13 | ## Install 14 | 15 | ```bash 16 | 17 | npm i blive-message-listener 18 | 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```ts 24 | import { startListen, type MsgHandler } from 'blive-message-listener' 25 | // 浏览器环境,从 '/browser' 导入 startListen 26 | // import { startListen } from 'blive-message-listener/browser' 27 | 28 | const handler: MsgHandler = { 29 | onIncomeDanmu: (msg) => { 30 | console.log(msg.id, msg.body) 31 | }, 32 | onIncomeSuperChat: (msg) => { 33 | console.log(msg.id, msg.body) 34 | }, 35 | } 36 | 37 | const instance = startListen(652581, handler) 38 | 39 | instance.close() 40 | ``` 41 | 42 | > 1. 需传入房间的长id。短id需转为长id才能接收到消息。(https://github.com/ddiu8081/blive-message-listener/issues/19) 43 | > 2. 2023年7月后需登录后才可展示完整用户名,可参考下一节 Options 配置连接选项 (https://github.com/ddiu8081/blive-message-listener/issues/29) 44 | 45 | ## Options 46 | 47 | 你可以向 `startListen` 传入第三个参数 `options` 来手动配置连接选项。 48 | 49 | ```ts 50 | interface MessageListenerOptions { 51 | /** 52 | * tiny-bilibili-ws 连接选项 53 | * 54 | * @see https://github.com/starknt/tiny-bilibili-ws 55 | */ 56 | ws?: WSOptions | TCPOptions 57 | } 58 | ``` 59 | 60 | ```ts 61 | startListen(652581, handler, { 62 | ws: { 63 | headers: { 64 | Cookie: 'xxx', 65 | }, 66 | uid: 0, 67 | } 68 | }) 69 | 70 | // or 71 | startListen(652581, handler, { 72 | ws: { 73 | platform: 'web', 74 | uid: 541993, 75 | key: '', 76 | buvid: '', 77 | } 78 | }) 79 | ``` 80 | 81 | ## Handlers & Type Definitions 82 | 83 | ### Common 84 | 85 | ```ts 86 | const startListen: (roomId: number, handler: MsgHandler) => MessageListener 87 | 88 | export interface MessageListener { 89 | /** 直播间房间号 */ 90 | roomId: number 91 | /** 关闭连接 */ 92 | close: () => void 93 | /** 刷新当前直播间热度 */ 94 | getAttention: () => Promise 95 | } 96 | 97 | export interface Message { 98 | /** 消息id */ 99 | id: string, 100 | /** 接收消息的时间,毫秒时间戳 */ 101 | timestamp: number, 102 | /** 消息类型 */ 103 | type: string, 104 | /** 消息内容 */ 105 | body: T 106 | /** 原始消息内容 */ 107 | raw: any 108 | } 109 | 110 | export interface User { 111 | /** 用户uid */ 112 | uid: number 113 | /** 用户名 */ 114 | uname: string 115 | /** 用户头像 */ 116 | face?: string 117 | /** 用户牌子·*/ 118 | badge?: { 119 | /** 是否点亮 */ 120 | active: boolean 121 | /** 牌子名称 */ 122 | name: string 123 | /** 牌子等级 */ 124 | level: number 125 | /** 牌子颜色 */ 126 | color: string 127 | /** 渐变色牌子,当用户长时间未消费,则会变为灰色,即 `#c0c0c0` */ 128 | gradient?: [string, string, string] 129 | /** 主播信息 */ 130 | anchor: { 131 | /** 主播uid */ 132 | uid: number 133 | /** 主播用户名 */ 134 | uname: string 135 | /** 主播房间号 */ 136 | room_id: number 137 | /** 是否为本直播间 */ 138 | is_same_room?: boolean 139 | } 140 | } 141 | /** 用户身份 */ 142 | identity?: { 143 | /** 直播榜单排名 */ 144 | rank: 0 | 1 | 2 | 3 145 | /** 大航海信息 */ 146 | guard_level: GuardLevel 147 | /** 房管 */ 148 | room_admin: boolean 149 | } 150 | } 151 | 152 | export enum GuardLevel { 153 | /** 无 */ 154 | None = 0, 155 | /** 总督 */ 156 | Zongdu = 1, 157 | /** 提督 */ 158 | Tidu = 2, 159 | /** 舰长 */ 160 | Jianzhang = 3, 161 | } 162 | ``` 163 | 164 | ### Handler 165 | 166 | Type definition can be also found in [src/parser](src/parser). 167 | 168 | #### 连接基础信息 169 | 170 | | Handler | Description | 171 | | --- | --- | 172 | | onOpen | 连接成功 | 173 | | onClose | 连接关闭 | 174 | | onError | 连接错误 | 175 | | onStartListen | 开始监听消息 | 176 | 177 |
178 | Type Definitions 179 | 180 | ##### Handler.onOpen 181 | 182 | 连接成功 183 | 184 | ```ts 185 | export type Handler = { 186 | /** 连接成功 */ 187 | onOpen: () => void, 188 | } 189 | ``` 190 | 191 | ##### Handler.onClose 192 | 193 | 连接关闭 194 | 195 | ```ts 196 | export type Handler = { 197 | /** 连接关闭 */ 198 | onClose: () => void, 199 | } 200 | ``` 201 | 202 | ##### Handler.onError 203 | 204 | 连接错误 205 | 206 | ```ts 207 | export type Handler = { 208 | /** 连接错误 */ 209 | onError: (e: Error) => void, 210 | } 211 | ``` 212 | 213 | ##### Handler.onStartListen 214 | 215 | 开始监听消息 216 | 217 | ```ts 218 | export type Handler = { 219 | /** 开始监听消息 */ 220 | onStartListen: () => void, 221 | } 222 | ``` 223 |
224 | 225 | #### 直播间基础信息 226 | 227 | | Handler | Description | 228 | | --- | --- | 229 | | onLiveStart | 直播开始消息 | 230 | | onLiveEnd | 直播结束消息 | 231 | | onAttentionChange | 直播间热度变化 | 232 | | onWatchedChange | 累计看过人数变化 | 233 | | onLikedChange | 累计点赞人数变化 | 234 | | onRankCountChange | 高能用户人数变化 | 235 | | onUserAction | 用户进入、关注、分享、点赞直播间 | 236 | | onRoomInfoChange | 直播间信息修改 | 237 | 238 |
239 | Type Definitions 240 | 241 | ##### handler.onLiveStart 242 | 243 | 直播开始消息 244 | 245 | ```ts 246 | export type Handler = { 247 | /** 直播开始消息 */ 248 | onLiveStart: (msg: Message) => void 249 | } 250 | 251 | type msgType = 'LIVE' 252 | 253 | export interface LiveStartMsg { 254 | /** 开播平台 */ 255 | live_platform: string 256 | /** 房间号 */ 257 | room_id: number 258 | } 259 | ``` 260 | 261 | ##### handler.onLiveEnd 262 | 263 | 直播结束消息 264 | 265 | ```ts 266 | export type Handler = { 267 | /** 直播结束消息 */ 268 | onLiveEnd: (msg: Message) => void 269 | } 270 | 271 | type msgType = 'PREPARING' 272 | 273 | export interface LiveEndMsg { 274 | /** 房间号 */ 275 | room_id: number 276 | } 277 | ``` 278 | 279 | ##### handler.onAttentionChange 280 | 281 | 直播间热度变化 282 | 283 | ```ts 284 | export type Handler = { 285 | /** 直播间热度变化 */ 286 | onAttentionChange: (msg: Message) => void 287 | } 288 | 289 | type msgType = 'heartbeat' 290 | 291 | export interface AttentionChangeMsg { 292 | /** 直播间热度 */ 293 | attention: number 294 | } 295 | ``` 296 | 297 | ##### handler.onWatchedChange 298 | 299 | 累计看过人数变化 300 | 301 | ```ts 302 | export type Handler = { 303 | /** 累计看过人数变化 */ 304 | onWatchedChange: (msg: Message) => void 305 | } 306 | 307 | type msgType = 'WATCHED_CHANGE' 308 | 309 | export interface WatchedChangeMsg { 310 | /** 累计入场人数 */ 311 | num: number 312 | /** 累计入场人数,格式化输出 */ 313 | text_small: string 314 | } 315 | ``` 316 | 317 | ##### handler.onLikedChange 318 | 319 | 累计点赞人数变化 320 | 321 | ```ts 322 | export type Handler = { 323 | /** 累计点赞人数变化 */ 324 | onLikedChange: (msg: Message) => void 325 | } 326 | 327 | type msgType = 'LIKE_INFO_V3_UPDATE' 328 | 329 | export interface LikedChangeMsg { 330 | /** 直播间点赞人数 */ 331 | count: number 332 | } 333 | ``` 334 | 335 | ##### handler.onRankCountChange 336 | 337 | 高能用户人数变化 338 | 339 | ```ts 340 | export type Handler = { 341 | /** 高能用户人数变化 */ 342 | onRankCountChange: (msg: Message) => void 343 | } 344 | 345 | type msgType = 'ONLINE_RANK_COUNT' 346 | 347 | export interface RankCountChangeMsg { 348 | /** 高能用户人数 */ 349 | count: number 350 | } 351 | ``` 352 | 353 | ##### handler.onUserAction 354 | 355 | 用户进入、关注、分享、点赞直播间 356 | 357 | - 舰长进入直播间时,有几率会触发两次 358 | - 舰长进入直播间时,uname 超长可能会省略号截断 359 | 360 | ```ts 361 | export type Handler = { 362 | /** 用户进入、关注、分享、点赞直播间 */ 363 | onUserAction: (msg: Message) => void 364 | } 365 | 366 | type msgType = 'INTERACT_WORD' | 'ENTRY_EFFECT' | 'LIKE_INFO_V3_CLICK' 367 | 368 | type UserAction = 'enter' | 'follow' | 'share' | 'like' | 'unknown' 369 | 370 | export interface UserActionMsg { 371 | user: User 372 | /** 事件类型 */ 373 | action: UserAction 374 | /** 事件时间,毫秒时间戳 */ 375 | timestamp: number 376 | } 377 | ``` 378 | 379 | ##### handler.onRoomInfoChange 380 | 381 | 直播间信息修改 382 | 383 | ```ts 384 | export type Handler = { 385 | /** 直播间信息修改 */ 386 | onRoomInfoChange: (msg: Message) => void 387 | } 388 | 389 | type msgType = 'ROOM_CHANGE' 390 | 391 | export interface RoomInfoChangeMsg { 392 | /** 直播间标题 */ 393 | title: string 394 | /** 一级分区id */ 395 | parent_area_id: number 396 | /** 一级分区名 */ 397 | parent_area_name: string 398 | /** 二级分区id */ 399 | area_id: number 400 | /** 二级分区名 */ 401 | area_name: string 402 | } 403 | ``` 404 |
405 | 406 | #### 弹幕相关 407 | 408 | | Handler | Description | 409 | | --- | --- | 410 | | onIncomeDanmu | 收到普通弹幕消息 | 411 | | onIncomeSuperChat | 收到醒目留言 | 412 | 413 |
414 | Type Definitions 415 | 416 | ##### handler.onIncomeDanmu 417 | 418 | 收到普通弹幕消息 419 | 420 | ```ts 421 | export type Handler = { 422 | /** 收到普通弹幕消息 */ 423 | onIncomeDanmu: (msg: Message) => void 424 | } 425 | 426 | type msgType = 'DANMU_MSG' 427 | 428 | export interface DanmuMsg { 429 | user: User 430 | /** 弹幕内容 */ 431 | content: string 432 | /** 弹幕类型:1 2 3:普通弹幕;4:底部弹幕;5:顶部弹幕 */ 433 | type: number 434 | /** 弹幕颜色 */ 435 | content_color: string 436 | /** 发送时间,毫秒时间戳 */ 437 | timestamp: number 438 | /** 是否为天选抽奖弹幕 */ 439 | lottery: boolean 440 | /** 表情弹幕内容 */ 441 | emoticon?: { 442 | id: string 443 | height: number 444 | width: number 445 | url: string 446 | } 447 | /** 弹幕内小表情映射,key为表情文字,如"[妙]" */ 448 | in_message_emoticon?: Record 456 | } 457 | ``` 458 | 459 | ##### handler.onIncomeSuperChat 460 | 461 | 收到醒目留言 462 | 463 | ```ts 464 | export type Handler = { 465 | /** 收到醒目留言 */ 466 | onIncomeSuperChat: (msg: Message) => void 467 | } 468 | 469 | type msgType = 'SUPER_CHAT_MESSAGE' 470 | 471 | export interface SuperChatMsg { 472 | /** 消息id */ 473 | id: number 474 | /** 发送用户 */ 475 | user: User 476 | /** 弹幕内容 */ 477 | content: string 478 | /** 弹幕颜色 */ 479 | content_color: string 480 | /** 价格,RMB */ 481 | price: number 482 | /** 持续时间,秒 */ 483 | time: number 484 | } 485 | ``` 486 |
487 | 488 | #### 礼物相关 489 | 490 | | Handler | Description | 491 | | --- | --- | 492 | | onGift | 收到礼物 | 493 | | onGuardBuy | 舰长上舰消息 | 494 | | onRedPocketStart | 红包抽奖开始 | 495 | | onRedPocketEnd | 红包抽奖结果 | 496 | | onAnchorLotteryStart | 主播天选时刻抽奖开启 | 497 | | onAnchorLotteryEnd | 主播天选时刻抽奖结果 | 498 | 499 |
500 | Type Definitions 501 | 502 | ##### handler.onGift 503 | 504 | 收到礼物 505 | 506 | * 礼物信息的用户牌子可见,但没有牌子对应主播的用户名及房间号,也无法判断 `is_same_room` 是否为本直播间。 507 | 508 | ```ts 509 | export type Handler = { 510 | /** 收到礼物 */ 511 | onGift: (msg: Message) => void 512 | } 513 | 514 | type msgType = 'SEND_GIFT' 515 | 516 | export interface GiftMsg { 517 | user: User 518 | /** 礼物id */ 519 | gift_id: number 520 | /** 礼物名称 */ 521 | gift_name: string 522 | /** 礼物价格类型 */ 523 | coin_type: 'silver' | 'gold' 524 | /** 礼物价格,除以1000为RMB */ 525 | price: number 526 | /** 礼物数量 */ 527 | amount: number 528 | /** 送礼指向主播信息,多人直播间可指定要送给的主播,单人直播间为空 */ 529 | send_master?: { 530 | uid: number 531 | uname: string 532 | room_id: number 533 | } 534 | /** 礼物连击 */ 535 | combo?: { 536 | /** 连击id */ 537 | batch_id: string 538 | /** 当前连击数(礼物总数) */ 539 | combo_num: number 540 | /** 连击礼物总价格,除以1000为RMB */ 541 | total_price: number 542 | } 543 | } 544 | ``` 545 | 546 | ##### handler.onGuardBuy 547 | 548 | 舰长上舰消息 549 | 550 | ```ts 551 | export type Handler = { 552 | /** 舰长上舰消息 */ 553 | onGuardBuy: (msg: Message) => void 554 | } 555 | 556 | type msgType = 'GUARD_BUY' 557 | 558 | export interface GuardBuyMsg { 559 | user: User 560 | /** 礼物id */ 561 | gift_id: number 562 | /** 礼物名称 */ 563 | gift_name: string 564 | /** 大航海信息 */ 565 | guard_level: GuardLevel 566 | /** 价格,RMB */ 567 | price: number 568 | /** 等级生效时间 */ 569 | start_time: number 570 | /** 等级过期时间 */ 571 | end_time: number 572 | } 573 | ``` 574 | 575 | ##### handler.onRedPocketStart 576 | 577 | 红包抽奖开始 578 | 579 | ```ts 580 | export type Handler = { 581 | /** 红包抽奖开始 */ 582 | onRedPocketStart: (msg: Message) => void 583 | } 584 | 585 | type msgType = 'POPULARITY_RED_POCKET_START' 586 | 587 | export interface RedPocketStartMsg { 588 | /** 红包抽奖id */ 589 | id: number 590 | /** 红包发送用户 */ 591 | user: User 592 | /** 开始时间,秒级时间戳 */ 593 | start_time: number 594 | /** 结束时间,秒级时间戳 */ 595 | end_time: number 596 | /** 持续时间,秒 */ 597 | duration: number 598 | /** 口令弹幕内容 */ 599 | danmu: string 600 | /** 红包奖品 */ 601 | awards: RedPocketStartAward[] 602 | /** 奖品总价值,除以1000为RMB */ 603 | total_price: number 604 | /** 剩余等待的红包数 */ 605 | wait_num: number 606 | } 607 | 608 | interface RedPocketStartAward { 609 | /** 奖品id */ 610 | gift_id: number 611 | /** 奖品名称 */ 612 | gift_name: string 613 | /** 奖品图片 */ 614 | gift_pic: string 615 | /** 奖品数量 */ 616 | num: number 617 | } 618 | ``` 619 | 620 | ##### handler.onRedPocketEnd 621 | 622 | 红包抽奖结果 623 | 624 | ```ts 625 | export type Handler = { 626 | /** 红包抽奖结果 */ 627 | onRedPocketEnd: (msg: Message) => void 628 | } 629 | 630 | type msgType = 'POPULARITY_RED_POCKET_WINNER_LIST' 631 | 632 | export interface RedPocketEndMsg { 633 | /** 红包抽奖id */ 634 | id: number 635 | /** 中奖人数 */ 636 | total_num: number 637 | /** 中奖用户列表 */ 638 | winner: ({ 639 | /** 用户uid */ 640 | uid: number 641 | /** 用户昵称 */ 642 | uname: string 643 | /** 奖品id */ 644 | award_id: number 645 | } & RedPocketEndAward)[] 646 | /** 红包奖品列表 */ 647 | awards: Record 648 | } 649 | 650 | interface RedPocketEndAward { 651 | /** 奖品类型,待补充 */ 652 | award_type: number 653 | /** 奖品名称 */ 654 | award_name: string 655 | /** 奖品图片 */ 656 | award_pic: string 657 | /** 奖品图片大图 */ 658 | award_big_pic: string 659 | /** 奖品价值,除以1000为RMB */ 660 | award_price: number 661 | } 662 | ``` 663 | 664 | ##### handler.onAnchorLotteryStart 665 | 666 | 主播天选时刻抽奖开启 667 | 668 | ```ts 669 | export type Handler = { 670 | /** 主播天选时刻抽奖开启 */ 671 | onAnchorLotteryStart: (msg: Message) => void 672 | } 673 | 674 | type msgType = 'ANCHOR_LOT_START' 675 | 676 | export interface AnchorLotteryStartMsg { 677 | /** 天选抽奖id */ 678 | id: number 679 | /** 开始时间,秒级时间戳 */ 680 | start_time: number 681 | /** 持续时间,秒 */ 682 | duration: number 683 | /** 天选奖品信息 */ 684 | award: { 685 | /** 奖品图片 */ 686 | image: string 687 | /** 奖品名称 */ 688 | name: string 689 | /** 奖品数量 */ 690 | num: number 691 | /** 是否为虚拟礼物奖品 */ 692 | virtual: boolean 693 | /** 虚拟奖品价值描述,实物奖品为空 */ 694 | price_text: string 695 | } 696 | /** 抽奖要求 */ 697 | require: { 698 | /** 口令弹幕内容,无需弹幕为空字符串 */ 699 | danmu: string 700 | /** 需送主播礼物,无需送礼为空 */ 701 | gift: { 702 | /** 礼物id */ 703 | id: string 704 | /** 礼物名称 */ 705 | name: string 706 | /** 礼物数量 */ 707 | num: number 708 | /** 单个礼物价值,除以1000为RMB */ 709 | price: number 710 | } | null 711 | /** 抽奖参与人群要求,无要求为空 */ 712 | user: { 713 | /** 参与人群限制(关注/粉丝勋章/大航海) */ 714 | type: 'follow' | 'medal' | 'guard' 715 | /** 参与人群限制等级,如粉丝勋章等级 */ 716 | value: number 717 | /** 参与人群限制描述 */ 718 | text: string 719 | } | null 720 | } 721 | } 722 | ``` 723 | 724 | ##### handler.onAnchorLotteryEnd 725 | 726 | 主播天选时刻抽奖结果 727 | 728 | ```ts 729 | export type Handler = { 730 | /** 主播天选时刻抽奖结果 */ 731 | onAnchorLotteryEnd: (msg: Message) => void 732 | } 733 | 734 | type msgType = 'ANCHOR_LOT_AWARD' 735 | 736 | export interface AnchorLotteryEndMsg { 737 | /** 天选抽奖id */ 738 | id: number 739 | /** 天选奖品信息 */ 740 | award: { 741 | /** 奖品图片 */ 742 | image: string 743 | /** 奖品名称 */ 744 | name: string 745 | /** 是否为虚拟礼物奖品 */ 746 | virtual: boolean 747 | } 748 | /** 中奖用户列表 */ 749 | winner: ({ 750 | /** 用户uid */ 751 | uid: number 752 | /** 用户昵称 */ 753 | uname: string 754 | /** 用户头像 */ 755 | face: number 756 | /** 用户粉丝勋章等级 */ 757 | level: number 758 | /** 中奖数量 */ 759 | num: number 760 | })[] 761 | } 762 | ``` 763 |
764 | 765 | #### 房间管理相关 766 | 767 | | Handler | Description | 768 | | --- | --- | 769 | | onRoomWarn | 房间被超管警告、切断 | 770 | | onRoomSilent | 房间开启、关闭全局禁言 | 771 | | onRoomAdminSet | 房间设立、撤销房管 | 772 | 773 |
774 | Type Definitions 775 | 776 | ##### handler.onRoomWarn 777 | 778 | 房间被超管警告、切断 779 | 780 | ```ts 781 | export type Handler = { 782 | /** 房间被超管警告、切断 */ 783 | onRoomWarn: (msg: Message) => void 784 | } 785 | 786 | type msgType = 'WARNING' | 'CUT_OFF' 787 | 788 | export interface RoomWarnMsg { 789 | /** 处理类型 */ 790 | type: 'warning' | 'cut' 791 | /** 处理原因 */ 792 | msg: string 793 | } 794 | ``` 795 | 796 | ##### handler.onRoomSilent 797 | 798 | 房间开启、关闭全局禁言 799 | 800 | ```ts 801 | export type Handler = { 802 | /** 房间开启、关闭全局禁言 */ 803 | onRoomSilent: (msg: Message) => void 804 | } 805 | 806 | type msgType = 'ROOM_SILENT_ON' | 'ROOM_SILENT_OFF' 807 | 808 | export interface RoomSilentMsg { 809 | /** 禁言类型(按用户等级、勋章等级、全员、关闭) */ 810 | type: 'level' | 'medal' | 'member' | 'off' 811 | /** 禁言等级 */ 812 | level: number 813 | /** 禁言结束时间,秒级时间戳,-1 为无限 */ 814 | second: number 815 | } 816 | ``` 817 | 818 | ##### handler.onRoomAdminSet 819 | 820 | 房间设立、撤销房管 821 | 822 | ```ts 823 | export type Handler = { 824 | /** 房间设立、撤销房管 */ 825 | onRoomAdminSet: (msg: Message) => void 826 | } 827 | 828 | type msgType = 'room_admin_entrance' | 'ROOM_ADMIN_REVOKE' 829 | 830 | export interface RoomAdminSetMsg { 831 | /** 类型(设立、撤销) */ 832 | type: 'set' | 'revoke' 833 | /** 用户uid */ 834 | uid: number 835 | } 836 | ``` 837 |
838 | 839 | #### 监听原始消息 840 | 841 | ```ts 842 | export type Handler = { 843 | /** 原始消息 */ 844 | raw: Record<'msg' | string, (msg: any) => void> 845 | } 846 | ``` 847 | 848 | 可在 `raw` 中监听任意原始消息。 849 | 850 | example: 851 | 852 | ```ts 853 | const handler: MsgHandler = { 854 | raw: { 855 | 'msg': (msg) => { 856 | // 监听所有 cmd 消息 857 | console.log(msg) 858 | }, 859 | 'INTERACT_WORD': (msg) => { 860 | // 监听特定的 cmd 861 | console.log(msg) 862 | }, 863 | } 864 | } 865 | 866 | startListen(652581, handler) 867 | ``` 868 | 869 | ## Credits 870 | 871 | - Based on [tiny-bilibili-ws](https://github.com/starknt/tiny-bilibili-ws) 872 | 873 | ## License 874 | 875 | MIT 876 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatter": { 3 | "enabled": true, 4 | "indentStyle": "space", 5 | "indentWidth": 2 6 | }, 7 | "javascript": { 8 | "formatter": { 9 | "quoteStyle": "single", 10 | "trailingComma": "es5", 11 | "semicolons": "asNeeded" 12 | } 13 | }, 14 | "linter": { 15 | "enabled": true, 16 | "rules": { 17 | "recommended": true, 18 | "suspicious": { 19 | "noExplicitAny": "off" 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /browser.d.ts: -------------------------------------------------------------------------------- 1 | export * from './dist/browser' -------------------------------------------------------------------------------- /example/.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org/ -------------------------------------------------------------------------------- /example/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Blive-message-listener 7 | 8 | 9 |

10 |   
11 | 
12 | 


--------------------------------------------------------------------------------
/example/browser/src.ts:
--------------------------------------------------------------------------------
 1 | import { startListen } from 'blive-message-listener/browser'
 2 | import type { MsgHandler } from 'blive-message-listener'
 3 | 
 4 | const logDom = document.getElementById('log')
 5 | if (!logDom) {
 6 |   throw new Error('log dom not found')
 7 | }
 8 | 
 9 | const handler: MsgHandler = {
10 |   onStartListen: () => {
11 |     console.log('start listen')
12 |     logDom.innerHTML += 'start listen
' 13 | }, 14 | onIncomeDanmu: (msg) => { 15 | console.log(msg) 16 | logDom.innerHTML += `${JSON.stringify(msg.body)}
` 17 | }, 18 | raw: { 19 | 20 | INTERACT_WORD: (msg) => { 21 | console.log(msg) 22 | logDom.innerHTML += `${JSON.stringify(msg)}
` 23 | }, 24 | }, 25 | } 26 | 27 | startListen(953650, handler, { 28 | ws: { 29 | uid: 541993, 30 | url: 'wss://xxxxxx.chat.bilibili.com:2245/sub', 31 | key: 'xxxxxx', 32 | }, 33 | }) 34 | -------------------------------------------------------------------------------- /example/node/node.ts: -------------------------------------------------------------------------------- 1 | import { startListen, type MsgHandler } from 'blive-message-listener' 2 | 3 | const handler: MsgHandler = { 4 | onStartListen: () => { 5 | console.log('start listen') 6 | }, 7 | onIncomeDanmu: (msg) => { 8 | console.log(msg) 9 | }, 10 | raw: { 11 | INTERACT_WORD: (msg) => { 12 | console.log(msg) 13 | }, 14 | }, 15 | } 16 | 17 | startListen(652581, handler, { 18 | ws: { 19 | headers: { 20 | Cookie: 'LIVE_BUVID=xxx; buvid3=xxx; xxx', 21 | }, 22 | uid: 541993, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "node": "tsx node/node.ts", 8 | "browser": "esbuild browser/src.ts --bundle --minify --outfile=browser/dist.js && serve browser" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "blive-message-listener": "workspace:*", 15 | "esbuild": "0.23.1", 16 | "serve": "^14.2.3", 17 | "tsx": "4.17.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_AWARD/real_award.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_AWARD", 3 | "data": { 4 | "award_dont_popup": 1, 5 | "award_image": "", 6 | "award_name": "AirPodsPro", 7 | "award_num": 1, 8 | "award_type": 0, 9 | "award_users": [ 10 | { 11 | "uid": 77777777771, 12 | "uname": "__MOCK_UNAME__", 13 | "face": "http://__MOCK_FACE__.jpg", 14 | "level": 0, 15 | "color": 9868950, 16 | "num": 1 17 | }, 18 | { 19 | "uid": 77777777771, 20 | "uname": "__MOCK_UNAME__", 21 | "face": "http://__MOCK_FACE__.jpg", 22 | "level": 15, 23 | "color": 6406234, 24 | "num": 1 25 | }, 26 | { 27 | "uid": 77777777771, 28 | "uname": "__MOCK_UNAME__", 29 | "face": "http://__MOCK_FACE__.jpg", 30 | "level": 20, 31 | "color": 6406234, 32 | "num": 1 33 | } 34 | ], 35 | "id": 3798530, 36 | "lot_status": 2, 37 | "url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html?is_live_half_webview=1&hybrid_biz=live-lottery-anchor&hybrid_half_ui=1,5,100p,100p,000000,0,30,0,0,1;2,5,100p,100p,000000,0,30,0,0,1;3,5,100p,100p,000000,0,30,0,0,1;4,5,100p,100p,000000,0,30,0,0,1;5,5,100p,100p,000000,0,30,0,0,1;6,5,100p,100p,000000,0,30,0,0,1;7,5,100p,100p,000000,0,30,0,0,1;8,5,100p,100p,000000,0,30,0,0,1", 38 | "web_url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html" 39 | } 40 | } -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_AWARD/virtual_award.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_AWARD", 3 | "data": { 4 | "award_dont_popup": 1, 5 | "award_image": "", 6 | "award_name": "情书", 7 | "award_num": 1, 8 | "award_price_text": "价值52电池", 9 | "award_type": 1, 10 | "award_users": [ 11 | { 12 | "uid": 77777777771, 13 | "uname": "__MOCK_UNAME__", 14 | "face": "http://__MOCK_FACE__.jpg", 15 | "level": 25, 16 | "color": 5805790, 17 | "bag_id": 4690129, 18 | "gift_id": 31250, 19 | "num": 1 20 | } 21 | ], 22 | "id": 3782966, 23 | "lot_status": 2, 24 | "ruid": 238971054, 25 | "url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html?is_live_half_webview=1&hybrid_biz=live-lottery-anchor&hybrid_half_ui=1,5,100p,100p,000000,0,30,0,0,1;2,5,100p,100p,000000,0,30,0,0,1;3,5,100p,100p,000000,0,30,0,0,1;4,5,100p,100p,000000,0,30,0,0,1;5,5,100p,100p,000000,0,30,0,0,1;6,5,100p,100p,000000,0,30,0,0,1;7,5,100p,100p,000000,0,30,0,0,1;8,5,100p,100p,000000,0,30,0,0,1", 26 | "web_url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html" 27 | } 28 | } -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_CHECKSTATUS/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_CHECKSTATUS", 3 | "data": { 4 | "id": 3783024, 5 | "status": 4, 6 | "uid": 77777777775 7 | } 8 | } -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_END/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_END", 3 | "data": { 4 | "id": 3783024 5 | } 6 | } -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_START/real_award_require_follow.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_START", 3 | "data": { 4 | "asset_icon": "https://i0.hdslb.com/bfs/live/627ee2d9e71c682810e7dc4400d5ae2713442c02.png", 5 | "asset_icon_webp": "https://i0.hdslb.com/bfs/live/b47453a0d42f30673b6d030159a96d07905d677a.webp", 6 | "award_image": "", 7 | "award_name": "AirPodsPro", 8 | "award_num": 3, 9 | "award_type": 0, 10 | "cur_gift_num": 0, 11 | "current_time": 1674738003, 12 | "danmu": "__MOCK_MESSAGE_CONTENT__", 13 | "danmu_new": [ 14 | { 15 | "danmu": "__MOCK_MESSAGE_CONTENT__", 16 | "danmu_view": "", 17 | "reject": false 18 | } 19 | ], 20 | "danmu_type": 0, 21 | "gift_id": 0, 22 | "gift_name": "", 23 | "gift_num": 1, 24 | "gift_price": 0, 25 | "goaway_time": 180, 26 | "goods_id": -99998, 27 | "id": 3798530, 28 | "is_broadcast": 1, 29 | "join_type": 0, 30 | "lot_status": 0, 31 | "max_time": 300, 32 | "require_text": "关注主播", 33 | "require_type": 1, 34 | "require_value": 0, 35 | "room_id": 77777777774, 36 | "send_gift_ensure": 0, 37 | "show_panel": 1, 38 | "start_dont_popup": 0, 39 | "status": 1, 40 | "time": 299, 41 | "url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html?is_live_half_webview=1&hybrid_biz=live-lottery-anchor&hybrid_half_ui=1,5,100p,100p,000000,0,30,0,0,1;2,5,100p,100p,000000,0,30,0,0,1;3,5,100p,100p,000000,0,30,0,0,1;4,5,100p,100p,000000,0,30,0,0,1;5,5,100p,100p,000000,0,30,0,0,1;6,5,100p,100p,000000,0,30,0,0,1;7,5,100p,100p,000000,0,30,0,0,1;8,5,100p,100p,000000,0,30,0,0,1", 42 | "web_url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html" 43 | } 44 | } -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_START/real_award_require_medal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_START", 3 | "data": { 4 | "asset_icon": "https://i0.hdslb.com/bfs/live/627ee2d9e71c682810e7dc4400d5ae2713442c02.png", 5 | "asset_icon_webp": "https://i0.hdslb.com/bfs/live/b47453a0d42f30673b6d030159a96d07905d677a.webp", 6 | "award_image": "", 7 | "award_name": "哔哩哔哩键盘", 8 | "award_num": 10, 9 | "award_type": 0, 10 | "cur_gift_num": 0, 11 | "current_time": 1674742001, 12 | "danmu": "__MOCK_MESSAGE_CONTENT__", 13 | "danmu_new": [ 14 | { 15 | "danmu": "__MOCK_MESSAGE_CONTENT__", 16 | "danmu_view": "", 17 | "reject": false 18 | } 19 | ], 20 | "danmu_type": 0, 21 | "gift_id": 0, 22 | "gift_name": "", 23 | "gift_num": 1, 24 | "gift_price": 0, 25 | "goaway_time": 180, 26 | "goods_id": -99998, 27 | "id": 3799279, 28 | "is_broadcast": 1, 29 | "join_type": 0, 30 | "lot_status": 0, 31 | "max_time": 300, 32 | "require_text": "当前主播粉丝勋章至少5级", 33 | "require_type": 2, 34 | "require_value": 5, 35 | "room_id": 77777777774, 36 | "send_gift_ensure": 0, 37 | "show_panel": 1, 38 | "start_dont_popup": 0, 39 | "status": 1, 40 | "time": 299, 41 | "url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html?is_live_half_webview=1&hybrid_biz=live-lottery-anchor&hybrid_half_ui=1,5,100p,100p,000000,0,30,0,0,1;2,5,100p,100p,000000,0,30,0,0,1;3,5,100p,100p,000000,0,30,0,0,1;4,5,100p,100p,000000,0,30,0,0,1;5,5,100p,100p,000000,0,30,0,0,1;6,5,100p,100p,000000,0,30,0,0,1;7,5,100p,100p,000000,0,30,0,0,1;8,5,100p,100p,000000,0,30,0,0,1", 42 | "web_url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html" 43 | } 44 | } -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_START/real_award_send_gift_require_no.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_START", 3 | "data": { 4 | "asset_icon": "https://i0.hdslb.com/bfs/live/627ee2d9e71c682810e7dc4400d5ae2713442c02.png", 5 | "asset_icon_webp": "https://i0.hdslb.com/bfs/live/b47453a0d42f30673b6d030159a96d07905d677a.webp", 6 | "award_image": "", 7 | "award_name": "168元红包", 8 | "award_num": 1, 9 | "award_type": 0, 10 | "cur_gift_num": 0, 11 | "current_time": 1674741611, 12 | "danmu": "", 13 | "danmu_new": [ 14 | { 15 | "danmu": "", 16 | "danmu_view": "", 17 | "reject": false 18 | } 19 | ], 20 | "danmu_type": 0, 21 | "gift_id": 31164, 22 | "gift_name": "粉丝团灯牌", 23 | "gift_num": 1, 24 | "gift_price": 1000, 25 | "goaway_time": 180, 26 | "goods_id": 15, 27 | "id": 3799234, 28 | "is_broadcast": 1, 29 | "join_type": 1, 30 | "lot_status": 0, 31 | "max_time": 600, 32 | "require_text": "无", 33 | "require_type": 0, 34 | "require_value": 0, 35 | "room_id": 77777777774, 36 | "send_gift_ensure": 0, 37 | "show_panel": 1, 38 | "start_dont_popup": 0, 39 | "status": 1, 40 | "time": 599, 41 | "url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html?is_live_half_webview=1&hybrid_biz=live-lottery-anchor&hybrid_half_ui=1,5,100p,100p,000000,0,30,0,0,1;2,5,100p,100p,000000,0,30,0,0,1;3,5,100p,100p,000000,0,30,0,0,1;4,5,100p,100p,000000,0,30,0,0,1;5,5,100p,100p,000000,0,30,0,0,1;6,5,100p,100p,000000,0,30,0,0,1;7,5,100p,100p,000000,0,30,0,0,1;8,5,100p,100p,000000,0,30,0,0,1", 42 | "web_url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html" 43 | } 44 | } -------------------------------------------------------------------------------- /mock/ANCHOR_LOT_START/virtual_award_require_follow.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ANCHOR_LOT_START", 3 | "data": { 4 | "asset_icon": "https://i0.hdslb.com/bfs/live/627ee2d9e71c682810e7dc4400d5ae2713442c02.png", 5 | "asset_icon_webp": "https://i0.hdslb.com/bfs/live/b47453a0d42f30673b6d030159a96d07905d677a.webp", 6 | "award_image": "", 7 | "award_name": "情书", 8 | "award_num": 1, 9 | "award_price_text": "价值52电池", 10 | "award_type": 1, 11 | "cur_gift_num": 0, 12 | "current_time": 1674450380, 13 | "danmu": "__MOCK_MESSAGE_CONTENT__", 14 | "danmu_new": [ 15 | { 16 | "danmu": "__MOCK_MESSAGE_CONTENT__", 17 | "danmu_view": "", 18 | "reject": false 19 | } 20 | ], 21 | "danmu_type": 0, 22 | "gift_id": 0, 23 | "gift_name": "", 24 | "gift_num": 0, 25 | "gift_price": 0, 26 | "goaway_time": 180, 27 | "goods_id": -99998, 28 | "id": 3783024, 29 | "is_broadcast": 1, 30 | "join_type": 0, 31 | "lot_status": 0, 32 | "max_time": 900, 33 | "require_text": "关注主播", 34 | "require_type": 1, 35 | "require_value": 0, 36 | "room_id": 77777777774, 37 | "send_gift_ensure": 0, 38 | "show_panel": 1, 39 | "start_dont_popup": 0, 40 | "status": 1, 41 | "time": 899, 42 | "url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html?is_live_half_webview=1&hybrid_biz=live-lottery-anchor&hybrid_half_ui=1,5,100p,100p,000000,0,30,0,0,1;2,5,100p,100p,000000,0,30,0,0,1;3,5,100p,100p,000000,0,30,0,0,1;4,5,100p,100p,000000,0,30,0,0,1;5,5,100p,100p,000000,0,30,0,0,1;6,5,100p,100p,000000,0,30,0,0,1;7,5,100p,100p,000000,0,30,0,0,1;8,5,100p,100p,000000,0,30,0,0,1", 43 | "web_url": "https://live.bilibili.com/p/html/live-lottery/anchor-join.html" 44 | } 45 | } -------------------------------------------------------------------------------- /mock/COMBO_SEND/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "COMBO_SEND", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777774:31036:1661527988.0356", 6 | "batch_combo_num": 5, 7 | "combo_id": "gift:combo_id:77777777771:77777777774:31036:1661527988.0302", 8 | "combo_num": 5, 9 | "combo_total_coin": 500, 10 | "dmscore": 32, 11 | "gift_id": 31036, 12 | "gift_name": "小花花", 13 | "gift_num": 0, 14 | "is_show": 1, 15 | "medal_info": { 16 | "anchor_roomid": 0, 17 | "anchor_uname": "", 18 | "guard_level": 0, 19 | "icon_id": 0, 20 | "is_lighted": 1, 21 | "medal_color": 6067854, 22 | "medal_color_border": 6067854, 23 | "medal_color_end": 6067854, 24 | "medal_color_start": 6067854, 25 | "medal_level": 3, 26 | "medal_name": "__MOCK_BADGE_NAME__", 27 | "special": "", 28 | "target_id": 77777777773 29 | }, 30 | "name_color": "", 31 | "r_uname": "__MOCK_BADGE_ANCHOR_UNAME__", 32 | "ruid": 77777777774, 33 | "send_master": null, 34 | "total_num": 5, 35 | "uid": 77777777771, 36 | "uname": "__MOCK_UNAME__" 37 | } 38 | } -------------------------------------------------------------------------------- /mock/CUT_OFF/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "CUT_OFF", 3 | "msg": "违反直播言论规范,请立即调整", 4 | "roomid": 77777777774 5 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG-4-0-2-2-2-0/emoticon_normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG:4:0:2:2:2:0", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 5566168, 9 | 1669884801147, 10 | -1972590318, 11 | 0, 12 | "2775f5e6", 13 | 0, 14 | 0, 15 | 0, 16 | "", 17 | 1, 18 | { 19 | "bulge_display": 0, 20 | "emoticon_unique": "official_147", 21 | "height": 60, 22 | "in_player_area": 1, 23 | "is_dynamic": 0, 24 | "url": "http://i0.hdslb.com/bfs/live/bbd9045570d0c022a984c637e406cb0e1f208aa9.png", 25 | "width": 150 26 | }, 27 | "{}", 28 | { 29 | "mode": 0, 30 | "show_player_type": 0, 31 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":5566168,\"dm_type\":1,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"赞\",\"user_hash\":\"662042086\",\"emoticon_unique\":\"official_147\",\"bulge_display\":0,\"recommend_score\":2,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 32 | }, 33 | { 34 | "activity_identity": "", 35 | "activity_source": 0, 36 | "not_show": 0 37 | } 38 | ], 39 | "赞", 40 | [ 41 | 77777777771, 42 | "__MOCK_UNAME__", 43 | 0, 44 | 0, 45 | 0, 46 | 10000, 47 | 1, 48 | "" 49 | ], 50 | [ 51 | 17, 52 | "__MOCK_BADGE_NAME__", 53 | "__MOCK_BADGE_ANCHOR_UNAME__", 54 | 77777777773, 55 | 13081892, 56 | "", 57 | 0, 58 | 13081892, 59 | 13081892, 60 | 13081892, 61 | 0, 62 | 1, 63 | 77777777772 64 | ], 65 | [ 66 | 1, 67 | 0, 68 | 9868950, 69 | ">50000", 70 | 0 71 | ], 72 | [ 73 | "", 74 | "" 75 | ], 76 | 0, 77 | 0, 78 | null, 79 | { 80 | "ts": 1669884801, 81 | "ct": "67A34DC5" 82 | }, 83 | 0, 84 | 0, 85 | null, 86 | null, 87 | 0, 88 | 63 89 | ] 90 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG-4-0-2-2-2-0/normal_no_badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG:4:0:2:2:2:0", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 16777215, 9 | 1669884799391, 10 | 1669882419, 11 | 0, 12 | "c7e84af0", 13 | 0, 14 | 0, 15 | 0, 16 | "", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":16777215,\"dm_type\":0,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__\",\"user_hash\":\"3353889520\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":2,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 24 | }, 25 | { 26 | "activity_identity": "", 27 | "activity_source": 0, 28 | "not_show": 0 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 0, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "" 41 | ], 42 | [], 43 | [ 44 | 0, 45 | 0, 46 | 9868950, 47 | ">50000", 48 | 0 49 | ], 50 | [ 51 | "", 52 | "" 53 | ], 54 | 0, 55 | 0, 56 | null, 57 | { 58 | "ts": 1669884799, 59 | "ct": "113C4255" 60 | }, 61 | 0, 62 | 0, 63 | null, 64 | null, 65 | 0, 66 | 7 67 | ] 68 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/emoticon_normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 16777215, 9 | 1664107099792, 10 | -123935767, 11 | 0, 12 | "dd9630cf", 13 | 0, 14 | 0, 15 | 0, 16 | "", 17 | 1, 18 | { 19 | "bulge_display": 0, 20 | "emoticon_unique": "official_147", 21 | "height": 60, 22 | "in_player_area": 1, 23 | "is_dynamic": 0, 24 | "url": "http://i0.hdslb.com/bfs/live/bbd9045570d0c022a984c637e406cb0e1f208aa9.png", 25 | "width": 150 26 | }, 27 | "{}", 28 | { 29 | "mode": 0, 30 | "show_player_type": 0, 31 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":16777215,\"dm_type\":1,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"赞\",\"user_hash\":\"3717607631\",\"emoticon_unique\":\"official_147\",\"bulge_display\":0,\"recommend_score\":8,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 32 | }, 33 | { 34 | "activity_identity": "", 35 | "activity_source": 0, 36 | "not_show": 0 37 | } 38 | ], 39 | "赞", 40 | [ 41 | 77777777771, 42 | "__MOCK_UNAME__", 43 | 0, 44 | 0, 45 | 0, 46 | 5000, 47 | 1, 48 | "" 49 | ], 50 | [], 51 | [ 52 | 0, 53 | 0, 54 | 9868950, 55 | ">50000", 56 | 0 57 | ], 58 | [ 59 | "", 60 | "" 61 | ], 62 | 0, 63 | 0, 64 | null, 65 | { 66 | "ts": 1664107099, 67 | "ct": "A4707D38" 68 | }, 69 | 0, 70 | 0, 71 | null, 72 | null, 73 | 0, 74 | 7 75 | ] 76 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/emoticon_special.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 16777215, 9 | 1662397982192, 10 | 1662392597, 11 | 0, 12 | "b234008d", 13 | 0, 14 | 0, 15 | 5, 16 | "#1453BAFF,#4C2263A2,#3353BAFF", 17 | 1, 18 | { 19 | "bulge_display": 1, 20 | "emoticon_unique": "room_21452505_191", 21 | "height": 162, 22 | "in_player_area": 1, 23 | "is_dynamic": 1, 24 | "url": "http://i0.hdslb.com/bfs/live/f1f2cb500de4bd3a5992f85ae2be574dd65a605d.png", 25 | "width": 162 26 | }, 27 | "{}", 28 | { 29 | "mode": 0, 30 | "show_player_type": 0, 31 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":16777215,\"dm_type\":1,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"哈哈\",\"user_hash\":\"2989752461\",\"emoticon_unique\":\"room_21452505_191\",\"bulge_display\":1,\"recommend_score\":10,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 32 | }, 33 | { 34 | "activity_identity": "", 35 | "activity_source": 0, 36 | "not_show": 0 37 | } 38 | ], 39 | "哈哈", 40 | [ 41 | 77777777771, 42 | "__MOCK_UNAME__", 43 | 0, 44 | 0, 45 | 0, 46 | 10000, 47 | 1, 48 | "#00D1F1" 49 | ], 50 | [ 51 | 15, 52 | "__MOCK_BADGE_NAME__", 53 | "__MOCK_BADGE_ANCHOR_UNAME__", 54 | 77777777773, 55 | 12478086, 56 | "", 57 | 0, 58 | 12478086, 59 | 12478086, 60 | 12478086, 61 | 0, 62 | 1, 63 | 77777777772 64 | ], 65 | [ 66 | 15, 67 | 0, 68 | 6406234, 69 | ">50000", 70 | 0 71 | ], 72 | [ 73 | "", 74 | "" 75 | ], 76 | 0, 77 | 3, 78 | null, 79 | { 80 | "ts": 1662397982, 81 | "ct": "C5436160" 82 | }, 83 | 0, 84 | 0, 85 | null, 86 | null, 87 | 0, 88 | 105 89 | ] 90 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/guard_jianzhang.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 14893055, 9 | 1662305224469, 10 | -1454170633, 11 | 0, 12 | "cc85b3b6", 13 | 0, 14 | 0, 15 | 5, 16 | "#1453BAFF,#4C2263A2,#3353BAFF", 17 | 1, 18 | { 19 | "bulge_display": 0, 20 | "emoticon_unique": "official_147", 21 | "height": 60, 22 | "in_player_area": 1, 23 | "is_dynamic": 0, 24 | "url": "http://i0.hdslb.com/bfs/live/bbd9045570d0c022a984c637e406cb0e1f208aa9.png", 25 | "width": 150 26 | }, 27 | "{}", 28 | { 29 | "mode": 0, 30 | "show_player_type": 0, 31 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":14893055,\"dm_type\":1,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"赞\",\"user_hash\":\"3431314358\",\"emoticon_unique\":\"official_147\",\"bulge_display\":0,\"recommend_score\":10,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 32 | }, 33 | { 34 | "activity_identity": "", 35 | "activity_source": 0, 36 | "not_show": 0 37 | } 38 | ], 39 | "赞", 40 | [ 41 | 77777777771, 42 | "__MOCK_UNAME__", 43 | 0, 44 | 0, 45 | 0, 46 | 10000, 47 | 1, 48 | "#00D1F1" 49 | ], 50 | [ 51 | 21, 52 | "__MOCK_BADGE_NAME__", 53 | "__MOCK_BADGE_ANCHOR_UNAME__", 54 | 77777777773, 55 | 1725515, 56 | "", 57 | 0, 58 | 6809855, 59 | 1725515, 60 | 5414290, 61 | 3, 62 | 1, 63 | 77777777772 64 | ], 65 | [ 66 | 1, 67 | 0, 68 | 9868950, 69 | ">50000", 70 | 0 71 | ], 72 | [ 73 | "", 74 | "" 75 | ], 76 | 0, 77 | 3, 78 | null, 79 | { 80 | "ts": 1662305224, 81 | "ct": "C05AAD36" 82 | }, 83 | 0, 84 | 0, 85 | null, 86 | null, 87 | 0, 88 | 105 89 | ] 90 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/guard_jianzhang_not_same_badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 14893055, 9 | 1662397726694, 10 | 1662390206, 11 | 0, 12 | "c389deeb", 13 | 0, 14 | 0, 15 | 5, 16 | "#1453BAFF,#4C2263A2,#3353BAFF", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":14893055,\"dm_type\":0,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__\",\"user_hash\":\"3280592619\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":10,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 24 | }, 25 | { 26 | "activity_identity": "", 27 | "activity_source": 0, 28 | "not_show": 0 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 0, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "#00D1F1" 41 | ], 42 | [ 43 | 15, 44 | "__MOCK_BADGE_NAME__", 45 | "__MOCK_BADGE_ANCHOR_UNAME__", 46 | 77777777773, 47 | 12478086, 48 | "", 49 | 0, 50 | 12478086, 51 | 12478086, 52 | 12478086, 53 | 0, 54 | 1, 55 | 77777777772 56 | ], 57 | [ 58 | 26, 59 | 0, 60 | 5805790, 61 | ">50000", 62 | 0 63 | ], 64 | [ 65 | "", 66 | "" 67 | ], 68 | 0, 69 | 3, 70 | null, 71 | { 72 | "ts": 1662397726, 73 | "ct": "4ADF4EA1" 74 | }, 75 | 0, 76 | 0, 77 | null, 78 | null, 79 | 0, 80 | 105 81 | ] 82 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/guard_tidu.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 4, 7 | 25, 8 | 14893055, 9 | 1662305318600, 10 | 336625456, 11 | 0, 12 | "73cef3cc", 13 | 0, 14 | 0, 15 | 2, 16 | "#19897EFF,#403F388E,#33897EFF", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":14893055,\"dm_type\":0,\"font_size\":25,\"player_mode\":4,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__\",\"user_hash\":\"1942942668\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":0,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 24 | }, 25 | { 26 | "activity_identity": "", 27 | "activity_source": 0, 28 | "not_show": 0 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 0, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "#E17AFF" 41 | ], 42 | [ 43 | 28, 44 | "__MOCK_BADGE_NAME__", 45 | "__MOCK_BADGE_ANCHOR_UNAME__", 46 | 77777777773, 47 | 398668, 48 | "", 49 | 0, 50 | 16771156, 51 | 398668, 52 | 6850801, 53 | 2, 54 | 1, 55 | 77777777772 56 | ], 57 | [ 58 | 24, 59 | 0, 60 | 5805790, 61 | ">50000", 62 | 0 63 | ], 64 | [ 65 | "", 66 | "" 67 | ], 68 | 0, 69 | 2, 70 | null, 71 | { 72 | "ts": 1662305318, 73 | "ct": "5B4884E8" 74 | }, 75 | 0, 76 | 0, 77 | null, 78 | null, 79 | 0, 80 | 112 81 | ] 82 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/guard_zongdu.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 4, 7 | 25, 8 | 14893055, 9 | 1662397511508, 10 | 1662391082, 11 | 0, 12 | "d02898ce", 13 | 0, 14 | 0, 15 | 1, 16 | "#33FFE99E,#40DCA731,#33FFE99E", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":14893055,\"dm_type\":0,\"font_size\":25,\"player_mode\":4,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__\",\"user_hash\":\"3492321486\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":1,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 24 | }, 25 | { 26 | "activity_identity": "", 27 | "activity_source": 0, 28 | "not_show": 0 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 0, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "#FF7C28" 41 | ], 42 | [ 43 | 32, 44 | "__MOCK_BADGE_NAME__", 45 | "__MOCK_BADGE_ANCHOR_UNAME__", 46 | 77777777773, 47 | 2951253, 48 | "", 49 | 0, 50 | 16771156, 51 | 2951253, 52 | 10329087, 53 | 1, 54 | 1, 55 | 77777777772 56 | ], 57 | [ 58 | 41, 59 | 0, 60 | 16746162, 61 | 27364, 62 | 0 63 | ], 64 | [ 65 | "title-674-1", 66 | "title-674-1" 67 | ], 68 | 0, 69 | 1, 70 | null, 71 | { 72 | "ts": 1662397511, 73 | "ct": "3AE73FE9" 74 | }, 75 | 0, 76 | 0, 77 | null, 78 | null, 79 | 0, 80 | 119 81 | ] 82 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/inmessage_emoji.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 16777215, 9 | 1673937625350, 10 | 1673937162, 11 | 0, 12 | "63ad848c", 13 | 0, 14 | 0, 15 | 0, 16 | "", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":16777215,\"dm_type\":0,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__[花][妙]\",\"user_hash\":\"1672316044\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":0,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\",\"animation\":{},\"emots\":{\"[妙]\":{\"emoticon_id\":210,\"emoji\":\"[妙]\",\"descript\":\"[妙]\",\"url\":\"http://i0.hdslb.com/bfs/live/08f735d950a0fba267dda140673c9ab2edf6410d.png\",\"width\":20,\"height\":20,\"emoticon_unique\":\"emoji_210\"},\"[花]\":{\"emoticon_id\":209,\"emoji\":\"[花]\",\"descript\":\"[花]\",\"url\":\"http://i0.hdslb.com/bfs/live/7dd2ef03e13998575e4d8a803c6e12909f94e72b.png\",\"width\":20,\"height\":20,\"emoticon_unique\":\"emoji_209\"}}}" 24 | }, 25 | { 26 | "activity_identity": "", 27 | "activity_source": 0, 28 | "not_show": 0 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__[花][妙]", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 0, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "" 41 | ], 42 | [], 43 | [ 44 | 12, 45 | 0, 46 | 6406234, 47 | ">50000", 48 | 1 49 | ], 50 | [ 51 | "", 52 | "" 53 | ], 54 | 0, 55 | 0, 56 | null, 57 | { 58 | "ts": 1673937625, 59 | "ct": "7EBFD4DA" 60 | }, 61 | 0, 62 | 0, 63 | null, 64 | null, 65 | 0, 66 | 210 67 | ] 68 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/lottery.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 1, 7 | 25, 8 | 65532, 9 | 1664108400169, 10 | 0, 11 | 0, 12 | "4475bb4a", 13 | 0, 14 | 2, 15 | 0, 16 | "", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":65532,\"dm_type\":0,\"font_size\":25,\"player_mode\":1,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__\",\"user_hash\":\"1148566346\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":9,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 24 | }, 25 | { 26 | "activity_identity": "3233432", 27 | "activity_source": 1, 28 | "not_show": 1 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 0, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "" 41 | ], 42 | [ 43 | 22, 44 | "__MOCK_BADGE_NAME__", 45 | "__MOCK_BADGE_ANCHOR_UNAME__", 46 | 77777777773, 47 | 1725515, 48 | "", 49 | 0, 50 | 6809855, 51 | 1725515, 52 | 5414290, 53 | 3, 54 | 1, 55 | 77777777772 56 | ], 57 | [ 58 | 27, 59 | 0, 60 | 5805790, 61 | ">50000", 62 | 0 63 | ], 64 | [ 65 | "", 66 | "" 67 | ], 68 | 0, 69 | 0, 70 | null, 71 | { 72 | "ts": 1664108400, 73 | "ct": "B1432FC3" 74 | }, 75 | 0, 76 | 0, 77 | null, 78 | null, 79 | 0, 80 | 70 81 | ] 82 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/rank_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 4, 7 | 25, 8 | 14893055, 9 | 1662398652809, 10 | 330017251, 11 | 0, 12 | "2dcb9010", 13 | 0, 14 | 0, 15 | 5, 16 | "#1453BAFF,#4C2263A2,#3353BAFF", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":14893055,\"dm_type\":0,\"font_size\":25,\"player_mode\":4,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__\",\"user_hash\":\"768315408\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":10,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 24 | }, 25 | { 26 | "activity_identity": "", 27 | "activity_source": 0, 28 | "not_show": 0 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 0, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "#00D1F1" 41 | ], 42 | [ 43 | 26, 44 | "__MOCK_BADGE_NAME__", 45 | "__MOCK_BADGE_ANCHOR_UNAME__", 46 | 77777777773, 47 | 398668, 48 | "", 49 | 0, 50 | 6809855, 51 | 398668, 52 | 6850801, 53 | 3, 54 | 1, 55 | 77777777772 56 | ], 57 | [ 58 | 20, 59 | 0, 60 | 6406234, 61 | ">50000", 62 | 3 63 | ], 64 | [ 65 | "", 66 | "" 67 | ], 68 | 0, 69 | 3, 70 | null, 71 | { 72 | "ts": 1662398652, 73 | "ct": "54DD45AA" 74 | }, 75 | 0, 76 | 0, 77 | null, 78 | null, 79 | 0, 80 | 105 81 | ] 82 | } -------------------------------------------------------------------------------- /mock/DANMU_MSG/room_admin.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "DANMU_MSG", 3 | "info": [ 4 | [ 5 | 0, 6 | 4, 7 | 25, 8 | 9920249, 9 | 1662398325906, 10 | 724666906, 11 | 0, 12 | "125b3efa", 13 | 0, 14 | 0, 15 | 5, 16 | "#1453BAFF,#4C2263A2,#3353BAFF", 17 | 0, 18 | "{}", 19 | "{}", 20 | { 21 | "mode": 0, 22 | "show_player_type": 0, 23 | "extra": "{\"send_from_me\":false,\"mode\":0,\"color\":9920249,\"dm_type\":0,\"font_size\":25,\"player_mode\":4,\"show_player_type\":0,\"content\":\"__MOCK_MESSAGE_CONTENT__\",\"user_hash\":\"307969786\",\"emoticon_unique\":\"\",\"bulge_display\":0,\"recommend_score\":0,\"main_state_dm_color\":\"\",\"objective_state_dm_color\":\"\",\"direction\":0,\"pk_direction\":0,\"quartet_direction\":0,\"anniversary_crowd\":0,\"yeah_space_type\":\"\",\"yeah_space_url\":\"\",\"jump_to_url\":\"\",\"space_type\":\"\",\"space_url\":\"\"}" 24 | }, 25 | { 26 | "activity_identity": "", 27 | "activity_source": 0, 28 | "not_show": 0 29 | } 30 | ], 31 | "__MOCK_MESSAGE_CONTENT__", 32 | [ 33 | 77777777771, 34 | "__MOCK_UNAME__", 35 | 1, 36 | 0, 37 | 0, 38 | 10000, 39 | 1, 40 | "#00D1F1" 41 | ], 42 | [ 43 | 26, 44 | "__MOCK_BADGE_NAME__", 45 | "__MOCK_BADGE_ANCHOR_UNAME__", 46 | 77777777773, 47 | 398668, 48 | "", 49 | 0, 50 | 6809855, 51 | 398668, 52 | 6850801, 53 | 3, 54 | 1, 55 | 77777777772 56 | ], 57 | [ 58 | 27, 59 | 0, 60 | 5805790, 61 | ">50000", 62 | 0 63 | ], 64 | [ 65 | "", 66 | "" 67 | ], 68 | 0, 69 | 3, 70 | null, 71 | { 72 | "ts": 1662398325, 73 | "ct": "1E68FDD2" 74 | }, 75 | 0, 76 | 0, 77 | null, 78 | null, 79 | 0, 80 | 105 81 | ] 82 | } -------------------------------------------------------------------------------- /mock/ENTRY_EFFECT/guard_jianzhang.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ENTRY_EFFECT", 3 | "data": { 4 | "id": 4, 5 | "uid": 77777777771, 6 | "target_id": 77777777775, 7 | "mock_effect": 0, 8 | "face": "http://__MOCK_FACE__.jpg", 9 | "privilege_type": 3, 10 | "copy_writing": "欢迎舰长 <%__MOCK_UNAME__%> 进入直播间", 11 | "copy_color": "#ffffff", 12 | "highlight_color": "#E6FF00", 13 | "priority": 1, 14 | "basemap_url": "https://i0.hdslb.com/bfs/live/mlive/11a6e8eb061c3e715d0a6a2ac0ddea2faa15c15e.png", 15 | "show_avatar": 1, 16 | "effective_time": 2, 17 | "web_basemap_url": "https://i0.hdslb.com/bfs/live/mlive/11a6e8eb061c3e715d0a6a2ac0ddea2faa15c15e.png", 18 | "web_effective_time": 2, 19 | "web_effect_close": 0, 20 | "web_close_time": 0, 21 | "business": 1, 22 | "copy_writing_v2": "欢迎舰长 <%__MOCK_UNAME__%> 进入直播间", 23 | "icon_list": [], 24 | "max_delay_time": 7, 25 | "trigger_time": 1664028287891896000, 26 | "identities": 6, 27 | "effect_silent_time": 0, 28 | "effective_time_new": 0, 29 | "web_dynamic_url_webp": "", 30 | "web_dynamic_url_apng": "", 31 | "mobile_dynamic_url_webp": "" 32 | } 33 | } -------------------------------------------------------------------------------- /mock/ENTRY_EFFECT/guard_none.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ENTRY_EFFECT", 3 | "data": { 4 | "id": 253, 5 | "uid": 77777777771, 6 | "target_id": 77777777775, 7 | "mock_effect": 0, 8 | "face": "http://__MOCK_FACE__.jpg", 9 | "privilege_type": 0, 10 | "copy_writing": "欢迎<%__MOCK_UNAME__%>进入直播间", 11 | "copy_color": "#ffffff", 12 | "highlight_color": "#FFF14B", 13 | "priority": 1, 14 | "basemap_url": "https://i0.hdslb.com/bfs/live/mlive/e6b09100caef61b8518f3c0a23f04636e2a8abaa.png", 15 | "show_avatar": 1, 16 | "effective_time": 3, 17 | "web_basemap_url": "https://i0.hdslb.com/bfs/live/mlive/e6b09100caef61b8518f3c0a23f04636e2a8abaa.png", 18 | "web_effective_time": 3, 19 | "web_effect_close": 1, 20 | "web_close_time": 900, 21 | "business": 4, 22 | "copy_writing_v2": "欢迎<%__MOCK_UNAME__%>进入直播间", 23 | "icon_list": [], 24 | "max_delay_time": 7, 25 | "trigger_time": 1664031341210628900, 26 | "identities": 1, 27 | "effect_silent_time": 0, 28 | "effective_time_new": 0, 29 | "web_dynamic_url_webp": "", 30 | "web_dynamic_url_apng": "", 31 | "mobile_dynamic_url_webp": "" 32 | } 33 | } -------------------------------------------------------------------------------- /mock/ENTRY_EFFECT/guard_tidu.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ENTRY_EFFECT", 3 | "data": { 4 | "id": 2, 5 | "uid": 77777777771, 6 | "target_id": 77777777775, 7 | "mock_effect": 0, 8 | "face": "http://__MOCK_FACE__.jpg", 9 | "privilege_type": 2, 10 | "copy_writing": "欢迎提督 <%__MOCK_UNAME__%> 进入直播间", 11 | "copy_color": "#ffffff", 12 | "highlight_color": "#FFF100", 13 | "priority": 1, 14 | "basemap_url": "https://i0.hdslb.com/bfs/live/mlive/74a41c65e422116d230d433042881fa5556f7870.png", 15 | "show_avatar": 1, 16 | "effective_time": 3, 17 | "web_basemap_url": "https://i0.hdslb.com/bfs/live/mlive/74a41c65e422116d230d433042881fa5556f7870.png", 18 | "web_effective_time": 3, 19 | "web_effect_close": 0, 20 | "web_close_time": 0, 21 | "business": 1, 22 | "copy_writing_v2": "欢迎提督 <%__MOCK_UNAME__%> 进入直播间", 23 | "icon_list": [], 24 | "max_delay_time": 7, 25 | "trigger_time": 1664028333539451100, 26 | "identities": 7, 27 | "effect_silent_time": 0, 28 | "effective_time_new": 0, 29 | "web_dynamic_url_webp": "", 30 | "web_dynamic_url_apng": "", 31 | "mobile_dynamic_url_webp": "" 32 | } 33 | } -------------------------------------------------------------------------------- /mock/GUARD_BUY/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "GUARD_BUY", 3 | "data": { 4 | "uid": 77777777771, 5 | "username": "__MOCK_UNAME__", 6 | "guard_level": 3, 7 | "num": 1, 8 | "price": 198000, 9 | "gift_id": 10003, 10 | "gift_name": "舰长", 11 | "start_time": 1661604507, 12 | "end_time": 1661604507 13 | } 14 | } -------------------------------------------------------------------------------- /mock/INTERACT_WORD/enter.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "INTERACT_WORD", 3 | "data": { 4 | "contribution": { 5 | "grade": 0 6 | }, 7 | "dmscore": 18, 8 | "fans_medal": { 9 | "anchor_roomid": 77777777773, 10 | "guard_level": 0, 11 | "icon_id": 0, 12 | "is_lighted": 1, 13 | "medal_color": 13081892, 14 | "medal_color_border": 13081892, 15 | "medal_color_end": 13081892, 16 | "medal_color_start": 13081892, 17 | "medal_level": 17, 18 | "medal_name": "__MOCK_BADGE_NAME__", 19 | "score": 189757, 20 | "special": "", 21 | "target_id": 77777777772 22 | }, 23 | "identities": [ 24 | 3, 25 | 1 26 | ], 27 | "is_spread": 0, 28 | "msg_type": 1, 29 | "privilege_type": 0, 30 | "roomid": 77777777774, 31 | "score": 1661728184731, 32 | "spread_desc": "", 33 | "spread_info": "", 34 | "tail_icon": 0, 35 | "timestamp": 1661528427, 36 | "trigger_time": 1661528426687646500, 37 | "uid": 77777777771, 38 | "uname": "__MOCK_UNAME__", 39 | "uname_color": "" 40 | } 41 | } -------------------------------------------------------------------------------- /mock/INTERACT_WORD/follow.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "INTERACT_WORD", 3 | "data": { 4 | "contribution": { 5 | "grade": 0 6 | }, 7 | "dmscore": 5, 8 | "fans_medal": { 9 | "anchor_roomid": 0, 10 | "guard_level": 0, 11 | "icon_id": 0, 12 | "is_lighted": 0, 13 | "medal_color": 0, 14 | "medal_color_border": 12632256, 15 | "medal_color_end": 12632256, 16 | "medal_color_start": 12632256, 17 | "medal_level": 0, 18 | "medal_name": "", 19 | "score": 0, 20 | "special": "", 21 | "target_id": 0 22 | }, 23 | "identities": [ 24 | 1 25 | ], 26 | "is_spread": 0, 27 | "msg_type": 2, 28 | "privilege_type": 0, 29 | "roomid": 77777777774, 30 | "score": 1665498663877, 31 | "spread_desc": "", 32 | "spread_info": "", 33 | "tail_icon": 0, 34 | "timestamp": 1665498663, 35 | "trigger_time": 1665498661870692600, 36 | "uid": 77777777771, 37 | "uname": "__MOCK_UNAME__", 38 | "uname_color": "" 39 | } 40 | } -------------------------------------------------------------------------------- /mock/INTERACT_WORD/share.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "INTERACT_WORD", 3 | "data": { 4 | "contribution": { 5 | "grade": 0 6 | }, 7 | "dmscore": 2, 8 | "fans_medal": { 9 | "anchor_roomid": 77777777773, 10 | "guard_level": 0, 11 | "icon_id": 0, 12 | "is_lighted": 0, 13 | "medal_color": 6067854, 14 | "medal_color_border": 12632256, 15 | "medal_color_end": 12632256, 16 | "medal_color_start": 12632256, 17 | "medal_level": 1, 18 | "medal_name": "__MOCK_BADGE_NAME__", 19 | "score": 199, 20 | "special": "", 21 | "target_id": 77777777772 22 | }, 23 | "identities": [ 24 | 1 25 | ], 26 | "is_spread": 0, 27 | "msg_type": 3, 28 | "privilege_type": 0, 29 | "roomid": 77777777774, 30 | "score": 1665498658956, 31 | "spread_desc": "", 32 | "spread_info": "", 33 | "tail_icon": 0, 34 | "timestamp": 1665498658, 35 | "trigger_time": 1665498658950536400, 36 | "uid": 77777777771, 37 | "uname": "__MOCK_UNAME__", 38 | "uname_color": "" 39 | } 40 | } -------------------------------------------------------------------------------- /mock/LIKE_INFO_V3_CLICK/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "LIKE_INFO_V3_CLICK", 3 | "data": { 4 | "show_area": 0, 5 | "msg_type": 6, 6 | "like_icon": "https://i0.hdslb.com/bfs/live/23678e3d90402bea6a65251b3e728044c21b1f0f.png", 7 | "uid": 77777777771, 8 | "like_text": "为主播点赞了", 9 | "uname": "__MOCK_UNAME__", 10 | "uname_color": "", 11 | "identities": [ 12 | 3, 13 | 1 14 | ], 15 | "fans_medal": { 16 | "target_id": 77777777772, 17 | "medal_level": 5, 18 | "medal_name": "__MOCK_BADGE_NAME__", 19 | "medal_color": 6126494, 20 | "medal_color_start": 6126494, 21 | "medal_color_end": 6126494, 22 | "medal_color_border": 6126494, 23 | "is_lighted": 1, 24 | "guard_level": 0, 25 | "special": "", 26 | "icon_id": 0, 27 | "anchor_roomid": 0, 28 | "score": 2500 29 | }, 30 | "contribution_info": { 31 | "grade": 0 32 | }, 33 | "dmscore": 20 34 | } 35 | } -------------------------------------------------------------------------------- /mock/LIKE_INFO_V3_UPDATE/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "LIKE_INFO_V3_UPDATE", 3 | "data": { 4 | "click_count": 11391 5 | } 6 | } -------------------------------------------------------------------------------- /mock/LIVE/push_stream.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "LIVE", 3 | "live_key": "293804801320940837", 4 | "voice_background": "", 5 | "sub_session_key": "293804801320940837sub_time:1664550373", 6 | "live_platform": "pc", 7 | "live_model": 0, 8 | "roomid": 77777777774 9 | } -------------------------------------------------------------------------------- /mock/LIVE/start_live.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "LIVE", 3 | "live_key": "293804801320940837", 4 | "voice_background": "", 5 | "sub_session_key": "293804801320940837sub_time:1664550373", 6 | "live_platform": "pc", 7 | "live_model": 0, 8 | "live_time": 1664550373, 9 | "roomid": 77777777774 10 | } -------------------------------------------------------------------------------- /mock/NOTICE_MSG/guard_buy.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "NOTICE_MSG", 3 | "id": 755, 4 | "name": "【新】舰长跑马灯", 5 | "full": { 6 | "head_icon": "https://i0.hdslb.com/bfs/live/9fe0397f142174516969a55bcb8705d658c658fb.png", 7 | "tail_icon": "https://i0.hdslb.com/bfs/live/822da481fdaba986d738db5d8fd469ffa95a8fa1.webp", 8 | "head_icon_fa": "https://i0.hdslb.com/bfs/live/9fe0397f142174516969a55bcb8705d658c658fb.png", 9 | "tail_icon_fa": "https://i0.hdslb.com/bfs/live/7d7af6107ef808438e6c3b4db0c7868bf78e5172.png", 10 | "head_icon_fan": 1, 11 | "tail_icon_fan": 4, 12 | "background": "#FFB03CFF", 13 | "color": "#FFFFFFFF", 14 | "highlight": "#B25AC1FF", 15 | "time": 10 16 | }, 17 | "half": { 18 | "head_icon": "", 19 | "tail_icon": "", 20 | "background": "", 21 | "color": "", 22 | "highlight": "", 23 | "time": 0 24 | }, 25 | "side": { 26 | "head_icon": "https://i0.hdslb.com/bfs/live/21b524fcc316e6d438415607d5531ecc2bf9f4ff.png", 27 | "background": "#FFE9C8FF", 28 | "color": "#EF903AFF", 29 | "highlight": "#D54900FF", 30 | "border": "#FFCFA4FF" 31 | }, 32 | "roomid": 77777777774, 33 | "real_roomid": 77777777774, 34 | "msg_common": "", 35 | "msg_self": "<%__MOCK_UNAME__%> 开通了舰长,今天是TA陪伴主播的第<%91%>天", 36 | "link_url": "", 37 | "msg_type": 3, 38 | "shield_uid": -1, 39 | "business_id": "", 40 | "scatter": { 41 | "min": 0, 42 | "max": 0 43 | }, 44 | "marquee_id": "", 45 | "notice_type": 0 46 | } -------------------------------------------------------------------------------- /mock/ONLINE_RANK_COUNT/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ONLINE_RANK_COUNT", 3 | "data": { 4 | "count": 1822 5 | } 6 | } -------------------------------------------------------------------------------- /mock/ONLINE_RANK_TOP3/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ONLINE_RANK_TOP3", 3 | "data": { 4 | "dmscore": 112, 5 | "list": [ 6 | { 7 | "msg": "恭喜 <%__MOCK_UNAME__%> 成为高能用户", 8 | "rank": 1 9 | } 10 | ] 11 | } 12 | } -------------------------------------------------------------------------------- /mock/ONLINE_RANK_V2/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ONLINE_RANK_V2", 3 | "data": { 4 | "list": [ 5 | { 6 | "uid": 77777777771, 7 | "face": "http://__MOCK_FACE__.jpg", 8 | "score": "2020", 9 | "uname": "__MOCK_UNAME__", 10 | "rank": 1, 11 | "guard_level": 3 12 | }, 13 | { 14 | "uid": 77777777771, 15 | "face": "http://__MOCK_FACE__.jpg", 16 | "score": "1795", 17 | "uname": "__MOCK_UNAME__", 18 | "rank": 2, 19 | "guard_level": 3 20 | }, 21 | { 22 | "uid": 77777777771, 23 | "face": "http://__MOCK_FACE__.jpg", 24 | "score": "1382", 25 | "uname": "__MOCK_UNAME__", 26 | "rank": 3, 27 | "guard_level": 3 28 | }, 29 | { 30 | "uid": 77777777771, 31 | "face": "http://__MOCK_FACE__.jpg", 32 | "score": "1032", 33 | "uname": "__MOCK_UNAME__", 34 | "rank": 4, 35 | "guard_level": 0 36 | }, 37 | { 38 | "uid": 77777777771, 39 | "face": "http://__MOCK_FACE__.jpg", 40 | "score": "815", 41 | "uname": "__MOCK_UNAME__", 42 | "rank": 5, 43 | "guard_level": 3 44 | }, 45 | { 46 | "uid": 77777777771, 47 | "face": "http://__MOCK_FACE__.jpg", 48 | "score": "800", 49 | "uname": "__MOCK_UNAME__", 50 | "rank": 6, 51 | "guard_level": 0 52 | }, 53 | { 54 | "uid": 77777777771, 55 | "face": "http://__MOCK_FACE__.jpg", 56 | "score": "727", 57 | "uname": "__MOCK_UNAME__", 58 | "rank": 7, 59 | "guard_level": 0 60 | } 61 | ], 62 | "rank_type": "gold-rank" 63 | } 64 | } -------------------------------------------------------------------------------- /mock/POPULARITY_RED_POCKET_NEW/normal_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "POPULARITY_RED_POCKET_NEW", 3 | "data": { 4 | "lot_id": 8646402, 5 | "start_time": 1674450361, 6 | "current_time": 1674450361, 7 | "wait_num": 0, 8 | "uname": "__MOCK_UNAME__", 9 | "uid": 77777777771, 10 | "action": "送出", 11 | "num": 1, 12 | "gift_name": "红包", 13 | "gift_id": 13000, 14 | "price": 20, 15 | "name_color": "#00D1F1", 16 | "medal_info": { 17 | "target_id": 77777777772, 18 | "special": "", 19 | "icon_id": 0, 20 | "anchor_uname": "", 21 | "anchor_roomid": 0, 22 | "medal_level": 8, 23 | "medal_name": "__MOCK_BADGE_NAME__", 24 | "medal_color": 6126494, 25 | "medal_color_start": 6126494, 26 | "medal_color_end": 6126494, 27 | "medal_color_border": 6126494, 28 | "is_lighted": 1, 29 | "guard_level": 0 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /mock/POPULARITY_RED_POCKET_NEW/normal_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "POPULARITY_RED_POCKET_NEW", 3 | "data": { 4 | "lot_id": 8649776, 5 | "start_time": 1674466205, 6 | "current_time": 1674466205, 7 | "wait_num": 0, 8 | "uname": "__MOCK_UNAME__", 9 | "uid": 77777777771, 10 | "action": "送出", 11 | "num": 1, 12 | "gift_name": "红包", 13 | "gift_id": 13000, 14 | "price": 100, 15 | "name_color": "#00D1F1", 16 | "medal_info": { 17 | "target_id": 77777777772, 18 | "special": "", 19 | "icon_id": 0, 20 | "anchor_uname": "", 21 | "anchor_roomid": 0, 22 | "medal_level": 27, 23 | "medal_name": "__MOCK_BADGE_NAME__", 24 | "medal_color": 398668, 25 | "medal_color_start": 398668, 26 | "medal_color_end": 6850801, 27 | "medal_color_border": 6809855, 28 | "is_lighted": 1, 29 | "guard_level": 3 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /mock/POPULARITY_RED_POCKET_START/normal_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "POPULARITY_RED_POCKET_START", 3 | "data": { 4 | "lot_id": 8646402, 5 | "sender_uid": 77777777771, 6 | "sender_name": "__MOCK_UNAME__", 7 | "sender_face": "http://__MOCK_FACE__.jpg", 8 | "join_requirement": 1, 9 | "danmu": "老板大气!点点红包抽礼物", 10 | "current_time": 1674450362, 11 | "start_time": 1674450361, 12 | "end_time": 1674450541, 13 | "last_time": 180, 14 | "remove_time": 1674450556, 15 | "replace_time": 1674450551, 16 | "lot_status": 1, 17 | "h5_url": "https://live.bilibili.com/p/html/live-app-red-envelope/popularity.html?is_live_half_webview=1&hybrid_half_ui=1,5,100p,100p,000000,0,50,0,0,1;2,5,100p,100p,000000,0,50,0,0,1;3,5,100p,100p,000000,0,50,0,0,1;4,5,100p,100p,000000,0,50,0,0,1;5,5,100p,100p,000000,0,50,0,0,1;6,5,100p,100p,000000,0,50,0,0,1;7,5,100p,100p,000000,0,50,0,0,1;8,5,100p,100p,000000,0,50,0,0,1&hybrid_rotate_d=1&hybrid_biz=popularityRedPacket&lotteryId=8646402", 18 | "user_status": 2, 19 | "awards": [ 20 | { 21 | "gift_id": 31212, 22 | "gift_name": "打call", 23 | "gift_pic": "https://s1.hdslb.com/bfs/live/461be640f60788c1d159ec8d6c5d5cf1ef3d1830.png", 24 | "num": 2 25 | }, 26 | { 27 | "gift_id": 31214, 28 | "gift_name": "牛哇", 29 | "gift_pic": "https://s1.hdslb.com/bfs/live/91ac8e35dd93a7196325f1e2052356e71d135afb.png", 30 | "num": 3 31 | }, 32 | { 33 | "gift_id": 31216, 34 | "gift_name": "i了i了", 35 | "gift_pic": "https://s1.hdslb.com/bfs/live/1157a445487b39c0b7368d91b22290c60fa665b2.png", 36 | "num": 3 37 | } 38 | ], 39 | "lot_config_id": 3, 40 | "total_price": 1600, 41 | "wait_num": 0 42 | } 43 | } -------------------------------------------------------------------------------- /mock/POPULARITY_RED_POCKET_START/normal_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "POPULARITY_RED_POCKET_START", 3 | "data": { 4 | "lot_id": 8649776, 5 | "sender_uid": 77777777771, 6 | "sender_name": "__MOCK_UNAME__", 7 | "sender_face": "https://__MOCK_FACE__.jpg", 8 | "join_requirement": 1, 9 | "danmu": "老板大气!点点红包抽礼物", 10 | "current_time": 1674466206, 11 | "start_time": 1674466205, 12 | "end_time": 1674466385, 13 | "last_time": 180, 14 | "remove_time": 1674466400, 15 | "replace_time": 1674466395, 16 | "lot_status": 1, 17 | "h5_url": "https://live.bilibili.com/p/html/live-app-red-envelope/popularity.html?is_live_half_webview=1&hybrid_half_ui=1,5,100p,100p,000000,0,50,0,0,1;2,5,100p,100p,000000,0,50,0,0,1;3,5,100p,100p,000000,0,50,0,0,1;4,5,100p,100p,000000,0,50,0,0,1;5,5,100p,100p,000000,0,50,0,0,1;6,5,100p,100p,000000,0,50,0,0,1;7,5,100p,100p,000000,0,50,0,0,1;8,5,100p,100p,000000,0,50,0,0,1&hybrid_rotate_d=1&hybrid_biz=popularityRedPacket&lotteryId=8649776", 18 | "user_status": 2, 19 | "awards": [ 20 | { 21 | "gift_id": 31213, 22 | "gift_name": "这个好诶", 23 | "gift_pic": "https://s1.hdslb.com/bfs/live/9260c680959428c45b3a2742e42ea7ae75e457ef.png", 24 | "num": 3 25 | }, 26 | { 27 | "gift_id": 31212, 28 | "gift_name": "打call", 29 | "gift_pic": "https://s1.hdslb.com/bfs/live/461be640f60788c1d159ec8d6c5d5cf1ef3d1830.png", 30 | "num": 8 31 | }, 32 | { 33 | "gift_id": 31214, 34 | "gift_name": "牛哇", 35 | "gift_pic": "https://s1.hdslb.com/bfs/live/91ac8e35dd93a7196325f1e2052356e71d135afb.png", 36 | "num": 10 37 | } 38 | ], 39 | "lot_config_id": 4, 40 | "total_price": 8000, 41 | "wait_num": 0 42 | } 43 | } -------------------------------------------------------------------------------- /mock/POPULARITY_RED_POCKET_WINNER_LIST/normal_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "POPULARITY_RED_POCKET_WINNER_LIST", 3 | "data": { 4 | "lot_id": 8646402, 5 | "total_num": 8, 6 | "winner_info": [ 7 | [ 8 | 77777777771, 9 | "__MOCK_UNAME__", 10 | 4674419, 11 | 31212 12 | ], 13 | [ 14 | 77777777771, 15 | "__MOCK_UNAME__", 16 | 4664664, 17 | 31212 18 | ], 19 | [ 20 | 77777777771, 21 | "__MOCK_UNAME__", 22 | 4664665, 23 | 31214 24 | ], 25 | [ 26 | 77777777771, 27 | "__MOCK_UNAME__", 28 | 4696284, 29 | 31214 30 | ], 31 | [ 32 | 77777777771, 33 | "__MOCK_UNAME__", 34 | 4651012, 35 | 31214 36 | ], 37 | [ 38 | 77777777771, 39 | "__MOCK_UNAME__", 40 | 4651580, 41 | 31216 42 | ], 43 | [ 44 | 77777777771, 45 | "__MOCK_UNAME__", 46 | 4664666, 47 | 31216 48 | ], 49 | [ 50 | 77777777771, 51 | "__MOCK_UNAME__", 52 | 4674420, 53 | 31216 54 | ] 55 | ], 56 | "awards": { 57 | "31212": { 58 | "award_type": 1, 59 | "award_name": "打call", 60 | "award_pic": "https://s1.hdslb.com/bfs/live/461be640f60788c1d159ec8d6c5d5cf1ef3d1830.png", 61 | "award_big_pic": "https://i0.hdslb.com/bfs/live/9e6521c57f24c7149c054d265818d4b82059f2ef.png", 62 | "award_price": 500 63 | }, 64 | "31214": { 65 | "award_type": 1, 66 | "award_name": "牛哇", 67 | "award_pic": "https://s1.hdslb.com/bfs/live/91ac8e35dd93a7196325f1e2052356e71d135afb.png", 68 | "award_big_pic": "https://i0.hdslb.com/bfs/live/3b74c117b4f265edcea261bc5608a58d3a7c300a.png", 69 | "award_price": 100 70 | }, 71 | "31216": { 72 | "award_type": 1, 73 | "award_name": "i了i了", 74 | "award_pic": "https://s1.hdslb.com/bfs/live/1157a445487b39c0b7368d91b22290c60fa665b2.png", 75 | "award_big_pic": "https://i0.hdslb.com/bfs/live/cfb9c3d9bdd2c25c95b7d859ebaa590ca9362adb.png", 76 | "award_price": 100 77 | } 78 | }, 79 | "version": 1 80 | } 81 | } -------------------------------------------------------------------------------- /mock/POPULARITY_RED_POCKET_WINNER_LIST/normal_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "POPULARITY_RED_POCKET_WINNER_LIST", 3 | "data": { 4 | "lot_id": 8649776, 5 | "total_num": 21, 6 | "winner_info": [ 7 | [ 8 | 77777777771, 9 | "__MOCK_UNAME__", 10 | 4692262, 11 | 31213 12 | ], 13 | [ 14 | 77777777771, 15 | "__MOCK_UNAME__", 16 | 4697565, 17 | 31213 18 | ], 19 | [ 20 | 77777777771, 21 | "__MOCK_UNAME__", 22 | 4650330, 23 | 31213 24 | ], 25 | [ 26 | 77777777771, 27 | "__MOCK_UNAME__", 28 | 4666099, 29 | 31212 30 | ], 31 | [ 32 | 77777777771, 33 | "__MOCK_UNAME__", 34 | 4692349, 35 | 31212 36 | ], 37 | [ 38 | 77777777771, 39 | "__MOCK_UNAME__", 40 | 4692261, 41 | 31212 42 | ], 43 | [ 44 | 77777777771, 45 | "__MOCK_UNAME__", 46 | 4666101, 47 | 31212 48 | ], 49 | [ 50 | 77777777771, 51 | "__MOCK_UNAME__", 52 | 4706407, 53 | 31212 54 | ], 55 | [ 56 | 77777777771, 57 | "__MOCK_UNAME__", 58 | 4647353, 59 | 31212 60 | ], 61 | [ 62 | 77777777771, 63 | "__MOCK_UNAME__", 64 | 4647351, 65 | 31212 66 | ], 67 | [ 68 | 77777777771, 69 | "__MOCK_UNAME__", 70 | 4650329, 71 | 31212 72 | ], 73 | [ 74 | 77777777771, 75 | "__MOCK_UNAME__", 76 | 4697566, 77 | 31214 78 | ], 79 | [ 80 | 77777777771, 81 | "__MOCK_UNAME__", 82 | 4666100, 83 | 31214 84 | ], 85 | [ 86 | 77777777771, 87 | "__MOCK_UNAME__", 88 | 4666102, 89 | 31214 90 | ], 91 | [ 92 | 77777777771, 93 | "__MOCK_UNAME__", 94 | 4697567, 95 | 31214 96 | ], 97 | [ 98 | 77777777771, 99 | "__MOCK_UNAME__", 100 | 4647352, 101 | 31214 102 | ], 103 | [ 104 | 77777777771, 105 | "__MOCK_UNAME__", 106 | 4666103, 107 | 31214 108 | ], 109 | [ 110 | 77777777771, 111 | "__MOCK_UNAME__", 112 | 4692350, 113 | 31214 114 | ], 115 | [ 116 | 77777777771, 117 | "__MOCK_UNAME__", 118 | 4699069, 119 | 31214 120 | ], 121 | [ 122 | 77777777771, 123 | "__MOCK_UNAME__", 124 | 4666104, 125 | 31214 126 | ], 127 | [ 128 | 77777777771, 129 | "__MOCK_UNAME__", 130 | 4699070, 131 | 31214 132 | ] 133 | ], 134 | "awards": { 135 | "31212": { 136 | "award_type": 1, 137 | "award_name": "打call", 138 | "award_pic": "https://s1.hdslb.com/bfs/live/461be640f60788c1d159ec8d6c5d5cf1ef3d1830.png", 139 | "award_big_pic": "https://i0.hdslb.com/bfs/live/9e6521c57f24c7149c054d265818d4b82059f2ef.png", 140 | "award_price": 500 141 | }, 142 | "31213": { 143 | "award_type": 1, 144 | "award_name": "这个好诶", 145 | "award_pic": "https://s1.hdslb.com/bfs/live/9260c680959428c45b3a2742e42ea7ae75e457ef.png", 146 | "award_big_pic": "https://i0.hdslb.com/bfs/live/dafd2e9e5c3086377124a9328e840cd21a3f6847.png", 147 | "award_price": 1000 148 | }, 149 | "31214": { 150 | "award_type": 1, 151 | "award_name": "牛哇", 152 | "award_pic": "https://s1.hdslb.com/bfs/live/91ac8e35dd93a7196325f1e2052356e71d135afb.png", 153 | "award_big_pic": "https://i0.hdslb.com/bfs/live/3b74c117b4f265edcea261bc5608a58d3a7c300a.png", 154 | "award_price": 100 155 | } 156 | }, 157 | "version": 1 158 | } 159 | } -------------------------------------------------------------------------------- /mock/PREPARING/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "PREPARING", 3 | "round": 1, 4 | "roomid": "77777777774" 5 | } -------------------------------------------------------------------------------- /mock/README.md: -------------------------------------------------------------------------------- 1 | # Mock 2 | 3 | 弹幕流原始数据 mock 文件。 4 | 5 | 原始数据中对应的信息已按如下规则遮盖: 6 | 7 | | 含义 | 遮盖值 | 8 | | --- | --- | 9 | | 用户 uid | `77777777771` | 10 | | 用户 uname | `__MOCK_UNAME__` | 11 | | 用户头像 url | `http://__MOCK_FACE__.jpg` | 12 | | 牌子名称 | `__MOCK_BADGE_NAME__` | 13 | | 牌子主播 uid | `77777777772` | 14 | | 牌子主播 uname | `__MOCK_BADGE_ANCHOR_UNAME__` | 15 | | 牌子主播房间号 | `77777777773` | 16 | | 弹幕内容 (表情弹幕除外) | `__MOCK_MESSAGE_CONTENT__`, `__MOCK_MESSAGE_CONTENT_2__` | 17 | | 当前房间号 | `77777777774` | 18 | | 当前房间主播 uid | `77777777775` | 19 | | 当前房间主播 uname | `__MOCK_MASTER_UNAME__` | -------------------------------------------------------------------------------- /mock/ROOM_ADMINS/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ROOM_ADMINS", 3 | "uids": [ 4 | 77777777771 5 | ] 6 | } -------------------------------------------------------------------------------- /mock/ROOM_ADMIN_REVOKE/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ROOM_ADMIN_REVOKE", 3 | "msg": "撤销房管", 4 | "uid": 77777777771 5 | } -------------------------------------------------------------------------------- /mock/ROOM_BLOCK_MSG/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ROOM_BLOCK_MSG", 3 | "data": { 4 | "dmscore": 30, 5 | "operator": 2, 6 | "uid": 77777777771, 7 | "uname": "__MOCK_UNAME__" 8 | }, 9 | "uid": "77777777771", 10 | "uname": "__MOCK_UNAME__" 11 | } -------------------------------------------------------------------------------- /mock/ROOM_CHANGE/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ROOM_CHANGE", 3 | "data": { 4 | "title": "直播间标题", 5 | "area_id": 375, 6 | "parent_area_id": 11, 7 | "area_name": " 科技", 8 | "parent_area_name": "知识", 9 | "live_key": "0", 10 | "sub_session_key": "" 11 | } 12 | } -------------------------------------------------------------------------------- /mock/ROOM_REAL_TIME_MESSAGE_UPDATE/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "ROOM_REAL_TIME_MESSAGE_UPDATE", 3 | "data": { 4 | "roomid": 77777777774, 5 | "fans": 828046, 6 | "red_notice": -1, 7 | "fans_club": 8744 8 | } 9 | } -------------------------------------------------------------------------------- /mock/ROOM_SILENT_OFF/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "type": "", 4 | "level": 0, 5 | "second": 0 6 | }, 7 | "cmd": "ROOM_SILENT_OFF" 8 | } -------------------------------------------------------------------------------- /mock/ROOM_SILENT_ON/all_member.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "type": "member", 4 | "level": 1, 5 | "second": 1673943338 6 | }, 7 | "cmd": "ROOM_SILENT_ON" 8 | } -------------------------------------------------------------------------------- /mock/ROOM_SILENT_ON/by_medal.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "type": "medal", 4 | "level": 4, 5 | "second": 1674050722 6 | }, 7 | "cmd": "ROOM_SILENT_ON" 8 | } -------------------------------------------------------------------------------- /mock/ROOM_SILENT_ON/by_user_level.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "type": "level", 4 | "level": 1, 5 | "second": 1673943135 6 | }, 7 | "cmd": "ROOM_SILENT_ON" 8 | } -------------------------------------------------------------------------------- /mock/ROOM_SILENT_ON/by_user_level_unlimited.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "type": "level", 4 | "level": 5, 5 | "second": -1 6 | }, 7 | "cmd": "ROOM_SILENT_ON" 8 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/PK票_target_user_no_badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "", 6 | "batch_combo_send": null, 7 | "beatId": "", 8 | "biz_source": "Live", 9 | "blind_gift": null, 10 | "broadcast_id": 0, 11 | "coin_type": "silver", 12 | "combo_resources_id": 1, 13 | "combo_send": null, 14 | "combo_stay_time": 3, 15 | "combo_total_coin": 0, 16 | "crit_prob": 0, 17 | "demarcation": 1, 18 | "discount_price": 0, 19 | "dmscore": 4, 20 | "draw": 0, 21 | "effect": 0, 22 | "effect_block": 1, 23 | "face": "http://__MOCK_FACE__.jpg", 24 | "face_effect_id": 0, 25 | "face_effect_type": 0, 26 | "float_sc_resource_id": 0, 27 | "giftId": 31531, 28 | "giftName": "PK票", 29 | "giftType": 5, 30 | "gold": 0, 31 | "guard_level": 0, 32 | "is_first": true, 33 | "is_special_batch": 0, 34 | "magnification": 1, 35 | "medal_info": { 36 | "anchor_roomid": 0, 37 | "anchor_uname": "", 38 | "guard_level": 0, 39 | "icon_id": 0, 40 | "is_lighted": 0, 41 | "medal_color": 0, 42 | "medal_color_border": 0, 43 | "medal_color_end": 0, 44 | "medal_color_start": 0, 45 | "medal_level": 0, 46 | "medal_name": "", 47 | "special": "", 48 | "target_id": 0 49 | }, 50 | "name_color": "", 51 | "num": 1, 52 | "original_gift_name": "", 53 | "price": 0, 54 | "rcost": 2985135, 55 | "remain": 0, 56 | "rnd": "1664108234130600001", 57 | "send_master": { 58 | "room_id": 77777777774, 59 | "uid": 77777777775, 60 | "uname": "__MOCK_MASTER_UNAME__" 61 | }, 62 | "silver": 0, 63 | "super": 0, 64 | "super_batch_gift_num": 0, 65 | "super_gift_num": 0, 66 | "svga_block": 0, 67 | "switch": true, 68 | "tag_image": "", 69 | "tid": "1664108234130600001", 70 | "timestamp": 1664108234, 71 | "top_list": null, 72 | "total_coin": 0, 73 | "uid": 77777777771, 74 | "uname": "__MOCK_UNAME__" 75 | } 76 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/Super_target_user.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:32073:1664108140.0149", 6 | "batch_combo_send": { 7 | "action": "投喂", 8 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:32073:1664108140.0149", 9 | "batch_combo_num": 5, 10 | "blind_gift": null, 11 | "gift_id": 32073, 12 | "gift_name": "Super", 13 | "gift_num": 1, 14 | "send_master": { 15 | "room_id": 77777777774, 16 | "uid": 77777777775, 17 | "uname": "__MOCK_MASTER_UNAME__" 18 | }, 19 | "uid": 77777777771, 20 | "uname": "__MOCK_UNAME__" 21 | }, 22 | "beatId": "0", 23 | "biz_source": "Live", 24 | "blind_gift": null, 25 | "broadcast_id": 0, 26 | "coin_type": "gold", 27 | "combo_resources_id": 0, 28 | "combo_send": null, 29 | "combo_stay_time": 3, 30 | "combo_total_coin": 25000, 31 | "crit_prob": 0, 32 | "demarcation": 2, 33 | "discount_price": 5000, 34 | "dmscore": 80, 35 | "draw": 0, 36 | "effect": 0, 37 | "effect_block": 0, 38 | "face": "http://__MOCK_FACE__.jpg", 39 | "face_effect_id": 0, 40 | "face_effect_type": 0, 41 | "float_sc_resource_id": 0, 42 | "giftId": 32073, 43 | "giftName": "Super", 44 | "giftType": 0, 45 | "gold": 0, 46 | "guard_level": 0, 47 | "is_first": false, 48 | "is_special_batch": 0, 49 | "magnification": 1, 50 | "medal_info": { 51 | "anchor_roomid": 0, 52 | "anchor_uname": "", 53 | "guard_level": 2, 54 | "icon_id": 0, 55 | "is_lighted": 1, 56 | "medal_color": 2951253, 57 | "medal_color_border": 16771156, 58 | "medal_color_end": 10329087, 59 | "medal_color_start": 2951253, 60 | "medal_level": 30, 61 | "medal_name": "__MOCK_BADGE_NAME__", 62 | "special": "", 63 | "target_id": 77777777775 64 | }, 65 | "name_color": "", 66 | "num": 1, 67 | "original_gift_name": "", 68 | "price": 5000, 69 | "rcost": 2985164, 70 | "remain": 0, 71 | "rnd": "1664108142111500001", 72 | "send_master": { 73 | "room_id": 77777777774, 74 | "uid": 77777777775, 75 | "uname": "__MOCK_MASTER_UNAME__" 76 | }, 77 | "silver": 0, 78 | "super": 0, 79 | "super_batch_gift_num": 5, 80 | "super_gift_num": 5, 81 | "svga_block": 0, 82 | "switch": true, 83 | "tag_image": "", 84 | "tid": "1664108142111500001", 85 | "timestamp": 1664108142, 86 | "top_list": null, 87 | "total_coin": 5000, 88 | "uid": 77777777771, 89 | "uname": "__MOCK_UNAME__" 90 | } 91 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/小花花_not_same_badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31036:1664028888.3527", 6 | "batch_combo_send": { 7 | "action": "投喂", 8 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31036:1664028888.3527", 9 | "batch_combo_num": 1, 10 | "blind_gift": null, 11 | "gift_id": 31036, 12 | "gift_name": "小花花", 13 | "gift_num": 1, 14 | "send_master": null, 15 | "uid": 77777777771, 16 | "uname": "__MOCK_UNAME__" 17 | }, 18 | "beatId": "", 19 | "biz_source": "Live", 20 | "blind_gift": null, 21 | "broadcast_id": 0, 22 | "coin_type": "gold", 23 | "combo_resources_id": 1, 24 | "combo_send": { 25 | "action": "投喂", 26 | "combo_id": "gift:combo_id:77777777771:77777777775:31036:1664028888.3508", 27 | "combo_num": 1, 28 | "gift_id": 31036, 29 | "gift_name": "小花花", 30 | "gift_num": 1, 31 | "send_master": null, 32 | "uid": 77777777771, 33 | "uname": "__MOCK_UNAME__" 34 | }, 35 | "combo_stay_time": 3, 36 | "combo_total_coin": 100, 37 | "crit_prob": 0, 38 | "demarcation": 1, 39 | "discount_price": 100, 40 | "dmscore": 16, 41 | "draw": 0, 42 | "effect": 0, 43 | "effect_block": 0, 44 | "face": "http://__MOCK_FACE__.jpg", 45 | "face_effect_id": 0, 46 | "face_effect_type": 0, 47 | "float_sc_resource_id": 0, 48 | "giftId": 31036, 49 | "giftName": "小花花", 50 | "giftType": 0, 51 | "gold": 0, 52 | "guard_level": 0, 53 | "is_first": true, 54 | "is_special_batch": 0, 55 | "magnification": 1, 56 | "medal_info": { 57 | "anchor_roomid": 0, 58 | "anchor_uname": "", 59 | "guard_level": 0, 60 | "icon_id": 0, 61 | "is_lighted": 0, 62 | "medal_color": 6067854, 63 | "medal_color_border": 12632256, 64 | "medal_color_end": 12632256, 65 | "medal_color_start": 12632256, 66 | "medal_level": 4, 67 | "medal_name": "__MOCK_BADGE_NAME__", 68 | "special": "", 69 | "target_id": 77777777772 70 | }, 71 | "name_color": "", 72 | "num": 1, 73 | "original_gift_name": "", 74 | "price": 100, 75 | "rcost": 68105379, 76 | "remain": 0, 77 | "rnd": "1664028888131100001", 78 | "send_master": null, 79 | "silver": 0, 80 | "super": 0, 81 | "super_batch_gift_num": 1, 82 | "super_gift_num": 1, 83 | "svga_block": 0, 84 | "switch": true, 85 | "tag_image": "", 86 | "tid": "1664028888131100001", 87 | "timestamp": 1664028888, 88 | "top_list": null, 89 | "total_coin": 100, 90 | "uid": 77777777771, 91 | "uname": "__MOCK_UNAME__" 92 | } 93 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/干杯_not_same_badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31251:1664028962.9340", 6 | "batch_combo_send": { 7 | "action": "投喂", 8 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31251:1664028962.9340", 9 | "batch_combo_num": 1, 10 | "blind_gift": null, 11 | "gift_id": 31251, 12 | "gift_name": "干杯", 13 | "gift_num": 1, 14 | "send_master": null, 15 | "uid": 77777777771, 16 | "uname": "__MOCK_UNAME__" 17 | }, 18 | "beatId": "", 19 | "biz_source": "Live", 20 | "blind_gift": null, 21 | "broadcast_id": 0, 22 | "coin_type": "gold", 23 | "combo_resources_id": 1, 24 | "combo_send": { 25 | "action": "投喂", 26 | "combo_id": "gift:combo_id:77777777771:77777777775:31251:1664028962.9308", 27 | "combo_num": 1, 28 | "gift_id": 31251, 29 | "gift_name": "干杯", 30 | "gift_num": 1, 31 | "send_master": null, 32 | "uid": 77777777771, 33 | "uname": "__MOCK_UNAME__" 34 | }, 35 | "combo_stay_time": 3, 36 | "combo_total_coin": 6600, 37 | "crit_prob": 0, 38 | "demarcation": 2, 39 | "discount_price": 6600, 40 | "dmscore": 16, 41 | "draw": 0, 42 | "effect": 0, 43 | "effect_block": 0, 44 | "face": "http://__MOCK_FACE__.jpg", 45 | "face_effect_id": 0, 46 | "face_effect_type": 0, 47 | "float_sc_resource_id": 0, 48 | "giftId": 31251, 49 | "giftName": "干杯", 50 | "giftType": 0, 51 | "gold": 0, 52 | "guard_level": 0, 53 | "is_first": true, 54 | "is_special_batch": 0, 55 | "magnification": 1, 56 | "medal_info": { 57 | "anchor_roomid": 0, 58 | "anchor_uname": "", 59 | "guard_level": 0, 60 | "icon_id": 0, 61 | "is_lighted": 0, 62 | "medal_color": 6067854, 63 | "medal_color_border": 12632256, 64 | "medal_color_end": 12632256, 65 | "medal_color_start": 12632256, 66 | "medal_level": 3, 67 | "medal_name": "__MOCK_BADGE_NAME__", 68 | "special": "", 69 | "target_id": 77777777772 70 | }, 71 | "name_color": "", 72 | "num": 1, 73 | "original_gift_name": "", 74 | "price": 6600, 75 | "rcost": 68105492, 76 | "remain": 0, 77 | "rnd": "1664028962121000004", 78 | "send_master": null, 79 | "silver": 0, 80 | "super": 0, 81 | "super_batch_gift_num": 1, 82 | "super_gift_num": 1, 83 | "svga_block": 0, 84 | "switch": true, 85 | "tag_image": "", 86 | "tid": "1664028962121000004", 87 | "timestamp": 1664028962, 88 | "top_list": null, 89 | "total_coin": 6600, 90 | "uid": 77777777771, 91 | "uname": "__MOCK_UNAME__" 92 | } 93 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/幸运之钥.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31709:1664028891.4592", 6 | "batch_combo_send": { 7 | "action": "投喂", 8 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31709:1664028891.4592", 9 | "batch_combo_num": 1, 10 | "blind_gift": null, 11 | "gift_id": 31709, 12 | "gift_name": "幸运之钥", 13 | "gift_num": 1, 14 | "send_master": null, 15 | "uid": 77777777771, 16 | "uname": "__MOCK_UNAME__" 17 | }, 18 | "beatId": "0", 19 | "biz_source": "Live", 20 | "blind_gift": null, 21 | "broadcast_id": 0, 22 | "coin_type": "gold", 23 | "combo_resources_id": 1, 24 | "combo_send": { 25 | "action": "投喂", 26 | "combo_id": "gift:combo_id:77777777771:77777777775:31709:1664028891.4558", 27 | "combo_num": 1, 28 | "gift_id": 31709, 29 | "gift_name": "幸运之钥", 30 | "gift_num": 1, 31 | "send_master": null, 32 | "uid": 77777777771, 33 | "uname": "__MOCK_UNAME__" 34 | }, 35 | "combo_stay_time": 3, 36 | "combo_total_coin": 1000, 37 | "crit_prob": 0, 38 | "demarcation": 2, 39 | "discount_price": 1000, 40 | "dmscore": 40, 41 | "draw": 0, 42 | "effect": 0, 43 | "effect_block": 0, 44 | "face": "http://__MOCK_FACE__.jpg", 45 | "face_effect_id": 0, 46 | "face_effect_type": 0, 47 | "float_sc_resource_id": 0, 48 | "giftId": 31709, 49 | "giftName": "幸运之钥", 50 | "giftType": 0, 51 | "gold": 0, 52 | "guard_level": 0, 53 | "is_first": true, 54 | "is_special_batch": 0, 55 | "magnification": 1, 56 | "medal_info": { 57 | "anchor_roomid": 0, 58 | "anchor_uname": "", 59 | "guard_level": 0, 60 | "icon_id": 0, 61 | "is_lighted": 1, 62 | "medal_color": 6126494, 63 | "medal_color_border": 6126494, 64 | "medal_color_end": 6126494, 65 | "medal_color_start": 6126494, 66 | "medal_level": 5, 67 | "medal_name": "__MOCK_BADGE_NAME__", 68 | "special": "", 69 | "target_id": 77777777775 70 | }, 71 | "name_color": "", 72 | "num": 1, 73 | "original_gift_name": "", 74 | "price": 1000, 75 | "rcost": 68105391, 76 | "remain": 0, 77 | "rnd": "1664028891111400001", 78 | "send_master": null, 79 | "silver": 0, 80 | "super": 0, 81 | "super_batch_gift_num": 1, 82 | "super_gift_num": 1, 83 | "svga_block": 0, 84 | "switch": true, 85 | "tag_image": "", 86 | "tid": "1664028891111400001", 87 | "timestamp": 1664028891, 88 | "top_list": null, 89 | "total_coin": 1000, 90 | "uid": 77777777771, 91 | "uname": "__MOCK_UNAME__" 92 | } 93 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/泡泡糖_not_same_badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31817:1664028895.0001", 6 | "batch_combo_send": { 7 | "action": "投喂", 8 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31817:1664028895.0001", 9 | "batch_combo_num": 1, 10 | "blind_gift": null, 11 | "gift_id": 31817, 12 | "gift_name": "泡泡糖", 13 | "gift_num": 1, 14 | "send_master": null, 15 | "uid": 77777777771, 16 | "uname": "__MOCK_UNAME__" 17 | }, 18 | "beatId": "", 19 | "biz_source": "live", 20 | "blind_gift": null, 21 | "broadcast_id": 0, 22 | "coin_type": "gold", 23 | "combo_resources_id": 1, 24 | "combo_send": { 25 | "action": "投喂", 26 | "combo_id": "gift:combo_id:77777777771:77777777775:31817:1664028894.9946", 27 | "combo_num": 1, 28 | "gift_id": 31817, 29 | "gift_name": "泡泡糖", 30 | "gift_num": 1, 31 | "send_master": null, 32 | "uid": 77777777771, 33 | "uname": "__MOCK_UNAME__" 34 | }, 35 | "combo_stay_time": 3, 36 | "combo_total_coin": 1100, 37 | "crit_prob": 0, 38 | "demarcation": 2, 39 | "discount_price": 1100, 40 | "dmscore": 24, 41 | "draw": 0, 42 | "effect": 0, 43 | "effect_block": 0, 44 | "face": "http://__MOCK_FACE__.jpg", 45 | "face_effect_id": 0, 46 | "face_effect_type": 0, 47 | "float_sc_resource_id": 0, 48 | "giftId": 31817, 49 | "giftName": "泡泡糖", 50 | "giftType": 0, 51 | "gold": 0, 52 | "guard_level": 0, 53 | "is_first": true, 54 | "is_special_batch": 0, 55 | "magnification": 1, 56 | "medal_info": { 57 | "anchor_roomid": 0, 58 | "anchor_uname": "", 59 | "guard_level": 0, 60 | "icon_id": 0, 61 | "is_lighted": 0, 62 | "medal_color": 6126494, 63 | "medal_color_border": 12632256, 64 | "medal_color_end": 12632256, 65 | "medal_color_start": 12632256, 66 | "medal_level": 5, 67 | "medal_name": "__MOCK_BADGE_NAME__", 68 | "special": "", 69 | "target_id": 77777777772 70 | }, 71 | "name_color": "", 72 | "num": 1, 73 | "original_gift_name": "", 74 | "price": 1100, 75 | "rcost": 68105392, 76 | "remain": 0, 77 | "rnd": "1664028894121100009", 78 | "send_master": null, 79 | "silver": 0, 80 | "super": 0, 81 | "super_batch_gift_num": 1, 82 | "super_gift_num": 1, 83 | "svga_block": 0, 84 | "switch": true, 85 | "tag_image": "", 86 | "tid": "1664028894121100009", 87 | "timestamp": 1664028894, 88 | "top_list": null, 89 | "total_coin": 1100, 90 | "uid": 77777777771, 91 | "uname": "__MOCK_UNAME__" 92 | } 93 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/粉丝团灯牌.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31164:1664028917.6514", 6 | "batch_combo_send": { 7 | "action": "投喂", 8 | "batch_combo_id": "batch:gift:combo_id:77777777771:77777777775:31164:1664028917.6514", 9 | "batch_combo_num": 1, 10 | "blind_gift": null, 11 | "gift_id": 31164, 12 | "gift_name": "粉丝团灯牌", 13 | "gift_num": 1, 14 | "send_master": null, 15 | "uid": 77777777771, 16 | "uname": "__MOCK_UNAME__" 17 | }, 18 | "beatId": "", 19 | "biz_source": "Live", 20 | "blind_gift": null, 21 | "broadcast_id": 0, 22 | "coin_type": "gold", 23 | "combo_resources_id": 1, 24 | "combo_send": { 25 | "action": "投喂", 26 | "combo_id": "gift:combo_id:77777777771:77777777775:31164:1664028917.6506", 27 | "combo_num": 1, 28 | "gift_id": 31164, 29 | "gift_name": "粉丝团灯牌", 30 | "gift_num": 1, 31 | "send_master": null, 32 | "uid": 77777777771, 33 | "uname": "__MOCK_UNAME__" 34 | }, 35 | "combo_stay_time": 3, 36 | "combo_total_coin": 1000, 37 | "crit_prob": 0, 38 | "demarcation": 2, 39 | "discount_price": 1000, 40 | "dmscore": 40, 41 | "draw": 0, 42 | "effect": 3, 43 | "effect_block": 0, 44 | "face": "http://__MOCK_FACE__.jpg", 45 | "face_effect_id": 0, 46 | "face_effect_type": 0, 47 | "float_sc_resource_id": 0, 48 | "giftId": 31164, 49 | "giftName": "粉丝团灯牌", 50 | "giftType": 0, 51 | "gold": 0, 52 | "guard_level": 0, 53 | "is_first": true, 54 | "is_special_batch": 0, 55 | "magnification": 1, 56 | "medal_info": { 57 | "anchor_roomid": 0, 58 | "anchor_uname": "", 59 | "guard_level": 0, 60 | "icon_id": 0, 61 | "is_lighted": 1, 62 | "medal_color": 9272486, 63 | "medal_color_border": 9272486, 64 | "medal_color_end": 9272486, 65 | "medal_color_start": 9272486, 66 | "medal_level": 9, 67 | "medal_name": "__MOCK_BADGE_NAME__", 68 | "special": "", 69 | "target_id": 77777777772 70 | }, 71 | "name_color": "", 72 | "num": 1, 73 | "original_gift_name": "", 74 | "price": 1000, 75 | "rcost": 68105405, 76 | "remain": 0, 77 | "rnd": "1954038616", 78 | "send_master": null, 79 | "silver": 0, 80 | "super": 0, 81 | "super_batch_gift_num": 1, 82 | "super_gift_num": 1, 83 | "svga_block": 0, 84 | "switch": true, 85 | "tag_image": "", 86 | "tid": "1664028917121000003", 87 | "timestamp": 1664028917, 88 | "top_list": null, 89 | "total_coin": 1000, 90 | "uid": 77777777771, 91 | "uname": "__MOCK_UNAME__" 92 | } 93 | } -------------------------------------------------------------------------------- /mock/SEND_GIFT/辣条_no_badge.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SEND_GIFT", 3 | "data": { 4 | "action": "投喂", 5 | "batch_combo_id": "", 6 | "batch_combo_send": null, 7 | "beatId": "", 8 | "biz_source": "Live", 9 | "blind_gift": null, 10 | "broadcast_id": 0, 11 | "coin_type": "silver", 12 | "combo_resources_id": 1, 13 | "combo_send": null, 14 | "combo_stay_time": 3, 15 | "combo_total_coin": 0, 16 | "crit_prob": 0, 17 | "demarcation": 1, 18 | "discount_price": 0, 19 | "dmscore": 4, 20 | "draw": 0, 21 | "effect": 0, 22 | "effect_block": 1, 23 | "face": "http://__MOCK_FACE__.jpg", 24 | "face_effect_id": 0, 25 | "face_effect_type": 0, 26 | "float_sc_resource_id": 0, 27 | "giftId": 1, 28 | "giftName": "辣条", 29 | "giftType": 5, 30 | "gold": 0, 31 | "guard_level": 0, 32 | "is_first": true, 33 | "is_special_batch": 0, 34 | "magnification": 1, 35 | "medal_info": { 36 | "anchor_roomid": 0, 37 | "anchor_uname": "", 38 | "guard_level": 0, 39 | "icon_id": 0, 40 | "is_lighted": 0, 41 | "medal_color": 0, 42 | "medal_color_border": 0, 43 | "medal_color_end": 0, 44 | "medal_color_start": 0, 45 | "medal_level": 0, 46 | "medal_name": "", 47 | "special": "", 48 | "target_id": 0 49 | }, 50 | "name_color": "", 51 | "num": 1, 52 | "original_gift_name": "", 53 | "price": 100, 54 | "rcost": 68104708, 55 | "remain": 0, 56 | "rnd": "1664028708120000001", 57 | "send_master": null, 58 | "silver": 0, 59 | "super": 0, 60 | "super_batch_gift_num": 0, 61 | "super_gift_num": 0, 62 | "svga_block": 0, 63 | "switch": true, 64 | "tag_image": "", 65 | "tid": "1664028708120000001", 66 | "timestamp": 1664028708, 67 | "top_list": null, 68 | "total_coin": 100, 69 | "uid": 77777777771, 70 | "uname": "__MOCK_UNAME__" 71 | } 72 | } -------------------------------------------------------------------------------- /mock/SUPER_CHAT_MESSAGE/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SUPER_CHAT_MESSAGE", 3 | "data": { 4 | "background_bottom_color": "#2A60B2", 5 | "background_color": "#EDF5FF", 6 | "background_color_end": "#405D85", 7 | "background_color_start": "#3171D2", 8 | "background_icon": "", 9 | "background_image": "https://i0.hdslb.com/bfs/live/a712efa5c6ebc67bafbe8352d3e74b820a00c13e.png", 10 | "background_price_color": "#7497CD", 11 | "color_point": 0.7, 12 | "dmscore": 72, 13 | "end_time": 1661528683, 14 | "gift": { 15 | "gift_id": 12000, 16 | "gift_name": "醒目留言", 17 | "num": 1 18 | }, 19 | "id": 4898587, 20 | "is_ranked": 1, 21 | "is_send_audit": 0, 22 | "medal_info": { 23 | "anchor_roomid": 77777777773, 24 | "anchor_uname": "__MOCK_BADGE_ANCHOR_UNAME__", 25 | "guard_level": 0, 26 | "icon_id": 0, 27 | "is_lighted": 1, 28 | "medal_color": "#be6686", 29 | "medal_color_border": 12478086, 30 | "medal_color_end": 12478086, 31 | "medal_color_start": 12478086, 32 | "medal_level": 16, 33 | "medal_name": "__MOCK_BADGE_NAME__", 34 | "special": "", 35 | "target_id": 77777777772 36 | }, 37 | "message": "__MOCK_MESSAGE_CONTENT__", 38 | "message_font_color": "#A3F6FF", 39 | "message_trans": "", 40 | "price": 30, 41 | "rate": 1000, 42 | "start_time": 1661528623, 43 | "time": 60, 44 | "token": "A1EC2662", 45 | "trans_mark": 0, 46 | "ts": 1661528623, 47 | "uid": 77777777771, 48 | "user_info": { 49 | "face": "http://__MOCK_FACE__.jpg", 50 | "face_frame": "", 51 | "guard_level": 0, 52 | "is_main_vip": 1, 53 | "is_svip": 0, 54 | "is_vip": 0, 55 | "level_color": "#61c05a", 56 | "manager": 0, 57 | "name_color": "#666666", 58 | "title": "title-111-1", 59 | "uname": "__MOCK_UNAME__", 60 | "user_level": 20 61 | } 62 | }, 63 | "roomid": 77777777774 64 | } -------------------------------------------------------------------------------- /mock/SUPER_CHAT_MESSAGE_DELETE/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SUPER_CHAT_MESSAGE_DELETE", 3 | "data": { 4 | "ids": [4898587] 5 | }, 6 | "roomid": 77777777774 7 | } -------------------------------------------------------------------------------- /mock/SUPER_CHAT_MESSAGE_JPN/jpn_translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SUPER_CHAT_MESSAGE_JPN", 3 | "data": { 4 | "id": "5176131", 5 | "uid": "77777777771", 6 | "price": 30, 7 | "rate": 1000, 8 | "message": "__MOCK_MESSAGE_CONTENT__", 9 | "message_jpn": "__MOCK_MESSAGE_CONTENT_2__", 10 | "is_ranked": 1, 11 | "background_image": "https://i0.hdslb.com/bfs/live/a712efa5c6ebc67bafbe8352d3e74b820a00c13e.png", 12 | "background_color": "#EDF5FF", 13 | "background_icon": "", 14 | "background_price_color": "#7497CD", 15 | "background_bottom_color": "#2A60B2", 16 | "ts": 1664454111, 17 | "token": "361B074C", 18 | "medal_info": { 19 | "icon_id": 0, 20 | "target_id": 77777777772, 21 | "special": "", 22 | "anchor_uname": "__MOCK_BADGE_ANCHOR_UNAME__", 23 | "anchor_roomid": 77777777773, 24 | "medal_level": 12, 25 | "medal_name": "__MOCK_BADGE_NAME__", 26 | "medal_color": "#8d7ca6" 27 | }, 28 | "user_info": { 29 | "uname": "__MOCK_UNAME__", 30 | "face": "http://__MOCK_FACE__.jpg", 31 | "face_frame": "https://i0.hdslb.com/bfs/live/80f732943cc3367029df65e267960d56736a82ee.png", 32 | "guard_level": 3, 33 | "user_level": 20, 34 | "level_color": "#61c05a", 35 | "is_vip": 0, 36 | "is_svip": 0, 37 | "is_main_vip": 1, 38 | "title": "title-155-1", 39 | "manager": 0 40 | }, 41 | "time": 60, 42 | "start_time": 1664454111, 43 | "end_time": 1664454171, 44 | "gift": { 45 | "num": 1, 46 | "gift_id": 12000, 47 | "gift_name": "醒目留言" 48 | } 49 | }, 50 | "roomid": "77777777774" 51 | } -------------------------------------------------------------------------------- /mock/SUPER_CHAT_MESSAGE_JPN/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "SUPER_CHAT_MESSAGE_JPN", 3 | "data": { 4 | "id": "4898652", 5 | "uid": "77777777771", 6 | "price": 100, 7 | "rate": 1000, 8 | "message": "__MOCK_MESSAGE_CONTENT__", 9 | "message_jpn": "", 10 | "is_ranked": 1, 11 | "background_image": "https://i0.hdslb.com/bfs/live/a712efa5c6ebc67bafbe8352d3e74b820a00c13e.png", 12 | "background_color": "#FFF1C5", 13 | "background_icon": "", 14 | "background_price_color": "#ECCF75", 15 | "background_bottom_color": "#E2B52B", 16 | "ts": 1661528878, 17 | "token": "56657C04", 18 | "medal_info": { 19 | "icon_id": 0, 20 | "target_id": 77777777772, 21 | "special": "", 22 | "anchor_uname": "__MOCK_BADGE_ANCHOR_UNAME__", 23 | "anchor_roomid": 77777777773, 24 | "medal_level": 26, 25 | "medal_name": "__MOCK_BADGE_NAME__", 26 | "medal_color": "#6154c" 27 | }, 28 | "user_info": { 29 | "uname": "__MOCK_UNAME__", 30 | "face": "http://__MOCK_FACE__.jpg", 31 | "face_frame": "https://i0.hdslb.com/bfs/live/80f732943cc3367029df65e267960d56736a82ee.png", 32 | "guard_level": 3, 33 | "user_level": 17, 34 | "level_color": "#61c05a", 35 | "is_vip": 0, 36 | "is_svip": 0, 37 | "is_main_vip": 0, 38 | "title": "0", 39 | "manager": 0 40 | }, 41 | "time": 300, 42 | "start_time": 1661528878, 43 | "end_time": 1661529178, 44 | "gift": { 45 | "num": 1, 46 | "gift_id": 12000, 47 | "gift_name": "醒目留言" 48 | } 49 | }, 50 | "roomid": "77777777774" 51 | } -------------------------------------------------------------------------------- /mock/USER_TOAST_MSG/guard_jianzhang_new.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "USER_TOAST_MSG", 3 | "data": { 4 | "anchor_show": true, 5 | "color": "#00D1F1", 6 | "dmscore": 90, 7 | "effect_id": 397, 8 | "end_time": 1674568760, 9 | "face_effect_id": 44, 10 | "gift_id": 10003, 11 | "guard_level": 3, 12 | "is_show": 0, 13 | "num": 1, 14 | "op_type": 1, 15 | "payflow_id": "2301242158351662179727302", 16 | "price": 198000, 17 | "role_name": "舰长", 18 | "room_effect_id": 590, 19 | "start_time": 1674568760, 20 | "svga_block": 0, 21 | "target_guard_count": 2072, 22 | "toast_msg": "<%__MOCK_UNAME__%> 开通了舰长,今天是TA陪伴主播的第1天", 23 | "uid": 77777777771, 24 | "unit": "月", 25 | "user_show": true, 26 | "username": "__MOCK_UNAME__" 27 | } 28 | } -------------------------------------------------------------------------------- /mock/USER_TOAST_MSG/guard_jianzhang_renew.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "USER_TOAST_MSG", 3 | "data": { 4 | "anchor_show": true, 5 | "color": "#00D1F1", 6 | "dmscore": 90, 7 | "effect_id": 397, 8 | "end_time": 1674568760, 9 | "face_effect_id": 44, 10 | "gift_id": 10003, 11 | "guard_level": 3, 12 | "is_show": 0, 13 | "num": 1, 14 | "op_type": 3, 15 | "payflow_id": "2301242159108192199408068", 16 | "price": 138000, 17 | "role_name": "舰长", 18 | "room_effect_id": 590, 19 | "start_time": 1674568760, 20 | "svga_block": 0, 21 | "target_guard_count": 2073, 22 | "toast_msg": "<%__MOCK_UNAME__%> 开通了舰长,今天是TA陪伴主播的第91天", 23 | "uid": 77777777771, 24 | "unit": "月", 25 | "user_show": true, 26 | "username": "__MOCK_UNAME__" 27 | } 28 | } -------------------------------------------------------------------------------- /mock/WARNING/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "WARNING", 3 | "msg": "图片内容不适宜,请立即调整", 4 | "roomid": 77777777774 5 | } -------------------------------------------------------------------------------- /mock/WATCHED_CHANGE/100.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "WATCHED_CHANGE", 3 | "data": { 4 | "num": 173, 5 | "text_small": "173", 6 | "text_large": "173人看过" 7 | } 8 | } -------------------------------------------------------------------------------- /mock/WATCHED_CHANGE/120k.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "WATCHED_CHANGE", 3 | "data": { 4 | "num": 120457, 5 | "text_small": "12.0万", 6 | "text_large": "12.0万人看过" 7 | } 8 | } -------------------------------------------------------------------------------- /mock/WATCHED_CHANGE/1k.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "WATCHED_CHANGE", 3 | "data": { 4 | "num": 1127, 5 | "text_small": "1127", 6 | "text_large": "1127人看过" 7 | } 8 | } -------------------------------------------------------------------------------- /mock/WATCHED_CHANGE/24m.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "WATCHED_CHANGE", 3 | "data": { 4 | "num": 23970210, 5 | "text_small": "2397.0万", 6 | "text_large": "2397.0万人看过" 7 | } 8 | } -------------------------------------------------------------------------------- /mock/WATCHED_CHANGE/90k.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "WATCHED_CHANGE", 3 | "data": { 4 | "num": 95785, 5 | "text_small": "9.5万", 6 | "text_large": "9.5万人看过" 7 | } 8 | } -------------------------------------------------------------------------------- /mock/room_admin_entrance/normal.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmd": "room_admin_entrance", 3 | "dmscore": 45, 4 | "level": 1, 5 | "msg": "系统提示:你已被主播设为房管", 6 | "uid": 77777777771 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blive-message-listener", 3 | "version": "0.5.0", 4 | "description": "Bilibili-live danmu listener with type", 5 | "type": "module", 6 | "main": "dist/index.js", 7 | "module": "dist/index.js", 8 | "types": "dist/index.d.ts", 9 | "homepage": "https://github.com/ddiu8081/blive-message-listener#readme", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/ddiu8081/blive-message-listener" 13 | }, 14 | "author": "ddiu8081 ", 15 | "license": "MIT", 16 | "scripts": { 17 | "dev": "tsup --watch", 18 | "build": "tsup" 19 | }, 20 | "exports": { 21 | ".": { 22 | "types": "./dist/index.d.ts", 23 | "require": "./dist/index.cjs", 24 | "import": "./dist/index.js" 25 | }, 26 | "./browser": { 27 | "types": "./dist/browser.d.ts", 28 | "require": "./dist/browser.cjs", 29 | "import": "./dist/browser.js" 30 | } 31 | }, 32 | "files": [ 33 | "dist", 34 | "browser.d.ts" 35 | ], 36 | "keywords": [ 37 | "bilibili", 38 | "bilibili-live", 39 | "danmaku", 40 | "danmu" 41 | ], 42 | "sideEffects": false, 43 | "dependencies": { 44 | "tiny-bilibili-ws": "^1.0.1" 45 | }, 46 | "devDependencies": { 47 | "tsup": "8.2.4", 48 | "typescript": "5.5.4" 49 | }, 50 | "packageManager": "pnpm@9.9.0" 51 | } 52 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - example -------------------------------------------------------------------------------- /src/browser.ts: -------------------------------------------------------------------------------- 1 | import { KeepLiveWS } from 'tiny-bilibili-ws/browser' 2 | import { listenAll, type MsgHandler } from './listener' 3 | import type { MessageListener } from '.' 4 | import type { BrowserWSOptions } from 'tiny-bilibili-ws/browser' 5 | 6 | interface MessageListenerWSOptions { 7 | /** 8 | * tiny-bilibili-ws 连接选项 9 | * 10 | * @see https://github.com/starknt/tiny-bilibili-ws 11 | */ 12 | ws?: BrowserWSOptions 13 | } 14 | 15 | export const startListen = (roomId: number, handler: MsgHandler, options?: MessageListenerWSOptions) => { 16 | const live = new KeepLiveWS(roomId, options?.ws!) 17 | 18 | listenAll(live, roomId, handler) 19 | 20 | const listenerInstance: MessageListener = { 21 | live: live, 22 | roomId: live.roomId, 23 | online: live.online, 24 | closed: live.closed, 25 | close: () => live.close(), 26 | getAttention: () => live.getOnline(), 27 | getOnline: () => live.getOnline(), 28 | reconnect: () => live.reconnect(), 29 | heartbeat: () => live.heartbeat(), 30 | send: (op, data) => live.send(op, data), 31 | } 32 | 33 | return listenerInstance 34 | } 35 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { KeepLiveTCP } from 'tiny-bilibili-ws' 2 | import { listenAll, type MsgHandler } from './listener' 3 | import type { TCPOptions, WS_OP } from 'tiny-bilibili-ws' 4 | import type { KeepLiveWS } from 'tiny-bilibili-ws/browser' 5 | 6 | export interface MessageListener { 7 | /** tiny-bilibili-ws 实例 */ 8 | live: KeepLiveTCP | KeepLiveWS 9 | /** 直播间房间号 */ 10 | roomId: number 11 | /** 人气值 */ 12 | online: number 13 | /** 是否关闭 */ 14 | closed: boolean 15 | /** 关闭连接 */ 16 | close: () => void 17 | /** 刷新当前直播间热度 */ 18 | getAttention: () => Promise 19 | /** 刷新当前直播间热度 */ 20 | getOnline: () => Promise 21 | /** 重新连接 */ 22 | reconnect: () => void 23 | /** 发送心跳 */ 24 | heartbeat: () => void 25 | /** 发送消息 */ 26 | send: (op: WS_OP, data?: Record | string) => void 27 | } 28 | 29 | interface MessageListenerTCPOptions { 30 | /** 31 | * tiny-bilibili-ws 连接选项 32 | * 33 | * @see https://github.com/starknt/tiny-bilibili-ws 34 | */ 35 | ws?: TCPOptions 36 | } 37 | 38 | export const startListen = (roomId: number, handler: MsgHandler, options?: MessageListenerTCPOptions) => { 39 | const live = new KeepLiveTCP(roomId, options?.ws) 40 | 41 | listenAll(live, roomId, handler) 42 | 43 | const listenerInstance: MessageListener = { 44 | live: live, 45 | roomId: live.roomId, 46 | online: live.online, 47 | closed: live.closed, 48 | close: () => live.close(), 49 | getAttention: () => live.getOnline(), 50 | getOnline: () => live.getOnline(), 51 | reconnect: () => live.reconnect(), 52 | heartbeat: () => live.heartbeat(), 53 | send: (op, data) => live.send(op, data), 54 | } 55 | 56 | return listenerInstance 57 | } 58 | 59 | export type { MsgHandler } 60 | export type { Message, User } from './types/app' 61 | export { GuardLevel } from './types/const' 62 | export * from './types/message' 63 | -------------------------------------------------------------------------------- /src/listener/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | HEARTBEAT, type AttentionChangeMsgHandler, 3 | LIVE, type LiveStartMsgHandler, 4 | PREPARING, type LiveStopMsgHandler, 5 | ANCHOR_LOT_AWARD, type AnchorLotteryEndMsgHandler, 6 | ANCHOR_LOT_START, type AnchorLotteryStartMsgHandler, 7 | DANMU_MSG, type DanmuMsgHandler, 8 | GUARD_BUY, type GuardBuyHandler, 9 | INTERACT_WORD, ENTRY_EFFECT, LIKE_INFO_V3_CLICK, type UserActionMsgHandler, 10 | LIKE_INFO_V3_UPDATE, type LikedChangeMsgHandler, 11 | ONLINE_RANK_COUNT, type RankCountChangeMsgHandler, 12 | POPULARITY_RED_POCKET_START, type RedPocketStartMsgHandler, 13 | POPULARITY_RED_POCKET_WINNER_LIST, type RedPocketEndMsgHandler, 14 | room_admin_entrance, ROOM_ADMIN_REVOKE, type RoomAdminSetMsgHandler, 15 | ROOM_CHANGE, type RoomInfoChangeHandler, 16 | ROOM_SILENT_ON, ROOM_SILENT_OFF, type RoomSilentMsgHandler, 17 | SEND_GIFT, type GiftHandler, 18 | SUPER_CHAT_MESSAGE, type SuperChatHandler, 19 | WARNING, CUT_OFF, type RoomWarnHandler, 20 | WATCHED_CHANGE, type WatchedChangeHandler, 21 | } from '../parser' 22 | import type { KeepLiveTCP, KeepLiveWS, Message as WSMessage } from 'tiny-bilibili-ws' 23 | import type { KeepLiveWS as KeepLiveWSB } from 'tiny-bilibili-ws/browser' 24 | import { normalizeDanmu } from '../utils/message' 25 | 26 | export type MsgHandler = Partial< 27 | { 28 | /** 连接成功 */ 29 | onOpen: () => void, 30 | /** 连接关闭 */ 31 | onClose: () => void, 32 | /** 连接错误 */ 33 | onError: (e: Error) => void, 34 | /** 开始监听消息 */ 35 | onStartListen: () => void, 36 | } 37 | & AttentionChangeMsgHandler 38 | & LiveStartMsgHandler 39 | & LiveStopMsgHandler 40 | & AnchorLotteryEndMsgHandler 41 | & AnchorLotteryStartMsgHandler 42 | & DanmuMsgHandler 43 | & GuardBuyHandler 44 | & UserActionMsgHandler 45 | & LikedChangeMsgHandler 46 | & RankCountChangeMsgHandler 47 | & RedPocketStartMsgHandler 48 | & RedPocketEndMsgHandler 49 | & RoomAdminSetMsgHandler 50 | & RoomInfoChangeHandler 51 | & RoomSilentMsgHandler 52 | & GiftHandler 53 | & SuperChatHandler 54 | & RoomWarnHandler 55 | & WatchedChangeHandler 56 | & { 57 | /** 原始消息 */ 58 | raw: Record<'msg' | string, (msg: any) => void> 59 | } 60 | > 61 | 62 | export const listenAll = (instance: KeepLiveTCP | KeepLiveWS | KeepLiveWSB, roomId: number, handler?: MsgHandler) => { 63 | if (!handler) return 64 | 65 | // Raw message handler 66 | const rawHandler = handler.raw || {} 67 | const rawHandlerNames = new Set(Object.keys(rawHandler)) 68 | const isHandleRaw = rawHandlerNames.size > 0 69 | 70 | // Common 71 | if (handler.onOpen) { 72 | instance.on('open', () => { 73 | handler.onOpen?.() 74 | }) 75 | } 76 | if (handler.onClose) { 77 | instance.on('close', () => { 78 | handler.onClose?.() 79 | }) 80 | } 81 | if (handler.onStartListen) { 82 | instance.on('live', () => { 83 | handler.onStartListen?.() 84 | }) 85 | } 86 | 87 | // HEARTBEAT 88 | if (handler[HEARTBEAT.handlerName] || rawHandlerNames.has(HEARTBEAT.eventName)) { 89 | rawHandlerNames.delete(HEARTBEAT.eventName) 90 | instance.on(HEARTBEAT.eventName, (data) => { 91 | isHandleRaw && rawHandler[HEARTBEAT.eventName]?.(data) 92 | const parsedData = HEARTBEAT.parser(data) 93 | handler[HEARTBEAT.handlerName]?.(normalizeDanmu(HEARTBEAT.eventName, parsedData, data)) 94 | }) 95 | } 96 | 97 | // LIVE 98 | if (handler[LIVE.handlerName] || rawHandlerNames.has(LIVE.eventName)) { 99 | rawHandlerNames.delete(LIVE.eventName) 100 | instance.on(LIVE.eventName, (data) => { 101 | isHandleRaw && rawHandler[LIVE.eventName]?.(data.data) 102 | const parsedData = LIVE.parser(data.data) 103 | handler[LIVE.handlerName]?.(normalizeDanmu(LIVE.eventName, parsedData, data.data)) 104 | }) 105 | } 106 | 107 | // PREPARING 108 | if (handler[PREPARING.handlerName] || rawHandlerNames.has(PREPARING.eventName)) { 109 | rawHandlerNames.delete(LIVE.eventName) 110 | instance.on(PREPARING.eventName, (data) => { 111 | isHandleRaw && rawHandler[PREPARING.eventName]?.(data.data) 112 | const parsedData = PREPARING.parser(data.data) 113 | handler[PREPARING.handlerName]?.(normalizeDanmu(PREPARING.eventName, parsedData, data.data)) 114 | }) 115 | } 116 | 117 | // ANCHOR_LOT_AWARD 118 | if (handler[ANCHOR_LOT_AWARD.handlerName] || rawHandlerNames.has(ANCHOR_LOT_AWARD.eventName)) { 119 | rawHandlerNames.delete(ANCHOR_LOT_AWARD.eventName) 120 | instance.on(ANCHOR_LOT_AWARD.eventName, (data) => { 121 | isHandleRaw && rawHandler[ANCHOR_LOT_AWARD.eventName]?.(data.data) 122 | const parsedData = ANCHOR_LOT_AWARD.parser(data.data, roomId) 123 | handler[ANCHOR_LOT_AWARD.handlerName]?.(normalizeDanmu(ANCHOR_LOT_AWARD.eventName, parsedData, data.data)) 124 | }) 125 | } 126 | 127 | // ANCHOR_LOT_START 128 | if (handler[ANCHOR_LOT_START.handlerName] || rawHandlerNames.has(ANCHOR_LOT_START.eventName)) { 129 | rawHandlerNames.delete(ANCHOR_LOT_START.eventName) 130 | instance.on(ANCHOR_LOT_START.eventName, (data) => { 131 | isHandleRaw && rawHandler[ANCHOR_LOT_START.eventName]?.(data.data) 132 | const parsedData = ANCHOR_LOT_START.parser(data.data, roomId) 133 | handler[ANCHOR_LOT_START.handlerName]?.(normalizeDanmu(ANCHOR_LOT_START.eventName, parsedData, data.data)) 134 | }) 135 | } 136 | 137 | // DANMU_MSG 138 | if (handler[DANMU_MSG.handlerName] || rawHandlerNames.has(DANMU_MSG.eventName)) { 139 | rawHandlerNames.delete(DANMU_MSG.eventName) 140 | instance.on(DANMU_MSG.eventName, (data) => { 141 | isHandleRaw && rawHandler[DANMU_MSG.eventName]?.(data.data) 142 | const parsedData = DANMU_MSG.parser(data.data, roomId) 143 | handler[DANMU_MSG.handlerName]?.(normalizeDanmu(DANMU_MSG.eventName, parsedData, data.data)) 144 | }) 145 | } 146 | 147 | // GUARD_BUY 148 | if (handler[GUARD_BUY.handlerName] || rawHandlerNames.has(GUARD_BUY.eventName)) { 149 | rawHandlerNames.delete(GUARD_BUY.eventName) 150 | instance.on(GUARD_BUY.eventName, (data) => { 151 | isHandleRaw && rawHandler[GUARD_BUY.eventName]?.(data.data) 152 | const parsedData = GUARD_BUY.parser(data.data) 153 | handler[GUARD_BUY.handlerName]?.(normalizeDanmu(GUARD_BUY.eventName, parsedData, data.data)) 154 | }) 155 | } 156 | 157 | // INTERACT_WORD, ENTRY_EFFECT, LIKE_INFO_V3_CLICK 158 | if (handler[INTERACT_WORD.handlerName] || handler[ENTRY_EFFECT.handlerName] || handler[LIKE_INFO_V3_CLICK.handlerName] || 159 | rawHandlerNames.has(INTERACT_WORD.eventName) || rawHandlerNames.has(ENTRY_EFFECT.eventName) || rawHandlerNames.has(LIKE_INFO_V3_CLICK.eventName)) { 160 | rawHandlerNames.delete(INTERACT_WORD.eventName) 161 | rawHandlerNames.delete(ENTRY_EFFECT.eventName) 162 | rawHandlerNames.delete(LIKE_INFO_V3_CLICK.eventName) 163 | instance.on(INTERACT_WORD.eventName, (data: WSMessage) => { 164 | isHandleRaw && rawHandler[INTERACT_WORD.eventName]?.(data.data) 165 | const parsedData = INTERACT_WORD.parser(data.data, roomId) 166 | handler[INTERACT_WORD.handlerName]?.(normalizeDanmu(INTERACT_WORD.eventName, parsedData, data.data)) 167 | }) 168 | instance.on(ENTRY_EFFECT.eventName, (data: WSMessage) => { 169 | isHandleRaw && rawHandler[ENTRY_EFFECT.eventName]?.(data.data) 170 | const parsedData = ENTRY_EFFECT.parser(data.data, roomId) 171 | handler[ENTRY_EFFECT.handlerName]?.(normalizeDanmu(ENTRY_EFFECT.eventName, parsedData, data.data)) 172 | }) 173 | instance.on(LIKE_INFO_V3_CLICK.eventName, (data: WSMessage) => { 174 | isHandleRaw && rawHandler[LIKE_INFO_V3_CLICK.eventName]?.(data.data) 175 | const parsedData = LIKE_INFO_V3_CLICK.parser(data.data, roomId) 176 | handler[LIKE_INFO_V3_CLICK.handlerName]?.(normalizeDanmu(LIKE_INFO_V3_CLICK.eventName, parsedData, data.data)) 177 | }) 178 | } 179 | 180 | // LIKE_INFO_V3_UPDATE 181 | if (handler[LIKE_INFO_V3_UPDATE.handlerName] || rawHandlerNames.has(LIKE_INFO_V3_UPDATE.eventName)) { 182 | rawHandlerNames.delete(LIKE_INFO_V3_UPDATE.eventName) 183 | instance.on(LIKE_INFO_V3_UPDATE.eventName, (data) => { 184 | isHandleRaw && rawHandler[LIKE_INFO_V3_UPDATE.eventName]?.(data.data) 185 | const parsedData = LIKE_INFO_V3_UPDATE.parser(data.data) 186 | handler[LIKE_INFO_V3_UPDATE.handlerName]?.(normalizeDanmu(LIKE_INFO_V3_UPDATE.eventName, parsedData, data.data)) 187 | }) 188 | } 189 | 190 | // ONLINE_RANK_COUNT 191 | if (handler[ONLINE_RANK_COUNT.handlerName] || rawHandlerNames.has(ONLINE_RANK_COUNT.eventName)) { 192 | rawHandlerNames.delete(ONLINE_RANK_COUNT.eventName) 193 | instance.on(ONLINE_RANK_COUNT.eventName, (data) => { 194 | isHandleRaw && rawHandler[ONLINE_RANK_COUNT.eventName]?.(data.data) 195 | const parsedData = ONLINE_RANK_COUNT.parser(data.data) 196 | handler[ONLINE_RANK_COUNT.handlerName]?.(normalizeDanmu(ONLINE_RANK_COUNT.eventName, parsedData, data.data)) 197 | }) 198 | } 199 | 200 | // POPULARITY_RED_POCKET_START 201 | if (handler[POPULARITY_RED_POCKET_START.handlerName] || rawHandlerNames.has(POPULARITY_RED_POCKET_START.eventName)) { 202 | rawHandlerNames.delete(POPULARITY_RED_POCKET_START.eventName) 203 | instance.on(POPULARITY_RED_POCKET_START.eventName, (data) => { 204 | isHandleRaw && rawHandler[POPULARITY_RED_POCKET_START.eventName]?.(data.data) 205 | const parsedData = POPULARITY_RED_POCKET_START.parser(data.data, roomId) 206 | handler[POPULARITY_RED_POCKET_START.handlerName]?.(normalizeDanmu(POPULARITY_RED_POCKET_START.eventName, parsedData, data.data)) 207 | }) 208 | } 209 | 210 | // POPULARITY_RED_POCKET_WINNER_LIST 211 | if (handler[POPULARITY_RED_POCKET_WINNER_LIST.handlerName] || rawHandlerNames.has(POPULARITY_RED_POCKET_WINNER_LIST.eventName)) { 212 | rawHandlerNames.delete(POPULARITY_RED_POCKET_WINNER_LIST.eventName) 213 | instance.on(POPULARITY_RED_POCKET_WINNER_LIST.eventName, (data) => { 214 | isHandleRaw && rawHandler[POPULARITY_RED_POCKET_WINNER_LIST.eventName]?.(data.data) 215 | const parsedData = POPULARITY_RED_POCKET_WINNER_LIST.parser(data.data, roomId) 216 | handler[POPULARITY_RED_POCKET_WINNER_LIST.handlerName]?.(normalizeDanmu(POPULARITY_RED_POCKET_WINNER_LIST.eventName, parsedData, data.data)) 217 | }) 218 | } 219 | 220 | // room_admin_entrance, ROOM_ADMIN_REVOKE 221 | if (handler[room_admin_entrance.handlerName] || handler[ROOM_ADMIN_REVOKE.handlerName] || rawHandlerNames.has(room_admin_entrance.eventName) || rawHandlerNames.has(ROOM_SILENT_OFF.eventName)) { 222 | rawHandlerNames.delete(room_admin_entrance.eventName) 223 | rawHandlerNames.delete(ROOM_ADMIN_REVOKE.eventName) 224 | instance.on(room_admin_entrance.eventName as any, (data: WSMessage) => { 225 | isHandleRaw && rawHandler[room_admin_entrance.eventName]?.(data.data) 226 | const parsedData = room_admin_entrance.parser(data.data, roomId) 227 | handler[room_admin_entrance.handlerName]?.(normalizeDanmu(room_admin_entrance.eventName, parsedData, data.data)) 228 | }) 229 | instance.on(ROOM_ADMIN_REVOKE.eventName as any, (data: WSMessage) => { 230 | isHandleRaw && rawHandler[ROOM_ADMIN_REVOKE.eventName]?.(data.data) 231 | const parsedData = ROOM_ADMIN_REVOKE.parser(data.data, roomId) 232 | handler[ROOM_ADMIN_REVOKE.handlerName]?.(normalizeDanmu(ROOM_ADMIN_REVOKE.eventName, parsedData, data.data)) 233 | }) 234 | } 235 | 236 | // ROOM_CHANGE 237 | if (handler[ROOM_CHANGE.handlerName] || rawHandlerNames.has(ROOM_CHANGE.eventName)) { 238 | rawHandlerNames.delete(ROOM_CHANGE.eventName) 239 | instance.on(ROOM_CHANGE.eventName, (data) => { 240 | isHandleRaw && rawHandler[ROOM_CHANGE.eventName]?.(data.data) 241 | const parsedData = ROOM_CHANGE.parser(data.data) 242 | handler[ROOM_CHANGE.handlerName]?.(normalizeDanmu(ROOM_CHANGE.eventName, parsedData, data.data)) 243 | }) 244 | } 245 | 246 | // ROOM_SILENT_ON, ROOM_SILENT_OFF 247 | if (handler[ROOM_SILENT_ON.handlerName] || handler[ROOM_SILENT_OFF.handlerName] || rawHandlerNames.has(ROOM_SILENT_ON.eventName) || rawHandlerNames.has(ROOM_SILENT_OFF.eventName)) { 248 | rawHandlerNames.delete(ROOM_SILENT_ON.eventName) 249 | rawHandlerNames.delete(ROOM_SILENT_OFF.eventName) 250 | instance.on(ROOM_SILENT_ON.eventName as any, (data: WSMessage) => { 251 | isHandleRaw && rawHandler[ROOM_SILENT_ON.eventName]?.(data.data) 252 | const parsedData = ROOM_SILENT_ON.parser(data.data, roomId) 253 | handler[ROOM_SILENT_ON.handlerName]?.(normalizeDanmu(ROOM_SILENT_ON.eventName, parsedData, data.data)) 254 | }) 255 | instance.on(ROOM_SILENT_OFF.eventName as any, (data: WSMessage) => { 256 | isHandleRaw && rawHandler[ROOM_SILENT_OFF.eventName]?.(data.data) 257 | const parsedData = ROOM_SILENT_OFF.parser(data.data, roomId) 258 | handler[ROOM_SILENT_OFF.handlerName]?.(normalizeDanmu(ROOM_SILENT_OFF.eventName, parsedData, data.data)) 259 | }) 260 | } 261 | 262 | // SEND_GIFT 263 | if (handler[SEND_GIFT.handlerName] || rawHandlerNames.has(SEND_GIFT.eventName)) { 264 | rawHandlerNames.delete(SEND_GIFT.eventName) 265 | instance.on(SEND_GIFT.eventName, (data: WSMessage) => { 266 | isHandleRaw && rawHandler[SEND_GIFT.eventName]?.(data.data) 267 | const parsedData = SEND_GIFT.parser(data.data) 268 | handler[SEND_GIFT.handlerName]?.(normalizeDanmu(SEND_GIFT.eventName, parsedData, data.data)) 269 | }) 270 | } 271 | 272 | // SUPER_CHAT_MESSAGE 273 | if (handler[SUPER_CHAT_MESSAGE.handlerName] || rawHandlerNames.has(SUPER_CHAT_MESSAGE.eventName)) { 274 | rawHandlerNames.delete(SUPER_CHAT_MESSAGE.eventName) 275 | instance.on(SUPER_CHAT_MESSAGE.eventName, (data) => { 276 | isHandleRaw && rawHandler[SUPER_CHAT_MESSAGE.eventName]?.(data.data) 277 | const parsedData = SUPER_CHAT_MESSAGE.parser(data.data, roomId) 278 | handler[SUPER_CHAT_MESSAGE.handlerName]?.(normalizeDanmu(SUPER_CHAT_MESSAGE.eventName, parsedData, data.data)) 279 | }) 280 | } 281 | 282 | // WARNING, CUT_OFF 283 | if (handler[WARNING.handlerName] || handler[CUT_OFF.handlerName] || rawHandlerNames.has(WARNING.eventName) || rawHandlerNames.has(CUT_OFF.eventName)) { 284 | rawHandlerNames.delete(WARNING.eventName) 285 | rawHandlerNames.delete(CUT_OFF.eventName) 286 | instance.on(WARNING.eventName as any, (data: WSMessage) => { 287 | isHandleRaw && rawHandler[WARNING.eventName]?.(data.data) 288 | const parsedData = WARNING.parser(data.data, roomId) 289 | handler[WARNING.handlerName]?.(normalizeDanmu(WARNING.eventName, parsedData, data.data)) 290 | }) 291 | instance.on(CUT_OFF.eventName as any, (data: WSMessage) => { 292 | isHandleRaw && rawHandler[CUT_OFF.eventName]?.(data.data) 293 | const parsedData = CUT_OFF.parser(data.data, roomId) 294 | handler[CUT_OFF.handlerName]?.(normalizeDanmu(CUT_OFF.eventName, parsedData, data.data)) 295 | }) 296 | } 297 | 298 | // WATCHED_CHANGE 299 | if (handler[WATCHED_CHANGE.handlerName] || rawHandlerNames.has(WATCHED_CHANGE.eventName)) { 300 | rawHandlerNames.delete(WATCHED_CHANGE.eventName) 301 | instance.on(WATCHED_CHANGE.eventName, (data) => { 302 | isHandleRaw && rawHandler[WATCHED_CHANGE.eventName]?.(data.data) 303 | const parsedData = WATCHED_CHANGE.parser(data.data) 304 | handler[WATCHED_CHANGE.handlerName]?.(normalizeDanmu(WATCHED_CHANGE.eventName, parsedData, data.data)) 305 | }) 306 | } 307 | 308 | // Rest raw events 309 | for (const eventName of rawHandlerNames) { 310 | instance.on(eventName as any, (data: WSMessage) => { 311 | rawHandler[eventName]?.(data.data) 312 | }) 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/parser/ANCHOR_LOT_AWARD.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { ANCHOR_LOT_AWARD as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface AnchorLotteryEndMsg { 5 | /** 天选抽奖id */ 6 | id: number 7 | /** 天选奖品信息 */ 8 | award: { 9 | /** 奖品图片 */ 10 | image: string 11 | /** 奖品名称 */ 12 | name: string 13 | /** 是否为虚拟礼物奖品 */ 14 | virtual: boolean 15 | } 16 | /** 中奖用户列表 */ 17 | winner: ({ 18 | /** 用户uid */ 19 | uid: number 20 | /** 用户昵称 */ 21 | uname: string 22 | /** 用户头像 */ 23 | face: number 24 | /** 用户粉丝勋章等级 */ 25 | level: number 26 | /** 中奖数量 */ 27 | num: number 28 | })[] 29 | } 30 | 31 | const parser = (data: DataType, roomId: number): AnchorLotteryEndMsg => { 32 | const rawData = data.data 33 | 34 | return { 35 | id: rawData.id, 36 | award: { 37 | image: rawData.award_image, 38 | name: rawData.award_name, 39 | virtual: (rawData as any).award_type === 1, 40 | }, 41 | winner: rawData.award_users.map((user: any) => ({ 42 | uid: user.uid, 43 | uname: user.uname, 44 | face: user.face, 45 | level: user.level, 46 | num: user.num, 47 | })), 48 | } 49 | } 50 | 51 | export const ANCHOR_LOT_AWARD = { 52 | parser, 53 | eventName: 'ANCHOR_LOT_AWARD' as const, 54 | handlerName: 'onAnchorLotteryEnd' as const, 55 | } 56 | 57 | export type Handler = { 58 | /** 主播天选时刻抽奖结果 */ 59 | onAnchorLotteryEnd: (msg: Message) => void 60 | } 61 | -------------------------------------------------------------------------------- /src/parser/ANCHOR_LOT_START.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { ANCHOR_LOT_START as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface AnchorLotteryStartMsg { 5 | /** 天选抽奖id */ 6 | id: number 7 | /** 开始时间,秒级时间戳 */ 8 | start_time: number 9 | /** 持续时间,秒 */ 10 | duration: number 11 | /** 天选奖品信息 */ 12 | award: { 13 | /** 奖品图片 */ 14 | image: string 15 | /** 奖品名称 */ 16 | name: string 17 | /** 奖品数量 */ 18 | num: number 19 | /** 是否为虚拟礼物奖品 */ 20 | virtual: boolean 21 | /** 虚拟奖品价值描述,实物奖品为空 */ 22 | price_text: string 23 | } 24 | /** 抽奖要求 */ 25 | require: { 26 | /** 口令弹幕内容,无需弹幕为空字符串 */ 27 | danmu: string 28 | /** 需送主播礼物,无需送礼为空 */ 29 | gift: { 30 | /** 礼物id */ 31 | id: string 32 | /** 礼物名称 */ 33 | name: string 34 | /** 礼物数量 */ 35 | num: number 36 | /** 单个礼物价值,除以1000为RMB */ 37 | price: number 38 | } | null 39 | /** 抽奖参与人群要求,无要求为空 */ 40 | user: { 41 | /** 参与人群限制(关注/粉丝勋章/大航海) */ 42 | type: 'follow' | 'medal' | 'guard' 43 | /** 参与人群限制等级,如粉丝勋章等级 */ 44 | value: number 45 | /** 参与人群限制描述 */ 46 | text: string 47 | } | null 48 | } 49 | } 50 | 51 | const parser = (data: DataType, roomId: number): AnchorLotteryStartMsg => { 52 | const rawData = data.data 53 | 54 | return { 55 | id: rawData.id, 56 | start_time: rawData.current_time, 57 | duration: rawData.max_time, 58 | award: { 59 | image: rawData.award_image, 60 | name: rawData.award_name, 61 | num: rawData.award_num, 62 | virtual: (rawData as any).award_type === 1, 63 | price_text: (rawData as any).award_price_text || '', 64 | }, 65 | require: { 66 | danmu: rawData.danmu || '', 67 | gift: rawData.gift_id ? { 68 | id: `${rawData.gift_id}`, 69 | name: rawData.gift_name, 70 | num: rawData.gift_num, 71 | price: rawData.gift_price, 72 | } : null, 73 | user: rawData.require_type ? { 74 | type: (['follow', 'medal', 'guard'] as const)[rawData.require_type - 1], 75 | value: rawData.require_value, 76 | text: rawData.require_text, 77 | } : null, 78 | }, 79 | } 80 | } 81 | 82 | export const ANCHOR_LOT_START = { 83 | parser, 84 | eventName: 'ANCHOR_LOT_START' as const, 85 | handlerName: 'onAnchorLotteryStart' as const, 86 | } 87 | 88 | export type Handler = { 89 | /** 主播天选时刻抽奖开启 */ 90 | onAnchorLotteryStart: (msg: Message) => void 91 | } 92 | -------------------------------------------------------------------------------- /src/parser/DANMU_MSG.ts: -------------------------------------------------------------------------------- 1 | import { intToColorHex } from '../utils/color' 2 | import type { Message, User } from '../types/app' 3 | import type { DANMU_MSG as DataType } from 'tiny-bilibili-ws' 4 | 5 | export interface DanmuMsg { 6 | user: User 7 | /** 弹幕内容 */ 8 | content: string 9 | /** 弹幕类型:1 2 3:普通弹幕;4:底部弹幕;5:顶部弹幕 */ 10 | type: number 11 | /** 弹幕颜色 */ 12 | content_color: string 13 | /** 发送时间,毫秒时间戳 */ 14 | timestamp: number 15 | /** 是否为天选抽奖弹幕 */ 16 | lottery: boolean 17 | /** 表情弹幕内容 */ 18 | emoticon?: { 19 | id: string 20 | height: number 21 | width: number 22 | url: string 23 | } 24 | /** 弹幕内小表情映射,key为表情文字,如"[妙]" */ 25 | in_message_emoticon?: Record 33 | } 34 | 35 | const parser = (data: DataType, roomId: number): DanmuMsg => { 36 | const rawData = data.info 37 | const content = rawData[1] 38 | const shouldParseInMessageEmoticon = /\[.*?\]/.test(content) 39 | let inMessageEmoticon 40 | if (shouldParseInMessageEmoticon) { 41 | const messageExtraInfo = JSON.parse(rawData[0][15].extra) 42 | const emoctionDict: Record = {} 50 | if (messageExtraInfo.emots) { 51 | inMessageEmoticon = Object.keys(messageExtraInfo.emots).reduce((acc, key) => { 52 | const emoticon = messageExtraInfo.emots[key] 53 | acc[key] = { 54 | id: emoticon.emoticon_unique, 55 | emoticon_id: emoticon.emoticon_id, 56 | height: emoticon.height, 57 | width: emoticon.width, 58 | url: emoticon.url, 59 | description: emoticon.descript, 60 | } 61 | return acc 62 | }, emoctionDict) 63 | } 64 | } 65 | return { 66 | user: { 67 | uid: rawData[2][0], 68 | uname: rawData[2][1], 69 | badge: rawData[3].length ? { 70 | active: rawData[3][7] !== 12632256, 71 | name: rawData[3][1], 72 | level: rawData[3][0], 73 | color: intToColorHex(rawData[3][4]), 74 | gradient: [ 75 | intToColorHex(rawData[3][7]), 76 | intToColorHex(rawData[3][8]), 77 | intToColorHex(rawData[3][9]), 78 | ], 79 | anchor: { 80 | uid: rawData[3][12], 81 | uname: rawData[3][2], 82 | room_id: rawData[3][3], 83 | is_same_room: rawData[3][3] === roomId, 84 | }, 85 | } : undefined, 86 | identity: { 87 | rank: rawData[4][4] as any, 88 | guard_level: rawData[7], 89 | room_admin: rawData[2][2] === 1, 90 | }, 91 | }, 92 | content, 93 | type: rawData[0][1], 94 | content_color: intToColorHex(rawData[0][3]), 95 | timestamp: rawData[0][4], 96 | lottery: rawData[0][9] !== 0, 97 | emoticon: (rawData[0][13] as any)?.emoticon_unique ? { 98 | id: (rawData[0][13] as any).emoticon_unique, 99 | height: (rawData[0][13] as any).height, 100 | width: (rawData[0][13] as any).width, 101 | url: (rawData[0][13] as any).url, 102 | } : undefined, 103 | in_message_emoticon: inMessageEmoticon, 104 | } 105 | } 106 | 107 | export const DANMU_MSG = { 108 | parser, 109 | eventName: 'DANMU_MSG' as const, 110 | handlerName: 'onIncomeDanmu' as const, 111 | } 112 | 113 | export type Handler = { 114 | /** 收到普通弹幕消息 */ 115 | onIncomeDanmu: (msg: Message) => void 116 | } 117 | -------------------------------------------------------------------------------- /src/parser/GUARD_BUY.ts: -------------------------------------------------------------------------------- 1 | import type { Message, User } from '../types/app' 2 | import type { GuardLevel } from '../types/const' 3 | import type { GUARD_BUY as DataType } from 'tiny-bilibili-ws' 4 | 5 | export interface GuardBuyMsg { 6 | user: User 7 | /** 礼物id */ 8 | gift_id: number 9 | /** 礼物名称 */ 10 | gift_name: string 11 | /** 大航海信息 */ 12 | guard_level: GuardLevel 13 | /** 价格,RMB */ 14 | price: number 15 | /** 等级生效时间 */ 16 | start_time: number 17 | /** 等级过期时间 */ 18 | end_time: number 19 | } 20 | 21 | const parser = (data: DataType): GuardBuyMsg => { 22 | const rawData = data.data 23 | return { 24 | user: { 25 | uid: rawData.uid, 26 | uname: rawData.username, 27 | }, 28 | gift_id: rawData.gift_id, 29 | gift_name: rawData.gift_name, 30 | guard_level: rawData.guard_level, 31 | price: rawData.price, 32 | start_time: rawData.start_time, 33 | end_time: rawData.end_time, 34 | } 35 | } 36 | 37 | export const GUARD_BUY = { 38 | parser, 39 | eventName: 'GUARD_BUY' as const, 40 | handlerName: 'onGuardBuy' as const, 41 | } 42 | 43 | export type Handler = { 44 | /** 舰长上舰消息 */ 45 | onGuardBuy: (msg: Message) => void 46 | } 47 | -------------------------------------------------------------------------------- /src/parser/HEARTBEAT.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | 3 | export interface AttentionChangeMsg { 4 | /** 直播间热度 */ 5 | attention: number 6 | } 7 | 8 | const parser = (data: number): AttentionChangeMsg => { 9 | return { 10 | attention: data, 11 | } 12 | } 13 | 14 | export const HEARTBEAT = { 15 | parser, 16 | eventName: 'heartbeat' as const, 17 | handlerName: 'onAttentionChange' as const, 18 | } 19 | 20 | export type Handler = { 21 | /** 直播间热度变化 */ 22 | onAttentionChange: (msg: Message) => void 23 | } 24 | -------------------------------------------------------------------------------- /src/parser/INTERACT_WORD_ENTRY_EFFECT.ts: -------------------------------------------------------------------------------- 1 | import { intToColorHex } from '../utils/color' 2 | import type { Message, User } from '../types/app' 3 | import type { INTERACT_WORD as DataType } from 'tiny-bilibili-ws' 4 | 5 | type UserAction = 'enter' | 'follow' | 'share' | 'like' | 'unknown' 6 | 7 | export interface UserActionMsg { 8 | user: User 9 | /** 事件类型 */ 10 | action: UserAction 11 | /** 事件时间,毫秒时间戳 */ 12 | timestamp: number 13 | } 14 | 15 | const parserNormal = (data: DataType & { data: { face?: string } }, roomId: number): UserActionMsg => { 16 | const rawData = data.data 17 | let actionType: UserAction = 'unknown' 18 | if (rawData.msg_type === 1) { 19 | actionType = 'enter' 20 | } else if (rawData.msg_type === 2) { 21 | actionType = 'follow' 22 | } else if (rawData.msg_type === 3) { 23 | actionType = 'share' 24 | } 25 | return { 26 | user: { 27 | uid: rawData.uid, 28 | uname: rawData.uname, 29 | face: rawData?.face, 30 | badge: rawData.fans_medal?.target_id ? { 31 | active: !!rawData.fans_medal?.is_lighted, 32 | name: rawData.fans_medal?.medal_name, 33 | level: rawData.fans_medal?.medal_level, 34 | color: intToColorHex(rawData.fans_medal?.medal_color), 35 | gradient: [ 36 | intToColorHex(rawData.fans_medal?.medal_color_start), 37 | intToColorHex(rawData.fans_medal?.medal_color_start), 38 | intToColorHex(rawData.fans_medal?.medal_color_end), 39 | ], 40 | anchor: { 41 | uid: rawData.fans_medal?.target_id, 42 | uname: '', 43 | room_id: rawData.fans_medal?.anchor_roomid, 44 | is_same_room: rawData.fans_medal?.anchor_roomid === roomId, 45 | } 46 | } : undefined, 47 | identity: { 48 | rank: 0, 49 | guard_level: rawData.privilege_type, 50 | room_admin: false, 51 | } 52 | }, 53 | action: actionType, 54 | timestamp: Math.ceil(rawData.trigger_time / 1000000), 55 | } 56 | } 57 | 58 | const parserGuard = (data: any, roomId: number): UserActionMsg => { 59 | const rawData = data.data 60 | const uname = /<%(.*)%>/.exec(rawData.copy_writing)?.[1] || '' 61 | return { 62 | user: { 63 | uid: rawData.uid, 64 | uname: uname, // 超长会有省略号 65 | identity: { 66 | rank: 0, 67 | guard_level: rawData.privilege_type, 68 | room_admin: false, 69 | } 70 | }, 71 | action: 'enter', 72 | timestamp: Math.ceil(rawData.trigger_time / 1000000), 73 | } 74 | } 75 | 76 | const parserLike = (data: any, roomId: number): UserActionMsg => { 77 | const rawData = data.data 78 | return { 79 | user: { 80 | uid: rawData.uid, 81 | uname: rawData.uname, 82 | badge: rawData.fans_medal?.target_id ? { 83 | active: rawData.fans_medal?.is_lighted, 84 | name: rawData.fans_medal?.medal_name, 85 | level: rawData.fans_medal?.medal_level, 86 | color: intToColorHex(rawData.fans_medal?.medal_color), 87 | gradient: [ 88 | intToColorHex(rawData.fans_medal?.medal_color_start), 89 | intToColorHex(rawData.fans_medal?.medal_color_start), 90 | intToColorHex(rawData.fans_medal?.medal_color_end), 91 | ], 92 | anchor: { 93 | uid: rawData.fans_medal?.target_id, 94 | uname: '', 95 | room_id: rawData.fans_medal?.anchor_roomid, // 返回为 0 96 | is_same_room: rawData.fans_medal?.anchor_roomid === roomId, 97 | } 98 | } : undefined, 99 | }, 100 | action: 'like', 101 | timestamp: Date.now(), 102 | } 103 | } 104 | 105 | const parser = (data: any, roomId: number): UserActionMsg => { 106 | const msgType = data.cmd 107 | if (msgType === 'ENTRY_EFFECT') { 108 | return parserGuard(data, roomId) 109 | } 110 | if (msgType === 'LIKE_INFO_V3_CLICK') { 111 | return parserLike(data, roomId) 112 | } 113 | // INTERACT_WORD 114 | return parserNormal(data, roomId) 115 | } 116 | 117 | export const INTERACT_WORD = { 118 | parser, 119 | eventName: 'INTERACT_WORD' as const, 120 | handlerName: 'onUserAction' as const, 121 | } 122 | 123 | export const ENTRY_EFFECT = { 124 | parser, 125 | eventName: 'ENTRY_EFFECT' as const, 126 | handlerName: 'onUserAction' as const, 127 | } 128 | 129 | export const LIKE_INFO_V3_CLICK = { 130 | parser, 131 | eventName: 'LIKE_INFO_V3_CLICK' as const, 132 | handlerName: 'onUserAction' as const, 133 | } 134 | 135 | export type Handler = { 136 | /** 用户进入、关注、分享、点赞直播间 */ 137 | onUserAction: (msg: Message) => void 138 | } 139 | -------------------------------------------------------------------------------- /src/parser/LIKE_INFO_V3_UPDATE.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { LIKE_INFO_V3_UPDATE as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface LikedChangeMsg { 5 | /** 直播间点赞人数 */ 6 | count: number 7 | } 8 | 9 | const parser = (data: DataType): LikedChangeMsg => { 10 | const rawData = data.data 11 | return { 12 | count: rawData.click_count, 13 | } 14 | } 15 | 16 | export const LIKE_INFO_V3_UPDATE = { 17 | parser, 18 | eventName: 'LIKE_INFO_V3_UPDATE' as const, 19 | handlerName: 'onLikedChange' as const, 20 | } 21 | 22 | export type Handler = { 23 | /** 累计点赞人数变化 */ 24 | onLikedChange: (msg: Message) => void 25 | } 26 | -------------------------------------------------------------------------------- /src/parser/LIVE.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { LIVE as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface LiveStartMsg { 5 | /** 开播平台 */ 6 | live_platform: string 7 | /** 房间号 */ 8 | room_id: number 9 | } 10 | 11 | const parser = (data: DataType): LiveStartMsg => { 12 | return { 13 | live_platform: data.live_platform, 14 | room_id: data.roomid, 15 | } 16 | } 17 | 18 | export const LIVE = { 19 | parser, 20 | eventName: 'LIVE' as const, 21 | handlerName: 'onLiveStart' as const, 22 | } 23 | 24 | export type Handler = { 25 | /** 直播开始消息 */ 26 | onLiveStart: (msg: Message) => void 27 | } 28 | -------------------------------------------------------------------------------- /src/parser/ONLINE_RANK_COUNT.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { ONLINE_RANK_COUNT as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface RankCountChangeMsg { 5 | /** 高能用户人数 */ 6 | count: number 7 | } 8 | 9 | const parser = (data: DataType): RankCountChangeMsg => { 10 | const rawData = data.data 11 | return { 12 | count: rawData.count, 13 | } 14 | } 15 | 16 | export const ONLINE_RANK_COUNT = { 17 | parser, 18 | eventName: 'ONLINE_RANK_COUNT' as const, 19 | handlerName: 'onRankCountChange' as const, 20 | } 21 | 22 | export type Handler = { 23 | /** 高能用户人数变化 */ 24 | onRankCountChange: (msg: Message) => void 25 | } 26 | -------------------------------------------------------------------------------- /src/parser/POPULARITY_RED_POCKET_START.ts: -------------------------------------------------------------------------------- 1 | import type { Message, User } from '../types/app' 2 | import type { POPULARITY_RED_POCKET_START as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface RedPocketStartMsg { 5 | /** 红包抽奖id */ 6 | id: number 7 | /** 红包发送用户 */ 8 | user: User 9 | /** 开始时间,秒级时间戳 */ 10 | start_time: number 11 | /** 结束时间,秒级时间戳 */ 12 | end_time: number 13 | /** 持续时间,秒 */ 14 | duration: number 15 | /** 口令弹幕内容 */ 16 | danmu: string 17 | /** 红包奖品 */ 18 | awards: RedPocketStartAward[] 19 | /** 奖品总价值,除以1000为RMB */ 20 | total_price: number 21 | /** 剩余等待的红包数 */ 22 | wait_num: number 23 | } 24 | 25 | interface RedPocketStartAward { 26 | /** 奖品id */ 27 | gift_id: number 28 | /** 奖品名称 */ 29 | gift_name: string 30 | /** 奖品图片 */ 31 | gift_pic: string 32 | /** 奖品数量 */ 33 | num: number 34 | } 35 | 36 | const parser = (data: DataType, roomId: number): RedPocketStartMsg => { 37 | const rawData = data.data 38 | 39 | return { 40 | id: rawData.lot_id, 41 | user: { 42 | uid: rawData.sender_uid, 43 | uname: rawData.sender_name, 44 | face: rawData.sender_face, 45 | }, 46 | start_time: rawData.start_time, 47 | end_time: rawData.end_time, 48 | duration: rawData.last_time, 49 | danmu: rawData.danmu, 50 | awards: rawData.awards, 51 | total_price: rawData.total_price, 52 | wait_num: rawData.wait_num, 53 | } 54 | } 55 | 56 | export const POPULARITY_RED_POCKET_START = { 57 | parser, 58 | eventName: 'POPULARITY_RED_POCKET_START' as const, 59 | handlerName: 'onRedPocketStart' as const, 60 | } 61 | 62 | export type Handler = { 63 | /** 红包抽奖开始 */ 64 | onRedPocketStart: (msg: Message) => void 65 | } 66 | -------------------------------------------------------------------------------- /src/parser/POPULARITY_RED_POCKET_WINNER_LIST.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | 3 | export interface RedPocketEndMsg { 4 | /** 红包抽奖id */ 5 | id: number 6 | /** 中奖人数 */ 7 | total_num: number 8 | /** 中奖用户列表 */ 9 | winner: ({ 10 | /** 用户uid */ 11 | uid: number 12 | /** 用户昵称 */ 13 | uname: string 14 | /** 奖品id */ 15 | award_id: number 16 | } & RedPocketEndAward)[] 17 | /** 红包奖品列表 */ 18 | awards: Record 19 | } 20 | 21 | interface RedPocketEndAward { 22 | /** 奖品类型,待补充 */ 23 | award_type: number 24 | /** 奖品名称 */ 25 | award_name: string 26 | /** 奖品图片 */ 27 | award_pic: string 28 | /** 奖品图片大图 */ 29 | award_big_pic: string 30 | /** 奖品价值,除以1000为RMB */ 31 | award_price: number 32 | } 33 | 34 | const parser = (data: any, roomId: number): RedPocketEndMsg => { 35 | const rawData = data.data 36 | 37 | return { 38 | id: rawData.lot_id, 39 | total_num: rawData.total_num, 40 | winner: rawData.winner_info.map((item: any[]) => ({ 41 | uid: item[0], 42 | uname: item[1], 43 | award_id: item[3], 44 | ...rawData.awards[item[3]], 45 | })), 46 | awards: rawData.awards, 47 | } 48 | } 49 | 50 | export const POPULARITY_RED_POCKET_WINNER_LIST = { 51 | parser, 52 | eventName: 'POPULARITY_RED_POCKET_WINNER_LIST' as const, 53 | handlerName: 'onRedPocketEnd' as const, 54 | } 55 | 56 | export type Handler = { 57 | /** 红包抽奖结果 */ 58 | onRedPocketEnd: (msg: Message) => void 59 | } 60 | -------------------------------------------------------------------------------- /src/parser/PREPARING.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { PREPARING as DataType } from 'tiny-bilibili-ws' 3 | 4 | 5 | export interface LiveEndMsg { 6 | /** 房间号 */ 7 | room_id: number 8 | } 9 | 10 | const parser = (data: DataType): LiveEndMsg => { 11 | return { 12 | room_id: parseInt(data.roomid), 13 | } 14 | } 15 | 16 | export const PREPARING = { 17 | parser, 18 | eventName: 'PREPARING' as const, 19 | handlerName: 'onLiveEnd' as const, 20 | } 21 | 22 | export type Handler = { 23 | /** 直播结束消息 */ 24 | onLiveEnd: (msg: Message) => void 25 | } 26 | -------------------------------------------------------------------------------- /src/parser/ROOM_ADMIN.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | 3 | export interface RoomAdminSetMsg { 4 | /** 类型(设立、撤销) */ 5 | type: 'set' | 'revoke' 6 | /** 用户uid */ 7 | uid: number 8 | } 9 | 10 | const parser = (data: any, roomId: number): RoomAdminSetMsg => { 11 | const msgType = data.cmd 12 | const rawData = data 13 | 14 | return { 15 | type: msgType === 'room_admin_entrance' ? 'set' : 'revoke', 16 | uid: rawData.uid, 17 | } 18 | } 19 | 20 | export const room_admin_entrance = { 21 | parser, 22 | eventName: 'room_admin_entrance' as const, 23 | handlerName: 'onRoomAdminSet' as const, 24 | } 25 | 26 | export const ROOM_ADMIN_REVOKE = { 27 | parser, 28 | eventName: 'ROOM_ADMIN_REVOKE' as const, 29 | handlerName: 'onRoomAdminSet' as const, 30 | } 31 | 32 | export type Handler = { 33 | /** 房间设立、撤销房管 */ 34 | onRoomAdminSet: (msg: Message) => void 35 | } 36 | -------------------------------------------------------------------------------- /src/parser/ROOM_CHANGE.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { ROOM_CHANGE as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface RoomInfoChangeMsg { 5 | /** 直播间标题 */ 6 | title: string 7 | /** 一级分区id */ 8 | parent_area_id: number 9 | /** 一级分区名 */ 10 | parent_area_name: string 11 | /** 二级分区id */ 12 | area_id: number 13 | /** 二级分区名 */ 14 | area_name: string 15 | } 16 | 17 | const parser = (data: DataType): RoomInfoChangeMsg => { 18 | const rawData = data.data 19 | return { 20 | title: rawData.title, 21 | parent_area_id: rawData.parent_area_id, 22 | parent_area_name: rawData.parent_area_name, 23 | area_id: rawData.area_id, 24 | area_name: rawData.area_name, 25 | } 26 | } 27 | 28 | export const ROOM_CHANGE = { 29 | parser, 30 | eventName: 'ROOM_CHANGE' as const, 31 | handlerName: 'onRoomInfoChange' as const, 32 | } 33 | 34 | export type Handler = { 35 | /** 直播间信息修改 */ 36 | onRoomInfoChange: (msg: Message) => void 37 | } 38 | -------------------------------------------------------------------------------- /src/parser/ROOM_SILENT.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { ROOM_SILENT_OFF as ROOM_SILENT_OFF_TYPE, ROOM_SILENT_ON as ROOM_SILENT_ON_TYPE } from 'tiny-bilibili-ws' 3 | 4 | 5 | export interface RoomSilentMsg { 6 | /** 禁言类型(按用户等级、勋章等级、全员、关闭) */ 7 | type: 'level' | 'medal' | 'member' | 'off' 8 | /** 禁言等级 */ 9 | level: number 10 | /** 禁言结束时间,秒级时间戳,-1 为无限 */ 11 | second: number 12 | } 13 | 14 | const parser = (data: ROOM_SILENT_OFF_TYPE | ROOM_SILENT_ON_TYPE, roomId: number): RoomSilentMsg => { 15 | const msgType = data.cmd 16 | const rawData = data.data 17 | 18 | return { 19 | type: msgType === 'ROOM_SILENT_OFF' ? 'off' : rawData.type as 'level' | 'medal' | 'member' | 'off', 20 | level: rawData.level, 21 | second: rawData.second, 22 | } 23 | } 24 | 25 | export const ROOM_SILENT_ON = { 26 | parser, 27 | eventName: 'ROOM_SILENT_ON' as const, 28 | handlerName: 'onRoomSilent' as const, 29 | } 30 | 31 | export const ROOM_SILENT_OFF = { 32 | parser, 33 | eventName: 'ROOM_SILENT_OFF' as const, 34 | handlerName: 'onRoomSilent' as const, 35 | } 36 | 37 | export type Handler = { 38 | /** 房间开启、关闭全局禁言 */ 39 | onRoomSilent: (msg: Message) => void 40 | } 41 | -------------------------------------------------------------------------------- /src/parser/SEND_GIFT.ts: -------------------------------------------------------------------------------- 1 | import { intToColorHex } from '../utils/color' 2 | import type { Message, User } from '../types/app' 3 | 4 | export interface GiftMsg { 5 | user: User 6 | /** 礼物id */ 7 | gift_id: number 8 | /** 礼物名称 */ 9 | gift_name: string 10 | /** 礼物价格类型 */ 11 | coin_type: 'silver' | 'gold' 12 | /** 礼物价格,除以1000为RMB */ 13 | price: number 14 | /** 礼物数量 */ 15 | amount: number 16 | /** 送礼指向主播信息,多人直播间可指定要送给的主播,单人直播间为空 */ 17 | send_master?: { 18 | uid: number 19 | uname: string 20 | room_id: number 21 | } 22 | /** 礼物连击 */ 23 | combo?: { 24 | /** 连击id */ 25 | batch_id: string 26 | /** 当前连击数(礼物总数) */ 27 | combo_num: number 28 | /** 连击礼物总价格,除以1000为RMB */ 29 | total_price: number 30 | } 31 | } 32 | 33 | const parser = (data: any): GiftMsg => { 34 | const rawData = data.data 35 | return { 36 | user: { 37 | uid: rawData.uid, 38 | uname: rawData.uname, 39 | face: rawData.face, 40 | badge: rawData.medal_info?.medal_level ? { 41 | active: rawData.medal_info.is_lighted === 1, 42 | name: rawData.medal_info.medal_name, 43 | level: rawData.medal_info.medal_level, 44 | color: intToColorHex(rawData.medal_info.medal_color), 45 | gradient: [ 46 | intToColorHex(rawData.medal_info.medal_color_start), 47 | intToColorHex(rawData.medal_info.medal_color_start), 48 | intToColorHex(rawData.medal_info.medal_color_end), 49 | ], 50 | anchor: { 51 | uid: rawData.medal_info.target_id, 52 | uname: rawData.medal_info.anchor_uname, // maybe '' 53 | room_id: rawData.medal_info.anchor_roomid, // maybe 0 54 | }, 55 | } : undefined, 56 | identity: { 57 | rank: 0, 58 | guard_level: rawData.guard_level, 59 | room_admin: false, 60 | } 61 | }, 62 | gift_id: rawData.giftId, 63 | gift_name: rawData.giftName, 64 | coin_type: rawData.coin_type, 65 | price: rawData.price, 66 | amount: rawData.num, 67 | send_master: rawData.send_master?.uid ? { 68 | uid: rawData.send_master.uid, 69 | uname: rawData.send_master.uname, 70 | room_id: rawData.send_master.room_id, 71 | } : undefined, 72 | // 礼物连击: 73 | // data.combo_send 仅首次连击不为空;data.batch_combo_send 速度过快时可能为空;data.batch_combo_id 常驻存在 74 | combo: rawData.batch_combo_id ? { 75 | batch_id: rawData.batch_combo_id, 76 | combo_num: rawData.super_batch_gift_num, 77 | total_price: rawData.combo_total_coin, 78 | } : undefined, 79 | } 80 | } 81 | 82 | export const SEND_GIFT = { 83 | parser, 84 | eventName: 'SEND_GIFT' as const, 85 | handlerName: 'onGift' as const, 86 | } 87 | 88 | export type Handler = { 89 | /** 收到礼物 */ 90 | onGift: (msg: Message) => void 91 | } 92 | -------------------------------------------------------------------------------- /src/parser/SUPER_CHAT_MESSAGE.ts: -------------------------------------------------------------------------------- 1 | import type { Message, User } from '../types/app' 2 | import type { SUPER_CHAT_MESSAGE as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface SuperChatMsg { 5 | /** 消息id */ 6 | id: number 7 | /** 发送用户 */ 8 | user: User 9 | /** 弹幕内容 */ 10 | content: string 11 | /** 弹幕颜色 */ 12 | content_color: string 13 | /** 价格,RMB */ 14 | price: number 15 | /** 持续时间,秒 */ 16 | time: number 17 | } 18 | 19 | const parser = (data: DataType, roomId: number): SuperChatMsg => { 20 | const rawData = data.data 21 | const { medal_info, user_info } = data.data 22 | return { 23 | id: rawData.id, 24 | user: { 25 | uid: rawData.uid, 26 | uname: rawData.user_info.uname, 27 | badge: medal_info ? { 28 | active: medal_info.is_lighted === 1, 29 | name: medal_info.medal_name, 30 | level: medal_info.medal_level, 31 | color: medal_info.medal_color, 32 | anchor: { 33 | uid: medal_info.target_id, 34 | uname: medal_info.anchor_uname, 35 | room_id: medal_info.anchor_roomid, 36 | is_same_room: medal_info.anchor_roomid === roomId, 37 | }, 38 | } : undefined, 39 | identity: { 40 | rank: 0, 41 | guard_level: user_info.guard_level || 0, 42 | room_admin: user_info.manager === 1, 43 | } 44 | }, 45 | content: rawData.message, 46 | content_color: rawData.background_price_color, 47 | price: rawData.price, 48 | time: rawData.time, 49 | } 50 | } 51 | 52 | export const SUPER_CHAT_MESSAGE = { 53 | parser, 54 | eventName: 'SUPER_CHAT_MESSAGE' as const, 55 | handlerName: 'onIncomeSuperChat' as const, 56 | } 57 | 58 | export type Handler = { 59 | /** 收到醒目留言 */ 60 | onIncomeSuperChat: (msg: Message) => void 61 | } 62 | -------------------------------------------------------------------------------- /src/parser/WARNING_CUT_OFF.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { WARNING as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface RoomWarnMsg { 5 | /** 处理类型 */ 6 | type: 'warning' | 'cut' 7 | /** 处理原因 */ 8 | msg: string 9 | } 10 | 11 | const parser = (data: DataType, roomId: number): RoomWarnMsg => { 12 | const msgType = data.cmd 13 | const rawData = data 14 | 15 | return { 16 | type: msgType === 'WARNING' ? 'warning' : 'cut', 17 | msg: rawData.msg, 18 | } 19 | } 20 | 21 | export const WARNING = { 22 | parser, 23 | eventName: 'WARNING' as const, 24 | handlerName: 'onRoomWarn' as const, 25 | } 26 | 27 | export const CUT_OFF = { 28 | parser, 29 | eventName: 'CUT_OFF' as const, 30 | handlerName: 'onRoomWarn' as const, 31 | } 32 | 33 | export type Handler = { 34 | /** 房间被超管警告、切断 */ 35 | onRoomWarn: (msg: Message) => void 36 | } 37 | -------------------------------------------------------------------------------- /src/parser/WATCHED_CHANGE.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | import type { WATCHED_CHANGE as DataType } from 'tiny-bilibili-ws' 3 | 4 | export interface WatchedChangeMsg { 5 | /** 累计入场人数 */ 6 | num: number 7 | /** 累计入场人数,格式化输出 */ 8 | text_small: string 9 | } 10 | 11 | const parser = (data: DataType): WatchedChangeMsg => { 12 | const rawData = data.data 13 | return { 14 | num: rawData.num, 15 | text_small: rawData.text_small, 16 | } 17 | } 18 | 19 | export const WATCHED_CHANGE = { 20 | parser, 21 | eventName: 'WATCHED_CHANGE' as const, 22 | handlerName: 'onWatchedChange' as const, 23 | } 24 | 25 | export type Handler = { 26 | /** 累计看过人数变化 */ 27 | onWatchedChange: (msg: Message) => void 28 | } 29 | -------------------------------------------------------------------------------- /src/parser/index.ts: -------------------------------------------------------------------------------- 1 | export { HEARTBEAT, type Handler as AttentionChangeMsgHandler, type AttentionChangeMsg } from './HEARTBEAT' 2 | export { LIVE, type Handler as LiveStartMsgHandler, type LiveStartMsg } from './LIVE' 3 | export { PREPARING, type Handler as LiveStopMsgHandler, type LiveEndMsg } from './PREPARING' 4 | export { ANCHOR_LOT_AWARD, type Handler as AnchorLotteryEndMsgHandler, type AnchorLotteryEndMsg } from './ANCHOR_LOT_AWARD' 5 | export { ANCHOR_LOT_START, type Handler as AnchorLotteryStartMsgHandler, type AnchorLotteryStartMsg } from './ANCHOR_LOT_START' 6 | export { DANMU_MSG, type Handler as DanmuMsgHandler, type DanmuMsg } from './DANMU_MSG' 7 | export { GUARD_BUY, type Handler as GuardBuyHandler, type GuardBuyMsg } from './GUARD_BUY' 8 | export { INTERACT_WORD, ENTRY_EFFECT, LIKE_INFO_V3_CLICK, type Handler as UserActionMsgHandler, type UserActionMsg } from './INTERACT_WORD_ENTRY_EFFECT' 9 | export { LIKE_INFO_V3_UPDATE, type Handler as LikedChangeMsgHandler, type LikedChangeMsg } from './LIKE_INFO_V3_UPDATE' 10 | export { ONLINE_RANK_COUNT, type Handler as RankCountChangeMsgHandler, type RankCountChangeMsg } from './ONLINE_RANK_COUNT' 11 | export { POPULARITY_RED_POCKET_START, type Handler as RedPocketStartMsgHandler, type RedPocketStartMsg } from './POPULARITY_RED_POCKET_START' 12 | export { POPULARITY_RED_POCKET_WINNER_LIST, type Handler as RedPocketEndMsgHandler, type RedPocketEndMsg } from './POPULARITY_RED_POCKET_WINNER_LIST' 13 | export { room_admin_entrance, ROOM_ADMIN_REVOKE, type Handler as RoomAdminSetMsgHandler, type RoomAdminSetMsg } from './ROOM_ADMIN' 14 | export { ROOM_CHANGE, type Handler as RoomInfoChangeHandler, type RoomInfoChangeMsg } from './ROOM_CHANGE' 15 | export { ROOM_SILENT_ON, ROOM_SILENT_OFF, type Handler as RoomSilentMsgHandler, type RoomSilentMsg } from './ROOM_SILENT' 16 | export { SEND_GIFT, type Handler as GiftHandler, type GiftMsg } from './SEND_GIFT' 17 | export { SUPER_CHAT_MESSAGE, type Handler as SuperChatHandler, type SuperChatMsg } from './SUPER_CHAT_MESSAGE' 18 | export { WARNING, CUT_OFF, type Handler as RoomWarnHandler, type RoomWarnMsg } from './WARNING_CUT_OFF' 19 | export { WATCHED_CHANGE, type Handler as WatchedChangeHandler, type WatchedChangeMsg } from './WATCHED_CHANGE' 20 | 21 | -------------------------------------------------------------------------------- /src/types/app.ts: -------------------------------------------------------------------------------- 1 | import { GuardLevel } from './const' 2 | 3 | export interface User { 4 | /** 用户uid */ 5 | uid: number 6 | /** 用户名 */ 7 | uname: string 8 | /** 用户头像 */ 9 | face?: string 10 | /** 用户牌子·*/ 11 | badge?: { 12 | /** 是否点亮 */ 13 | active: boolean 14 | /** 牌子名称 */ 15 | name: string 16 | /** 牌子等级 */ 17 | level: number 18 | /** 牌子颜色 */ 19 | color: string 20 | /** 渐变色牌子,当用户长时间未消费,则会变为灰色,即 `#c0c0c0` */ 21 | gradient?: [string, string, string] 22 | /** 主播信息 */ 23 | anchor: { 24 | /** 主播uid */ 25 | uid: number 26 | /** 主播用户名 */ 27 | uname: string 28 | /** 主播房间号 */ 29 | room_id: number 30 | /** 是否为本直播间 */ 31 | is_same_room?: boolean 32 | } 33 | } 34 | /** 用户身份 */ 35 | identity?: { 36 | /** 直播榜单排名 */ 37 | rank: 0 | 1 | 2 | 3 38 | /** 大航海信息 */ 39 | guard_level: GuardLevel 40 | /** 房管 */ 41 | room_admin: boolean 42 | } 43 | } 44 | 45 | export interface Message { 46 | /** 消息id */ 47 | id: string, 48 | /** 接收消息的时间,毫秒时间戳 */ 49 | timestamp: number, 50 | /** 消息类型 */ 51 | type: string, 52 | /** 消息内容 */ 53 | body: T 54 | /** 原始消息内容 */ 55 | raw: any 56 | } 57 | -------------------------------------------------------------------------------- /src/types/const.ts: -------------------------------------------------------------------------------- 1 | export enum GuardLevel { 2 | /** 无 */ 3 | None = 0, 4 | /** 总督 */ 5 | Zongdu = 1, 6 | /** 提督 */ 7 | Tidu = 2, 8 | /** 舰长 */ 9 | Jianzhang = 3, 10 | } -------------------------------------------------------------------------------- /src/types/message.ts: -------------------------------------------------------------------------------- 1 | export type { 2 | AttentionChangeMsg, 3 | LiveStartMsg, 4 | LiveEndMsg, 5 | AnchorLotteryEndMsg, 6 | AnchorLotteryStartMsg, 7 | DanmuMsg, 8 | GuardBuyMsg, 9 | UserActionMsg, 10 | LikedChangeMsg, 11 | RankCountChangeMsg, 12 | RedPocketStartMsg, 13 | RedPocketEndMsg, 14 | RoomAdminSetMsg, 15 | RoomInfoChangeMsg, 16 | RoomSilentMsg, 17 | GiftMsg, 18 | SuperChatMsg, 19 | RoomWarnMsg, 20 | WatchedChangeMsg, 21 | } from '../parser' -------------------------------------------------------------------------------- /src/utils/color.ts: -------------------------------------------------------------------------------- 1 | export const intToColorHex = (int: number) => { 2 | const hex = int.toString(16) 3 | return `#${hex.padStart(6, '0')}` 4 | } -------------------------------------------------------------------------------- /src/utils/message.ts: -------------------------------------------------------------------------------- 1 | import type { Message } from '../types/app' 2 | 3 | export const normalizeDanmu = (msgType: string, body: T, rawBody: any): Message => { 4 | const timestamp = Date.now() 5 | const randomText = Math.floor(Math.random() * 10000).toString() 6 | // @ts-ignore 7 | const id = `${timestamp}:${msgType}:${body.user?.uid}:${randomText}` 8 | return { 9 | id, 10 | timestamp, 11 | type: msgType, 12 | body, 13 | raw: rawBody, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ESNext", 4 | "target": "ESNext", 5 | "lib": ["ESNext", "dom"], 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "moduleResolution": "Node", 9 | "skipLibCheck": true, 10 | "isolatedModules": true, 11 | } 12 | } -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | format: ['esm', 'cjs'], 5 | entry: ['src/index.ts', 'src/browser.ts'], 6 | dts: true, 7 | clean: true, 8 | }) 9 | --------------------------------------------------------------------------------