0
votes

Like the title I need to play a gtts file into a voice channel what is the easiest way to do this? I am using python 3.7. Here is my code so far:

@client.command(name="repeat")
async def repeat(context):
  import gtts, ffmpeg
  from discord.ext import commands
  # grab the user who sent the command
  user = context.message.author
  voice_channel = user.voice.voice_channel
  channel = None
  # only play music if user is in a voice channel
  if voice_channel != None:
    await ctx.send(f"What do you want to say, type cancel to cancel:")
        
    def check(msg):
      return msg.author == ctx.author and msg.channel == ctx.channel
        
    try:
      msg = await client.wait_for("message", check=check, timeout=120)
  
    except asyncio.TimeoutError:
      await ctx.send("Sorry, you didn't reply in time!")
      
    if(msg.content == 'cancel'):
      return
    
    tts = gtts.gTTS(str(msg.content), lang="en")
    tts.save("text.mp3")
    
    # grab user's voice channel
    channel = voice_channel.name
    await client.say('User is in channel: '+ channel)
    # create StreamPlayer
    vc = await client.join_voice_channel(voice_channel)
    player = vc.create_ffmpeg_player('text.mp3', after=lambda: print('done'))
    player.start()
    while not player.is_done():
      await asyncio.sleep(1)
      # disconnect after the player has finished
      player.stop()
      await vc.disconnect()
    else:
      await client.say('User is not in a channel.')

I am still new to making bots on discord so my code looks like a dumpster fire, I am currently working on fixing/compacting my code. once again I am using python 3.7

1

1 Answers

3
votes

So, firstly you should put any imports at the start of your file (Same area you import discord)

Now onto the code, I will simplify it a bit. My code does not ask the user for text, instead this code uses the input called after the command. I.e !repeat Hello will speak hello in the voice channel

First, here is some code to connect to a voice channel. For this purpose, we will keep it separate. This is some nice code, and somewhat better then the gtts code I will attempt.

@bot.command()
async def connect(ctx, *, channel: discord.VoiceChannel=None):
    """
    Connect to a voice channel
    This command also handles moving the bot to different channels.

    Params:
    - channel: discord.VoiceChannel [Optional]
        The channel to connect to. If a channel is not specified, an attempt to join the voice channel you are in
        will be made.
    """
    if not channel:
        try:
            channel = ctx.author.voice.channel
        except AttributeError:
            raise InvalidVoiceChannel('No channel to join. Please either specify a valid channel or join one.')

    vc = ctx.voice_client

    if vc:
        if vc.channel.id == channel.id:
            return
        try:
            await vc.move_to(channel)
        except asyncio.TimeoutError:
            raise VoiceConnectionError(f'Moving to channel: <{channel}> timed out.')
    else:
        try:
            await channel.connect()
        except asyncio.TimeoutError:
            raise VoiceConnectionError(f'Connecting to channel: <{channel}> timed out.')

    await ctx.send(f'Connected to: **{channel}**', delete_after=20)

Cool, so now we can connect to the channel. So here's my go at actually doing some gtts work. I wont talk to much, it is all commented. However, if theres an issue just leave a comment :)

The following is the required import from gtts import gTTS

@bot.command()
async def repeat(ctx, *, text=None):
    """
    A command which saves `text` into a speech file with
    gtts and then plays it back in the current voice channel.

    Params:
     - text [Optional]
        This will be the text we speak in the voice channel
    """
    if not text:
        # We have nothing to speak
        await ctx.send(f"Hey {ctx.author.mention}, I need to know what to say please.")
        return

    vc = ctx.voice_client # We use it more then once, so make it an easy variable
    if not vc:
        # We are not currently in a voice channel
        await ctx.send("I need to be in a voice channel to do this, please use the connect command.")
        return

    # Lets prepare our text, and then save the audio file
    tts = gTTS(text=text, lang="en")
    tts.save("text.mp3")

    try:
        # Lets play that mp3 file in the voice channel
        vc.play(discord.FFmpegPCMAudio('text.mp3'), after=lambda e: print(f"Finished playing: {e}"))

        # Lets set the volume to 1
        vc.source = discord.PCMVolumeTransformer(vc.source)
        vc.source.volume = 1

    # Handle the exceptions that can occur
    except ClientException as e:
        await ctx.send(f"A client exception occured:\n`{e}`")
    except TypeError as e:
        await ctx.send(f"TypeError exception:\n`{e}`")
    except OpusNotLoaded as e:
        await ctx.send(f"OpusNotLoaded exception: \n`{e}`")

Now, in order to leave the current voice channel you can do the following

@bot.command()
async def disconnect(ctx):
    """
    Disconnect from a voice channel, if in one
    """
    vc = ctx.voice_client

    if not vc:
        await ctx.send("I am not in a voice channel.")
        return

    await vc.disconnect()
    await ctx.send("I have left the voice channel!")