跳到主要内容

文档

从下载双击就能跑的本机使用,到部署到服务器让观众访问,再到改源码做二次开发,每个环节都有一章。按顺序读能跑通全流程,也可以直接跳到你要解决的那个问题。

快速开始

下载、放进独立目录、双击启动、扫码登录

下载软件

在下表里找到你的系统和架构,下载对应的可执行文件。拿不准架构时,Windows 一律选 amd64;macOS 看左上角苹果菜单里的关于本机,里面写着 M1 / M2 / M3 这类 M 芯片的选 arm64,写着 Intel 的选 amd64。

平台架构下载
Windows64 位(amd64)BiliLiveAssistant-windows-amd64.exe
macOSApple Silicon(arm64)BiliLiveAssistant-darwin-arm64
macOSIntel(amd64)BiliLiveAssistant-darwin-amd64
Linux64 位(amd64)BiliLiveAssistant-linux-amd64
LinuxARM(树莓派等,arm64)BiliLiveAssistant-linux-arm64

上面是 v2.6.0 的固定下载地址,想拿最新版本请到 Releases 页面。

如果因为网络原因连不上 GitHub,可以从 百度网盘 下载同样的文件。

启动程序

先新建一个空文件夹,把下载好的文件放进去,再运行它。

程序第一次运行会在自己所在的目录里生成 config.yaml、数据库文件和logs 日志目录。放进一个单独的文件夹,以后备份或者整台电脑搬走,复制这一个文件夹就够了。

  • Windows:双击 BiliLiveAssistant-windows-amd64.exe 即可。
  • macOS:系统默认会拦下未签名的程序,先照着Mac 使用说明处理一次,之后双击就能运行。
  • Linux:先在终端给它执行权限(chmod +x 文件名),再双击,或在终端里执行 ./BiliLiveAssistant-linux-amd64。

登录并开启监听

  1. 浏览器打开 http://localhost:25443/admin,进入管理后台
  2. 用 B 站账号扫码登录
  3. 填入你的直播间房号,打开监听开关
  4. 完成。之后弹幕、礼物等数据会实时记录并展示在后台,机器人按你的配置自动工作

机器人需要你的电脑保持开机联网,关掉程序它就会停止工作,商城也一并无法访问。

Mac 使用说明

从网上下载的程序会被 macOS 自动打上隔离标记,未经过 Apple 签名与公证的应用一律会被拦下。本项目是无偿开源项目,没有购买 Apple 开发者证书,所以需要你手动执行一次命令把这个标记去掉,之后就能正常运行。

第 1 步:拿到文件路径

先按快速开始下载文件并放进新建的文件夹,然后打开终端(在 启动台 或 应用程序 实用工具 里都能找到)。

把文件直接拖进终端窗口,路径会自动填进去,例如:/Users/xxx/bilibili-live-assistant/BiliLiveAssistant

这就是这个文件的绝对路径。下面两步的命令里都要用到它,把它复制下来备用。

第 2 步:授予执行权限

在终端里粘贴下面这行并回车(末尾的路径换成你刚才拖出来的那串):

chmod +x /Users/xxx/bilibili-live-assistant/BiliLiveAssistant

chmod +x 是告诉系统这个文件可以执行。这条命令成功时不会输出任何内容,没有报错就是成功了。

第 3 步:移除隔离标记

同样在终端里执行下面这行:

sudo xattr -r -d com.apple.quarantine /Users/xxx/bilibili-live-assistant/BiliLiveAssistant

系统会要求输入开机密码。输入时终端不会显示任何字符,连星号都不会有,这是正常的,输完直接回车即可。

移除隔离标记等于跳过系统的安全检查,所以只对本项目官方发布页下载的文件执行这条命令。来源不明的程序不要这么做。

第 4 步:运行

现在双击文件就能运行了。也可以在终端里执行./BiliLiveAssistant(需先 cd 到文件所在目录)。

如果双击后提示已损坏或无法验证开发者,通常是因为文件被移动过或重新下载过。隔离标记是记录在文件上的,重新下载后按上面的命令再做一次即可。

进阶配置

配置文件逐项说明,以及可用的启动参数

启动命令

在终端里启动程序时可以带上参数,覆盖配置文件里的对应设置。双击启动等价于下面两条默认值。

参数默认值说明
-port25443服务监听端口
-configconfig.yaml配置文件路径,不存在时自动创建

先在程序所在目录打开终端:Windows 在文件夹空白处右键 在终端中打开,macOS 在文件夹上右键 新建位于文件夹位置的终端窗口,然后执行:

# macOS / Linux
./BiliLiveAssistant -port 8080 -config conf/config.yaml

# Windows
.\BiliLiveAssistant.exe -port 8080 -config conf/config.yaml

这条命令让程序监听 8080 端口,并读取程序目录下conf 文件夹里的 config.yaml 作为配置文件;该文件不存在时会自动创建。

config.example.yaml

server: # 服务配置
  port: 25443 # 服务监听端口,可用启动参数 -port 覆盖
  read_timeout: 15 # 读取请求超时(秒)
  write_timeout: 15 # 写出响应超时(秒)
  idle_timeout: 60 # 空闲连接保持时间(秒)

database: # 数据库配置
  # 换驱动前请先用 `BiliLiveAssistant db export` 导出旧库数据,改完配置后再 `db import` 导入新库(导入只接受空库)
  driver: sqlite # 数据库驱动:mysql | postgres | sqlite
  # mysql: # mysql 数据库配置(不使用可不进行配置)
  #   host: 127.0.0.1 # 数据库地址
  #   port: 3306 # 数据库端口
  #   user: user # 数据库用户名
  #   password: password # 数据库密码
  #   dbname: databasename # 数据库名,不存在时自动创建
  # postgres: # postgres 数据库配置(不使用可不进行配置)
  #   host: 127.0.0.1 # 数据库地址
  #   port: 5432 # 数据库端口
  #   user: user # 数据库用户名
  #   password: password # 数据库密码
  #   dbname: databasename # 数据库名,不存在时自动创建
  sqlite: # sqlite 数据库配置(不使用可不进行配置)
    filepath: data.db # 数据库文件路径,相对路径相对于配置文件所在目录

pool: # 数据库连接池配置(仅 mysql / postgres 生效,sqlite 固定为单个连接)
  max_open_conns: 25 # 最大打开连接数
  max_idle_conns: 10 # 最大空闲连接数
  conn_max_lifetime: 3600 # 连接最长存活时间(秒),到期后重建
  conn_max_idle_time: 1800 # 连接最长空闲时间(秒),超时后关闭

# redis: # redis 配置(用于登录态单点校验,不使用可不进行配置,留空则跳过该校验)
#   host: 127.0.0.1 # redis 地址
#   port: 6379 # redis 端口
#   password: # redis 密码,无密码留空
#   db: 0 # 库编号
#   pool_size: 20 # 连接池最大连接数
#   min_idle_conns: 5 # 连接池保持的最小空闲连接数

jwt: # jwt 配置
  secret: "your-secret-at-least-32-characters-long!!" # 需要替换为32位的随机字母或数字内容
  access_ttl: 7200 # 访问令牌有效期(秒)
  refresh_ttl: 604800 # 刷新令牌有效期(秒)

cors: # CORS 跨域配置
  allowed_origins: [] # 允许的前端来源列表,为空时允许所有来源
  # allowed_origins:
  #   - "http://localhost:5173"
  #   - "https://your-domain.com"

altcha: # altcha 验证码配置(留空则可以不启用验证码)
  hmac_key: "your-secret-at-least-32-characters-long!!" # 需要替换为32位的随机字母或数字内容

crypto: # 商城请求加密配置
  sign_secret: "your-secret-at-least-32-characters-long!!" # 请求加密 HMAC 签名密钥,需要替换为32位的随机字母或数字内容,必须与前端 shop/.env 中 VITE_SIGN_SECRET 保持一致,生产环境务必修改
  timestamp: 60 # 请求时间偏差窗口(秒),用户请求数据中携带的时间与服务器时间误差超过此设置时视为重放攻击
  require_encryption: false # 是否强制请求体必须加密(仅全 HTTPS 部署可置 true,纯 HTTP 部署必须保持 false)

live: # B站 直播监听配置
  state_file: "bilibili_state.json" # B站 Cookie 持久化文件路径
  test_uids: [] # 测试机器人 UID 白名单,命中的机器人只记录日志不真正发送弹幕(可为空)

file: # 文件存储配置
  upload_dir: uploads # 上传图片落盘目录(对外访问前缀固定为 /uploads)。相对路径以本配置文件所在目录为基准,也可填绝对路径

log: # 日志配置
  dir: logs # 日志根目录,业务模块日志按模块名在其下建子目录,Gin 访问日志、标准库输出、GORM 慢 SQL 分别落在 gin/、stdlog/、gorm/

export: # 数据导出配置
  max_rows: 5000000 # 单次导出行数上限,超出直接拒绝并提示缩小筛选范围
  max_concurrent: 2 # 同时进行的导出数上限
  batch_size: 10000 # 分块扫描的每块行数,越大查询次数越少、单次内存占用越高
  ticket_ttl: 120 # 下载凭证有效期(秒)
  max_duration: 600 # 单次导出墙钟上限(秒)
  write_idle_timeout: 30 # 客户端写空闲上限(秒),每写一块续期一次

# 说明:database.sqlite.filepath、live.state_file、file.upload_dir、log.dir 的相对路径
# 都以本配置文件所在目录为基准解析,不受启动时工作目录影响。
# 例如配置文件在 /opt/app/config.yaml,upload_dir 填 uploads,实际落盘到 /opt/app/uploads。

配置说明

  • 从哪读配置:不带 -config 启动时,程序读取自己所在目录下的config.yaml;文件不存在会自动生成一份默认配置。想换位置,用-config 指过去即可。
  • 注释就是开关:以 # 开头的行都是注释。把行首的 # 删掉,这段配置立刻就生效,MySQL、PostgreSQL、Redis 都是这么启用的。
  • 改完要重启:配置文件只在程序启动时读取一次,改完必须重启程序才会生效。
  • jwt.secret:用来签发登录令牌。自动生成时会写入一串 64 位随机十六进制字符串;手动修改时长度不能少于 32 位,否则程序会拒绝启动。改动之后,所有已登录的会话都会失效,需要重新登录。
  • altcha.hmac_key:留空表示不启用登录验证码。填入任意非空字符串后,管理后台与商城的登录都会多出一道验证码,能有效挡住撞库和脚本爆破。
  • crypto.sign_secret:商城请求的 HMAC 签名密钥,留空表示不启用请求加密。启用后必须与商城前端构建时的VITE_SIGN_SECRET完全一致,否则请求会被判为验签失败(错误码 10009)。填自己生成的 32 位随机字母数字即可,示例里那个your-secret-at-least-32-characters-long!!只是占位符。详情见HTTPS 与请求加密。
  • 和自动生成的关系:首次运行生成的 config.yaml是这份示例的精简版:字段完全一致,只是去掉了大部分注释,并把jwt.secret 填成了随机值。字段含义以这份示例的注释为准。

数据备份与迁移

数据文件在哪、怎么备份、换电脑怎么搬

数据都存在哪

业务数据几乎都在数据库里,剩下几个文件也要一起带走,才算一份完整的备份。

文件 / 目录里面是什么
config.yaml全部配置:端口、数据库连接、各类密钥
data.db全部业务数据:弹幕、礼物、积分、订单、观众、商品都在这里。换成 MySQL / PostgreSQL 之后不会有这个文件,数据在数据库服务那边
bilibili_state.jsonB 站登录态。删掉就得重新扫码登录
uploads/上传的图片,比如商品图和头像
logs/运行日志,排查问题时才用得上,不备份也不影响使用
rsa_*.pem商城请求加密用的密钥对,缺失时会自动重新生成

这些文件都放在配置文件所在的目录里(默认就是程序所在的文件夹),它们之间的相对路径也以这个目录为基准。

备份数据

最省事的办法:停掉程序,把整个文件夹复制一份放到别处(U 盘、网盘、另一块硬盘都行)。配置、数据、图片一次全带走。

要长期存档,或者准备换数据库:用程序自带的导出命令,先停掉程序再执行:

# Windows
BiliLiveAssistant.exe db export -config config.yaml -out backup.sql.gz

# macOS / Linux
./BiliLiveAssistant db export -config config.yaml -out backup.sql.gz

不带 -out 时,导出文件会写到配置文件所在目录,文件名形如dump-sqlite-20260930-101500.sql;以.gz 结尾会自动压缩。同名文件已存在时会拒绝覆盖,确认要覆盖就加上-force。

导出结束会打印表数和行数。导出文件是标准 SQL,交给sqlite3 / mysql /psql 自带的客户端导入也没问题。

恢复与换电脑

手里是整个文件夹的备份:把它复制到新电脑,再把里面的程序文件换成新系统对应的版本,双击运行即可。配置和数据都在,不用重新登录,也不用重新配置。

手里只有导出的 SQL 文件:在新电脑上装好程序,参照进阶配置把 config.yaml 调整好,然后执行:

./BiliLiveAssistant db import -config config.yaml -in backup.sql.gz

两个必须注意的点

  • 导出和导入都要在程序停止时做。程序正在写数据时导出,不同表的数据可能不在同一个时间点上。导出收尾时会核对行数,对不上会报错,但并不保证每次都能发现。
  • 导入只接受空库。目标库里已经有数据会被直接拒绝,并列出是哪几张表、各有多少行。这样导入失败时不会留下一个半旧半新的库,清空重来即可。

升级到新版本

哪些文件必须保留、覆盖的顺序

升级步骤

  1. 停止正在运行的程序
  2. 把整个文件夹复制一份留作退路,万一升级后有问题,可以直接换回来
  3. 按快速开始下载新版本
  4. 用新下载的程序文件覆盖文件夹里旧的那个,其他文件一个都不要动
  5. 重启服务,让它跑新的版本(按部署方式操作,见下一张卡片)

数据库表结构的升级由程序在启动时自动完成,不需要你手动处理,也不会动到已有数据。

按部署方式重启

换好文件之后要重启才会生效,但怎么停、怎么起取决于你当初用哪种方式部署:

部署方式怎么停、怎么起
本地双击运行关掉程序窗口就是停,重新双击就是起
终端部署换完文件先补一次 chmod +x,再执行sudo systemctl restart bililive;想确认有没有起来,用sudo systemctl status bililive。安装步骤见终端部署
宝塔面板部署在 网站 的 Go 项目里找到这个项目,点重启即可。安装步骤见宝塔面板部署

万一重启后没起来,去看程序目录下的 logs文件夹。程序在正式运行模式下只往文件里写日志,终端和服务日志里看不到内容。

只替换程序,其他都留着

升级时真正需要更新的只有程序文件本身。它长这样:Windows 下是.exe,macOS / Linux 下没有扩展名,就是你在Mac 使用说明里授权过的那个文件。

文件升级时怎么处理
程序文件替换成新下载的
config.yaml保留。你的配置全在里面
data.db保留。所有业务数据都在里面
bilibili_state.json保留。删了要重新扫码登录
uploads/保留。上传的图片在里面
logs/留着或删掉都可以

Mac 用户注意

新下载的程序会被 macOS 重新打上隔离标记,覆盖之后要把Mac 使用说明里的 chmod +x 和sudo xattr 两条命令再执行一遍,否则新版本会被系统拦下。

切换数据库

内置 SQLite 之外,换 MySQL / PostgreSQL

情况一:还没有数据,直接换

  1. 停止程序
  2. 编辑 config.yaml(或你用-config 指定的那个文件),把database.driver 改成 mysql或 postgres,再把对应那几行前面的 # 删掉,填上地址、端口、用户名、密码和库名
  3. 重新启动程序。库不存在时会自动创建

情况二:已有数据,要一起迁过去

不同数据库之间的数据不是互通的,得先导出再导入。

  1. 停止程序
  2. 用旧配置把旧库的数据导出来:./BiliLiveAssistant db export -config config.yaml -out backup.sql.gz
  3. 编辑 config.yaml,换掉database.driver 并填好新库的连接信息
  4. 确认目标库已经建好,而且是空的,导入只接受空库
  5. 导入:./BiliLiveAssistant db import -config config.yaml -in backup.sql.gz
  6. 重新启动程序

导出和导入都必须在程序停止时执行。命令的完整说明与两个注意事项见数据备份与迁移。

SQLite 还是 MySQL / PostgreSQL

  • 默认用 SQLite:不需要额外安装和运行任何数据库服务,在低配机器上表现也很好,迁移就是复制一个文件。日常使用机器人、积分商城这些功能完全够用。
  • 它的短板:写入压力大的时候会吃力。比如百舰以上的直播间弹幕刷屏,同时又有一大批观众在积分商城抢购同一件商品。
  • 拿不准就先别换。真遇到瓶颈再换,数据随时可以按上面的步骤迁过去。迁移过程中卡住了,也可以联系作者,顺手帮个小忙他是愿意的。

让外网访问到你的服务

端口、内网穿透、本地模式的边界

默认只有本机能访问

机器人在你自己的电脑上跑起来之后,只有这台电脑能打开它的后台和积分商城。后台App 配置页会直接告诉你当前是不是这种情况。如果是,页面顶部会显示一行当前是本地模式的提示。

想让别人也能访问,本质上只有一件事要做:让人能在网络上找到你这台机器提供的服务。至于这台机器是谁的,反而不重要。

两条路

  1. 把自己的电脑变成服务器。服务器本质上就是一台电脑,所以这条路技术上完全可行。麻烦的地方在于怎么让别人在网络上访问到你的电脑,通常只有两种办法:
    • 用花生壳、Cloudflare Tunnel 这类内网穿透工具:要么有带宽和流量限制,要么在国内提供不了足够稳定的服务
    • 从运营商那里申请公网固定 IP,但多数家庭宽带申请不到

    这个项目本身是完整支持外网访问的,只要能让别人访问到你电脑上的对应端口,就一定能跑起来。

  2. 租一台云服务器(推荐)。后面几章都按这个方案来讲。

不推荐第一条路,不只是因为操作复杂:机器人需要 24 小时不断电、不断网地跑着,而个人电脑很难保证这一点。

服务器与域名准备

怎么买、怎么解析

云服务器是什么

云服务器就是从互联网大厂那里租一台机器来用。好处是它保证稳定的连续运行和网络供给,坏处是要花点钱。费用和配置挂钩,这个项目本身吃不了多少性能,1 核 1G 的最低配也跑得起来,按服务商不同,通常每月开销在 50 元左右。

常见的有这几家,服务和价格各有差异:

没有经验的话推荐直接用阿里云。它未必是最有性价比的,但作为国内市场份额长期第一的服务商,各种服务的完善程度和保障是在线的。

买服务器时要做对的五个选择

前往阿里云 - 云服务器 ECS,支付宝扫码登录,在页面里点创建实例即可购买。不同配置、不同时间的活动价格都不一样,这里不做固定推荐,但有五件事需要强调:

  1. 地域一定要选香港。选大陆地区对外提供服务需要备案,手续繁琐;选国外会导致国内用户难以访问。香港是最优解:既省掉备案,又能给国内用户稳定的访问速度。
  2. 系统镜像选 Ubuntu。在云服务器和互联网公司的 Web 服务场景里,Ubuntu 是目前最主流的选择之一,后续所有教程也都基于 Ubuntu。
  3. 带宽计费模式选按使用流量。访问的人不多时这样更划算:固定带宽是按支持的速度收费,不管有没有被使用都要花钱,速度越高越贵;按使用流量则按实际用量计费,没人访问就不花钱,速度拉满也不会有额外开支。阿里云还有 CDT 模式会送不少流量,小流量站点基本等于免费。
  4. 尽量选 2 核 2G 甚至更高。这个项目没什么性能要求,1 核 1G 也能跑,但机器的负载是由访问人数决定的,配置越好能接待的人越多。1 核 1G 面对几百上千人同时大量访问时可能会卡顿。另外 2 核 2G 以下的机器通常得用命令行部署来省性能,装不了可视化面板,对新手不太友好。最后,太低配的机器反而不一定便宜。
  5. 登录凭证选自定义密码。比密钥对更方便后续连接。

关于第 4 条不必太焦虑:云服务器都支持后续升降配,差价多退少补,性能不足或过剩时随时调整就行。只是想试试的话,还可以先按按量付费按小时开一台机器部署,确定要长期用了再转成包年包月,免得买了才发现不想用。

域名是什么,在哪买

不论你买云服务器还是用内网穿透,最后拿到的地址都是一串数字 IP,外人访问时用的也是这串数字。域名就是给这串数字起的一个名字。

比如39.156.70.46 和 baidu.com指向的是同一台服务器,输入哪个都能打开百度。有个好记的域名,好处是别人更容易记住你的地址,后续服务器换了 IP,用户也还能照常访问。

购买域名:前往阿里云 - 域名注册,搜索心仪的关键词注册即可。

把域名解析到服务器

买完域名还要告诉网络这个域名指向哪台服务器,这一步叫解析。前往阿里云 - 域名解析,找到刚买的域名,在操作里选择解析设置,添加一条记录:

  1. 记录类型选 A,表示解析到一个 IPv4 地址
  2. 主机记录随意,具体含义见解析页面的说明
  3. 记录值填你服务器的 IPv4 地址。服务器创建好后,可以在阿里云 - 云服务器 ECS页面里查看

终端部署

通过命令行部署,最大限度节省服务器性能开支

什么时候选这个方案

终端部署本身不复杂,只是全程要用命令行操作,习惯 Windows 可视化界面的朋友可能会不适应。它的好处是能最大程度省下性能开支:维持一个可视化界面本身就要占用一部分资源,这也是服务器大多用终端操作的原因。

性能有富余的服务器,两种部署方式随意挑;1 核 1G 这类性能紧张的机器,强烈推荐用终端部署。

第 1 步:把程序传上去

  1. 下载一个连接服务器的工具,比如 electerm
  2. 新建连接。主机地址填服务器的 IP,用户名和密码是购买服务器时创建的那套。用户名通常默认是root,密码就是你购买时设置的密码
  3. 连接后切到 SFTP 视图:左侧是你电脑的文件目录,右侧是服务器的。把BiliLiveAssistant-linux-amd64从左侧拖到右侧(也可以在左侧文件上右键选上传)。建议先在服务器的/opt 下新建一个文件夹,比如bilibili-live-assistant,把程序放进去
  4. 上传完成后,在右侧的那个文件上右键,选择编辑权限,把所有权限都勾上然后提交
  5. 右键复制文件路径,应该形如/opt/bilibili-live-assistant/BiliLiveAssistant-linux-amd64

第 2 步:注册成系统服务

回到 SSH 窗口,创建 systemd 服务文件:

sudo vim /etc/systemd/system/bililive.service

粘贴下面的内容,把两个路径替换成你刚刚复制的那串:

[Unit]
Description=BiliLiveAssistant Service
# 确保在网络就绪后再启动
After=network.target

[Service]
# 用 root 运行,避免权限问题
User=root
Group=root

# 可执行文件的绝对路径(不需要任何参数)
ExecStart=/opt/bilibili-live-assistant/BiliLiveAssistant-linux-amd64

# 关键配置:无论因为什么原因退出(崩溃、被杀),都自动重启
Restart=always
RestartSec=5

# 工作目录设置为二进制文件所在目录
WorkingDirectory=/opt/bilibili-live-assistant

[Install]
# 正常开机后启动
WantedBy=multi-user.target

不会用 vim 的话:按 i 进入编辑模式(左下角会出现insert),改完按esc 退出编辑(insert消失),再输入 :wq 回车保存。

两个路径里 WorkingDirectory 不能省。程序默认会去当前工作目录找config.yaml,设成程序所在目录之后,配置文件和数据才会老老实实落在同一个文件夹里。

然后让配置生效并启动:

sudo systemctl daemon-reload
sudo systemctl enable --now bililive

第 3 步:放行端口并打开控制台

在浏览器里访问 服务器IP:25443/admin 就能打开控制台了。

如果打不开,通常是 25443 端口没放行。前往阿里云 - 云服务器 ECS,找到创建的服务器点进去,进入安全组,在入方向添加一条规则:访问来源填0.0.0.0/0,访问目的填25443,保存即可。

第 4 步:绑定域名(可选)

没买域名的话到这里就结束了。买了域名,就继续把它绑上去。先装并启动 nginx:

sudo apt update && sudo apt install nginx -y && sudo systemctl start nginx && sudo systemctl enable nginx

然后新建一个站点配置文件(文件名用你的域名就行):

sudo vim /etc/nginx/sites-enabled/你的域名

粘贴下面的内容,把 server_name 换成你真实的域名(多个域名用空格隔开,就是解析时填的那些)。行尾的分号别删:

server {
    listen 80;
    server_name your-domain-1.com www.your-domain-1.com;  # 换成你的域名,多个用空格隔开

    location / {
        proxy_pass http://127.0.0.1:25443;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

检查配置有没有写错,再让它生效:

sudo nginx -t              # 正常会输出 syntax is ok 和 test is successful
sudo systemctl reload nginx

现在访问 域名/admin 就能打开控制台了。打不开的话,多半是 80 和 443 端口没放行,和前面放行 25443 一样,把这两个端口也加上。

第 5 步:申请 HTTPS 证书(可选)

到这一步为止,用域名访问控制台和积分商城时浏览器会提示不安全。不在意的话可以就此收工;想去掉这个提示,就得配一张证书:

  1. 安装 certbot 来自动签发证书。安装过程中可能问Continue? [Y/n],输入 y回车即可
  2. 执行签发命令。这里的域名指 nginx 里配置的server_name,多个域名可以一起申请,后面继续跟-d 域名2 即可
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d 你的域名

接着按提示填一个邮箱,然后一路输入 y 同意服务条款就行。要申请证书的域名必须先能正常访问,否则会失败。完成后就可以通过https://域名/admin 访问控制台了。

宝塔面板部署

全图形化,不使用命令行完成服务器部署

什么时候选这个方案

宝塔面板是在服务器上装一个可视化界面,之后所有部署操作都在网页上点选完成。好处很直接:看得见摸得着,比命令行好理解得多。

代价有两个:面板本身如果存在安全漏洞会带来风险,维持这个可视化界面也要占一部分性能。所以只推荐 2 核 2G 及以上的服务器用这种方式,配置不够的话请走终端部署。

第 1 步:装好宝塔面板

  1. 前往宝塔面板官网,复制通用安装脚本的命令
  2. 前往阿里云 - 云服务器 ECS,找到你创建的服务器点进去,选择 远程连接 立即登录 连上服务器
  3. 登录后粘贴刚才复制的命令,回车执行。中途如果出现类似Do you want to install Bt-Panel to the /www directory now?(y/n)的询问,输入 y 回车即可
  4. 安装完成后会给出登录地址和账号密码,保存好这些信息,后面的操作都要在浏览器里打开该地址、用这套账号密码登录来完成

如果打不开这个登录地址,说明对应端口没放行。和下面放行 25443 的做法一样,在安全组的入方向里把面板端口也加上即可。

第 2 步:上传程序并创建项目

  1. 登录宝塔面板,左侧菜单选文件,把BiliLiveAssistant-linux-amd64上传到服务器。建议先在 /opt 下新建一个文件夹,比如bilibili-live-assistant
  2. 上传完成后右键该文件,选择权限,把所有权限都勾上并提交
  3. 左侧菜单选网站,顶部切到Go 项目,点添加项目。项目执行文件选刚上传的那个,项目名称随便写,项目端口填25443,勾上后面的放行端口再保存

现在访问 服务器IP:25443/admin 就能打开控制台了。

第 3 步:绑定域名(可选)

没买域名的话到这里就结束了;买了域名就继续:

  1. 左侧软件商店里搜索 nginx 并安装
  2. 回到 网站 Go 项目,点项目名称(或名称后面的设置),选择域名管理,把先前解析好的域名填进去,可以加多个
  3. 选择外网映射,开启它

现在访问 域名/admin 就能打开控制台了。打不开的话多半是 80 / 443 端口没放行,和放行 25443 一样,把这两个也加上。

第 4 步:申请 HTTPS 证书(可选)

到这一步为止,用域名访问时浏览器会提示不安全。想去掉这个提示:

  1. 回到开启外网映射的那个位置,选择SSL,再选Let's Encrypt,申请证书

有的宝塔版本这里叫免费证书,申请时会让你选品牌,选Let's Encrypt 就行。要申请证书的域名必须先能正常访问,否则会失败。完成后就可以通过https://域名/admin 访问控制台了。

图片存到阿里云 OSS

配置上传目录与访问加速

先说结论

这一章是把项目部署到服务器、并且开了积分商城之后才会用到的。本地跑的话不用管它。

没配置 OSS 时,商城里的图片默认就放在服务器上(落盘目录由配置文件里的file.upload_dir 决定),用户每次打开商城都要从你这台服务器把图拉下来。图片一多、人一多,就会慢。

服务器的流量是怎么被消耗的

用户访问积分商城看到的内容,都是你这台服务器提供的。网络这东西运营商两头通吃:用户要消耗流量获取资源,服务器也要消耗流量提供资源。

举个实际的例子:一张 1M 的图片,一个用户要看它,用户消耗 1M 流量获取,服务器也要花 1M 流量提供;如果有 10 个人同时要看,服务器就得提供 10 个 1M。

就像买家与卖家:要数据的人有很多,能提供数据的机器只有一台。瓶颈就出在这里。

为什么图片会加载得慢

  • 带宽被均分。用户看到图片的速度受木桶效应影响:服务器只能提供 1M/s 的话,一个用户哪怕本地能跑 10M/s,拉一张 5M 的图也要 5 秒;10 个人一起访问就把这 1M/s 均分了,每人只有 1/10 的速度,10 个人都要 50 秒才能拿到那张图
  • 物理距离。服务器在香港,数据从香港的机房出发,送到深圳用户和送到北京用户完全不是一回事,距离决定了北京用户天然要比深圳用户慢一点

OSS 做了什么

阿里云 OSS 就是来解决这个问题的。把图片这类吃流量的资源交给阿里云托管,它会把这些资源分发到全国各省的节点上:北京用户就近访问北京,深圳用户就近访问深圳,既没有距离问题,速度也不用担心。

这样一来,用户获取图片不再经过你的服务器,服务器只需要提供极少量的数据,访问速度自然就上去了。

在哪里配置

在管理后台的App 配置页里,有一块阿里云 OSS 配置,填四项内容即可:

  • OSS 地址(Endpoint):形如 oss-cn-hangzhou.aliyuncs.com,带不带 https:// 都可以
  • bucket 名:你创建的存储空间名称
  • AccessKey ID 与 AccessKey Secret:阿里云的访问密钥

该页面本身就写了每一步该去阿里云控制台的哪里创建 Bucket、申请 AccessKey,照着填就行。另外,配置 OSS 之前已经上传的图片还可以用同步功能搬过去,不用重新上传。

HTTPS 与请求加密

证书申请、开启强制加密

什么时候需要关心这一章

请求加密的前提是三件事都做到位:租了服务器做远程访问、用了域名、并且开了HTTPS。

只在本地电脑上跑的朋友不用管这一章,数据根本没出过你的机器,谈不上泄漏。

做远程访问又担心数据安全的话,先把域名和 HTTPS 解决好,这两样没搞定之前,其他任何加固手段意义都不大。域名怎么买见服务器与域名准备;证书怎么申请按你的部署方式,见终端部署或宝塔面板部署。

开启请求加密

web/.env 和 shop/.env里各有一个 VITE_SIGN_SECRET,默认都是空的,也就是不加密。想让管理后台和积分商城的请求都加密传输,按下面两步来:

  1. 在两个 .env 文件里都填上同一串 32 位随机字母数字,然后重新打包并更新到服务器
  2. 把 config.yaml 里的crypto.sign_secret 改成完全一致的内容,然后重启程序

密钥自己生成就行,随便敲 32 位字母和数字即可。别用配置文件里那个your-secret-at-least-32-characters-long!!,那只是占位符,所有照抄的人密钥都一样,加密就失去意义了。

两边的值必须一模一样。对不上会直接导致验签失败,请求全部被拒(错误码 10009),管理后台和商城都进不去。

这一步需要重新构建前端。发行版里打包好的前端是带着当时的密钥编译进去的,要换成你自己的密钥就必须用源码重新打包一次,构建环境见自行构建与打包。

开启强制加密

上面只是让前端会加密。如果用 config.yaml 里的crypto.require_encryption,还能进一步要求服务端只接受加密请求:

  • false(默认):明文和加密请求都接受
  • true:明文请求一律拒绝,只认加密请求

只有全站 HTTPS 才能设成 true。浏览器只在 HTTPS(或本机的 localhost)下才提供加密用的接口,纯 HTTP 访问时前端想做加密也做不到,所以纯 HTTP 部署必须保持 false,否则等于把自己挡在门外。

建议的操作顺序:先把域名和 HTTPS 彻底跑通、能从浏览器正常访问,再回来开加密。

本地开发环境

Go / Node / make dev

需要装什么

本地构建需要 Go 1.25+ 和 Node 24 LTS(或更高的稳定版)。

依赖下载地址
Go前往下载
Node.js前往下载

安装方式各自的下载页里都有说明,这里不再赘述。尽量装更高版本的稳定版。

启动开发环境

在项目根目录执行 make dev,会同时拉起 Go 服务和后台管理前端:

make dev

管理后台前端跑在 http://localhost:3200,接口跑在http://localhost:25443,前端的开发服务器会把/api 的请求转发到 25443。只想跑其中一边的话:

  • make dev-go:只启动 Go 服务
  • make dev-web:只启动管理后台前端
  • make dev-shop:只启动积分商城前端

开发和调试完成后要出正式产物,见自行构建与打包。

自行构建与打包

自行构建三平台产物与校验文件

构建步骤

  1. 克隆项目到本地(或者直接在GitHub 代码仓库下载代码包):
    git clone https://github.com/zxc7563598/bilibili-live-assistant.git
  2. 复制一份 web/.env.example,去掉.example 后缀,重命名为web/.env:它控制管理后台前端。按需调整,默认什么都不用改
  3. 同样复制一份 shop/.env.example 并重命名为shop/.env:它控制积分商城前端。同样按需调整
  4. 在项目根目录执行构建:
    make release

产物长什么样

make release 会并行构建三个平台、共五个可执行文件,全部输出到bin/ 目录:

文件对应平台
BiliLiveAssistant-linux-amd64Linux 64 位
BiliLiveAssistant-linux-arm64Linux ARM(树莓派等)
BiliLiveAssistant-darwin-amd64macOS Intel
BiliLiveAssistant-darwin-arm64macOS Apple Silicon
BiliLiveAssistant-windows-amd64.exeWindows 64 位
SHA256SUMS上表中所有文件的 SHA256 校验和,用来核对下载到的文件有没有损坏

构建是把前端页面一起打进程序里的,所以出来的每个文件都是完整可用的,拿走单个文件就能跑。

还有不少其他命令留在 Makefile 里:只构建当前平台的make build、单独构建前端、清理产物等等。可以直接打开文件看,或者执行make help 列出全部。

项目结构与分层说明

DDD 分层、各层职责

分层与调用关系

Go 后端代码全部在 internal 下,按四层组织。依赖是单向的:handler 调 service,service 调 repository,repository 用 model,反过来不行。

层目录职责
modelinternal/model定义数据结构,供下面各层使用
repositoryinternal/repository定义数据怎么读写,供 service 调用
serviceinternal/service业务规则,决定如何使用 repository
handlerinternal/handler处理接口请求,调用 service

程序启动后从 cmd/server/main.go 进入,由internal/bootstrap把上面这些层装配成一张可运行的对象图。每一层目录下都有一份CLAUDE.md 写着该层的约定,动手改代码前值得先读一遍。

完整目录结构

每个目录负责什么,注释里都写清楚了。内容较长,可以横向滚动查看。

.
├── cmd                                              # 可执行程序入口
│   └── server                                       # 唯一的二进制入口(编译产物 BiliLiveAssistant)
│       ├── db.go                                    # db export / db import 子命令:跨数据库格式的 SQL 导出与导入
│       └── main.go                                  # 进程入口:解析启动参数、装配 bootstrap、启动 HTTP 服务
├── config.example.yaml                              # 配置示例文件,部署时复制为 config.yaml 再改数据库等连接信息
├── docs                                             # Swagger 文档(由 make swagger 从接口注释生成,勿手改)
├── internal                                         # 后端核心代码,外部模块不可导入
│   ├── appconfig                                    # app_configs 表的内存缓存:启动时全量加载,业务侧直接读内存不查库
│   ├── bootstrap                                    # 依赖注入与装配层,把各层对象连成一张可运行的对象图
│   │   ├── app.go                                   # 应用生命周期:初始化数据库、缓存、定时任务等基础设施
│   │   ├── CLAUDE.md                                # 本目录的开发规范说明
│   │   ├── export.go                                # 导出相关依赖(数据导出/备份)的装配
│   │   ├── handler.go                               # Handler 层构造与依赖注入
│   │   ├── repository.go                            # Repository 层构造与依赖注入
│   │   ├── router.go                                # 路由注册:中间件挂载与接口路径映射到 handler
│   │   └── service.go                               # Service 层构造与依赖注入
│   ├── config                                       # 配置结构定义与加载(YAML → 结构体)
│   │   ├── config.go                                # 顶层 Config 结构与加载入口
│   │   ├── database.go                              # 数据库(MySQL / PostgreSQL / SQLite)配置
│   │   ├── default.go                               # 未配置项的默认值填充
│   │   ├── path.go                                  # 运行目录解析:相对路径统一以配置文件所在目录为基准转绝对路径
│   │   └── redis.go                                 # Redis 连接配置(可选,用于 JWT 会话)
│   ├── dto                                          # 数据传输对象:接口的入参出参与模型解耦
│   │   ├── input                                    # 请求入参结构体(绑定 + 校验)
│   │   └── resp                                     # 响应出参结构体(对前端暴露的字段)
│   ├── dump                                         # SQL 导出/导入引擎:读写 sqlite3/mysql/psql 通用的 SQL 文件
│   ├── enum                                         # 枚举定义(直播状态、礼物类型、菜单类型等)
│   ├── handler                                      # HTTP 接口层:参数绑定、调 service、按统一格式返回,不含业务逻辑
│   ├── i18n                                         # 国际化与多语言错误信息
│   │   ├── code.go                                  # 错误码常量定义
│   │   ├── embed.go                                 # 把 locales 下的语言文件编译期嵌入二进制
│   │   ├── i18n.go                                  # 翻译器初始化与按语言取文案
│   │   └── locales                                  # 各语言文案文件(zh-CN / en-US 等)
│   ├── logger                                       # 日志模块
│   ├── middleware                                   # Gin 中间件
│   │   ├── authAdmin.go                             # 管理后台身份认证
│   │   ├── authUser.go                              # 商城(普通用户)身份认证
│   │   ├── corsMiddleware.go                        # 跨域处理
│   │   ├── language.go                              # 解析请求语言,切换 i18n 上下文
│   │   ├── rateLimit.go                             # 接口限流
│   │   ├── rbac.go                                  # 基于角色的权限校验
│   │   └── requestDecrypt.go                         # 请求体解密(前端加密传输,后端按分组解密)
│   ├── migrate                                      # 数据库迁移与初始化数据
│   ├── model                                        # 数据模型:GORM 结构体与表结构映射
│   ├── region                                       # 行政区划数据:嵌入 regions.json,提供省市县查询与校验
│   ├── repository                                   # 数据访问层:只负责数据库读写,一个聚合一个子包
│   ├── response                                     # 统一响应封装(code / message / data 结构)
│   ├── robotconfig                                  # 机器人配置:机器人配置表的结构体化解析与内存缓存
│   ├── service                                      # 业务逻辑层:核心规则都在这里,一个领域一个子包
│   ├── validation                                   # 参数校验器:自定义校验规则注册
│   ├── version                                      # 版本号信息(编译期注入)
│   └── webui                                        # 前端构建产物的嵌入目录
├── Makefile                                         # 构建脚本:dev / build / release / swagger 等命令
├── pkg                                              # 可复用的公共库,不依赖 internal
│   ├── bilibili                                     # B 站 SDK:登录鉴权、直播间长连接、弹幕收发
│   ├── cron                                         # 定时任务调度器封装
│   ├── crypto                                       # 加解密:RSA、密码哈希、请求体解密
│   ├── fileutil                                     # 文件上传与保存工具
│   ├── imagetype                                    # 图片真实类型探测(按文件头识别,防扩展名伪造)
│   ├── jwt                                           # JWT 生成与解析
│   ├── oss                                           # 对象存储上传客户端
│   ├── pagination                                    # 分页参数与结果封装
│   ├── ptr                                           # 指针工具(取字面量地址等)
│   ├── roomgroup                                     # 直播间分组
│   ├── sqlutil                                       # SQL 辅助:LIKE 转义等
│   ├── timeutil                                      # 时间格式化与解析工具
│   ├── tokenizer                                     # 分词器(含停用词表,用于消息匹配/搜索)
│   └── util                                          # 杂项通用函数
├── README.md                                        # 项目说明文档
├── shop                                             # 商城用户端前端(Vue 3 + Vite,移动端 H5/PWA)
│   ├── src
│   │   ├── App.vue                                  # 应用根组件
│   │   ├── assets                                   # 参与构建的静态资源(图片、字体等)
│   │   ├── components                               # 可复用业务组件
│   │   ├── data                                     # 前端静态数据(如 regions.json 地区数据)
│   │   ├── main.js                                  # 应用入口:创建挂载 Vue 实例
│   │   ├── pages                                    # 页面级组件(首页、详情、下单、个人中心等)
│   │   ├── router                                   # 路由配置
│   │   ├── static                                   # 原样拷贝的静态文件
│   │   ├── style.css                                # 全局样式
│   │   └── utils                                    # 前端工具函数(请求封装等)
├── test                                             # 集成测试:B 站接口连通性、登录、消息解密等
└── web                                              # 管理后台前端(Vue 3 + Naive UI + Vite)
    ├── src
    │   ├── api                                      # 接口请求封装,按业务模块划分
    │   ├── App.vue                                  # 应用根组件
    │   ├── assets                                   # 参与构建的静态资源
    │   ├── components                               # 可复用组件(表格、表单弹窗等)
    │   ├── composables                              # 组合式函数(可复用的响应式逻辑)
    │   ├── data                                     # 前端静态数据(如 regions.json 地区数据)
    │   ├── directives                               # 自定义指令
    │   ├── layouts                                  # 页面布局(侧边栏、顶栏、标签页框架)
    │   ├── main.js                                  # 应用入口
    │   ├── router                                   # 路由配置与菜单到组件的映射
    │   ├── settings.js                              # 全局设置(主题、标题等)
    │   ├── store                                    # Pinia 状态管理
    │   ├── styles                                   # 全局样式与主题变量
    │   ├── utils                                    # 前端工具函数(请求、加密、权限等)
    │   └── views                                    # 页面级组件,按业务模块分子目录

接口文档查看与生成

Swagger 入口、如何生成或查看文档

怎么看接口文档

把项目跑起来看。用 make dev-go启动后,浏览器打开下面任意一个地址即可,两个入口内容一样,只是排版不同:

不想跑项目看。打开在线预览,把仓库里 docs/swagger.json 的内容粘进去就行。

文档是怎么来的

文档不是手写的,而是从接口的注释里生成的。在项目根目录执行:

make swagger

它会把最新的内容重新写进 docs/ 目录。这个目录由命令生成,不要手动改,改了下次生成就被覆盖了。想改文档内容,改对应的接口注释。

这个命令依赖 swag 工具。没装的话会提示 go install github.com/swaggo/swag/cmd/swag@latest,照着装一次即可。

二次开发

新增一个机器人功能:从枚举到 handler 的完整链路

先看整条链路

加一个功能,本质上是沿着下面这条链路自下而上补齐缺的环节。每一环都先判断现有的够不够用,够用就跳过,不够才动手。

顺序层位置什么时候需要动
1枚举internal/enum引入了新的类型、状态、分类
2modelinternal/model现有的表装不下要存的数据
3repositoryinternal/repository/<模块>没有现成方法读写这批数据
4serviceinternal/service/<模块>没有现成方法实现这段业务
5handlerinternal/handler/<模块>需要把这个能力对外暴露成接口
6前端web/ 或 shop/需要让用户在页面上操作

动手前先确认影响范围:这次改动到底会碰到上面哪几环。往下一节开始,就是逐环的判断标准。

第 1 步:需要新的枚举值吗

取值有限的东西(类型、状态、开关、分类)都要用枚举表达,不要在代码里直接写 0、1、2 这类魔法值。

要新增枚举的话,两件事一起做:

  1. 在 internal/enum 下加一个枚举类型,实现Key()、Text()、IsValid()三个方法
  2. 在 internal/i18n/locales 的zh.yaml 和 en.yaml里补上对应的文案节点

枚举类型要和数据库字段类型保持一致:数据库是 tinyint就用 int 枚举,是 varchar就用 string 枚举。

第 2 步:数据库结构支持吗

判断:现有的表结构装不装得下这个功能要存的数据?装得下就直接看下一步。

装不下就改 internal/model,再分两种情况:

  • 新增一个 model:在 internal/migrate/migrate.go里挂载它;如果这张表需要预置数据,再酌情考虑在internal/migrate/seed.go 里填充
  • 修改已有的 model:先判断这次改动会不会影响历史数据。会的话,在internal/migrate 下写一个修复方案,并在migrate.go 里执行它

第 3 步:有现成的 repository 方法吗

判断:读写这批数据的方法是不是已经写过了?有就直接看下一步。

没有就创建或完善对应模块的 repository,并确保它注册在internal/bootstrap/repository.go 里,供 service 层调用。

写 repository 的四条规矩:

  1. 继承 internal/repository/base,通用的增删改查直接复用
  2. 和 model 一一对应,每个 repository 只管自己同名 model 的数据,不碰别人的
  3. 涉及多张表的操作,统一放在主表的 repository 里
  4. 一个方法里有不止一步数据操作时,要加事务,防止出现意外情况

第 4 步:有现成的 service 方法吗

判断:这段业务逻辑是不是已经实现了?有就直接看下一步。

没有就创建或完善对应的 service,并确保它注册在internal/bootstrap/service.go 里,供 handler 层调用。service 的核心任务是接住请求、编排 repository、返回结果或异常错误码。

写 service 的五条规矩:

  1. 所有数据操作都通过注入进来的 repository 完成,绝不直接操作数据
  2. 按业务类型确定该动哪个 service,尽量避免无意义的注入
  3. 不依赖外部的数据结构,接收和返回的结构体都定义在internal/service/对应模块/dto.go
  4. 一个方法只实现单一功能,别设计得太复杂;逻辑多就拆到同目录的common.go 里,由主方法调用
  5. 错误码要成体系:每个模块都有自己专属的错误码段,在internal/service/对应模块/code.go里定义,文案对应 internal/i18n/locales 下的文件

第 5 步:需要暴露接口吗

判断:这个能力要不要让前端调用?不需要(比如纯后台跑的定时任务)就直接看下一步。

需要就创建或完善对应的 handler,并确保它注册在internal/bootstrap/handler.go 里,由internal/bootstrap/router.go 暴露出去。handler 的核心任务是过滤用户数据、编排 service、把 service 返回的信息处理成对外的形状。

写 handler 的三条规矩:

  1. 在 internal/dto/input里创建请求结构体,把不合法、不完整的请求拦在外面
  2. 调用 service,处理返回结果,并记录日志
  3. 把 service 返回的数据转换成对外结构体,定义在internal/dto/resp 里

第 6 步:需要改前端吗

接口测通之后再动前端。管理后台在 web/,积分商城在shop/,改对应的那个就行。

上面每一层目录下都有一份 CLAUDE.md,写清了该层的约定和写法示例。真动手之前先读对应那份,或者直接照着已有的同类模块抄一遍,会比自己从头琢磨快得多。

免责声明与风险提示

使用边界、自动化对账号的影响

使用边界

  • 本项目仅供学习交流使用,请在你本人拥有或已获得授权的直播间内使用。
  • 请勿用于抓取他人数据、商业经营,或任何违反平台规则与法律法规的场景。
  • 程序基于 B 站的公开接口工作,只做读取与正常互动,不会篡改平台上的任何数据。

关于账号

自动化工具的运行会受到平台规则调整、风控策略变化等外部因素影响,这类不确定性无法由本项目控制。

另外,同一个账号在多台设备上同时活跃时,登录态容易失效。失效后重新扫码即可恢复,但在失效期间产生的那部分数据不会被记录。

所以建议用一个单独的账号专门跑机器人,与日常使用的账号分开。

数据与责任

所有数据(登录态、弹幕记录、积分、订单、配置等)都保存在你自己的设备上,项目不会上传到任何第三方服务器,你随时可以自行备份、导出或删除。

请在使用前自行评估适用范围与潜在影响。是否使用、如何使用,由使用者自行判断和决定; 因使用本项目产生的任何直接或间接后果,均由使用者自行承担,作者不承担相关责任。

继续使用即表示你已阅读并理解上述内容。如果无法接受,请不要使用本项目。

想深入了解?在 GitHub 上查看源代码和完整的 README。

在 GitHub 上查看