从零搭建定时任务QQ机器人:OneBot+aiocqhttp+APScheduler完整指南 📅 发布时间:2026/9/2 15:19:22 👁 浏览次数: 想做一个定时在 QQ 群发消息、每天定时提醒的机器人但搜了一圈资料后发现要么框架太老跑不起来要么只给概念不给能跑的代码。这篇文章就用一套足够简单、足够快的方案把定时任务 QQ 机器人的完整搭建过程讲清楚包含架构原理、完整可运行代码、常见报错排查和生产环境建议。新手可以照着一步步配置有基础的开发者可以直接复制代码改配置使用。1. 定时任务 QQ 机器人是什么能做什么1.1 什么是 QQ 机器人QQ 机器人本质上是一个能接收 QQ 消息事件、主动调用 QQ 接口发送消息的自动化程序。它通过协议层与 QQ 账号建立连接然后由业务代码决定“收到什么消息做什么回应”或者“到什么时间点主动发什么消息”。定时任务 QQ 机器人就是在普通 QQ 机器人的基础上加入定时任务调度能力。普通的机器人是事件驱动型比如群里有人发消息才触发回复而定时机器人既有事件处理能力又有时间驱动能力比如每天上午 9 点自动在群里发送打卡提醒或者每隔 30 分钟向指定用户推送一条状态信息。1.2 定时任务机器人能解决什么问题定时任务机器人最常见的应用场景包括每日早安、晚安定时发送。每周定期发送周报提醒或会议提醒。每隔一段时间自动拉取天气、股票、赛事等数据并推送到群。监控类任务定时检查服务状态异常时主动通知群成员。群活跃度维护定时发送话题引导消息。这类需求的共性特点是不需要用户主动发消息触发而是按照指定时间点或时间间隔自动执行。如果只靠人工操作很容易遗漏定时任务机器人可以把这些重复工作自动化。1.3 主流实现方案对比当前实现 QQ 机器人主要有三条技术路线方案原理优点缺点基于 OneBot 协议实现如 NapCat、LLOneBot协议实现负责登录 QQ通过 WebSocket/HTTP 与业务代码通信生态成熟社区资料多接入简单账号存在风控风险需使用小号基于 QQ 官方机器人平台使用官方开放接口合规、稳定功能受平台限制个人订阅类需求支持有限自写协议层直接解析 QQ 客户端协议可控性强工作量大逆向成本高不建议个人去碰本文选择的是第一种方案也是目前个人开发者做 QQ 机器人的主流方式使用 OneBot 协议实现负责账号接入Python 端通过 aiocqhttp 作为协议客户端再结合 APScheduler 完成定时任务。2. 环境准备与整体架构2.1 技术选型说明整个项目涉及四个核心组件OneBot v11 协议实现负责账号登录、消息收发、与 QQ 服务器的数据交互。本文以社区常用的 NapCat 为例类似的还有 LLOneBot、Lagrange 等。aiocqhttpPython 端 OneBot v11 协议的 WebSocket 客户端库负责接收消息事件、调用发送接口。APSchedulerPython 业界常用的任务调度库支持 cron 表达式、间隔触发、日期触发等模式。Python 3.10/3.11运行环境。这套组合的思路是OneBot 协议实现负责“能和 QQ 通信”aiocqhttp 负责“Python 侧能收发消息”APScheduler 负责“定时触发动作”三个组件各司其职代码量可以压缩到很少。2.2 安装 Python 环境与依赖建议使用 Python 3.10 或 3.11。版本太新时个别依赖库可能存在 asyncio 兼容性问题如果你想省心直接用 3.11 比较稳妥。创建项目目录并安装依赖mkdir qq_timer_bot cd qq_timer_bot pip install aiocqhttp APScheduler可以创建一个requirements.txt方便复现aiocqhttp0.9.0 APScheduler3.10.0安装完成后可以用一行命令验证python -c import aiocqhttp, apscheduler; print(Dependencies OK)如果打印出Dependencies OK说明依赖安装成功。2.3 准备 OneBot 协议登录端这一步容易被新手卡住。简单来说OneBot 协议实现是一个独立程序你需要从对应项目 Release 页面下载适合你系统的版本。启动程序按照提示登录一个 QQ 号。建议使用小号不要使用常用主号。在控制台或配置文件中新增一个“反向 WebSocket 客户端”连接地址填写后面 Python 机器人监听的地址。以本文的配置为例Python 机器人监听127.0.0.1:8080那么反向 WebSocket 的地址就填ws://127.0.0.1:8080/ws/注意不同 OneBot 实现的配置界面差异较大具体按钮名称可能不同但核心概念都是“正向 WebSocket”和“反向 WebSocket”。aiocqhttp 默认是服务端模式所以要配置的是反向 WebSocket 客户端。启动顺序很重要要先启动 Python 机器人再启动 OneBot 协议端或者两者都启动后协议端会自动重连。如果协议端先启动连接不上会自动重试问题也不大。2.4 项目结构与端口规划整个项目建议这样组织qq_timer_bot/ ├── requirements.txt ├── config.py # 配置项集中管理 ├── tasks.py # 定时任务定义与调度器 └── bot.py # 机器人主程序端口规划上8080是 Python 机器人的 WebSocket 监听端口OneBot 协议端作为客户端主动连接过来。如果本机端口被占用可以换成其他端口但需要同步修改 OneBot 协议端配置。3. 核心原理拆解3.1 OneBot v11 协议与事件模型OneBot v11 是一套标准的 QQ 机器人通信协议它定义了消息事件、通知事件、请求事件以及各种 API 调用格式。在 OneBot 的模型里QQ 客户端负责与 QQ 服务器通信当群里有新消息时OneBot 协议实现会把消息事件封装成 JSON通过 WebSocket 推送给业务端。业务端也可以调用 API让 OneBot 协议实现去发送消息。事件主要分为三类类型示例说明消息事件group_message、private_message群消息、私聊消息通知事件group_increase、friend_add群成员变动、好友添加请求事件friend_request、group_request好友申请、加群申请aiocqhttp 会把这些事件转换成 Python 对象你通过装饰器bot.on_message()、bot.on_notice()即可注册对应的处理函数。3.2 aiocqhttp 的工作方式aiocqhttp 是 NoneBot 生态中的轻量级 OneBot v11 SDK。它的工作模式如下启动时创建一个 HTTP/WebSocket 服务默认路径是/ws/。等待 OneBot 协议端建立反向 WebSocket 连接。收到事件后将 JSON 数据转换为Event对象分发到注册的处理器。业务代码调用bot.call_action()时通过已经建立的 WebSocket 连接发送 API 请求给协议端。简单理解aiocqhttp 帮你处理了 WebSocket 连接、事件解析、API 封装你只需要关心业务逻辑。3.3 APScheduler 调度模型APScheduler 是 Python 里非常强大的任务调度库。它支持四种触发器触发器适用场景示例date指定时间执行一次明天上午 10 点执行一次interval固定间隔循环执行每 30 分钟执行一次cron按时间表达式执行每天早上 9 点执行combine组合多个条件两周一次的周一执行在定时任务机器人的场景中最常用的是cron和interval。cron 触发器示例# 每天早上 9 点发消息 CronTrigger(hour9, minute0) # 每周一早上 9 点发消息 CronTrigger(day_of_weekmon, hour9, minute0) # 每隔一周的周一早上 9 点执行 CronTrigger(day_of_weekmon, week1/2, hour9, minute0)interval 触发器示例# 每 30 分钟执行一次 IntervalTrigger(minutes30) # 每 2 小时执行一次 IntervalTrigger(hours2)week1/2表示从第一周开始每间隔 2 周执行一次配合day_of_weekmon就是“每隔一周的周一执行”。3.4 定时任务与机器人事件循环的协作机制这里涉及一个关键点APScheduler 的异步调度器AsyncIOScheduler必须运行在机器人同一个 asyncio 事件循环中。Python 的 asyncio 模型下aiocqhttp 的 WebSocket 服务在一个事件循环中运行APScheduler 的异步任务也必须挂载到同一个事件循环里否则会出现定时任务不触发、协程没有运行环境等诡异问题。因此正确做法是先创建AsyncIOScheduler。在bot.run()之前调用scheduler.start()。这样当bot.run()启动事件循环后调度器也能在同一循环中执行任务。如果定时任务里执行的是 async 函数那么 APScheduler 会自动把协程提交到当前事件循环不需要额外处理。4. 完整实战案例下面我们实现一个具备如下功能的定时任务机器人每天早上 9 点向指定群发送早安提醒。每 30 分钟向指定 QQ 发送私聊消息。群内发送“任务列表”可以查看当前定时任务。管理员发送“暂停任务”和“恢复任务”可以控制调度器。4.1 编写 config.py所有需要手动调整的配置集中放在这里# 文件config.py # 目标群号替换成你自己的测试群 GROUP_ID 12345678 # 目标 QQ 号用于定时私聊测试 TARGET_QQ 123456789 # 管理员 QQ用于执行暂停、恢复等控制命令 ADMIN_QQ 123456780 # 机器人 WebSocket 服务监听地址和端口 HOST 127.0.0.1 PORT 8080 # 定时任务参数 MORNING_HOUR 9 MORNING_MINUTE 0 INTERVAL_MINUTES 30实际使用时把GROUP_ID、TARGET_QQ、ADMIN_QQ替换成你自己的 QQ 号或群号。4.2 编写 tasks.py这个文件负责创建调度器、注册定时任务、提供任务查询方法# 文件tasks.py from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.interval import IntervalTrigger # 使用 AsyncIOScheduler任务函数可以是 async 函数 scheduler AsyncIOScheduler(timezoneAsia/Shanghai) # 发送函数由 bot.py 注入避免循环导入 send_group None send_private None def init_tasks(send_group_func, send_private_func): global send_group, send_private send_group send_group_func send_private send_private_func # 每天早上 9 点发送群消息 scheduler.add_job( morning_notice, CronTrigger(hour9, minute0), idmorning_notice, replace_existingTrue, misfire_grace_time60, ) # 每 30 分钟发送一次私聊消息 scheduler.add_job( interval_notice, IntervalTrigger(minutes30), idinterval_notice, replace_existingTrue, misfire_grace_time60, ) async def morning_notice(): if send_group: await send_group(早上好定时任务机器人开始工作啦~) async def interval_notice(): if send_private: await send_private(这是一条每 30 分钟触发一次的定时私聊消息。) def get_all_jobs(): jobs scheduler.get_jobs() return [ { id: job.id, next_run_time: job.next_run_time.strftime(%Y-%m-%d %H:%M:%S) if job.next_run_time else None, } for job in jobs ]代码中的misfire_grace_time60表示任务原定执行时间错过 60 秒内仍允许补执行。如果机器人因为短暂卡顿错过了执行时间这个参数能有效降低丢任务概率。4.3 编写 bot.py这是机器人的主程序负责建立 WebSocket 服务、注册消息处理器、启动调度器# 文件bot.py import config from aiocqhttp import CQHttp from tasks import scheduler, init_tasks, get_all_jobs # 创建机器人实例 bot CQHttp() async def send_group_message(message: str): try: await bot.call_action(send_group_msg, group_idconfig.GROUP_ID, messagemessage) except Exception as e: print(f[定时任务] 群消息发送失败: {e}) async def send_private_message(message: str): try: await bot.call_action(send_private_msg, user_idconfig.TARGET_QQ, messagemessage) except Exception as e: print(f[定时任务] 私聊消息发送失败: {e}) bot.on_message() async def on_message(event): text event.message.strip() if text hello: await bot.send(event, 你好我是定时任务机器人。) elif text 任务列表: jobs get_all_jobs() if jobs: text_list \n.join( f任务 {job[id]}下次执行 {job[next_run_time]} for job in jobs ) await bot.send(event, f当前定时任务如下\n{text_list}) else: await bot.send(event, 当前没有定时任务。) elif text 暂停任务 and str(event.user_id) str(config.ADMIN_QQ): scheduler.pause_all() await bot.send(event, 已暂停全部定时任务。) elif text 恢复任务 and str(event.user_id) str(config.ADMIN_QQ): scheduler.resume_all() await bot.send(event, 已恢复全部定时任务。) if __name__ __main__: # 注入发送函数 init_tasks(send_group_message, send_private_message) # 先启动调度器再启动机器人保证两者使用同一个事件循环 scheduler.start() print(定时任务调度器已启动。) print(f反向 WebSocket 服务已启动ws://{config.HOST}:{config.PORT}/ws/) # 启动机器人服务 bot.run(hostconfig.HOST, portconfig.PORT)bot.call_action()是 aiocqhttp 提供的通用 API 调用方法第一个参数是 OneBot 协议中的动作名后面是参数。虽然也可以直接通过bot.send_group_msg、bot.send_private_msg这种属性式写法调用但call_action更通用清晰。4.4 启动与运行整个启动流程如下启动 Python 机器人python bot.py看到如下输出表示机器人服务已打开定时任务调度器已启动。 反向 WebSocket 服务已启动ws://127.0.0.1:8080/ws/启动 OneBot 协议端配置反向 WebSocket 地址为ws://127.0.0.1:8080/ws/。协议端连接成功后你会看到输出中新增了连接日志。用另一个 QQ 向机器人发送hello机器人会回复你好。4.5 执行结果验证在目标群中每天 9 点会收到早安提醒。目标 QQ 每 30 分钟会收到定时私聊消息。给机器人发任务列表它会返回类似内容当前定时任务如下 任务 morning_notice下次执行 2025-07-10 09:00:00 任务 interval_notice下次执行 2025-07-09 10:30:00管理员发送暂停任务后任务不再触发发送恢复任务后恢复执行。同时需要注意的是定时任务机器人需要保持 Python 程序长期运行。如果是本地开发机建议放到云服务器、路由器等可以 7x24 小时开机的环境。5. 常见问题与排查思路5.1 高频问题汇总表问题现象常见原因解决思路机器人收不到消息WebSocket 连接未建立检查连接地址是否完整端口是否正确定时任务不触发调度器未启动或事件循环不一致确认scheduler.start()在bot.run()之前调用发送消息报错账号掉线或 API 参数错误查看 OneBot 协议端日志确认账号在线状态定时任务执行多次程序重复启动使用进程锁或检测端口占用Python 3.12 启动报错asyncio API 变化切换 Python 3.11或使用维护更新更积极的框架消息发不出提示风控发送频率过高降低频率使用小号避免营销内容5.2 连接不上的排查步骤如果机器人收不到消息按下面顺序排查第一步确认 OneBot 协议端配置的地址是否为ws://127.0.0.1:8080/ws/注意末尾的/ws/不能少。如果端口不是 8080同步修改两处。第二步确认 Python 机器人是否真的在监听netstat -ano | findstr 8080Linux 下用ss -tlnp | grep 8080第三步看 OneBot 协议端的日志。连接成功时会有一条类似WebSocket connected的日志如果一直连接失败多半是地址写错或端口被防火墙拦截。5.3 定时任务不触发的排查步骤如果机器人能正常收发消息但定时任务不触发优先检查scheduler.start()