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

如何实现Python Discord Bot异步执行Selenium预约检测并发送消息

问题解答

1. 第一种方案(独立任务+通知Bot)的最简实现

你的核心问题是check_all()是同步阻塞函数,直接用asyncio.create_task()没用——因为它还是会在Discord Bot的事件循环线程里执行,卡死整个Bot。最简解决方式是把同步的check_all()放到线程池里跑,彻底脱离事件循环线程,不阻塞Bot的正常运行。

具体改动代码

from concurrent.futures import ThreadPoolExecutor
import asyncio

# 全局线程池,避免重复创建
executor = ThreadPoolExecutor(max_workers=2)

@tasks.loop(minutes=5)
async def send_message():
  channel_debug = discord.utils.get(client.get_all_channels(), name='debug')
  current_time = datetime.now(BerlinTz)
  formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
  await channel_debug.send(f'{formatted_time}: Checking all cities ...')
  
  # 用线程池执行同步的check_all,不阻塞事件循环
  loop = asyncio.get_running_loop()
  response = await loop.run_in_executor(executor, check_all, data)

  # 后续逻辑完全不变
  city_availability = check_availability(response)
  await channel_debug.send(city_availability)
  await channel_debug.send(response)
  for entry in city_availability:
    if entry['send_msg_flag'] == True:
      print(entry['city'], "is available")
      channel_city = discord.utils.get(client.get_all_channels(), name=entry['city'].lower()) 
      filtered_response = [element for element in response if element['city'].lower() == entry['city'].lower()]
      formatted_output = format_data(filtered_response)
      await channel_city.send(formatted_output)
    else:
      print(entry['city'], "is NOT available")

如果非要做完全独立的定时任务(比如单独脚本),最简方式是:

  • 写个独立Python脚本,用time.sleep(300)做5分钟定时,执行check_all()
  • 执行完后通过Discord Webhook直接给指定频道发结果,不需要和Bot做复杂通信
  • 但这种方式需要维护额外进程,不如线程池方案简洁。

2. 第二种方案(双协程+数据库)是否更优?如何维护?

是否更优?

要看你的场景:

  • 如果只是简单的定时检测+通知,第一种线程池方案更轻量,改动少
  • 如果后续要扩展功能(比如保存历史数据、支持多Bot实例、加前端查看状态),第二种方案更优——它完全解耦了检测逻辑和Bot逻辑,Bot只负责读库发消息,不会被任何阻塞操作影响,稳定性更高。

如何维护双协程?

核心是让检测协程和Bot协程通过数据库通信,用asyncio.gather()同时启动两个协程:

示例代码结构

import asyncio
import discord
from discord.ext import tasks
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(max_workers=2)

# 检测协程:每5分钟跑一次check_all,更新数据库
async def check_and_update_db():
  while True:
    loop = asyncio.get_running_loop()
    response = await loop.run_in_executor(executor, check_all, data)
    # 替换成你的数据库更新逻辑(比如sqlite/PostgreSQL)
    update_database_with_latest_data(response)
    await asyncio.sleep(300)  # 5分钟间隔

async def main():
  # 初始化Discord Bot
  intents = discord.Intents.default()
  client = discord.Client(intents=intents)

  @client.event
  async def on_ready():
    print(f'Logged in as {client.user}')
    # Bot启动后,启动读库发消息的定时任务
    send_message_from_db.start()

  @tasks.loop(minutes=5)
  async def send_message_from_db():
    channel_debug = discord.utils.get(client.get_all_channels(), name='debug')
    current_time = datetime.now(BerlinTz)
    formatted_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
    await channel_debug.send(f'{formatted_time}: Checking database for updates ...')
    
    # 从数据库读取最新检测结果
    response = get_latest_data_from_db()
    city_availability = check_availability(response)
    
    # 后续发消息逻辑和之前一致
    await channel_debug.send(city_availability)
    await channel_debug.send(response)
    for entry in city_availability:
      if entry['send_msg_flag'] == True:
        print(entry['city'], "is available")
        channel_city = discord.utils.get(client.get_all_channels(), name=entry['city'].lower()) 
        filtered_response = [element for element in response if element['city'].lower() == entry['city'].lower()]
        formatted_output = format_data(filtered_response)
        await channel_city.send(formatted_output)
      else:
        print(entry['city'], "is NOT available")

  # 同时启动检测协程和Bot
  await asyncio.gather(
    check_and_update_db(),
    client.start('你的Bot令牌')
  )

if __name__ == '__main__':
  asyncio.run(main())

注意事项

  • 数据库选择:简单场景用sqlite3即可,高并发场景用异步库比如aiosqlite或asyncpg
  • 数据一致性:检测协程更新数据库时用事务,避免Bot读到半更新的数据
  • 定时逻辑:检测协程用asyncio.sleep(),不要用同步的time.sleep(),否则会阻塞协程

总结

  • 快速解决阻塞问题:选第一种方案的线程池实现,改动最小
  • 长期扩展需求:选第二种方案,解耦逻辑更稳定
  • 之前asyncio.create_task()无效的原因:check_all()是同步阻塞函数,必须放到线程/进程里执行,不能直接在事件循环线程跑

内容的提问来源于stack exchange,提问作者Ilyas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 12:42:07