pythonasynchronousasync-awaitdiscorddiscord.py

Discord py not sending messages when being called from the after method from voice.play(... after=...)


from discord.ext import commands
from discord import FFmpegPCMAudio
from asyncio import run

FFMPEG_OPTIONS = {'before_options': '-avioflags direct', 'options': '-vn -sn -dn -movflags +faststart -b:a 49k'}
class Play(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.run = False
        self.voice = ""

    async def play_song(self, ctx ,data=()):
        if data != ():
            data = "+".join(data)
            print(data)
        else:
            print("Passes Automatically")
            await ctx.send("auto pass")

        await ctx.send("playing")

        source = FFmpegPCMAudio("https://www2.iis.fraunhofer.de/AAC/ChID-BLITS-EBU-Narration.mp4", **FFMPEG_OPTIONS)
        await self.voice.play(source, after=lambda x=None: run(self.play_song(ctx)))

    @commands.Cog.listener()
    async def on_ready(self):
        print("Play is working")


    @commands.command(pass_context=True)
    async def play(self, ctx, *initial_user_input):
        if not self.run:
            if ctx.author.voice:
                channel = ctx.message.author.voice.channel
                self.voice = await channel.connect()
                self.run = True
                await self.play_song(ctx, initial_user_input)
            else:
                await ctx.send("Channel Not Found")
        else:
            await self.play_song(ctx, initial_user_input)


async def setup(bot):
    await bot.add_cog(Play(bot))

The above code is what I'm using it works fine (the message sending etc) when the play_song is called from the play command. when the play_song method is called from the lambda function. it works as expected until reaching await ctx.send("testing"). It should obviously send the message in the discord channel but it hangs indefinetely.

Ive tried calling the play function from the lambda function (same problem). I've tried passing in the channel id instead of the context but neither works. I've ran it without the await at which point it just says it was never awaited. I've also tested it with an async generator in case it was the asyncio.run but same problem. Help appreciated.


Solution

  • its probably because running the song is like the main task the bot is working on so it cant do anything else until it completes or smth so instead of asyncio.run use self.bot.loop.create_task

    and i recommend adding a song_finished methodso you know when its done helps for debugging overall and you can change things anytime heres the modified code

    from discord.ext import commands
    from discord import FFmpegPCMAudio
    import asyncio
    
    FFMPEG_OPTIONS = {'before_options': '-avioflags direct', 'options': '-vn -sn -dn -movflags +faststart -b:a 49k'}
    class Play(commands.Cog):
        def __init__(self, bot):
            self.bot = bot
            self.run = False
            self.voice = None
    
        async def play_song(self, ctx, data=()):
            if data:
                data = "+".join(data)
                print(data)
            else:
                print("Passes Automatically")
                await ctx.send("auto pass")
    
            await ctx.send("playing")
    
            source = FFmpegPCMAudio("https://www2.iis.fraunhofer.de/AAC/ChIDBLITS-EBU-Narration.mp4", **FFMPEG_OPTIONS)
            self.voice.play(source, after=lambda e:self.bot.loop.create_task(self.song_finished(ctx)))
    
        async def song_finished(self, ctx):
            await ctx.send("Song finished playing")
            # Optionally, you can call play_song again if you have more songs to play
    
        @commands.Cog.listener()
        async def on_ready(self):
            print("Play is working")
    
        @commands.command(pass_context=True)
        async def play(self, ctx, *initial_user_input):
            if not self.run:
                if ctx.author.voice:
                    channel = ctx.message.author.voice.channel
                    self.voice = await channel.connect()
                    self.run = True
                    await self.play_song(ctx, initial_user_input)
                else:
                    await ctx.send("Channel Not Found")
            else:
                await self.play_song(ctx, initial_user_input)
    async def setup(bot):
        await bot.add_cog(Play(bot))