Go library for subscribing to and sending Bilibili live room danmaku (弹幕) via WebSocket and HTTP API.
- Pub/Sub API — typed callbacks (
OnDanmaku,OnGift, etc.) and channel-based subscription - Send danmaku — send messages via
Client.SendDanmakuor standaloneSender - Auto-split — long messages split into chunks with rate limiting
- Multiple rooms — subscribe to many rooms with a single client
- Auto-reconnect — exponential backoff on disconnect
- Brotli + Zlib — handles all Bilibili compression formats
- Thread-safe — register handlers and send from any goroutine
- Cookie support — optional authenticated access for richer data
- Clean shutdown — context cancellation propagates everywhere
go get github.com/MatchaCake/bilibili_dm_libpackage main
import (
"context"
"fmt"
"os"
"os/signal"
dm "github.com/MatchaCake/bilibili_dm_lib"
)
func main() {
client := dm.NewClient(
dm.WithRoomID(510), // short room IDs are resolved automatically
)
client.OnDanmaku(func(d *dm.Danmaku) {
fmt.Printf("[弹幕] %s: %s\n", d.Sender, d.Content)
})
client.OnGift(func(g *dm.Gift) {
fmt.Printf("[礼物] %s %s %s x%d\n", g.User, g.Action, g.GiftName, g.Num)
})
client.OnSuperChat(func(sc *dm.SuperChat) {
fmt.Printf("[SC ¥%d] %s: %s\n", sc.Price, sc.User, sc.Message)
})
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
client.Start(ctx)
}client := dm.NewClient(dm.WithRoomID(21452505))
events := client.Subscribe()
go client.Start(ctx)
for ev := range events {
switch d := ev.Data.(type) {
case *dm.Danmaku:
fmt.Printf("%s: %s\n", d.Sender, d.Content)
case *dm.Gift:
fmt.Printf("Gift: %s x%d from %s\n", d.GiftName, d.Num, d.User)
}
}client := dm.NewClient(
dm.WithRoomID(510),
dm.WithRoomID(21452505),
)
// Or add rooms dynamically after Start:
client.AddRoom(12345)
client.RemoveRoom(510)Providing cookies enables richer danmaku data (full medal info, etc.):
client := dm.NewClient(
dm.WithRoomID(510),
dm.WithCookie("your_SESSDATA", "your_bili_jct"),
)client := dm.NewClient(
dm.WithRoomID(510),
dm.WithCookie("your_SESSDATA", "your_bili_jct"),
)
// Send after the client is started.
go func() {
ctx := context.Background()
err := client.SendDanmaku(ctx, 510, "Hello from Go!")
if err != nil {
log.Println("send failed:", err)
}
}()
client.Start(ctx)Use NewSender when you only need to send without subscribing:
sender := dm.NewSender(
dm.WithSenderCookie("your_SESSDATA", "your_bili_jct"),
dm.WithMaxLength(30), // UL20+ users can send up to 30 chars
dm.WithCooldown(3 * time.Second),
)
ctx := context.Background()
// Send with default scroll mode.
err := sender.Send(ctx, 510, "Hello!")
// Send with specific display mode.
err = sender.SendWithMode(ctx, 510, "Pinned!", dm.ModeTop)Long messages are automatically split into chunks and sent with rate-limiting pauses between each chunk.
| CMD | Callback | Struct | Description |
|---|---|---|---|
DANMU_MSG |
OnDanmaku |
Danmaku |
Chat messages |
SEND_GIFT |
OnGift |
Gift |
Gift events |
SUPER_CHAT_MESSAGE |
OnSuperChat |
SuperChat |
Super Chat messages |
GUARD_BUY |
OnGuardBuy |
GuardBuy |
Captain/Admiral/Governor purchases |
LIVE |
OnLive |
LiveEvent |
Room goes live |
PREPARING |
OnPreparing |
LiveEvent |
Room goes offline |
INTERACT_WORD |
OnInteractWord |
InteractWord |
Entry, follow, share |
| (any) | OnRawEvent |
[]byte |
Catch-all for unrecognised commands |
go run ./cmd/example -room 510With cookies:
go run ./cmd/example -room 510 -sessdata YOUR_SESSDATA -bili-jct YOUR_BILI_JCTClient (pub/sub hub + sender)
├── roomConn (room 510) ← goroutine: connect → auth → read loop
│ ├── heartbeat goroutine ← sends heartbeat every 30s
│ └── auto-reconnect ← exponential backoff on failure
├── roomConn (room 21452505)
│ └── ...
├── dispatch
│ ├── typed callbacks (OnDanmaku, OnGift, ...)
│ ├── raw event callback (OnRawEvent)
│ └── channel subscribers (Subscribe)
└── Sender (lazy init) ← SendDanmaku → HTTP POST /msg/send
├── auto-split long messages
└── per-room rate limiting
This library implements the Bilibili live WebSocket danmaku protocol:
- Resolve short room ID → real room ID via HTTP API
- Fetch WSS server host + auth token via HTTP API
- Connect to
wss://{host}:{port}/sub - Send auth packet (16-byte header + JSON body, protover=3)
- Send heartbeat every 30 seconds
- Receive command packets (raw JSON, Brotli, or Zlib compressed)
Packets use a 16-byte big-endian header:
[0:4]Total size,[4:6]Header size (16),[6:8]Protocol version,[8:12]Operation type,[12:16]Sequence
MIT