You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

循环调用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机器人控制,希望解决能多次调用机器人函数的问题。

错误原因
  1. 全局Client实例重复使用:函数外定义了全局client对象,第一次调用client.run()后用client.close()关闭了会话,但第二次调用时仍复用这个已关闭的实例,导致session is closed错误。
  2. 事件重复绑定:每次调用botFunction都会重复绑定on_ready和on_message事件,即使创建新Client,多次绑定也会导致逻辑混乱。
  3. 全局变量依赖:用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 00:45:57