资讯动态

基于go-cqhttp与FastAPI构建QQ机器人:实现OpenClaw自动化信息推送

发布时间:2026/8/15 7:31:12 来源:尧图企业网站定制
1. 项目缘起为什么要把OpenClaw和QQ机器人连起来最近在折腾一些自动化流程发现一个挺有意思的需求我手头有个自己写的工具姑且叫它“OpenClaw”主要功能是帮我从各种网页或者API里抓取、处理一些结构化的信息比如新闻摘要、商品价格、天气数据或者是一些特定格式的文档内容。这东西用起来挺顺手但有个问题——它是个命令行工具或者是个跑在后台的服务每次想看结果都得去开个终端敲命令或者刷新一下网页界面总觉得不够“丝滑”。这时候我就想要是能把处理结果直接推送到我日常高频使用的聊天软件里比如QQ那不就方便多了吗想象一下我设置一个定时任务让OpenClaw每天早上9点自动抓取行业资讯然后整理成简报直接发到我的QQ上或者我临时想查个数据直接在QQ里一下机器人发个指令它就能调用OpenClaw去干活然后把结果返回来。这种“服务找人”的模式比“人找服务”体验好太多了。所以“OpenClaw接入QQ机器人”这个事本质上是在搭建一个“信息处理中枢”与“即时通讯前端”之间的桥梁。OpenClaw负责后端的“重活”——数据获取、清洗、分析和格式化QQ机器人则充当了最自然、最便捷的用户交互界面。这个组合能极大地拓展OpenClaw的应用场景让它从一个“工程师工具”变成一个可以服务更广泛人群的“智能助手”。无论是个人用于信息聚合提醒还是小团队内部用来同步数据看板都非常实用。2. 技术栈选型与核心架构设计要实现这个目标我们需要一个稳定、可扩展的QQ机器人框架作为中间件来接收QQ消息、解析指令、调用OpenClaw并返回结果。目前社区主流的选择有几个我们需要根据OpenClaw的特性和我们的需求来权衡。2.1 QQ机器人框架对比目前比较活跃和成熟的方案主要有基于Mirai生态的各类框架以及go-cqhttp配合其他语言SDK的方案。NoneBot2 (Python):这是一个异步的、插件化的机器人框架生态非常丰富。它底层可以对接go-cqhttp一个兼容OneBot协议的QQ客户端实现。如果你的OpenClaw本身就是用Python写的或者你更熟悉Python生态NoneBot2是集成度最高的选择。它的插件系统可以让你把OpenClaw的功能封装成一个或多个插件管理起来非常清晰。Koishi (JavaScript/TypeScript):这是一个功能极其强大的机器人框架同样基于插件化前端有图形化控制台。它原生支持go-cqhttp。如果你的团队更偏向Web全栈或者希望有更美观的管理界面Koishi是很好的选择。你可以用Node.js写插件来调用OpenClaw如果OpenClaw提供HTTP API或者通过子进程执行命令。直接使用 go-cqhttp 的 HTTP/WebSocket API:这是最灵活但也最“原始”的方案。go-cqhttp会提供一个标准的HTTP API或WebSocket服务你可以用任何语言Python, Java, Go, PHP等编写一个后台服务监听这些接口实现消息处理和逻辑调用。这种方式耦合度最低适合对架构有洁癖或者OpenClaw核心逻辑非常复杂、不便嵌入特定框架的情况。2.2 我们的架构决策为了普适性我们假设OpenClaw是一个独立的进程或服务它可能通过命令行参数调用也可能提供了一个本地HTTP API。我们选择go-cqhttp 自定义中间层服务的架构。理由如下解耦清晰OpenClaw的业务逻辑和QQ机器人的消息调度逻辑完全分离。OpenClaw可以独立升级、部署机器人服务只负责协议转换和路由。语言无关中间层服务可以用你最熟悉的语言来写无论是调用OpenClaw的命令行还是请求它的HTTP接口都很方便。便于扩展未来如果想接入微信、钉钉等其他平台只需要为中间层服务增加新的消息适配器即可OpenClaw核心代码无需改动。因此最终架构流如下QQ用户 --发送消息-- go-cqhttp --(HTTP上报)-- 我们的自定义中间层服务 --(调用)-- OpenClaw OpenClaw --(返回结果)-- 我们的自定义中间层服务 --(HTTP API调用)-- go-cqhttp --(发送消息)-- QQ用户/群这个架构中go-cqhttp负责QQ协议通讯我们的服务负责业务逻辑OpenClaw负责核心数据处理。3. 实战部署一步步搭建桥梁接下来我们进入实操环节。我会以使用Python编写中间层服务为例因为它语法简洁生态库丰富适合快速原型开发。3.1 第一步部署与配置 go-cqhttp下载前往go-cqhttp的GitHub发布页根据你的操作系统Windows, Linux, macOS下载对应的可执行文件。首次运行在终端中运行它首次运行会生成配置文件config.yml和设备信息文件device.json。关键配置 (config.yml):account: # 账号配置 uin: 1233456 # QQ账号 password: # 密码为空推荐使用扫码登录 encrypt: false # 是否启用密码加密如启用需使用工具加密 # 连接服务列表 servers: - http: # HTTP通信配置 host: 127.0.0.1 port: 5700 # HTTP服务端口 secret: your_http_secret_key # 密钥用于验证中间层服务需要带上 post: - url: http://127.0.0.1:8080/cqhttp/event # 重点事件上报地址指向我们的中间层服务 secret: your_http_secret_key # 与上面一致 - ws: # WebSocket配置可选用于主动推送 host: 127.0.0.1 port: 6700这里最核心的是servers.http.post.url它告诉go-cqhttp所有收到的事件消息、加群请求等都要POST到这个URL。secret用于简单鉴权。登录再次运行go-cqhttp根据提示选择扫码登录或密码登录。登录成功后它会监听5700端口HTTP API和6700端口WebSocket并开始向http://127.0.0.1:8080/cqhttp/event上报事件。3.2 第二步编写Python中间层服务我们的服务需要做两件事1. 接收go-cqhttp上报的事件并处理2. 调用OpenClaw。我们使用FastAPI来快速搭建一个HTTP服务因为它异步性能好写起来简单。pip install fastapi uvicorn requests新建一个文件bot_server.pyfrom fastapi import FastAPI, Request, HTTPException, Header from pydantic import BaseModel import subprocess import json import asyncio from typing import Optional import httpx app FastAPI() # 配置项应与 go-cqhttp 配置一致 CQHTTP_POST_SECRET your_http_secret_key CQHTTP_API_URL http://127.0.0.1:5700 # go-cqhttp 的API地址 API_TOKEN your_token # 调用API时可选的Token # 定义消息上报的数据模型简化版 class CQEvent(BaseModel): post_type: str message_type: str sub_type: str message_id: int user_id: int message: str raw_message: str font: int sender: dict group_id: Optional[int] None # 如果是群消息则有此字段 # 1. 接收事件上报的端点 app.post(/cqhttp/event) async def handle_event(request: Request, x_signature: Optional[str] Header(None)): 处理 go-cqhttp 上报的所有事件。 x_signature 是 go-cqhttp 可能携带的签名头这里我们用固定的secret验证。 # 简单的Secret验证生产环境建议更复杂的签名验证 client_host request.client.host # 这里可以加IP白名单校验 # if client_host not in [127.0.0.1]: # raise HTTPException(status_code403, detailForbidden) body_bytes await request.body() try: event_data json.loads(body_bytes) except json.JSONDecodeError: raise HTTPException(status_code400, detailInvalid JSON) # 这里可以验证 secret例如通过请求头或body的某个字段 # 本例假设在url配置了secretgo-cqhttp会在header X-Signature 携带签名验证逻辑略。 # 只处理私聊和群聊中的文本消息 if event_data.get(post_type) message: message_type event_data.get(message_type) user_id event_data.get(user_id) group_id event_data.get(group_id) raw_message event_data.get(raw_message, ).strip() # 判断是否是调用OpenClaw的命令例如以 !claw 或 /claw 开头 if raw_message.startswith(!claw ): command_args raw_message[6:] # 去掉 !claw 前缀 # 异步处理避免阻塞事件上报 asyncio.create_task(process_openclaw_command(command_args, user_id, group_id, message_type)) return {status: ok} # 2. 处理OpenClaw命令的异步任务 async def process_openclaw_command(args: str, user_id: int, group_id: Optional[int], message_type: str): 调用OpenClaw并返回结果到QQ。 # 这里根据你的OpenClaw调用方式编写 # 方式A命令行调用假设OpenClaw是个命令行工具 try: # 安全警告直接拼接命令参数有风险务必做好过滤和验证 # 这里仅作示例生产环境需要严格校验args safe_args [arg for arg in args.split() if arg.isalnum()] # 一个简单的安全过滤 if not safe_args: result_text 参数无效请检查输入。 else: # 假设 openclaw 命令在PATH中 process await asyncio.create_subprocess_exec( openclaw, *safe_args, stdoutasyncio.subprocess.PIPE, stderrasyncio.subprocess.PIPE ) stdout, stderr await process.communicate() if process.returncode 0: result_text stdout.decode(utf-8, errorsignore)[:500] # 限制长度 else: result_text f执行失败: {stderr.decode(utf-8, errorsignore)} except FileNotFoundError: result_text 错误未找到 openclaw 命令请检查安装和PATH配置。 except Exception as e: result_text f调用过程发生未知错误: {str(e)} # 方式BHTTP API调用假设OpenClaw提供了HTTP服务 # async with httpx.AsyncClient() as client: # try: # resp await client.post(http://localhost:8000/claw, json{query: args}, timeout30.0) # resp.raise_for_status() # result_data resp.json() # result_text result_data.get(result, 无结果返回) # except httpx.RequestError as e: # result_text f请求OpenClaw服务失败: {str(e)} # except Exception as e: # result_text f处理响应失败: {str(e)} # 3. 将结果发送回QQ await send_qq_message(result_text, user_id, group_id, message_type) # 3. 调用 go-cqhttp API 发送消息 async def send_qq_message(message: str, user_id: int, group_id: Optional[int], message_type: str): 通过 go-cqhttp 的 HTTP API 发送消息 api_url f{CQHTTP_API_URL}/send_msg payload { message_type: message_type, user_id: user_id, group_id: group_id, message: message, auto_escape: False # 允许发送CQ码如图片 } # 清理None值 payload {k: v for k, v in payload.items() if v is not None} headers {Authorization: fBearer {API_TOKEN}} if API_TOKEN else {} async with httpx.AsyncClient() as client: try: resp await client.post(api_url, jsonpayload, headersheaders, timeout10.0) resp.raise_for_status() print(f消息发送成功: {resp.json()}) except httpx.RequestError as e: print(f发送消息API请求失败: {e}) except Exception as e: print(f发送消息失败: {e}) if __name__ __main__: import uvicorn uvicorn.run(app, host127.0.0.1, port8080)这个服务做了三件事在/cqhttp/event端点接收go-cqhttp推送的消息事件。识别以!claw开头的命令提取参数。在process_openclaw_command异步函数中通过子进程调用本地的openclaw命令行工具示例A或者通过HTTP调用OpenClaw服务示例B注释状态。获取结果后调用go-cqhttp的/send_msgAPI将结果发回给对应的用户或群。3.3 第三步启动与测试确保go-cqhttp已在运行并登录。在终端运行你的中间层服务python bot_server.py。现在服务运行在http://127.0.0.1:8080。用你的QQ向机器人账号发送消息!claw get_news。观察go-cqhttp的日志和你的bot_server.py输出查看消息上报、命令处理、API调用的整个流程。如果一切正常你应该会收到机器人回复的OpenClaw执行结果。4. 核心细节解析与安全加固上面的示例是一个最简可用的原型但在生产环境中我们需要考虑更多。4.1 命令解析与权限控制我们不能让任何人都能随意调用OpenClaw尤其是可能执行危险操作的命令。我们需要一个更健壮的解析器和权限系统。import re from functools import wraps # 定义允许的命令和参数模式 ALLOWED_COMMANDS { get_news: r^(\d)?$, # 可选数字参数如 !claw get_news 5 query_price: r^[a-zA-Z0-9_]$, # 商品ID help: r^$, # 无参数 } # 简单的用户权限映射实际应从数据库或配置读取 USER_PERMISSIONS { 12345678: [get_news, help], # 用户A只能看新闻和帮助 87654321: [get_news, query_price, help], # 用户B权限更多 } def check_permission(user_id: int, command: str) - bool: 检查用户是否有执行该命令的权限 allowed_commands USER_PERMISSIONS.get(user_id, []) return command in allowed_commands async def process_command_v2(raw_msg: str, user_id: int): 增强版命令解析 match re.match(r^!claw\s(\w)(?:\s(.))?$, raw_msg) if not match: return 命令格式错误。正确格式!claw 命令 [参数] cmd, arg_str match.groups() args arg_str.split() if arg_str else [] # 1. 检查命令是否存在于白名单 if cmd not in ALLOWED_COMMANDS: return f未知命令: {cmd}。输入 !claw help 查看帮助。 # 2. 检查用户权限 if not check_permission(user_id, cmd): return 权限不足无法执行此命令。 # 3. 验证参数格式 param_pattern ALLOWED_COMMANDS[cmd] # 将参数列表拼接成字符串进行匹配简单处理 param_to_check .join(args) if args else if not re.match(param_pattern, param_to_check): return f命令 {cmd} 的参数格式不正确。 # 4. 安全地构造系统命令或API参数 # 绝对不要直接拼接使用参数列表。 # 例如对于命令行调用 safe_args [openclaw, cmd] args # 现在 safe_args 是一个列表subprocess会安全处理 # ... 后续调用逻辑4.2 异步处理与超时管理OpenClaw任务可能耗时较长比如抓取大量网页。我们必须避免阻塞主事件循环并设置超时。async def call_openclaw_with_timeout(cmd_args: list, timeout: int 60): 带超时的命令行调用 try: process await asyncio.create_subprocess_exec( *cmd_args, stdoutasyncio.subprocess.PIPE, stderrasyncio.subprocess.PIPE ) try: stdout, stderr await asyncio.wait_for(process.communicate(), timeouttimeout) except asyncio.TimeoutError: process.kill() # 超时则杀死进程 await process.wait() # 等待进程终止 return None, f命令执行超时{timeout}秒已终止。, -1 return stdout, stderr, process.returncode except FileNotFoundError: return None, 未找到可执行文件。, -1 except Exception as e: return None, f创建子进程失败: {str(e)}, -14.3 结果格式化与多媒体支持纯文本可能不够友好。OpenClaw可以返回结构化数据JSON由中间层服务格式化成更易读的消息甚至支持图片。async def format_and_send_result(raw_data, user_id, group_id, msg_type): 格式化结果并发送 # 假设 raw_data 是OpenClaw返回的JSON # {type: text, content: ...} # {type: image, url: http://...} # {type: news_list, items: [...]} if not raw_data: message 未获取到有效结果。 await send_qq_message(message, user_id, group_id, msg_type) return resp_type raw_data.get(type, text) if resp_type text: message raw_data[content][:1000] # 限制长度 elif resp_type image: # 使用CQ码发送图片 image_url raw_data[url] message f[CQ:image,file{image_url}] elif resp_type news_list: items raw_data[items][:5] # 最多5条 msg_parts [最新资讯] for i, item in enumerate(items, 1): msg_parts.append(f{i}. {item[title]} - {item[brief]}) message \n.join(msg_parts) else: message f未知的响应类型: {resp_type} await send_qq_message(message, user_id, group_id, msg_type)5. 生产环境部署与运维要点当这个机器人开始服务真实用户时我们需要考虑稳定性、可维护性和可观测性。5.1 进程管理与高可用不能让服务因为一个未处理的异常就彻底挂掉。推荐使用进程管理工具。Systemd (Linux):为go-cqhttp和你的bot_server.py分别创建service文件。可以配置Restartalways和RestartSec3让它们在崩溃后自动重启。# /etc/systemd/system/openclaw-bot.service [Unit] DescriptionOpenClaw QQ Bot Service Afternetwork.target [Service] Typesimple Useryour_username WorkingDirectory/path/to/your/bot ExecStart/usr/bin/python3 /path/to/your/bot/bot_server.py Restartalways RestartSec3 EnvironmentPATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin [Install] WantedBymulti-user.targetDocker Compose:将go-cqhttp和中间层服务都容器化用docker-compose.yml定义依赖和重启策略部署和管理更干净。version: 3 services: go-cqhttp: image: ... # 或使用构建的镜像 volumes: - ./cqhttp-data:/data restart: unless-stopped bot-server: build: ./bot-server depends_on: - go-cqhttp restart: unless-stopped5.2 日志与监控完善的日志是排查问题的生命线。结构化日志使用structlog或logging模块的JSONFormatter将时间、级别、用户ID、命令、结果状态、耗时等关键字段结构化输出。便于后续用ELK或Loki收集分析。import logging import sys logger logging.getLogger(__name__) handler logging.StreamHandler(sys.stdout) # 配置JSON格式器 logger.addHandler(handler) logger.setLevel(logging.INFO) async def process_command(...): start_time asyncio.get_event_loop().time() logger.info(command_received, user_iduser_id, commandcmd, argsargs) # ... 处理逻辑 duration asyncio.get_event_loop().time() - start_time logger.info(command_completed, user_iduser_id, commandcmd, successsuccess, durationduration)关键指标监控可以简单地在代码中埋点统计命令调用次数、成功率、平均耗时定期打印或推送到监控系统如Prometheus。5.3 配置管理不要将secret、API Token、用户权限列表等硬编码在代码里。使用环境变量或配置文件。import os from pydantic_settings import BaseSettings class Settings(BaseSettings): cqhttp_api_url: str http://localhost:5700 cqhttp_post_secret: str api_token: Optional[str] None allowed_users: dict {} # 可以从环境变量JSON字符串解析 class Config: env_file .env settings Settings()然后在.env文件或系统环境变量中配置。5.4 网络与安全HTTPS:如果中间层服务暴露在公网例如为了回调务必使用HTTPS。go-cqhttp上报的post.url可以配置为https://your-domain.com/cqhttp/event。可以使用Nginx反向代理并配置SSL证书。IP白名单在中间层服务的/cqhttp/event端点严格校验请求来源IP只允许go-cqhttp所在服务器的IP。限流对每个用户或每个QQ号进行命令调用频率限制防止滥用。from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.util import get_remote_address from slowapi.errors import RateLimitExceeded limiter Limiter(key_funclambda: request.headers.get(X-Real-IP, global)) app.state.limiter limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) app.post(/event) limiter.limit(5/minute) # 每分钟5次 async def handle_event(...): ...6. 进阶玩法与扩展思路基础功能跑通后可以玩出更多花样。6.1 状态管理与会话上下文让机器人变得更“智能”能处理多轮对话。例如用户问!claw 查询天气机器人回复“请问查询哪个城市”用户再回复“北京”机器人再调用OpenClaw查询北京天气。这需要引入一个简单的会话状态机或使用内存数据库如Redis来存储上下文。为每个(user_id, session_id)存储当前状态和临时数据。6.2 集成其他数据源与动作OpenClaw不再是唯一的数据处理器。中间层服务可以作为一个“机器人中枢”根据命令路由到不同的后端服务。!claw news- 调用 OpenClaw!todo add 买牛奶- 调用 TodoList 服务!server status- 调用运维监控API 这样一个QQ机器人就成为了团队的统一操作入口。6.3 图形化结果与富文本除了文字和图片go-cqhttp还支持发送XML和JSON格式的卡片消息需要客户端支持可以做出更美观的新闻卡片、数据报表预览等。这需要更复杂的结果格式化逻辑但体验提升巨大。6.4 插件化改造如果你发现中间层服务的代码越来越臃肿可以考虑将其改造成类似NoneBot2的插件化架构。定义一个基础的Plugin类每个功能如新闻查询、价格监控都是一个独立的插件负责自己的命令解析、权限检查和逻辑处理。主程序只负责加载插件、路由消息。这样功能迭代会清晰很多。整个接入过程从最初的一个简单想法到搭建起一个稳定、可扩展的生产级服务涉及了网络通信、安全编程、异步处理、系统部署等多个方面的知识。最关键的体会是一定要把边界划清楚go-cqhttp只管协议中间服务只管路由和业务逻辑组装OpenClaw只管核心数据处理。各司其职出了问题也容易定位。另外对用户输入保持绝对警惕任何从QQ消息中提取出来用于构造系统命令或API参数的部分都必须经过严格的白名单校验或转义这是线上服务安全运行的底线。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价