云端免挂QQ智能机器人搭建指南:发信API与智能回复全解析
2026/9/24 19:26:12 网站建设 项目流程

简介:这是一套基于服务器或虚拟主机运行的QQ云端免挂机器人发信API资源,面向需要实现24小时自动发信、减少本地挂机依赖的服务器管理者和轻量自动化开发者。整套资源共11个文件,压缩包仅42KB,以6个PHP接口文件为主,配合txt说明文档、菜单配置、CSS样式及指定回复规则,基本覆盖API部署、参数配置与基础回复逻辑;已有205人学习下载。资源包含可运行的PHP源码、必看说明与使用教程,用户上传解压并按文档配置接入点、接收方、发送频率和身份验证信息后,即可在云端持续监听并发送QQ消息,适合自动回复、定时通知等长时在线场景。目前功能偏向单一发信,但胜在轻量易部署,可作为二次开发基础,按需扩展图片/文件发送、关键词触发或与其他服务集成。下载包内目录简洁,文件用途明确,适合具备基础服务器管理经验的用户快速上手。

1. 云端免挂的 QQ 智能机器人:一条消息发出去背后到底有没有人

很多团队和开发者第一次听说「QQ云端免挂智能机器人云端发信API」这个标题时,会下意识把它拆成三件事:免挂、智能机器人、API。真实场景里它们是一个整体——把个人 QQ 账号的收消息、回消息能力搬进云服务器,让一个常驻进程以「机器人」身份在群里聊天;对外暴露的则是一套 HTTP/WebSocket 接口,任何业务系统都能调用它去发私聊、发群聊、发图片和回复消息。

这套方案解决的核心问题只有一个:让账号不依赖本地电脑,不依赖手机常亮,机器人长期在线。常见做法是用开源协议端模拟客户端登录,把事件流通过 WebSocket 推给后端服务,后端再拼装消息内容,走 API 把结果发出去。适合手里没有官方机器人接口资质,又需要在多个群、多个账号上做自动化回复的个人开发者和中小团队。

我自己前前后后搭过三轮类似的系统,从早期 Windows 机上挂一个壳、每天醒来发现掉线,到后来整个链路放在 Linux 轻量服务器上连续运行两个月不用碰。这篇文章会把整个落地方案按顺序拆开:架构怎么分、登录和发信怎么通、智能回复怎么接、以及最容易翻车的那几个点。

2. 拆开「免挂 + 发信 API」:协议端、事件流与云端发信怎么分工

2.1 免挂到底免的是什么:把本地挂机挪进云端服务

「免挂」这个词源自早期聊天机器人必须有一台电脑或一部手机挂着 QQ,一旦锁屏、断网、换机,机器人就消失。云端免挂的本质,是把客户端跑在数据中心的一台 Linux 机器上,通过设备协议直接与服务端通信,不依赖完整图形界面。

这里面最关键的一个概念叫协议端。它做的工作是代替官方客户端完成登录、心跳、收发消息和维持长连接;对外它把消息这种「事件」抽象成标准 JSON,把「发消息」这种操作抽象成接口。你可以把它理解成一个无头版的 QQ 客户端,没有聊天窗口,只有 stdin/stdout 般的消息流。

我习惯把系统拆成三个角色:

  • 协议端:维持账号在线,收发消息,暴露事件和操作接口;
  • 智能服务:订阅事件流,把收到的消息清洗、拼 prompt、调大模型或自研策略,决定回什么;
  • 发信通道:调用协议端暴露的发信 API,把回复投递到群或好友。

如果一个机器人只是做告警通知,不需要智能,那么协议端加发信 API 就够用;如果要做智能问答、群管理、关键词回复,就必须把「智能服务」放在协议端和发信 API 之间。注意别把智能逻辑直接堆在协议端里,更不要在事件回调解里同步调用大模型接口,否则一次慢请求会让整个发送队列堵死。

2.2 事件与发信:单向通知容易做,回信要拿到会话令牌

很多人第一次调通协议端后,会卡在一个问题上:我能收到群里有人发言,但不知道怎么回过去。这里要分清两个方向:

入站是事件推送。协议端收到新消息后,把消息体、发送者、群号、时间戳打包成一个 JSON 事件,通过 WebSocket 或 HTTP 回调推给你的服务。它的方向是协议端到业务服务,只读。

出站是动作调用。你的服务要发消息,必须向协议端发起一次 API 请求,携带目标类型、目标 ID、消息内容,甚至可以是图片、语音。它的方向是业务服务到协议端。这两个方向走的是不同端口、不同连接,所以你会看到协议端同时开着 HTTP 端口和 WebSocket 端口。

理解这一层后,最常见的误解就消失了:不是「我收到消息后直接在回调里 return 一句」,而是你在回调里触发一次 HTTP 调用,把要回复的话交给发信 API。消息 ID 在入站时已经生成,出站时也可以拿到新消息的 ID,这两套 ID 体系用于做去重和追踪。

2.3 选型对比:开源协议端与 API 服务的组合怎么选

标题里的「云端发信 API」并不是一个现成的商业服务,它是一层你自己封装的东西。最省事的做法是选一个开源协议端,把它的 HTTP 接口直接当作发信 API 用,再在外面套一层你自己的鉴权和限流。我对比过常见方案:

方案登录方式出站接口事件推送适合场景
官方机器人平台平台审核HTTP 接口回调有资质的正式机器人,禁个人号
开源协议端 A(Linux 容器化方案)扫码/快速登录兼容 OneBot 11WebSocket/反向 WS个人号、多账号、云端部署
开源协议端 B(跨平台框架)扫码自研 APIWebSocket单号轻量自用
自研 Mini 客户端协议逆向不推荐,维护成本极高

我一般推荐第一款开源方案跑在 Docker 里,因为它把 Linux 底层的登录、依赖和会话文件都打包好了,你只需要管理配置。底层是 OneBot 11 协议,这是目前大多数机器人框架通用的 JSON 协议,资料多,踩坑也能查到。

还有两个容易被忽视的选型点:一是协议端必须支持扫码登录后快速登录复用会话,否则云端重启一次就要重新扫码;二是它的 HTTP 接口必须支持自定义鉴权头,否则发信接口裸奔在公网上,等于把账号交出去。

3. 在云服务器跑通最小发信链路:安装、登录、投递第一条消息

3.1 准备一台能长期连网的轻量机器:规格与网络要求

云端免挂有一个硬性前提:服务器必须能访问腾讯服务的消息通道。这意味着你在本地搭建时一切正常,换成某些网络隔离环境后可能无法登录。我建议用一台国内或中国港澳台的常规云主机,带宽不需要高,1M 都够,因为聊天消息量远小于网页流量。

硬件规格上,一个账号占用的内存和 CPU 非常低。我自己的经验是:单账号 1 核 1G 内存足够;跑 3 到 5 个账号,2 核 2G 也宽松。真正占用资源的是你后续接入的智能模型客户端,因为大模型接口往往是外发的 HTTP 调用,内存不大但网络延迟影响大。

系统选 Debian 12 或 Ubuntu 22.04 都可以;Docker 是必须装的,因为协议端依赖特定运行环境。写代码和跑智能服务时,再单独起一个容器或直接在宿主机装 Python 3.10+。我有一个习惯:协议端和业务服务永远分开目录和容器,方便单独重启。

3.2 拉起无头协议端:一条命令启动,扫码完成登录

先给协议端建一个工作目录,把配置和会话数据放在宿主机,这样容器升级或重启后登录态还在。下面是我常用的启动命令:

mkdir -p ~/napcat/config && cd ~/napcat docker run -d \ --name napcat \ --restart=always \ --network host \ -e NAPCAT_UID=$(id -u) \ -e NAPCAT_GID=$(id -g) \ -v ~/napcat/config:/app/config \ docker.napcat.dev/napcat/napcat:latest

这段命令里几个参数值得单独说:--network host让容器直接复用宿主机网络,协议端的端口监听在宿主机上,避免 Docker 端口映射的额外复杂度;NAPCAT_UIDNAPCAT_GID用当前用户运行,防止挂载目录下生成 root 归属的文件,后面维护会方便很多。--restart=always是免挂的命脉,服务器重启后容器自动拉起来,不需要人干预。

启动后,从容器日志里找到管理面板的访问地址和端口。常见做法是打开http://服务器IP:6099/webui进入管理界面,账号密码在首次启动日志中给出。进入后选择「扫码登录」,用手机 QQ 扫二维码,这一步会把登录态写入配置目录。

3.3 打开云端发信 API:配置 HTTP 端口与访问令牌

登录成功后,协议端管理面板里会有一项「网络配置」,这里就是云端发信 API 的开关。你需要开启 HTTP 服务器,设置端口和鉴权令牌。令牌是一串自定义字符,所有外部调用都要带这个令牌,相当于这个 API 的钥匙。

有一处细节要提醒:有些版本默认监听 127.0.0.1,云端环境里如果你要从另一台机器访问,需要把监听地址改成 0.0.0.0。改了之后,防火墙和安全组把该端口放通。为了安全,端口不要用默认的 3000,改成高位随机端口,比如 18080。

配置完成后,协议端会生成一段连接信息,内容类似下面这样:

HTTP 服务器地址: http://0.0.0.0:18080 HTTP 访问令牌: your-secret-token WebSocket 服务器地址: ws://0.0.0.0:3001 WebSocket 访问令牌: your-secret-token

现在这套运行环境已经具备两个基本能力:WebSocket 用来向你的服务推送新消息事件;HTTP 端口用来接收发信指令。两个端口共用一个令牌,也可以分别设置。建议把它们保存在独立配置文件中,不要写死在代码里。

3.4 用 curl 验证 API 可用:收到回执才算数

配置完成后,先用命令行确认发信链路通不通。找一个测试群,执行下面的请求:

curl -X POST 'http://127.0.0.1:18080/Http_api' \ -H "Authorization: Bearer your-secret-token" \ -H "Content-Type: application/json" \ -d '{ "action": "send_msg", "params": { "message_type": "group", "group_id": 6789012345, "message": "hello from cloud api" } }'

正常情况下返回的 JSON 里会包含status: ok和消息的message_id,这个 ID 就是这条消息在全链路中的凭证。如果返回retcode: 100这类错误,通常是发送失败,需要在第 5 章排查。

拿到回执后我会再做一步:把message_id存到本地,然后去群里看一眼内容和发送者,确认是账号自己发出的。这一步排除了「接口通了但实际没发出去」的假阳性。到这里,云端发信 API 的最小闭环已经成立。

4. 给机器人接上「智能」:事件订阅、AI 回复与 API 封装

4.1 订阅群消息与私聊事件:WebSocket 事件包长什么样

现在协议端已经能发消息,但机器人还不能自己知道该什么时候发。这需要订阅事件流。协议端的 WebSocket 接口会持续推送消息事件,事件包是一个标准 JSON。一条群消息通常长这样:

{ "post_type": "message", "message_type": "group", "group_id": 6789012345, "user_id": 1234567890, "message_id": -123456789, "message": [ { "type": "text", "data": { "text": "你好" } }, { "type": "at", "data": { "qq": "10001" } } ], "raw_message": "[CQ:at,qq=10001]你好" }

关键字段就四个:post_type用来判断是消息还是通知;message_type区分群和私聊;message是消息段的数组,里面的 text 段就是文本内容;user_id是发言者。注意不要直接用raw_message里的 CQ 码去处理文本,那种格式是给老的发送接口用的,解析容易踩坑。

写一个最简订阅程序,验证事件能推到你的代码里:

import asyncio import json import websockets WS_URL = "ws://127.0.0.1:3001" TOKEN = "your-secret-token" async def listen(): async with websockets.connect( WS_URL, additional_headers={"Authorization": f"Bearer {TOKEN}"} ) as ws: print("事件通道已连接") async for raw in ws: event = json.loads(raw) if event.get("post_type") == "message": msg = event.get("raw_message", "") print( f"{event.get('message_type')} " f"{event.get('user_id')}: {msg}" ) asyncio.run(listen())

这段代码的逻辑说明:async for raw in ws会持续收到协议端推送的每个事件,不需要你主动轮询。post_type == "message"过滤掉加群、退群、撤回等系统通知。如果连接被断开,async for会抛异常退出,实际生产代码里要套一个 while True 重连逻辑,后面章节再展开。

4.2 把消息交给智能模型再发出去:一个最小可用的 Python 服务

订阅通了之后,把「收到消息」和「发送消息」串起来。最简洁的方式是收到消息后调一个大模型 API 生成回复,再调用协议端 HTTP 接口发出去。以下是一个可直接跑的完整服务骨架:

import asyncio import json import re import httpx import websockets NAPCAT_API = "http://127.0.0.1:18080/Http_api" NAPCAT_TOKEN = "your-secret-token" LLM_API = "https://api.deepseek.com/chat/completions" LLM_KEY = "sk-your-key" def parse_text(message_segments): parts = [] for seg in message_segments: if seg.get("type") == "text": parts.append(seg["data"].get("text", "")) elif seg.get("type") == "at": qq = seg["data"].get("qq") if qq: parts.append(f"@{qq}") return "".join(parts) async def generate_reply(text: str) -> str: prompt = f"你是一个群聊机器人,请用简短的中文回答:{text}" async with httpx.AsyncClient(timeout=30) as client: resp = await client.post( LLM_API, headers={"Authorization": f"Bearer {LLM_KEY}"}, json={ "model": "deepseek-chat", "messages": [{"role": "user", "content": prompt}], "temperature": 0.7, }, ) data = resp.json() return data["choices"][0]["message"]["content"] async def send_to_group(group_id: int, reply: str): payload = { "action": "send_msg", "params": { "message_type": "group", "group_id": group_id, "message": reply, }, } async with httpx.AsyncClient(timeout=10) as client: r = await client.post( NAPCAT_API, headers={"Authorization": f"Bearer {NAPCAT_TOKEN}"}, json=payload, ) return r.json() async def main(): while True: try: async with websockets.connect( "ws://127.0.0.1:3001", additional_headers={"Authorization": f"Bearer {NAPCAT_TOKEN}"} ) as ws: async for raw in ws: event = json.loads(raw) if event.get("post_type") != "message": continue segments = event.get("message", []) text = parse_text(segments) if not text: continue reply = await generate_reply(text) if event.get("message_type") == "group": await send_to_group(event["group_id"], reply) else: # 私聊用 send_private_msg pass except Exception as exc: print("连接断开,重连中:", exc) await asyncio.sleep(3) asyncio.run(main())

这个服务的参数说明:

  • parse_text把消息段拼成纯文本,顺带保留 at 信息;大模型不认识 CQ 码,这一步必须做;
  • generate_reply调用大模型,超时设为 30 秒;群聊场景建议把超时降到 15 秒,因为用户等太久就失去意义;
  • send_to_group复用协议端 HTTP,timeout=10保证请求不会一直挂住;
  • 外层while True是保命逻辑,WebSocket 断开后自动重建。

注意这版代码是「同步式智能回复」:一条消息进来,先等大模型,再等发送,期间后续事件会堆积。对于一个活跃群这是灾难,后面第 6 章讲队列化解。

4.3 发信 API 的参数细节:at、图片、引用回复

「发消息」这三个字背后有一堆参数细节。标题里的云端发信 API 如果只封装了文本发送,那它是残废的。日常需求至少还包括 at 成员、发图片、引用回复。这三个动作在 OneBot 11 协议里都通过消息段实现,也就是message字段不再是字符串,而是一个数组。

需求message 写法说明
发纯文本直接传字符串兼容性最好
at 某人[{"type":"at","data":{"qq":"12345"}}]at 全体是all
发本地图片[{"type":"image","data":{"file":"/data/pic.jpg"}}]文件必须协议端能访问
发网络图片[{"type":"image","data":{"file":"https://..."}}]需要协议端能出网
引用回复[{"type":"reply","data":{"id":"原消息ID"}}]需把原消息 ID 传进去

把这些段组合起来就能实现「引用那条消息 + at 发送者 + 回复正文」,这是一个群管理机器人最高频的三个动作。我的做法是封装一个build_reply_message函数,接收textreply_idat_user三个参数,返回消息段数组。这样业务层不用关心协议格式。

另外要留意file字段是本地路径时,路径要写成协议端容器能访问到的路径,而不是你服务所在机器的路径。这是新手最常踩的坑:服务在宿主机上跑,图片放在/tmp/a.jpg,协议端在容器里看不见,就会发图失败。

5. 云端免挂机器人避坑:常见故障与排查顺序

5.1 扫码过期或 token 失效:登录态被挤,不重登就静默断线

现象:机器人连续运行几天后突然不回消息,检查协议端 WebSocket 显示已连接,但推送事件停了。看日志发现登录态过期,页面提示需要重新扫码。

原因:QQ 服务端会在长时间不发消息、IP 变化、设备信息变化时让登录态失效。这和你用手机挂 QQ 一个道理,只是云端环境没有主动交互的界面,失效后不容易察觉。

解决:不要重复扫码,那个是最低效的。先在协议端管理页面找到「快速登录」入口,它会读取之前保存的会话文件,双击即可恢复。如果失效频繁,检查服务器出口 IP 是否经常变化;固定出口 IP 后登录态会持久很多。另外,密码登录现在基本不可用,扫码登录后立刻导出会话文件做备份是标准做法。

5.2 消息已收到但发不出去:能收不能发的 3 个原因

现象:事件订阅正常,机器人在群里被 @ 了也有日志,但调用 HTTP 接口返回错误码,群里没有任何动静。

原因一:账号本身被禁言。群里发消息返回错误码120或类似,这种情况只能等禁言结束,程序无需处理,但要记录日志。

原因二:发送频率过高触发风控。连续多次调用发信 API,协议端会拒绝部分请求。这个在代码里必须显式捕获并退避。

原因三:HTTP 路径不对。一开始我查了半天,因为访问根路径返回 404,正确路径是/Http_api,大小写敏感。这属于协议端的特殊路由设计,试一遍马上定位。

解决:给所有发信请求统一打印返回的statusretcode,不要只打印 HTTP 状态码。HTTP 200 不代表发送成功,真正的结果在响应体里。

5.3 发信 API 偶发超时与重复投递:做到幂等要加消息 ID 去重

现象:日志里看到某条回复发了两次,群成员收到两条一模一样的内容;有时请求超时报错,但群里其实已经发出去。

原因:发信请求是网络操作,响应可能在服务端成功处理、报文回传途中丢失。此时客户端不知道结果,会重试一次,于是产生重复消息。

注意:网络烧的是双向的。判断是否重复不能只看业务内容,同一个 prompt 可能生成两次相同回复,这种情况即便没有超时也会重。

解决:在业务层维护一个已发送消息的容器,发送前生成一个唯一seq,把收到的原始消息 ID、目标群、seq 绑定,成功后再遇到相同 seq 直接丢弃。不需要额外引入 Redis,一个字典配合过期清理在这个场景够用。我习惯把去重逻辑放在发送之前,而不是发送之后,因为发送后判重挡不住重试窗口。

5.4 被风控判定异常:频率、内容与设备指纹的三重限制

现象:账号正常,代码正常,但发送一段时间后所有接口都返回失败,网页端登录也提示环境异常。

原因:免挂机器人本质上是非官方客户端,频率突增和内容模式异常会触发服务端风控。常见触发点包括:同一时间大量消息;回复内容里频繁出现 URL;多个账号在同一台服务器上共用相同 IP;消息内容重复率过高。

解决有几个实用技巧。每次发送之间加 1 到 3 秒随机延时,不要用固定间隔。对所有外发内容做敏感词过滤。多账号运行时,尽量给每个账号配置独立的网络入口,避免同 IP 大量 QQ 同时在线。还要注意群内主动发言比回复发言更容易触发风控,回复类机器人翻车率低于主动推送类。

如果已经被临时限制,最快恢复手段是停止发送,保持在线状态,等待数小时到一天。千万不要反复测试接口,那会拉长限制时间。

6. 让发信 API 更抗造:队列削峰、发送前检查与详解可观测性

前面第 4 章我特意留了一个尾巴——同步式智能回复会被慢请求卡死。这一个章节把这段补上。可靠做法的核心是引入一个独立的发送队列:事件订阅进程只负责把「待回复的消息」放进队列,发送 worker 从队列取任务,调用大模型,最终调用发信 API。这样大模型超时只影响当前任务,不会阻塞新消息的事件接收。

发送前做一次状态检查也很有必要。调用发信 API 前,先问协议端一次get_login_info,如果返回的账号 ID 为空,说明登录态已丢,这时直接跳过本轮回复,等待重连完成。不然会出现「大模型已经生成了回复,发送时才发现账号掉线」的浪费。

可观测这块,我会为每条消息打三个日志点:收到事件时记录message_id;发送请求前记录seq和内容长度;发送成功后记录协议端返回的message_id。三组日志串起来,结合时间戳,能准确说出哪一跳延迟高。有了这套链路,再配合 5 分钟内自动重连与 3 次发信重试,云端免挂机器人才算真正能丢在服务器上不管。

这个方向我前后踩了三次:第一次栽在登录态丢失,第二次摔在同步式回复把 WebSocket 事件拉垮,第三次是因为重复发送被群主提醒才意识到健壮性不够。现在我的原则是「可以慢,不要乱」,宁可让一条回复晚几秒,也绝不让同一条消息发两遍。希望这套思路帮你在做云端发信 API 时把最不该栽的坑避开,剩下的迭代阻力就不大了。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询