类型友好的 Bilibili 直播间弹幕监听库。
- 将 原始数据 转为更友好的格式输出
- Node 环境与浏览器环境支持
- 支持监听与获取原始消息
npm i blive-message-listener
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()
- 需传入房间的长id。短id需转为长id才能接收到消息。(ddiu8081#19)
- 2023年7月后需登录后才可展示完整用户名,可参考下一节 Options 配置连接选项 (ddiu8081#29)
你可以向 startListen
传入第三个参数 options
来手动配置连接选项。
interface MessageListenerOptions {
/**
* tiny-bilibili-ws 连接选项
*
* @see https://github.com/starknt/tiny-bilibili-ws
*/
ws?: WSOptions | TCPOptions
}
startListen(652581, handler, {
ws: {
platform: 'web',
uid: 541993,
key: '<login_key>',
buvid: '<login_buvid>',
}
})
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,
}
Type definition can be also found in src/parser.
Handler | Description |
---|---|
onOpen | 连接成功 |
onClose | 连接关闭 |
onError | 连接错误 |
onStartListen | 开始监听消息 |
Type Definitions
连接成功
export type Handler = {
/** 连接成功 */
onOpen: () => void,
}
连接关闭
export type Handler = {
/** 连接关闭 */
onClose: () => void,
}
连接错误
export type Handler = {
/** 连接错误 */
onError: (e: Error) => void,
}
开始监听消息
export type Handler = {
/** 开始监听消息 */
onStartListen: () => void,
}
Handler | Description |
---|---|
onLiveStart | 直播开始消息 |
onLiveEnd | 直播结束消息 |
onAttentionChange | 直播间热度变化 |
onWatchedChange | 累计看过人数变化 |
onLikedChange | 累计点赞人数变化 |
onRankCountChange | 高能用户人数变化 |
onUserAction | 用户进入、关注、分享、点赞直播间 |
onRoomInfoChange | 直播间信息修改 |
Type Definitions
直播开始消息
export type Handler = {
/** 直播开始消息 */
onLiveStart: (msg: Message<LiveStartMsg>) => void
}
type msgType = 'LIVE'
export interface LiveStartMsg {
/** 开播平台 */
live_platform: string
/** 房间号 */
room_id: number
}
直播结束消息
export type Handler = {
/** 直播结束消息 */
onLiveEnd: (msg: Message<LiveEndMsg>) => void
}
type msgType = 'PREPARING'
export interface LiveEndMsg {
/** 房间号 */
room_id: number
}
直播间热度变化
export type Handler = {
/** 直播间热度变化 */
onAttentionChange: (msg: Message<AttentionChangeMsg>) => void
}
type msgType = 'heartbeat'
export interface AttentionChangeMsg {
/** 直播间热度 */
attention: number
}
累计看过人数变化
export type Handler = {
/** 累计看过人数变化 */
onWatchedChange: (msg: Message<WatchedChangeMsg>) => void
}
type msgType = 'WATCHED_CHANGE'
export interface WatchedChangeMsg {
/** 累计入场人数 */
num: number
/** 累计入场人数,格式化输出 */
text_small: string
}
累计点赞人数变化
export type Handler = {
/** 累计点赞人数变化 */
onLikedChange: (msg: Message<LikedChangeMsg>) => void
}
type msgType = 'LIKE_INFO_V3_UPDATE'
export interface LikedChangeMsg {
/** 直播间点赞人数 */
count: number
}
高能用户人数变化
export type Handler = {
/** 高能用户人数变化 */
onRankCountChange: (msg: Message<RankCountChangeMsg>) => void
}
type msgType = 'ONLINE_RANK_COUNT'
export interface RankCountChangeMsg {
/** 高能用户人数 */
count: number
}
用户进入、关注、分享、点赞直播间
- 舰长进入直播间时,有几率会触发两次
- 舰长进入直播间时,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
}
直播间信息修改
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 | 收到醒目留言 |
Type Definitions
收到普通弹幕消息
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
}>
}
收到醒目留言
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 | 主播天选时刻抽奖结果 |
Type Definitions
收到礼物
- 礼物信息的用户牌子可见,但没有牌子对应主播的用户名及房间号,也无法判断
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
}
}
舰长上舰消息
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
}
红包抽奖开始
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
}
红包抽奖结果
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
}
主播天选时刻抽奖开启
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
}
}
主播天选时刻抽奖结果
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 | 房间设立、撤销房管 |
Type Definitions
房间被超管警告、切断
export type Handler = {
/** 房间被超管警告、切断 */
onRoomWarn: (msg: Message<RoomWarnMsg>) => void
}
type msgType = 'WARNING' | 'CUT_OFF'
export interface RoomWarnMsg {
/** 处理类型 */
type: 'warning' | 'cut'
/** 处理原因 */
msg: string
}
房间开启、关闭全局禁言
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
}
房间设立、撤销房管
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)
- Based on tiny-bilibili-ws
MIT