【QQ机器人】简易部署,仅使用官方开源python代码,无需外部框架接入

【QQ机器人】简易部署,仅使用官方开源python代码,无需外部框架接入
官方最近对使用AIGC接口的机器人进行了封禁,且以往的WebSocket服务将陆续不再支持,故本文旨在以最为基础的办法,不使用外部框架,利用Webhook方式接入机器人

准备工作

前往QQ开放平台注册机器人账号

沙箱配置

“沙箱配置”中配置用于机器人测试的群聊、私聊账号以及频道信息

开发管理

“开发管理”中可以看到机器人的ID、密钥等信息,很重要,后续需要使用。IP白名单中需要放行你的服务器公网IP

回调配置

“回调配置”中需要配置Webhook服务使用的回调地址,需要准备一个已备案的域名,并且域名需要解析到你的公网IP上。同时,域名需要部署SSL证书(能用https访问)。

可以仿照我的后缀写,后续配置Webhook时需要使用

你的域名/qqbot-webhook/callback

填写好此时会提示“校验失败”,不用着急,因为Webhook服务还没有启用,过一会儿会回来重填

篇幅原因,不介绍服务器租用、公网购买、证书部署等相关的知识,网上有文章很多可自查

添加事件省事建议全选,也可以自选需要的,选好后需要在右下角确认配置

阿里云的免费SSL个人测试证书不含CA,回调地址验证时会失败,建议使用腾讯云的免费SSL证书(腾讯云免费SSL证书

 

代码部署

本文疏于解释的地方可参考官方文档官方源代码

创建python环境

在自己的服务器上合适的位置(本文使用/opt/qqbot)创建用于管理机器人代码的文件夹,并从代码仓库导入文件

git clone https://github.com/Space-ash/QQbot.git

在你的项目目录创建python虚拟环境

# 进入你的项目目录 cd /opt/qqbot # 建议使用虚拟环境 python3 -m venv .venv source .venv/bin/activate

安装依赖

# 安装依赖 pip install --upgrade pip pip install fastapi uvicorn pynacl # 改为你的requirements.txt路径 pip install -r /opt/qqbot/requirements.txt

端口放行

阅读官方API文档中“事件订阅与通知”一节,回调地址允许配置的端口号为:80、443、8080、8443,因此需要在服务器对应的端口配置回调代码

此外需要在服务器安全组放行6196端口

端口描述类型
6195企业微信默认端口可选
6199QQ 个人号(aiocqhttp)默认端口可选
6196QQ 官方接口(Webhook)默认端口需要
本文使用的是apache,如果你需要使用nginx或caddy等,可以自行查阅相关资料或询问AI

找到解析到服务器的域名对应的apache配置文件,文件名通常为"你的域名.conf"

<VirtualHost *:80> # ... # 此处是默认有的一大段代码,不用管 # ... # 只用插入这一段,反代 AstrBot 回调 ProxyPass "/qqbot-webhook/callback" "http://127.0.0.1:6196/qqbot-webhook/callback" ProxyPassReverse "/qqbot-webhook/callback" "http://127.0.0.1:6196/qqbot-webhook/callback" </VirtualHost> <VirtualHost *:443> # ... # 此处是默认有的一大段代码,不用管 # ... # 只用插入这一段,反代回调,需要在相应的地方改为你的回调地址后缀 ProxyPass "/qqbot-webhook/callback" "http://127.0.0.1:6196/qqbot-webhook/callback" ProxyPassReverse "/qqbot-webhook/callback" "http://127.0.0.1:6196/qqbot-webhook/callback" # 如果以后用 WS,把升级头转过去 RewriteEngine On RewriteCond %{HTTP:Upgrade} websocket [NC] RewriteRule ^/qqbot-webhook/(.*) ws://127.0.0.1:6196/qqbot-webhook/$1 [P,L] </VirtualHost>

重启apache服务,加载配置文件

service httpd restart

qqbot_webhook.py

使用Webhook服务的主要文件,打开后你需要填入“开发管理”中查看到的机器人ID和密钥

# ======= 明文写入(仅用于你这台服务器;不要提交到仓库)======= APP_ID = "your_app_id_here" # 替换为你的 Bot AppID BOT_SECRET = "your_bot_secret_here" # Bot Secret / AppSecret # ============================================================

如果需要保存日志,则将此处的LOG_DIR改为你的地址

# ---------------- 日志配置 ---------------- LOG_DIR = "/opt/qqbot/logs" # 替换为你的webhook日志地址 os.makedirs(LOG_DIR, exist_ok=True) def write_log(level: str, msg: str): """单文件临时日志,全部写到 qqbot_webhook.log""" path = os.path.join(LOG_DIR, "qqbot_webhook.log") line = f"{datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} | {level} | {msg}\n" with open(path, "a+", encoding="utf-8") as f: f.write(line) f.flush() # -----------------------------------------

回调地址后缀"/qqbot-webhook/callback"则需要改为你希望填写在“回调配置”中的地址后缀

@app.post("/qqbot-webhook/callback") # 改为你的回调地址 async def qqbot_callback(request: Request): write_log("INFO", "=== 收到Webhook请求 ===") # 读取原始 body(验签与 op=13 都可能用到) raw = await request.body()

qqbot_webhook.service

service文件用于服务器后台挂载Webhook服务,需要将WorkingDirectory改为你的service地址,ExecStart改为你的unicorn地址

# /opt/qqbot/qqbot_webhook.service [Unit] Description=QQ Bot Webhook (FastAPI) After=network.target [Service] User=www-data WorkingDirectory=/opt/qqbot # 替换为你的service文件的地址 ExecStart=/opt/qqbot/.venv/bin/uvicorn qqbot_webhook:app --host 127.0.0.1 --port 6196 # 替换为你的uvicorn文件地址 Restart=always RestartSec=3 [Install] WantedBy=multi-user.target

启用Webhook服务

把你的项目路径下的service单元链接进系统搜索路径

sudo systemctl link /opt/qqbot/qqbot_webhook.service

启用服务,以后每次修改Webhook相关的代码,均需要重新启用服务

sudo systemctl daemon-reload sudo systemctl enable --now qqbot_webhook sudo systemctl restart qqbot_webhook sudo systemctl status qqbot_webhook

验证是否启用成功

curl -v -i http://127.0.0.1:6196/qqbot-webhook/callback \ -H 'Content-Type: application/json' \ -d '{"op":13,"d":{"plain_token":"Arq0D5UvOp","event_ts":"1741"}}'

如果终端返回200 OK以及一个含有"plain_token"和"signature"的json,则说明服务启用成功了

* Trying 127.0.0.1:6196... * Connected to 127.0.0.1 (127.0.0.1) port 6196 > POST /qqbot-webhook/callback HTTP/1.1 > Host: 127.0.0.1:6196 > User-Agent: curl/8.5.0 > Accept: */* > Content-Type: application/json > Content-Length: 76 > < HTTP/1.1 200 OK HTTP/1.1 200 OK < date: Tue, 21 Oct 2025 13:37:25 GMT date: Tue, 21 Oct 2025 13:37:25 GMT < server: uvicorn server: uvicorn < content-length: 181 content-length: 181 < content-type: application/json content-type: application/json < * Connection #0 to host 127.0.0.1 left intact {"plain_token":"Arq0D5UvOp","signature":"d7d2e0...83d605"}

如果出现400,404,500等各种各样的报错,可以在终端查看日志。排查错误

journalctl -u qqbot_webhook -n 100 -f

成功后回到官方的“回调配置”网页,在请求地址中填入你的回调地址,发现不再提示校验失败

你的域名/qqbot-webhook/callback

保存配置后,在终端进入项目的python虚拟环境,并运行机器人

# 启用虚拟环境 source .venv/bin/activate # 运行机器人 python3 ./yourbot/demo_yourbot.py

可以看到终端输出,则说明机器人启动成功了

[INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中... [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 2964 [INFO] (client.py:181)_bot_init [botpy] 程序启动... [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1 [INFO] (client.py:242)bot_connect [botpy] 会话启动中... [INFO] (gateway.py:115)ws_connect [botpy] 启动中... [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中... [INFO] (gateway.py:85)on_message [botpy] 机器人「说怪话-测试中」启动成功! [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动... [INFO] (demo_chimera.py:17)on_ready robot 「说怪话-测试中」 on_ready!

打开QQ,私聊机器人,可以看到机器人回复了你的消息

如果机器人没有回复,可以查看logs/qqbot_webhook.log中的信息,或者利用journalctl -u qqbot_webhook -n 100 -f查看终端的运行日志进行调试

正常的配置到此就结束了!如果有后续扩展功能需求的可以往后看。

功能扩展(施工中)

鄙人能力有限,所以也只能本着摸着石头过河的原则,遇到一些对开发有用的信息便记录在此处,如有谬误,望各位大佬指出!

PS.吐槽一句官方文档太久不更新了,查阅了好多资源也比较零碎,整理不易,望海涵

发送私聊消息C2C_MESSAGE_CREATE

这是我配置的第一个测试事件,花费了大量的时间。本质上就是下面这段代码

# 目前这段代码在qqbot_webhook.py中,后续可以单独打包 import botpy from botpy.http import BotHttp from botpy.api import BotAPI from botpy.message import C2CMessage from botpy.types.gateway import MessagePayload from yourbot.demo_yourbot import MyClient # 导入 demo_chimera.py 中的 MyClient def build_message_payload(event: dict) -> MessagePayload: # 补全缺失字段 return { "author": event.get("author", {}), "channel_id": event.get("channel_id", ""), # C2C消息可能没有,可设为"" "content": event.get("content", ""), "guild_id": event.get("guild_id", ""), # C2C消息可能没有,可设为"" "id": event.get("id", ""), "member": event.get("member", {}), # C2C消息可能没有,可设为{} "message_reference": event.get("message_reference", {}), "mentions": event.get("mentions", []), "attachments": event.get("attachments", []), "seq": event.get("seq", 0), "seq_in_channel": event.get("seq_in_channel", ""), "timestamp": event.get("timestamp", ""), } # --- 普通事件(op=0)需要验签 --- if op == 0: # ... # 前面是验签的代码 # ... # 获取事件数据 event = payload.get("d", {}) event_type = payload.get("t", "") # 检查是否为 C2C 消息创建事件 if event_type == "C2C_MESSAGE_CREATE": write_log("INFO", "=== C2C_MESSAGE_CREATE事件 ===") http = BotHttp(timeout=5, app_id=APP_ID, secret=BOT_SECRET) api = BotAPI(http) event_id = event.get("id", "") payload = build_message_payload(event) message = C2CMessage(api, event_id, payload) intents = botpy.Intents(public_messages=True) client = MyClient(intents=intents) await client.on_c2c_message_create(message) 

主要是最后await传递的数据格式需要对应,从而需要有正确的http,api,payload读入。因此需要查阅botpy库中的相关代码(message.py, aip.py, gateway.py, http.py),需要了解各个class之间是怎么传递数据的。

比较坑的一点是payload格式没有统一,在不同的使用场景都会缺失参数,导致C2CMessage传参错误。比如私聊消息的时候,返回的payload是不带channel_id的,所以我自己写了一个build_message_payload函数用于匹配payload格式,空缺的参数设为空,这样传参才不会报错。

关联代码片段1:demo_yourbot.py

# demo_yourbot.py import botpy from botpy import logging from botpy.ext.cog_yaml import read from botpy.message import C2CMessage test_config = read(os.path.join(os.path.dirname(__file__), "config.yaml")) _log = logging.get_logger() class MyClient(botpy.Client): async def on_ready(self): _log.info(f"robot 「{self.robot.name}」 on_ready!") async def on_c2c_message_create(self, message: C2CMessage): _log.info(f"收到消息: {message.content} 来自 {message.author.user_openid}") await message._api.post_c2c_message( openid=message.author.user_openid, msg_type=0, msg_id=message.id, content=f"我收到了你的消息:{message.content}" ) 

关联代码片段2:message.py

传参格式:C2CMessage(api, event_id, data),api来自于BotAPI,event_id来自返回的json格式的payload中的"id",data需要用函数build_message_payload修正格式后的payload
class C2CMessage(BaseMessage): __slots__ = ("author",) def __init__(self, api: BotAPI, event_id, data: gateway.MessagePayload): super().__init__(api, event_id, data) self.author = self._User(data.get("author", {})) def __repr__(self): slots = self.__slots__ + super().__slots__ return str({items: str(getattr(self, items)) for items in slots if not items.startswith("_")}) class _User: def __init__(self, data): self.user_openid = data.get("user_openid", None) def __repr__(self): return str(self.__dict__) async def reply(self, **kwargs): return await self._api.post_c2c_message(openid=self.author.user_openid, msg_id=self.id, **kwargs)

关联代码片段3:api.py

传参格式:BotAPI(http),http来自于BotHttp
class BotAPI: """ 机器人相关的API接口类 使用注意: - 如果要直接使用api,可以通过client的内部成员变量,通过`self.api.xx`来使用 - 设置超时时间: Client(timeout=5) - API当前返回的所有自定义类型数据为字典数据,通过TypedDict进行类型提示 """ def __init__(self, http: BotHttp): """ Args: http (BotHttp): 用于发送请求的 http 客户端。 """ self._http = http

关联代码片段4:http.py

传参格式:BotHttp(timeout, app_id, secret)
class BotHttp: """ TODO 增加请求重试功能 @veehou TODO 增加并发请求的锁控制 @veehou """ def __init__( self, timeout: int, is_sandbox: bool = False, app_id: str = None, secret: str = None, ): self.timeout = timeout self.is_sandbox = is_sandbox self._token: Optional[Token] = None if not app_id else Token(app_id=app_id, secret=secret) self._session: Optional[aiohttp.ClientSession] = None self._global_over: Optional[asyncio.Event] = None self._headers: Optional[dict] = None

关联代码片段5:gateway.py

需要完整的payload,因此使用函数build_message_payload对接收到的payload进行修正
class MessagePayload(TypedDict): author: UserPayload channel_id: str content: str guild_id: str id: str member: Member message_reference: MessageRefPayload mentions: List[UserPayload] attachments: List[MessageAttachPayload] seq: int seq_in_channel: str timestamp: str

 

Read more

免费且完全开源的金融平台,金融数据集软件openbb

免费且完全开源的金融平台,金融数据集软件openbb

首个免费且完全开源的金融平台 repo:https://github.com/OpenBB-finance/OpenBB 手册:https://docs.openbb.co/odp/python/quickstart agent:https://github.com/OpenBB-finance/agents-for-openbb 提供股票、期权、加密货币、外汇、宏观经济、固定收益等多种金融工具的访问权限,并提供广泛的扩展功能,以满足用户的不同需求。 注册 OpenBB Hub,充分利用 OpenBB 生态系统。 还开源了一个可以访问 OpenBB 中所有数据的 AI 金融分析师代理,该存储库可以在此找到这里。 1. 安装 OpenBB 平台可以通过运行 pip install openbb 作为 PyPI

By Ne0inhk
[JAVA探索之路]带你理解Git工作流程

[JAVA探索之路]带你理解Git工作流程

目录 引言 一、Git核心概念 二、四种主流工作流 中心化工作流 功能分支工作流 GitFlow工作流 Forking工作流 场景选择推荐 三、Git实用工具和小技巧  Git钩子 急救命令 四、一些小建议 引言 想象一下,你和几个朋友一起写一本小说。如果大家都直接在同一个文档上改,很快就会乱套:有人删了重要情节,有人同时修改同一段落,最后谁也不知道哪个版本是对的。 Git就是解决这个问题的“超级版本管理器”,而工作流程就是大家约定好的“写作规矩”。没有规矩,再好的工具也会用乱。今天,我就带你理清各种Git工作流,找到适合你团队的那一套。 一、Git核心概念 * 仓库:就是你的项目文件夹,Git会记录里面所有文件的变化 * 提交:相当于给当前版本拍张“快照”,并写上说明 * 分支:从主线分出去的“平行世界”,可以在里面大胆实验而不影响主线 * 合并:把分支的改动整合回主线 简单来说,

By Ne0inhk
【开源自荐】 AI Selector:一款通用 AI 配置组件,让你的应用快速接入 20+ LLM AI厂商

【开源自荐】 AI Selector:一款通用 AI 配置组件,让你的应用快速接入 20+ LLM AI厂商

在线演示:https://tombcato.github.io/ai-selector/react/index.html 开源地址:https://github.com/tombcato/ai-selector 官网介绍:https://tombcato.github.io/ai-selector 前言 如果你正在开发一个需要用户配置 AI 服务的应用(比如 AI 写作助手、智能客服、代码补全工具等),你可能会遇到这些问题: * 🤯 需要对接多家 AI 厂商(OpenAI、Claude、Gemini、DeepSeek…),每家 API 格式都不一样 * 📜 需要维护一长串模型列表,还要时刻关注厂商更新 * 🎨 还要设计一套 UI 让用户方便地选择和配置 AI Selector 就是为解决这些问题而生的。 Smart

By Ne0inhk

通过GitHub Projects管理ms-swift开发路线图

通过 GitHub Projects 管理 ms-swift 开发路线图 在大模型技术飞速演进的今天,一个关键问题日益凸显:如何将前沿算法快速、稳定地转化为可落地的生产系统?研究团队常常面临这样的困境——训练脚本写了一堆,部署流程各自为政,多模态支持零散拼凑,最终导致迭代缓慢、协作困难。尤其当项目涉及数百种异构模型和多种硬件平台时,缺乏统一工程框架的成本会被急剧放大。 魔搭社区推出的 ms-swift 正是为解决这一难题而生。它不仅是一个微调与部署工具包,更是一套面向生产环境的大模型工程基础设施。从预训练到量化推理,从文本模型到多模态系统,ms-swift 提供了全链路标准化能力。更重要的是,这样一个复杂系统的持续演进,并非依赖少数核心开发者推动,而是通过 GitHub Projects 实现透明化、社区化的路线图管理。 这种“开源 + 敏捷看板”的组合,让功能规划不再藏于内部会议纪要中,而是公开可见、可参与、可追踪。每一个新特性、每一次优化,都以卡片形式展现在项目面板上,关联具体的 issue、PR 和里程碑。

By Ne0inhk