循环调用discord.py的client.run()与client.close()报错session is closed
问题描述
我是discord.py和Python新手,开发了一款商品库存监控程序:主程序用Selenium Firefox循环检测商品是否可用,商品可用时调用Discord机器人函数,机器人发送提醒并接收我的交互指令(继续监控、停止、选择支付卡等)。第一次调用机器人函数正常,但第二次调用时机器人无法重连,提示session is closed,程序虽能运行但功能失效。
机器人函数代码如下:
import discord import time #Discord Bot parameters intents = discord.Intents.default() intents.message_content = True client = discord.Client(intents=intents) channelFirefox = [here goes the discord channel ID] #Tarjetas t1 = ["1111","1/11","111"] t2 = ["2222","2/22","222"] t3 = ["3333","3/33","333"] t4 = ["444","4/44","444"] def botFunction(channelFirefox): #Discord Bot routines @client.event async def on_ready(): channel = client.get_channel(channelFirefox) print(f'We have logged in as {client.user}') await channel.send('Productos pillados !') await channel.send('Para comprar: comprar t1/t2/t3/t4') await channel.send('Para continuar: y ') await channel.send('Para parar: parar ') @client.event async def on_message(message): global procesoCompra,tarjeta if message.channel.id == channelFirefox: if message.author == client.user: return if message.content.startswith('comprar t1'): await message.channel.send('Comprando con tarjeta 1') procesoCompra = "comprar" tarjeta = t1 await client.close() if message.content.startswith('comprar t2'): await message.channel.send('Comprando con tarjeta 2') procesoCompra = "comprar" tarjeta = t2 await client.close() if message.content.startswith('comprar t3'): await message.channel.send('Comprando con tarjeta 3') procesoCompra = "comprar" tarjeta = t3 await client.close() if message.content.startswith('comprar t4'): await message.channel.send('Comprando con tarjeta 4') procesoCompra = "comprar" tarjeta = t4 await client.close() if message.content.startswith('y'): await message.channel.send('Continua buscando') procesoCompra = "y" tarjeta = ["000","0/0","0"] await client.close() if message.content.startswith('parar'): await message.channel.send('Este perfil ha sido parado') procesoCompra = "stop" tarjeta = ["000","0/0","0"] await client.close() client.run([HERE GOES THE BOT TOKEN]) return procesoCompra,tarjeta
最初把函数放在主程序内,问题相同;移到外部文件后依旧。主程序本身运行正常,之前用命令行交互,现在改成Discord机器人控制,希望解决能多次调用机器人函数的问题。
错误原因
- 全局Client实例重复使用:函数外定义了全局
client对象,第一次调用client.run()后用client.close()关闭了会话,但第二次调用时仍复用这个已关闭的实例,导致session is closed错误。 - 事件重复绑定:每次调用
botFunction都会重复绑定on_ready和on_message事件,即使创建新Client,多次绑定也会导致逻辑混乱。 - 全局变量依赖:用
global变量传递结果,代码耦合度高,多次调用时易出现状态混乱。
解决方案
方案一:重构为长期运行的机器人(推荐)
discord.py的机器人设计为长期在线运行,通过共享状态和主程序交互,避免反复启动关闭。
步骤1:重构机器人代码(单独运行)
import discord from discord.ext import commands import asyncio intents = discord.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix="!", intents=intents) # 支付卡数据 cards = { "t1": ["1111","1/11","111"], "t2": ["2222","2/22","222"], "t3": ["3333","3/33","333"], "t4": ["444","4/44","444"] } # 和主程序交互的状态变量 monitor_status = { "action": None, "selected_card": None, "channel_id": None } @bot.event async def on_ready(): print(f'机器人已登录:{bot.user}') # 定义交互命令 @bot.command(name="comprar") async def comprar(ctx, card_name: str): if card_name not in cards: await ctx.send(f"无效卡名,可选:{', '.join(cards.keys())}") return monitor_status["action"] = "comprar" monitor_status["selected_card"] = cards[card_name] await ctx.send(f'将使用{card_name}进行购买') @bot.command(name="continuar") async def continuar(ctx): monitor_status["action"] = "continuar" monitor_status["selected_card"] = None await ctx.send('继续监控库存') @bot.command(name="parar") async def parar(ctx): monitor_status["action"] = "stop" monitor_status["selected_card"] = None await ctx.send('已停止监控') # 主程序调用的提醒函数 async def send_alert_and_wait(channel_id): monitor_status["action"] = None monitor_status["channel_id"] = channel_id channel = bot.get_channel(channel_id) if not channel: return None, None await channel.send('Productos pillados !') await channel.send('Para comprar: !comprar t1/t2/t3/t4') await channel.send('Para continuar: !continuar ') await channel.send('Para parar: !parar ') # 等待用户指令 while monitor_status["action"] is None: await asyncio.sleep(1) action = monitor_status["action"] card = monitor_status["selected_card"] return action, card if __name__ == "__main__": bot.run("[HERE GOES THE BOT TOKEN]")
步骤2:主程序通过asyncio与机器人交互
import asyncio from selenium import webdriver # 导入机器人模块(假设机器人代码在discord_bot.py文件中) from discord_bot import send_alert_and_wait, bot async def monitor_stock(): driver = webdriver.Firefox() channel_id = [你的频道ID] while True: # 替换为实际库存检测逻辑 stock_available = True if stock_available: print("商品可用,发送提醒") action, card = await send_alert_and_wait(channel_id) if action == "comprar": print(f"使用卡{card}购买") # 执行购买逻辑 elif action == "continuar": print("继续监控") elif action == "stop": print("停止监控") break await asyncio.sleep(10) # 检测间隔 driver.quit() if __name__ == "__main__": loop = asyncio.get_event_loop() # 并行运行机器人和监控任务 tasks = [ loop.create_task(bot.start("[HERE GOES THE BOT TOKEN]")), loop.create_task(monitor_stock()) ] loop.run_until_complete(asyncio.gather(*tasks))
方案二:每次调用创建新Client(临时方案)
若必须每次调用启动关闭机器人,需在函数内创建新Client实例:
import discord import asyncio #Tarjetas t1 = ["1111","1/11","111"] t2 = ["2222","2/22","222"] t3 = ["3333","3/33","333"] t4 = ["444","4/44","444"] def botFunction(channelFirefox): # 每次调用创建新的Client实例 intents = discord.Intents.default() intents.message_content = True client = discord.Client(intents=intents) procesoCompra = None tarjeta = None @client.event async def on_ready(): channel = client.get_channel(channelFirefox) print(f'We have logged in as {client.user}') await channel.send('Productos pillados !') await channel.send('Para comprar: comprar t1/t2/t3/t4') await channel.send('Para continuar: y ') await channel.send('Para parar: parar ') @client.event async def on_message(message): nonlocal procesoCompra, tarjeta if message.channel.id != channelFirefox: return if message.author == client.user: return if message.content.startswith('comprar t1'): await message.channel.send('Comprando con tarjeta 1') procesoCompra = "comprar" tarjeta = t1 await client.close() elif message.content.startswith('comprar t2'): await message.channel.send('Comprando con tarjeta 2') procesoCompra = "comprar" tarjeta = t2 await client.close() elif message.content.startswith('comprar t3'): await message.channel.send('Comprando con tarjeta 3') procesoCompra = "comprar" tarjeta = t3 await client.close() elif message.content.startswith('comprar t4'): await message.channel.send('Comprando con tarjeta 4') procesoCompra = "comprar" tarjeta = t4 await client.close() elif message.content.startswith('y'): await message.channel.send('Continua buscando') procesoCompra = "y" tarjeta = ["000","0/0","0"] await client.close() elif message.content.startswith('parar'): await message.channel.send('Este perfil ha sido parado') procesoCompra = "stop" tarjeta = ["000","0/0","0"] await client.close() asyncio.run(client.run("[HERE GOES THE BOT TOKEN]")) return procesoCompra, tarjeta
注意事项
- 方案二频繁启动关闭机器人,可能触发Discord频率限制,不推荐长期使用。
- 方案一更符合discord.py设计理念,稳定性更高。
内容的提问来源于stack exchange,提问作者itegumo
相关产品推荐
相关产品推荐

