Home

Awesome

blive-message-listener

npm

类型友好的 Bilibili 直播间弹幕监听库。

Features

Install


npm i blive-message-listener

Usage

import { startListen, type MsgHandler } from 'blive-message-listener'
// 浏览器环境,从 '/browser' 导入 startListen
// import { startListen } from 'blive-message-listener/browser'

const handler: MsgHandler = {
  onIncomeDanmu: (msg) => {
    console.log(msg.id, msg.body)
  },
  onIncomeSuperChat: (msg) => {
    console.log(msg.id, msg.body)
  },
}

const instance = startListen(652581, handler)

instance.close()
  1. 需传入房间的长id。短id需转为长id才能接收到消息。(https://github.com/ddiu8081/blive-message-listener/issues/19)
  2. 2023年7月后需登录后才可展示完整用户名,可参考下一节 Options 配置连接选项 (https://github.com/ddiu8081/blive-message-listener/issues/29)

Options

你可以向 startListen 传入第三个参数 options 来手动配置连接选项。

interface MessageListenerOptions {
  /**
   * tiny-bilibili-ws 连接选项
   *
   * @see https://github.com/starknt/tiny-bilibili-ws
   */
  ws?: WSOptions | TCPOptions
}
startListen(652581, handler, {
  ws: {
    headers: {
      Cookie: 'xxx',
    },
    uid: 0,
  }
})

// or
startListen(652581, handler, {
  ws: {
    platform: 'web',
    uid: 541993,
    key: '<login_key>',
    buvid: '<login_buvid>',
  }
})

Handlers & Type Definitions

Common

const startListen: (roomId: number, handler: MsgHandler) => MessageListener

export interface MessageListener {
  /** 直播间房间号 */
  roomId: number
  /** 关闭连接 */
  close: () => void
  /** 刷新当前直播间热度 */
  getAttention: () => Promise<number>
}

export interface Message<T> {
  /** 消息id */
  id: string,
  /** 接收消息的时间,毫秒时间戳 */
  timestamp: number,
  /** 消息类型 */
  type: string,
  /** 消息内容 */
  body: T
  /** 原始消息内容 */
  raw: any
}

export interface User {
  /** 用户uid */
  uid: number
  /** 用户名 */
  uname: string
  /** 用户头像 */
  face?: string
  /** 用户牌子·*/
  badge?: {
    /** 是否点亮 */
    active: boolean
    /** 牌子名称 */
    name: string
    /** 牌子等级 */
    level: number
    /** 牌子颜色 */
    color: string
    /** 渐变色牌子,当用户长时间未消费,则会变为灰色,即 `#c0c0c0` */
    gradient?: [string, string, string]
    /** 主播信息 */
    anchor: {
      /** 主播uid */
      uid: number
      /** 主播用户名 */
      uname: string
      /** 主播房间号 */
      room_id: number
      /** 是否为本直播间 */
      is_same_room?: boolean
    }
  }
  /** 用户身份 */
  identity?: {
    /** 直播榜单排名 */
    rank: 0 | 1 | 2 | 3
    /** 大航海信息 */
    guard_level: GuardLevel
    /** 房管 */
    room_admin: boolean
  }
}

export enum GuardLevel {
  /** 无 */
  None = 0,
  /** 总督 */
  Zongdu = 1,
  /** 提督 */
  Tidu = 2,
  /** 舰长 */
  Jianzhang = 3,
}

Handler

Type definition can be also found in src/parser.

连接基础信息

HandlerDescription
onOpen连接成功
onClose连接关闭
onError连接错误
onStartListen开始监听消息
<details> <summary>Type Definitions</summary>
Handler.onOpen

连接成功

export type Handler = {
  /** 连接成功 */
  onOpen: () => void,
}
Handler.onClose

连接关闭

export type Handler = {
  /** 连接关闭 */
  onClose: () => void,
}
Handler.onError

连接错误

export type Handler = {
  /** 连接错误 */
  onError: (e: Error) => void,
}
Handler.onStartListen

开始监听消息

export type Handler = {
  /** 开始监听消息 */
  onStartListen: () => void,
}
</details>

直播间基础信息

HandlerDescription
onLiveStart直播开始消息
onLiveEnd直播结束消息
onAttentionChange直播间热度变化
onWatchedChange累计看过人数变化
onLikedChange累计点赞人数变化
onRankCountChange高能用户人数变化
onUserAction用户进入、关注、分享、点赞直播间
onRoomInfoChange直播间信息修改
<details> <summary>Type Definitions</summary>
handler.onLiveStart

直播开始消息

export type Handler = {
  /** 直播开始消息 */
  onLiveStart: (msg: Message<LiveStartMsg>) => void
}

type msgType = 'LIVE'

export interface LiveStartMsg {
  /** 开播平台 */
  live_platform: string
  /** 房间号 */
  room_id: number
}
handler.onLiveEnd

直播结束消息

export type Handler = {
  /** 直播结束消息 */
  onLiveEnd: (msg: Message<LiveEndMsg>) => void
}

type msgType = 'PREPARING'

export interface LiveEndMsg {
  /** 房间号 */
  room_id: number
}
handler.onAttentionChange

直播间热度变化

export type Handler = {
  /** 直播间热度变化 */
  onAttentionChange: (msg: Message<AttentionChangeMsg>) => void
}

type msgType = 'heartbeat'

export interface AttentionChangeMsg {
  /** 直播间热度 */
  attention: number
}
handler.onWatchedChange

累计看过人数变化

export type Handler = {
  /** 累计看过人数变化 */
  onWatchedChange: (msg: Message<WatchedChangeMsg>) => void
}

type msgType = 'WATCHED_CHANGE'

export interface WatchedChangeMsg {
  /** 累计入场人数 */
  num: number
  /** 累计入场人数,格式化输出 */
  text_small: string
}
handler.onLikedChange

累计点赞人数变化

export type Handler = {
  /** 累计点赞人数变化 */
  onLikedChange: (msg: Message<LikedChangeMsg>) => void
}

type msgType = 'LIKE_INFO_V3_UPDATE'

export interface LikedChangeMsg {
  /** 直播间点赞人数 */
  count: number
}
handler.onRankCountChange

高能用户人数变化

export type Handler = {
  /** 高能用户人数变化 */
  onRankCountChange: (msg: Message<RankCountChangeMsg>) => void
}

type msgType = 'ONLINE_RANK_COUNT'

export interface RankCountChangeMsg {
  /** 高能用户人数 */
  count: number
}
handler.onUserAction

用户进入、关注、分享、点赞直播间

export type Handler = {
  /** 用户进入、关注、分享、点赞直播间 */
  onUserAction: (msg: Message<UserActionMsg>) => void
}

type msgType = 'INTERACT_WORD' | 'ENTRY_EFFECT' | 'LIKE_INFO_V3_CLICK'

type UserAction = 'enter' | 'follow' | 'share' | 'like' | 'unknown'

export interface UserActionMsg {
  user: User
  /** 事件类型 */
  action: UserAction
  /** 事件时间,毫秒时间戳 */
  timestamp: number
}
handler.onRoomInfoChange

直播间信息修改

export type Handler = {
  /** 直播间信息修改 */
  onRoomInfoChange: (msg: Message<RoomInfoChangeMsg>) => void
}

type msgType = 'ROOM_CHANGE'

export interface RoomInfoChangeMsg {
  /** 直播间标题 */
  title: string
  /** 一级分区id */
  parent_area_id: number
  /** 一级分区名 */
  parent_area_name: string
  /** 二级分区id */
  area_id: number
  /** 二级分区名 */
  area_name: string
}
</details>

弹幕相关

HandlerDescription
onIncomeDanmu收到普通弹幕消息
onIncomeSuperChat收到醒目留言
<details> <summary>Type Definitions</summary>
handler.onIncomeDanmu

收到普通弹幕消息

export type Handler = {
  /** 收到普通弹幕消息 */
  onIncomeDanmu: (msg: Message<DanmuMsg>) => void
}

type msgType = 'DANMU_MSG'

export interface DanmuMsg {
  user: User
  /** 弹幕内容 */
  content: string
  /** 发送时间,毫秒时间戳 */
  timestamp: number
  /** 是否为天选抽奖弹幕 */
  lottery: boolean
  /** 表情弹幕内容 */
  emoticon?: {
    id: string
    height: number
    width: number
    url: string
  }
  /** 弹幕内小表情映射,key为表情文字,如"[妙]" */
  in_message_emoticon?: Record<string, {
    id: string
    emoticon_id: number
    height: number
    width: number
    url: string
    description: string
  }>
}
handler.onIncomeSuperChat

收到醒目留言

export type Handler = {
  /** 收到醒目留言 */
  onIncomeSuperChat: (msg: Message<SuperChatMsg>) => void
}

type msgType = 'SUPER_CHAT_MESSAGE'

export interface SuperChatMsg {
  /** 消息id */
  id: number
  /** 发送用户 */
  user: User
  /** 弹幕内容 */
  content: string
  /** 弹幕颜色 */
  content_color: string
  /** 价格,RMB */
  price: number
  /** 持续时间,秒 */
  time: number
}
</details>

礼物相关

HandlerDescription
onGift收到礼物
onGuardBuy舰长上舰消息
onRedPocketStart红包抽奖开始
onRedPocketEnd红包抽奖结果
onAnchorLotteryStart主播天选时刻抽奖开启
onAnchorLotteryEnd主播天选时刻抽奖结果
<details> <summary>Type Definitions</summary>
handler.onGift

收到礼物

export type Handler = {
  /** 收到礼物 */
  onGift: (msg: Message<GiftMsg>) => void
}

type msgType = 'SEND_GIFT'

export interface GiftMsg {
  user: User
  /** 礼物id */
  gift_id: number
  /** 礼物名称 */
  gift_name: string
  /** 礼物价格类型 */
  coin_type: 'silver' | 'gold'
  /** 礼物价格,除以1000为RMB */
  price: number
  /** 礼物数量 */
  amount: number
  /** 送礼指向主播信息,多人直播间可指定要送给的主播,单人直播间为空 */
  send_master?: {
    uid: number
    uname: string
    room_id: number
  }
  /** 礼物连击 */
  combo?: {
    /** 连击id */
    batch_id: string
    /** 当前连击数(礼物总数) */
    combo_num: number
    /** 连击礼物总价格,除以1000为RMB */
    total_price: number
  }
}
handler.onGuardBuy

舰长上舰消息

export type Handler = {
  /** 舰长上舰消息 */
  onGuardBuy: (msg: Message<GuardBuyMsg>) => void
}

type msgType = 'GUARD_BUY'

export interface GuardBuyMsg {
  user: User
  /** 礼物id */
  gift_id: number
  /** 礼物名称 */
  gift_name: string
  /** 大航海信息 */
  guard_level: GuardLevel
  /** 价格,RMB */
  price: number
  /** 等级生效时间 */
  start_time: number
  /** 等级过期时间 */
  end_time: number
}
handler.onRedPocketStart

红包抽奖开始

export type Handler = {
  /** 红包抽奖开始 */
  onRedPocketStart: (msg: Message<RedPocketStartMsg>) => void
}

type msgType = 'POPULARITY_RED_POCKET_START'

export interface RedPocketStartMsg {
  /** 红包抽奖id */
  id: number
  /** 红包发送用户 */
  user: User
  /** 开始时间,秒级时间戳 */
  start_time: number
  /** 结束时间,秒级时间戳 */
  end_time: number
  /** 持续时间,秒 */
  duration: number
  /** 口令弹幕内容 */
  danmu: string
  /** 红包奖品 */
  awards: RedPocketStartAward[]
  /** 奖品总价值,除以1000为RMB */
  total_price: number
  /** 剩余等待的红包数 */
  wait_num: number
}

interface RedPocketStartAward {
  /** 奖品id */
  gift_id: number
  /** 奖品名称 */
  gift_name: string
  /** 奖品图片 */
  gift_pic: string
  /** 奖品数量 */
  num: number
}
handler.onRedPocketEnd

红包抽奖结果

export type Handler = {
  /** 红包抽奖结果 */
  onRedPocketEnd: (msg: Message<RedPocketEndMsg>) => void
}

type msgType = 'POPULARITY_RED_POCKET_WINNER_LIST'

export interface RedPocketEndMsg {
  /** 红包抽奖id */
  id: number
  /** 中奖人数 */
  total_num: number
  /** 中奖用户列表 */
  winner: ({
    /** 用户uid */
    uid: number
    /** 用户昵称 */
    uname: string
    /** 奖品id */
    award_id: number
  } & RedPocketEndAward)[]
  /** 红包奖品列表 */
  awards: Record<string, RedPocketEndAward>
}

interface RedPocketEndAward {
  /** 奖品类型,待补充 */
  award_type: number
  /** 奖品名称 */
  award_name: string
  /** 奖品图片 */
  award_pic: string
  /** 奖品图片大图 */
  award_big_pic: string
  /** 奖品价值,除以1000为RMB */
  award_price: number
}
handler.onAnchorLotteryStart

主播天选时刻抽奖开启

export type Handler = {
  /** 主播天选时刻抽奖开启 */
  onAnchorLotteryStart: (msg: Message<AnchorLotteryStartMsg>) => void
}

type msgType = 'ANCHOR_LOT_START'

export interface AnchorLotteryStartMsg {
  /** 天选抽奖id */
  id: number
  /** 开始时间,秒级时间戳 */
  start_time: number
  /** 持续时间,秒 */
  duration: number
  /** 天选奖品信息 */
  award: {
    /** 奖品图片 */
    image: string
    /** 奖品名称 */
    name: string
    /** 奖品数量 */
    num: number
    /** 是否为虚拟礼物奖品 */
    virtual: boolean
    /** 虚拟奖品价值描述,实物奖品为空 */
    price_text: string
  }
  /** 抽奖要求 */
  require: {
    /** 口令弹幕内容,无需弹幕为空字符串 */
    danmu: string
    /** 需送主播礼物,无需送礼为空 */
    gift: {
      /** 礼物id */
      id: string
      /** 礼物名称 */
      name: string
      /** 礼物数量 */
      num: number
      /** 单个礼物价值,除以1000为RMB */
      price: number
    } | null
    /** 抽奖参与人群要求,无要求为空 */
    user: {
      /** 参与人群限制(关注/粉丝勋章/大航海) */
      type: 'follow' | 'medal' | 'guard'
      /** 参与人群限制等级,如粉丝勋章等级 */
      value: number
      /** 参与人群限制描述 */
      text: string
    } | null
  }
}
handler.onAnchorLotteryEnd

主播天选时刻抽奖结果

export type Handler = {
  /** 主播天选时刻抽奖结果 */
  onAnchorLotteryEnd: (msg: Message<AnchorLotteryEndMsg>) => void
}

type msgType = 'ANCHOR_LOT_AWARD'

export interface AnchorLotteryEndMsg {
  /** 天选抽奖id */
  id: number
  /** 天选奖品信息 */
  award: {
    /** 奖品图片 */
    image: string
    /** 奖品名称 */
    name: string
    /** 是否为虚拟礼物奖品 */
    virtual: boolean
  }
  /** 中奖用户列表 */
  winner: ({
    /** 用户uid */
    uid: number
    /** 用户昵称 */
    uname: string
    /** 用户头像 */
    face: number
    /** 用户粉丝勋章等级 */
    level: number
    /** 中奖数量 */
    num: number
  })[]
}
</details>

房间管理相关

HandlerDescription
onRoomWarn房间被超管警告、切断
onRoomSilent房间开启、关闭全局禁言
onRoomAdminSet房间设立、撤销房管
<details> <summary>Type Definitions</summary>
handler.onRoomWarn

房间被超管警告、切断

export type Handler = {
  /** 房间被超管警告、切断 */
  onRoomWarn: (msg: Message<RoomWarnMsg>) => void
}

type msgType = 'WARNING' | 'CUT_OFF'

export interface RoomWarnMsg {
  /** 处理类型 */
  type: 'warning' | 'cut'
  /** 处理原因 */
  msg: string
}
handler.onRoomSilent

房间开启、关闭全局禁言

export type Handler = {
  /** 房间开启、关闭全局禁言 */
  onRoomSilent: (msg: Message<RoomSilentMsg>) => void
}

type msgType = 'ROOM_SILENT_ON' | 'ROOM_SILENT_OFF'

export interface RoomSilentMsg {
  /** 禁言类型(按用户等级、勋章等级、全员、关闭) */
  type: 'level' | 'medal' | 'member' | 'off'
  /** 禁言等级 */
  level: number
  /** 禁言结束时间,秒级时间戳,-1 为无限 */
  second: number
}
handler.onRoomAdminSet

房间设立、撤销房管

export type Handler = {
  /** 房间设立、撤销房管 */
  onRoomAdminSet: (msg: Message<RoomAdminSetMsg>) => void
}

type msgType = 'room_admin_entrance' | 'ROOM_ADMIN_REVOKE'

export interface RoomAdminSetMsg {
  /** 类型(设立、撤销) */
  type: 'set' | 'revoke'
  /** 用户uid */
  uid: number
}
</details>

监听原始消息

export type Handler = {
  /** 原始消息 */
  raw: Record<'msg' | string, (msg: any) => void>
}

可在 raw 中监听任意原始消息。

example:

const handler: MsgHandler = {
  raw: {
    'msg': (msg) => {
      // 监听所有 cmd 消息
      console.log(msg)
    },
    'INTERACT_WORD': (msg) => {
      // 监听特定的 cmd
      console.log(msg)
    },
  }
}

startListen(652581, handler)

Credits

License

MIT