如何检测Discord频道X分钟内无消息发送?(discord.py)
解决Discord频道无消息检测的问题
看起来你在实现定时触发话题的机器人时,计时逻辑有点偏差——原代码的问题在于你一开始就固定了一个未来的timer时间,没有持续跟踪频道的最新消息状态,而且循环逻辑也没处理好。我来帮你修正这个问题,同时优化代码的健壮性。
原代码的核心问题
- 固定的
timer变量:你一开始就把timer设为当前时间加指定时长,但如果在asyncio.sleep(time)期间频道有新消息,这个timer不会更新,到点还是会发送话题,不符合“频道X分钟无消息才触发”的需求。 - 全局变量冲突:
global keepLooping会导致多个频道同时使用命令时互相干扰,比如一个频道停止监控会影响所有频道。 - 缺乏异常处理:如果频道从未有过消息,
ctx.channel.last_message会是None,直接访问created_at会报错。 - 无限循环无等待:进入
while keepLooping后没有asyncio.sleep,会疯狂占用资源,甚至导致机器人被限制。
修正后的实现方案
下面是优化后的代码,解决了上述问题,还支持多频道独立监控、停止监控等功能:
import discord import asyncio import datetime as dt import random from discord.ext import commands bot = commands.Bot(command_prefix='@') # 替换成你的话题列表 TOPIC_LIST = ["你最近发现了什么有趣的小众爱好?", "如果能和五年前的自己说一句话,你会说什么?", "推荐一部你反复刷过的电影吧!"] # 用字典跟踪每个频道的监控任务,避免全局变量冲突 monitoring_tasks = {} @bot.command(name="timedtopic") async def start_timed_topic(ctx, minutes: int): channel = ctx.channel # 检查频道是否已在监控中 if channel.id in monitoring_tasks: await ctx.send(f"👉 这个频道已经在进行{monitoring_tasks[channel.id]['minutes']}分钟的话题监控啦!") return monitor_seconds = minutes * 60 await ctx.send(f"✅ 已启动监控:当{channel.mention}连续{minutes}分钟无消息时,自动发送随机话题!") async def monitor_channel_activity(): try: while True: now = dt.datetime.utcnow() # 处理频道从未有过消息的情况 if channel.last_message is None: embed = discord.Embed(title="💬 来聊点什么吧!", description=random.choice(TOPIC_LIST)) await channel.send(embed=embed) await asyncio.sleep(monitor_seconds) continue # 计算最后一条消息与当前时间的差值 time_since_last_msg = now - channel.last_message.created_at # 满足触发条件:时间差≥设定时长,且最后一条不是机器人发的 if time_since_last_msg.total_seconds() >= monitor_seconds and channel.last_message.author != bot.user: embed = discord.Embed(title="💬 来聊点什么吧!", description=random.choice(TOPIC_LIST)) await channel.send(embed=embed) # 发送后等待设定时长再继续监控,避免频繁刷屏 await asyncio.sleep(monitor_seconds) else: # 每分钟检查一次,平衡实时性和资源占用 await asyncio.sleep(60) except asyncio.CancelledError: # 任务取消时清理记录 del monitoring_tasks[channel.id] await ctx.send(f"🛑 {channel.mention}的话题监控已停止!") # 创建并启动监控任务 task = bot.loop.create_task(monitor_channel_activity()) monitoring_tasks[channel.id] = {"task": task, "minutes": minutes} @bot.command(name="stopmonitor") async def stop_timed_topic(ctx): channel = ctx.channel if channel.id in monitoring_tasks: monitoring_tasks[channel.id]["task"].cancel() else: await ctx.send("❌ 这个频道目前没有在进行话题监控哦!") # 替换成你的机器人Token bot.run("YOUR_BOT_TOKEN")
代码关键细节说明
- 多频道隔离:用
monitoring_tasks字典存储每个频道的监控任务,避免全局变量冲突,支持同时监控多个频道。 - 动态时间检测:每次循环都计算当前时间与最后消息的时间差,确保只有当频道真的安静了指定时长才触发话题。
- 异常处理:处理了频道无消息的情况,避免报错。
- 资源优化:每分钟检查一次频道状态,既保证不会错过触发时机,又不会过度占用机器人资源。
- 停止功能:添加了
stopmonitor命令,让用户可以随时停止监控。
使用方法
- 输入
@timedtopic 5启动监控:当频道连续5分钟无消息时,机器人会自动发送随机话题。 - 输入
@stopmonitor停止当前频道的监控。
这样应该就能完美实现你想要的功能啦!
内容的提问来源于stack exchange,提问作者Libby




