这次我想在 Linux 服务器上把一套 QQ 机器人真正跑稳,于是最后落下来的方案是 NapCat + NoneBot2 + GsCore,前面再加一层 Caddy 做 HTTPS 和反向代理。思路并不复杂,但真正一路走下来,问题几乎都出在那些“看起来只是小细节”的地方:终端编码、脚手架命令、Web 后台监听地址、证书验证,甚至是最基础的 DNS。
这篇文章把整套过程整理成一份可以直接照着走的教程,也把我们这次遇到的坑和修法一并写下来。它不是“理想环境下一次成功”的笔记,而是更接近真实部署现场的版本。
我们最终采用的是下面这条链路:
QQ / 群消息
-> NapCat
-> NoneBot2
-> nonebot-plugin-genshinuid
-> GsCore
-> 具体业务插件(如 GenshinUID)
如果需要把后台安全地开放到公网,则再额外加一层:
Browser
-> Caddy (HTTPS / reverse_proxy)
-> NapCat WebUI / GsCore WebConsole
这里我有两个实际建议:
6099 和 8765 不要直接裸露到公网,尽量只让 Caddy 回源到本机端口。NapCat:负责协议端和 QQ 登录,本质上是机器人和 QQ 之间的入口。NoneBot2:负责 Bot 框架本身,接收 OneBot 事件、分发插件、组织逻辑。GsCore:早柚核心,负责统一连接和插件生态。nonebot-plugin-genshinuid:NoneBot2 侧连接 GsCore 的官方插件。Caddy:负责自动 HTTPS、反向代理,以及把后台入口放到统一域名下。本文默认:
root 或 sudo 权限建议目录结构像这样:
~/qqbot # NoneBot2 项目
~/gsuid_core # GsCore 本体
NapCat 官方 Linux 安装器可以直接使用:
curl -o napcat.sh https://nclatest.znin.net/NapNeko/NapCat-Installer/main/script/install.sh
bash napcat.sh --tui
安装后,通常可以通过下面的命令再次进入它的 TUI:
sudo napcat
刚打开 Napcat Shell 时,我们看到的是一堆类似 ~@~A、~M~U 的乱码,而不是正常中文界面。这不是 NapCat 坏了,而是典型的终端字符集和 TERM 设置问题。
先检查:
locale
echo $LANG
echo $TERM
如果不是 UTF-8,可以先临时修正:
export LANG=zh_CN.UTF-8
export LC_ALL=zh_CN.UTF-8
export TERM=xterm-256color
sudo napcat
如果服务器没有对应 locale,再补:
sudo apt update
sudo apt install -y locales
sudo locale-gen zh_CN.UTF-8
sudo update-locale LANG=zh_CN.UTF-8
如果你不想使用中文 locale,用通用的 C.UTF-8 也可以:
export LANG=C.UTF-8
export LC_ALL=C.UTF-8
export TERM=xterm-256color
sudo napcat
NapCat 的 WebUI 默认通常在 6099 端口。启动后,可以在日志里看到带 token 的访问地址。登录后完成 QQ 扫码,并修改好 WebUI 密码。
先建立 Python 虚拟环境:
mkdir -p ~/qqbot
cd ~/qqbot
python -m venv .venv --prompt nonebot2
source .venv/bin/activate
安装基础依赖:
pip install "nonebot2[fastapi]"
pip install nonebot-adapter-onebot
创建 .env:
HOST=127.0.0.1
PORT=8080
ONEBOT_ACCESS_TOKEN=改成你自己的长随机串
COMMAND_START=["/"]
COMMAND_SEP=["."]
创建 bot.py:
import nonebot
from nonebot.adapters.onebot.v11 import Adapter as OneBotV11Adapter
nonebot.init()
driver = nonebot.get_driver()
driver.register_adapter(OneBotV11Adapter)
if __name__ == "__main__":
nonebot.run()
启动试一下:
source .venv/bin/activate
python bot.py
在 NapCat 的网络配置里,新建一个 WebSocket 客户端,把它指向:
ws://127.0.0.1:8080/onebot/v11/ws
Token 要和 .env 中的 ONEBOT_ACCESS_TOKEN 保持一致。
如果出现 403,优先检查 token 是否一字不差。
根据 GsCore 官方文档,把它单独安装在 Bot 目录的同级:
cd ~
git clone https://github.com/Genshin-bots/gsuid_core.git --depth=1 --single-branch
cd gsuid_core
官方更推荐 uv:
uv python install 3.13
uv sync --python 3.13
uv run python -m ensurepip
启动 GsCore:
uv run core
首次启动后,会生成:
~/gsuid_core/data/config.json
~/gsuid_core/data/core_config.json
GsCore 在 NoneBot2 侧的官方插件是 nonebot-plugin-genshinuid。
nb 命令不存在我们一开始按文档执行:
nb plugin install nonebot-plugin-genshinuid
结果报错:
-bash: nb: command not found
原因不是项目坏了,而是环境里没装 nb-cli。nb 不是 Python 自带命令,它是 NoneBot 的脚手架。
有两种解法:
nb-clicd ~/qqbot
source .venv/bin/activate
pip install nb-cli
nb plugin install nonebot-plugin-genshinuid
pip如果你已经手写了 bot.py,其实直接安装插件通常就够了:
cd ~/qqbot
source .venv/bin/activate
pip install nonebot-plugin-genshinuid
这次我们更推荐第二种,因为路径更短,也更少引入额外依赖。
.env 加上 GsCore 配置在原有 .env 下面继续补:
gsuid_core_ws_token=123
gsuid_core_host=localhost
gsuid_core_port=8765
gsuid_core_botid=NoneBot2
config.json编辑:
~/gsuid_core/data/config.json
至少确认这些值:
{
"HOST": "localhost",
"PORT": "8765",
"masters": ["你的QQ号"],
"WS_TOKEN": "123",
"TRUSTED_IPS": ["127.0.0.1"]
}
注意:
WS_TOKEN 要和 NoneBot .env 里的 gsuid_core_ws_token 一致masters 填你自己的 QQ 号,否则后面很多核心命令你没有权限执行GsCore 本体只能算平台,真正的功能还需要业务插件。
如果你已经把自己加入 masters,可以在聊天里让 GsCore 直接安装:
core安装插件GenshinUID
如果你更想手动装:
cd ~/gsuid_core/plugins
git clone -b v4 https://github.com/KimigaiiWuyi/GenshinUID.git --depth=1 --single-branch
安装完记得重启 GsCore。
GsCore 的后台是它的 Web Console,默认路径是:
http://127.0.0.1:8765/app
如果你在本机浏览器访问,这通常就够了。
我们在公网直接访问时拿到了 HTTP ERROR 502,后来看到 GsCore 的提示日志:
WebConsole挂载于本地, 如想外网访问请修改data/config.json中host为0.0.0.0!
这说明后台只监听本机,外部访问当然进不来。最直接的修法是把:
"HOST": "localhost"
改成:
"HOST": "0.0.0.0"
但更好的做法是不要直接把 8765 裸暴露出去,而是让 GsCore 继续只监听本机,再交给 Caddy 反向代理。
从实际安全性上看,把 6099 和 8765 直接开公网不是个好习惯。更稳妥的方式是:
80/443Caddy 统一做 HTTPS 和反向代理在 Ubuntu / Debian 上可以这样装:
sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
让后端都只监听本机:
127.0.0.1:6099127.0.0.1:8765然后写 /etc/caddy/Caddyfile:
{
email yourmail@example.com
}
napcat.example.com {
reverse_proxy 127.0.0.1:6099
}
gscore.example.com {
reverse_proxy 127.0.0.1:8765
}
检查并重载:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl status caddy
Caddy 启动后本身是正常的,但日志里一直在报 challenge failed。最后发现问题并不在 Caddy,而是更前面的基础设施:我忘了去 Cloudflare 里添加 DNS 记录。
这类错误很典型,通常是下面几种之一:
80/443 没有对公网放通80 或 443查看 Caddy 日志:
sudo journalctl -u caddy -n 80 --no-pager
检查域名解析:
dig +short napcat.你的域名 A
dig +short gscore.你的域名 A
检查监听情况:
ss -lntp | grep ':80\|:443'
检查防火墙:
sudo ufw status
如果你在 Cloudflare 里忘了加 DNS,那么 Caddy 的 ACME 验证当然不可能成功。这个问题很“基础”,但也最常见。
如果只是自己使用后台,我建议这样收口:
6099 和 8765 不对公网开放80/443Caddy 统一反代这里要特别提醒一点:邮箱不是认证机制。它只是 Caddy 申请证书时的联系邮箱,不是登录保护。真正保护你后台的,是 HTTPS、反代边界、后台密码、token,以及你有没有把源端口直接开到公网。
部署完成后,推荐按这个顺序启动和排查:
NapCatNoneBot2GsCoreCaddy如果有问题,也按这条链路往后看:
QQ 登录是否正常
-> OneBot WebSocket 是否正常
-> NoneBot 是否启动
-> GsCore 是否已连接
-> 插件是否已加载
-> Web 后台是否可访问
-> HTTPS / DNS / Caddy 是否正常
先看 LANG、LC_ALL、TERM,别急着怀疑软件本身。
nb 命令不存在,往往只是没装 nb-cli如果你走的是手写 bot.py 路线,很多时候直接 pip install 插件反而更省事。
看到 host=localhost 或“挂载于本地”的提示,就不要继续往外网层面猜了。
反代、TLS、证书这些东西看起来高级,但最常见的故障点往往仍然是最基础的那层。
这套方案最后跑通时,我最强烈的感受并不是“终于把机器人装好了”,而是这类部署工作的难点从来都不只是安装命令本身。真正决定你后面省不省心的,是边界有没有收清楚:协议端和框架端是不是分离,后台是不是裸露在公网,域名是不是已经指对,日志是不是能帮你快速定位问题。
把这些边界收好之后,NapCat + NoneBot2 + GsCore + Caddy 其实是一套非常顺手的组合。它不算最轻,也不算最花哨,但足够清晰,足够稳定,也足够适合长期维护。