Search code examples
pythondiscord.pypython-asyncio

How to run multiple Discord clients simultaneously


I've created a few bots using the discord.py library and I now want to build a 'dispatcher' that reads a configuration file and launches them all. Each of my bots is created as a class extending from an abstract bot class. However, I'm stuck at running them simultaneously. These are some things I've tried:

  • Using threads. Eg: threading.Thread(target=discord.Client('token').run)).start(). Doesn't work because Client.run() tries to start the asyncio event loop again, causing an error (RuntimeError: Cannot close a running event loop).
  • Using os.system/multiprocessing/subprocess. To run .py files containing bots. Doesn't work because os.system etc blocks until the subprocess has ended (ie the bot is killed). I'd also prefer not to use this method because it's a bi
  • Creating tasks and putting them on a single asyncio loop (shown below).

MRE of the last method I tried:

import discord
import asyncio

class Bot:
    client = discord.Client()

    def __init__(self, token):
        self.token = token

        print('Bot initiated')

        @self.client.event
        async def on_ready():
            print(f'Logged in as {self.client.user}')
        
        @self.client.event
        async def on_message(message):
            print(message.content)
        
    async def run(self):
        print('Bot running')
        self.client.run(self.token)

if __name__ == '__main__':
    bot1 = Bot('bot token here')
    bot2 = Bot('bot token here')
    loop = asyncio.get_event_loop()
    loop.create_task(bot1.run())
    loop.create_task(bot2.run())
    loop.run_forever()

This doesn't work at all - the first bot freezes in the run method and never even logs in. For testing, both bots were logging into the same bot account but that's irrelevant to the problem.

I presume that the ideal solution would be a way to asynchronously run a discord.Client, but I haven't come across any way to do this.


Solution

  • Easiest approach would be using subprocess.Popen

    import sys
    import subprocess
    
    files = ["bot1.py", "bot2.py", ...]
    
    for f in files:
        subprocess.Popen(
             [sys.executable, f], stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )
    

    It will start all the files in the background.