Search code examples
pythonasync-awaitdiscordpython-multithreading

Discord.py VoiceChannel.connect() causes a thread to hang


I'm trying to write a simple discord bot in python. I want the bot to be able to join a voice channel, and constantly keep repeating an mp3 file. I firstly tried using a while loop in the on_ready() function, which had an understandably bad result. I have now tried using a thread to run the loop seperately from the main thread, but discord.py's functions use async/await, so I've had to improvise (badly).

import asyncio
import threading

import discord
from discord import app_commands

intents = discord.Intents.all()
client = discord.Client(intents=intents)
discord.Permissions.manage_nicknames = True
tree = app_commands.CommandTree(client)


def targeter():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(play_sounds())
    loop.close()

async def play_sounds():
    for channel in client.guilds[1].channels:
        if str(channel.type) == "voice":
            print("started")
            player_channel = await channel.connect()
            print("done")

@client.event
async def on_ready():
    await tree.sync()
    threading.Thread(target=targeter).start()

client.run("token")

When I run said thread, it gets to the line where I introduce the awaited discord.VoiceChannel.connect() function and hangs. The rest of the commands work fine, it's just that thread. The connect() command usually returns a VoiceClient within a few milliseconds when run normally. Is this something to do with me trying to run functions across threads? If so, is there a better solution I'm not seeing?

>>> started

Solution

  • Why use threads? Take a look at tasks instead. Create a task that runs every minute or so; have it join the VC if it's not in it (aka on first run) and then have it check that song is still running/playing.

    Take a look here at creating a player. Save that player object in the task, and then when the task loop runs you can do player.is_done() to check if it's still playing or not - and then start playing the song again.