npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

blive-message-listener

v0.5.0

Published

Bilibili-live danmu listener with type

Downloads

85

Readme

blive-message-listener

npm

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

Features

  • 原始数据 转为更友好的格式输出
  • Node 环境与浏览器环境支持
  • 支持监听与获取原始消息

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.

连接基础信息

| Handler | Description | | --- | --- | | onOpen | 连接成功 | | onClose | 连接关闭 | | onError | 连接错误 | | onStartListen | 开始监听消息 |

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,
}

直播间基础信息

| Handler | Description | | --- | --- | | onLiveStart | 直播开始消息 | | onLiveEnd | 直播结束消息 | | onAttentionChange | 直播间热度变化 | | onWatchedChange | 累计看过人数变化 | | onLikedChange | 累计点赞人数变化 | | onRankCountChange | 高能用户人数变化 | | onUserAction | 用户进入、关注、分享、点赞直播间 | | onRoomInfoChange | 直播间信息修改 |

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

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

  • 舰长进入直播间时,有几率会触发两次
  • 舰长进入直播间时,uname 超长可能会省略号截断
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
}

弹幕相关

| Handler | Description | | --- | --- | | onIncomeDanmu | 收到普通弹幕消息 | | onIncomeSuperChat | 收到醒目留言 |

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
}

礼物相关

| Handler | Description | | --- | --- | | onGift | 收到礼物 | | onGuardBuy | 舰长上舰消息 | | onRedPocketStart | 红包抽奖开始 | | onRedPocketEnd | 红包抽奖结果 | | onAnchorLotteryStart | 主播天选时刻抽奖开启 | | onAnchorLotteryEnd | 主播天选时刻抽奖结果 |

handler.onGift

收到礼物

  • 礼物信息的用户牌子可见,但没有牌子对应主播的用户名及房间号,也无法判断 is_same_room 是否为本直播间。
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
  })[]
}

房间管理相关

| Handler | Description | | --- | --- | | onRoomWarn | 房间被超管警告、切断 | | onRoomSilent | 房间开启、关闭全局禁言 | | onRoomAdminSet | 房间设立、撤销房管 |

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
}

监听原始消息

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