如何让discord.py机器人仅在首次启动时调用指定函数
解决Discord.py机器人仅首次启动执行指定函数的问题
问题分析
你通过on_ready()事件触发restock()函数,但机器人离线重登(或脚本重启)时,on_ready()会重复触发,导致restock()被多次执行。尝试用全局变量控制但无效,核心原因有两点:
- 脚本重启后全局变量会被重置
- 代码中未正确声明全局变量,导致修改的是局部变量而非全局变量
修复步骤与解决方案
1. 针对脚本未重启的重连场景
如果只是机器人与Discord的连接断开后重连(脚本本身未重启),可以通过正确使用全局变量来控制:
# 初始化全局变量 restockkk = False @bot.event async def on_ready(): global restockkk logchannel = bot.get_channel(ID_CHANNEL) await logchannel.send(f'Logged in as {bot.user}') await bot.change_presence(activity=discord.Game(name="Buddy is busy being the GOAT")) # 仅在未启动过restock时执行 if not restockkk: await restock() async def restock(): global restockkk logchannel = bot.get_channel(960147355691253800) await logchannel.send('Restock monitor on') # 修改全局变量 restockkk = True while True: try: if not restockkk: await bot.change_presence(activity=discord.Game(name="Buddy is busy being the GOAT")) break # ... 你的业务代码 await asyncio.sleep(120) # ... 你的业务代码 await asyncio.sleep(120) except Exception as e: await logchannel.send(f'exception occurred: {str(e)}') # 不要递归调用restock,改用continue避免栈溢出 continue @bot.command(pass_context=True) async def stop(message): global restockkk if message.author.id == myId: restockkk = False await message.channel.send('Restock monitor is off') else: await message.channel.send('You are not allowed to use this command')
2. 针对脚本重启的场景(永久标记首次启动)
如果机器人因崩溃等原因导致脚本完全重启,全局变量会失效,此时需要用本地文件来标记是否已执行过首次启动:
import os # 标记文件路径,用于记录是否已完成首次启动 STARTUP_MARKER = ".first_start_done" @bot.event async def on_ready(): logchannel = bot.get_channel(ID_CHANNEL) await logchannel.send(f'Logged in as {bot.user}') await bot.change_presence(activity=discord.Game(name="Buddy is busy being the GOAT")) # 检查标记文件是否存在,不存在则执行首次启动逻辑 if not os.path.exists(STARTUP_MARKER): await restock() # 创建标记文件,标记首次启动已完成 with open(STARTUP_MARKER, 'w') as f: f.write("1") async def restock(): logchannel = bot.get_channel(960147355691253800) await logchannel.send('Restock monitor on') while True: try: # ... 你的业务代码 await asyncio.sleep(120) # ... 你的业务代码 await asyncio.sleep(120) except Exception as e: await logchannel.send(f'exception occurred: {str(e)}') continue @bot.command(pass_context=True) async def stop(message): global restockkk if message.author.id == myId: restockkk = False await message.channel.send('Restock monitor is off') # 如果需要下次启动重新执行restock,可删除标记文件 # os.remove(STARTUP_MARKER) else: await message.channel.send('You are not allowed to use this command')
注意事项
- 不要在
restock()的异常处理中递归调用自身,否则会导致栈溢出,改用continue重新进入循环 - 使用文件标记时,需确保机器人有读写该文件的权限
- 若执行
stop命令后希望下次启动重新执行restock(),可在stop命令逻辑中删除标记文件
内容的提问来源于stack exchange,提问作者maurijn.vd
相关产品推荐
相关产品推荐

