如何创建机器人上线后每10秒永久运行的循环任务?
问题分析与解决方案
核心问题
你的任务只运行一次或无法正常循环,大概率是同步数据库操作阻塞了asyncio事件循环,导致discord.py的任务调度无法正常工作,另外启动时机的处理也存在疏漏。
具体修正步骤
1. 改用异步MongoDB驱动
如果当前使用的是同步pymongo,必须替换为异步驱动motor——在async函数里执行同步IO会直接卡住事件循环,任务自然无法重复触发。
2. 修改任务代码为异步操作
将数据库查询和更新改为异步调用,并用async for遍历查询结果:
import datetime from discord.ext import tasks from motor.motor_asyncio import AsyncIOMotorClient # 初始化异步数据库客户端 client = AsyncIOMotorClient("你的MongoDB连接字符串") votedb = client["你的数据库名"] @tasks.loop(seconds=10) async def check_votes(): now = datetime.datetime.now() # 异步查询符合条件的用户 voted_users_cursor = votedb.votes.find({"voted": "true"}) # 使用async for遍历异步游标 async for user in voted_users_cursor: vote_time = user.get("vote_time") # 确保vote_time是datetime类型(MongoDB存ISODate则无需转换) if isinstance(vote_time, datetime.datetime): elapsed_time = now - vote_time if elapsed_time > datetime.timedelta(seconds=30): # 异步更新数据 await votedb.votes.update_one( {"user_id": user.get("user_id")}, {"$set": {"voted": "false"}} )
3. 正确启动任务
方式一:在on_ready事件中启动(推荐)
添加运行状态判断,避免机器人重连时重复启动任务:
@bot.event async def on_ready(): print(f"已登录为 {bot.user}") if not check_votes.is_running(): check_votes.start()
方式二:在setup_hook中启动
确保setup_hook是异步函数,且任务无阻塞操作:
async def setup_hook(): if not check_votes.is_running(): check_votes.start() bot.setup_hook = setup_hook
额外注意事项
- 确保MongoDB中
vote_time字段存储的是datetime类型,而非字符串,否则会出现类型错误导致任务中断。如果存的是字符串,需要先转换:from dateutil import parser vote_time = parser.parse(user.get("vote_time")) - 如果任务运行中抛出异常会自动停止循环,建议添加异常捕获:
@tasks.loop(seconds=10) async def check_votes(): try: # 原任务代码 except Exception as e: print(f"任务执行出错: {e}") check_votes.restart() # 让任务继续运行
内容的提问来源于stack exchange,提问作者TheRealKurumi
相关产品推荐
相关产品推荐

