直播中突发网络中断?开播后发现忘开弹幕机?该功能可在未打开弹幕机时持续监控直播间事件,并周期同步至本地,真正做到不错过任何礼物。云端事件同步只适用于控制台模式
- 云端事件可获取最近 72 小时(可配置)内的所有付费礼物事件
- 在控制台中会每 20 分钟自动与云端同步,用于获取主播可能因为网络原因而遗漏的礼物
- 开启 Bridge Mode 后,可与 LEB SDK 进行联动,创建属于您自己的弹幕应用
此功能的配置需要具有一定的计算机基础,如果您是主播,请将此文档移交给社团/公会内相应的技术人员
前置条件
- 熟悉容器或 Kubernetes 的基本操作
- 高中及以上学历(如果您是初中生并且完全可以理解下方的部署文档,可通过 Discord 联系我)
请确认满足上述条件,否则不建议继续阅读
服务器最低要求
- linux/amd64
- CPU:至少 0.25 核
- 内存:至少 256 MB,推荐 512 MB 或更多。内存需求根据监控直播间数量线性增长
- 服务器可长时间稳定与哔哩哔哩弹幕服务器建立连接,推荐日本、新加坡、或中国(域名需备案)节点的服务器
- PostgreSQL
- Redis(可选),用于缓存近期的弹幕消息
Serverless 要求
目前测试通过 Koyeb/Vercel + Supabase/Neon/Render 的排列组合
容器可用标签
latest:最新稳定版<major>.<minor>.<patch>:指定版本edge:最新开发版sha-<hash>:指定 commit hash 的版本
更多标签和版本请查看容器介绍页面
安装方法
- 通过 Docker 或 Docker Compose 或 Kubernetes 进行部署
- 设置公网访问:需要支持 HTTPS 访问,可通过 Traefik、Nginx、Caddy、或 serverless 云服务进行配置
- 输入 API:配置好后,在 LAPLACE Chat 的配置器 - 进阶 - 自定义云端事件 API 中填入您的 API 地址
Docker Compose 配置示例
以下为在 Docker Compose 中部署 LAPLACE Event Fetcher 的配置示例
services:
lef:
image: ghcr.io/laplace-live/event-fetcher:latest
environment:
DATABASE_URL: postgresql://lef:lef@lef-pg:5432/lef
ROOMS: 25034104,456117
TZ: Asia/Shanghai # Recommended, this ensures all cron tasks are executed in CST
depends_on:
- lef-pg
- lef-redis # See below
# If you want to run the server on a different port, you can uncomment the following lines. Default is 8080.
# ports:
# - 9696:8080
restart: always
lef-pg:
image: postgres:18-alpine # Works with version 16 and above
environment:
POSTGRES_DB: lef
POSTGRES_USER: lef
POSTGRES_PASSWORD: lef
volumes:
# For PostgreSQL 17 and below, you should mount to `/var/lib/postgresql/data`
- lef-db:/var/lib/postgresql
restart: always
healthcheck:
test: pg_isready -U lef -h 127.0.0.1
interval: 5s
# Redis is optional for serving recent chat messages
lef-redis:
image: redis:latest
volumes:
- lef-redis:/data
restart: always
volumes:
lef-db:
lef-redis:Kubernetes 配置示例
以下为在 Kubernetes 中部署 LAPLACE Event Fetcher 的配置示例
apiVersion: v1
kind: ConfigMap
metadata:
name: lef-config
data:
ROOMS: "25034104,456117"
TZ: "Asia/Shanghai"apiVersion: v1
kind: Secret
metadata:
name: lef-secret
type: Opaque
stringData:
DATABASE_URL: "postgresql://lef:lef@lef-pg:5432/lef"
POSTGRES_DB: "lef"
POSTGRES_USER: "lef"
POSTGRES_PASSWORD: "lef"apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: lef-pg-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10GiapiVersion: apps/v1
kind: Deployment
metadata:
name: lef-pg
spec:
replicas: 1
selector:
matchLabels:
app: lef-pg
template:
metadata:
labels:
app: lef-pg
spec:
containers:
- name: postgres
image: postgres:18-alpine # Works with version 16 and above
envFrom:
- secretRef:
name: lef-secret
ports:
- containerPort: 5432
volumeMounts:
- name: postgres-storage
# For PostgreSQL 17 and below, you should mount to `/var/lib/postgresql/data`
mountPath: /var/lib/postgresql
livenessProbe:
exec:
command:
- pg_isready
- -U
- lef
- -h
- 127.0.0.1
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: lef-pg-pvc
---
apiVersion: v1
kind: Service
metadata:
name: lef-pg
spec:
selector:
app: lef-pg
ports:
- port: 5432apiVersion: apps/v1
kind: Deployment
metadata:
name: lef
spec:
replicas: 1
selector:
matchLabels:
app: lef
template:
metadata:
labels:
app: lef
spec:
containers:
- name: lef
image: ghcr.io/laplace-live/event-fetcher:latest
envFrom:
- configMapRef:
name: lef-config
- secretRef:
name: lef-secret
ports:
- containerPort: 8080
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: lef
spec:
selector:
app: lef
ports:
- port: 80
targetPort: 8080Ingress、HTTPS 请根据自己的实际需求进行配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: lef-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
rules:
- host: lef.example.tld
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: lef
port:
number: 80部署:Koyeb + Neon
首先,您需要在 Supabase 中创建一个 PostgreSQL 数据库:
- 在 Supabase 控制台中创建一个新数据库
- 将区域改为
AWS US East (N. Virginia) - 记住数据库密码,并从 Connection Details - Connection string 中记下连接字符串
接下来,我们需要在 Koyeb 上部署 event fetcher:
- 创建一个新应用,并选择 Docker 作为部署方式
- 使用
ghcr.io/laplace-live/event-fetcher作为镜像。留空或填写edge可使用最新的测试版镜像 - 将区域改为
WAS(靠近您刚创建的数据库) - 选择
eMicro实例(更大的实例可以承载更多直播间) - 点击
Advanced按钮,并添加以下环境变量:ROOMS和DATABASE_URL。DATABASE_URL的值就是上一步获取的连接字符串,格式类似postgresql://postgres:<DB_PASSWORD>@xxxxxxxxxxxxxxxxxxxx.us-east-2.aws.neon.tech/neondb?sslmode=require - 点击 Deploy
已测试的 Serverless 平台
以下组合均已测试并可正常运行:
- Koyeb + Supabase
- Koyeb + Neon
- Render
- Render + Neon
WebSocket API (Bridge Mode)
Event Fetcher 在根路径 / 提供 WebSocket 模式,用于实时事件流,可作为 Event Bridge Server 的替代方案。设置 BRIDGE_MODE=1 或 BRIDGE_MODE=true 即可启用此功能。启用后,服务器会接受来自 LAPLACE Event Bridge SDK 的 WebSocket 连接
身份验证
若已配置 BRIDGE_TOKEN,客户端需要使用 Sec-WebSocket-Protocol 请求头进行身份验证。例如:
const ws = new WebSocket("ws://localhost:8080/", ["client", "auth-token"]);或者,您也可以通过 token 查询参数传入令牌:
const ws = new WebSocket("ws://localhost:8080/?token=auth-token");直播间筛选
您可以通过 rooms 查询参数筛选特定直播间的事件。请传入以逗号分隔的直播间 ID 列表:
// Only receive events from rooms 456117 and 25034104
const ws = new WebSocket("ws://localhost:8080/?rooms=456117,25034104");
// With query-style authentication
const ws = new WebSocket(
"ws://localhost:8080/?token=auth-token&rooms=456117,25034104",
);若未提供 rooms 参数,您将收到所有已配置直播间的事件
用户 UID 筛选
您可以通过 uids 查询参数筛选特定用户 UID 的事件。请传入以逗号分隔的 UID 列表:
// Only receive events triggered by users 11153765 or 1234567
const ws = new WebSocket("ws://localhost:8080/?uids=11153765,1234567");rooms 和 uids 可以组合使用;同时设置两者时,事件必须同时匹配两个筛选条件才会发送。启用 UID 筛选后,不含 uid(或 uid 为 0)的事件会被排除
之后,服务器每处理一个事件,您都会实时收到相应的 LaplaceEvent
测试
项目包含测试客户端 websocket-test.html,可在浏览器中打开,用于测试 WebSocket 连接并查看实时事件。测试页面还包含 Mock Event Sender 面板,可通过 /mock 端点发送测试事件
您也可以使用 wscat 测试 WebSocket 连接:
# Via Sec-WebSocket-Protocol header
wscat -c ws://localhost:8080 -s client -s <auth-token>
# Via token query parameter
wscat -c 'ws://localhost:8080/?token=<auth-token>'
# With room filtering
wscat -c 'ws://localhost:8080/?rooms=456117,25034104'
# With uid filtering
wscat -c 'ws://localhost:8080/?uids=11153765,1234567'
# With both query-style authentication and room/uid filtering
wscat -c 'ws://localhost:8080/?token=<auth-token>&rooms=456117,25034104&uids=11153765'通过 curl 发送模拟事件:
curl -X POST http://localhost:8080/mock \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-admin-key-at-least-12-chars" \
-d '{"type": "message", "message": "Test message", "username": "TestUser"}'心跳机制
WebSocket 连接包含心跳机制,用于检测失效连接并从中恢复:
- 服务器 → 客户端:服务器每 10 秒发送一条
ping消息 - 客户端 → 服务器:客户端必须回复
pong消息 - 超时:如果 60 秒内未收到
pong,连接将关闭 - 自动重连:兼容的客户端(LAPLACE Event Bridge SDK 1.0.0 及以上版本)会自动重新连接
这可确保长连接即使遇到网络问题、代理超时或其他中断,也能保持稳定
注意:此心跳功能仅在 LAPLACE Event Fetcher 中提供,LAPLACE Event Bridge (LEB) 服务器不支持此功能,因为 LEB 设计为在本地运行,无需担心连接稳定性
OpenTelemetry
本服务提供可选的 OpenTelemetry 支持,用于分布式追踪和可观测性。配置后,会自动为 PostgreSQL 操作添加追踪
Axiom 日志(可选)
如果您使用 Axiom,并希望在追踪之外获得结构化日志,当服务在 OTLP 请求头中检测到 Axiom 凭据时,会自动配置 Pino logger 将日志发送到 Axiom:
- 当
OTEL_EXPORTER_OTLP_HEADERS中同时包含X-Axiom-Dataset和Authorization时,日志会自动发送 - 日志级别默认为
debug
已纳入追踪的组件
启用 OpenTelemetry 后,以下组件会自动纳入追踪:
- PostgreSQL:所有数据库查询和操作
- HTTP 请求:所有传入的 HTTP 请求和响应
API
您可以在自己的实例上通过 /openapi 路径查看 API 文档
GET /events/<room_id>:按直播间 ID 从数据库获取事件- 查询参数:
?full=1:获取所有事件?uids=<uid1>,<uid2>:按一个或多个用户 UID 筛选事件(以逗号分隔)。uid不匹配的事件将被排除
- 查询参数:
POST /upload:上传事件并存入数据库- 请求头:
Authorization:Bearer <ADMIN_TOKEN>(必填,令牌长度至少为 12 个字符)
- 请求体:
- 原始 JSON
LaplaceEvent[]或 LAPLACE Chat Archive (.lca)
- 原始 JSON
- 请求头:
POST /mock:模拟一个LaplaceEvent,并将其广播给所有已连接的 WebSocket 客户端(便于测试)。示例见 @laplace.live/event-types- 请求头:
Authorization:Bearer <ADMIN_TOKEN>(必填,令牌长度至少为 12 个字符)
- 请求体:
- 至少包含
type字段的 JSON 对象(例如{"type": "message", "message": "Test message"}) - 事件中的所有字段均会被广播,并额外添加
mock: true标志和当前时间戳
- 至少包含
- 请求头:
GET /ping:检查服务器是否正常运行。此端点会连接数据库,成功时返回 JSON 格式的pong和 200 状态码,失败时返回 500 状态码GET /info:获取当前实例的信息,包括已连接的直播间和主播信息- 请求头:
Authorization:<AUTH_TOKEN>或Bearer <AUTH_TOKEN>(配置AUTH_TOKEN时为必填)
- 请求头:
环境变量
PORT(可选):服务器监听的端口。默认值:8080ROOMS:要抓取的直播间,多个直播间可用逗号分隔。默认值:456117。建议每个节点添加的直播间少于 10 个,否则哔哩哔哩会阻止您获取事件DATABASE_URL:要连接的数据库,例如postgresql://username:password@pg:5432/lefREDIS_URL(可选):要连接的 Redis,例如redis://username:password@redis:6379EVENTS_KEEP(可选):超过此保留时长的事件将被丢弃。默认值:72(小时)REDIS_EVENT_LIMIT(可选):每个直播间可存储在 Redis 中的最大事件数。默认值:100RESTART_WAIT(可选):重新建立连接前的等待时间。默认值:2000(毫秒)RESTART_INTERVAL(可选):以 cron 格式表示的连接重启间隔。默认值:0 6,18 * * *(每天早上 6:00 和下午 6:00)LOGIN_SYNC_TOKEN(可选):来自 LAPLACE Login Sync 扩展的令牌。默认值:undefined。多个密钥可用逗号分隔AUTH_TOKEN(可选):一个长随机字符串,作为访问 REST API 的凭据。适用于只想向已授权用户开放 API 的情况。默认值:undefined。多个令牌可用逗号分隔(原名AUTH_KEY,仍可使用,但会显示弃用警告)ADMIN_TOKEN(可选):一个长随机字符串,作为向云端上传事件和发送模拟事件的凭据,长度至少为 12 个字符。默认值:undefined。若要上传事件或使用 mock 端点,必须设置此变量。可使用openssl rand -hex 32生成随机字符串(原名ADMIN_KEY,仍可使用,但会显示弃用警告)BRIDGE_MODE(可选):启用 WebSocket bridge 模式以进行实时事件流。设置为false或0可禁用。默认值:undefined(禁用)(原名WEBSOCKET_BRIDGE,仍可使用,但会显示弃用警告)BRIDGE_TOKEN(可选):用于 WebSocket 身份验证的密码。设置后,客户端必须提供此密码才能连接 WebSocket 端点。默认值:undefined(原名WEBSOCKET_BRIDGE_AUTH,仍可使用,但会显示弃用警告)OTEL_EXPORTER_OTLP_ENDPOINT(可选):OpenTelemetry 端点 URL,例如https://api.example.com。未设置时,OpenTelemetry 将被禁用OTEL_EXPORTER_OTLP_HEADERS(可选):随 OpenTelemetry 请求发送的请求头,格式为以逗号分隔的key=value,例如Authorization=Bearer <token>,X-Dataset=my-datasetOTEL_TRACE_SAMPLE_RATE(可选):OpenTelemetry 追踪的采样率。默认值:0.1(10%)
开发
bun run dev # session 1
bunx drizzle-kit studio # session 2
# init db and create migrations
bunx drizzle-kit migrate
# Update schemas
bunx drizzle-kit generateFAQ
为什么没有 Docker Hub?
Docker Hub 很烂
为什么不开源?
- 我不认为其他用户会为这个项目做出贡献
- 我不需要靠开源项目帮助我找工作
- 我不希望其他用户滥用这个项目
最終更新日:2026年9月19日