How can I send audio? - aiogram - python-3.x

An error occurs when sending audio: aiogram.utils.exceptions.InvalidHTTPUrlContent: Failed to get http url content. Here is the code I use:
#dp.message_handler(content_types = ["voice"])
async def getVoice(message: types.Message):
await bot.send_chat_action(message.from_user.id, "upload_voice")
await bot.send_audio(message.from_user.id, "audio.mp3", performer = "Performer", title = "Title")

you can also send that audio in a binary type:
#dispatcher.message_handler()
def send_audio(message: types.Message):
with open("audio/file/path/audio.mp3", mode="rb") as file:
binary_content = file.read()
await message.reply(binary_content)

If you want to send local file:
await bot.send_audio(message.from_user.id, open("audio.mp3", "r"), performer = "Performer", title = "Title")

Related

D.py/rewrite - Confessions System

I made a confessions system but there’s some things that are wrong with it. How would I make it so when users want to type, they don’t have to put in *confess and they can just type whatever they want without needing to use a command? And how do I make a mod logs channel to log the deleted confessions with the author name, etc.?
import discord
from discord.ext import commands
class Confess(commands.Cog):
def __init__(self, client: discord.ext.commands.Bot):
self.client = client
#commands.command()
async def confess(self, ctx: commands.Context, *, message: str):
channel = self.client.get_channel(806649868314869760)
await ctx.message.delete()
embed = discord.Embed(title="Success", description=f"I've received your confession and sent it to the <#806649874379964487> channel!")
embed.set_footer(text="Confessions")
await ctx.send(embed=embed, delete_after=10)
channel = self.client.get_channel(806649874379964487)
embed = discord.Embed(title="Confession", description=f"{message}")
embed.set_footer(text="All confessions are anonymous.")
await channel.send(embed=embed)
def setup(client):
client.add_cog(Confess(client))
For the first question
If you want to use a "command" without actually using a command you could make an on_message event, check the id of the channel (like a confessions channel) and if it matches then do the thing
Example:
#commands.Cog.listener()
async def on_message(message):
if message.channel.id == some_channel_id_here:
channel = self.client.get_channel(806649868314869760)
await message.delete()
embed = discord.Embed(title="Success", description=f"I've received your confession and sent it to the <#806649874379964487> channel!")
embed.set_footer(text="Confessions")
await message.channel.send(embed=embed, delete_after=10)
channel = self.client.get_channel(806649874379964487)
embed = discord.Embed(title="Confession", description=f"{message}")
embed.set_footer(text="All confessions are anonymous.")
await channel.send(embed=embed)
For the second question
You can use get_channel again to get the log channel and post in there. (If you mean't on how to check if someone deleted a message/confession, use on_message_delete)
Example:
#commands.command()
async def confess(self, ctx: commands.Context, *, message: str):
channel = self.client.get_channel(806649868314869760)
log_channel = self.client.get_channel(log_channel_id)
await ctx.message.delete()
embed = discord.Embed(title="Success", description=f"I've received your confession and sent it to the <#806649874379964487> channel!")
embed.set_footer(text="Confessions")
await ctx.send(embed=embed, delete_after=10)
channel = self.client.get_channel(806649874379964487)
embed = discord.Embed(title="Confession", description=f"{message}")
embed.set_footer(text="All confessions are anonymous.")
await channel.send(embed=embed)
await logchannel.send("User confessed something!")

Why won't the message edit embed send when a message is edited? (discord.py)

My log embed isn't sending when a message is edited. Does anyone see why?
#client.event
async def on_message_edit(before, after):
with open('logchannel.json', 'r') as f:
logs = json.load(f)
logchannel = discord.utils.get(logs)
embed = discord.Embed(color=0x0000ff, title="Message Edited", description=f"{before.author.mention} edited a message.")
embed.add_field(name="Original Message", value=before.content, inline=True)
embed.add_field(name="Edited Version", value=after.content, inline=True)
await logchannel.send(embed=embed)
discord.utils.get is for getting an element with specified attributes from an iterable. You're looking for discord.Client.get_channel.
Also, r mode and inline=True are unnecessary. They are implied.
#client.event
async def on_message_edit(before, after):
with open('logchannel.json') as f:
logs = json.load(f)
logchannel = client.get_channel(logs)
embed = discord.Embed(color=0x0000ff, title='Message Edited', description=f'{before.author.mention} edited a message.')
embed.add_field(name='Original Message', value=before.content)
embed.add_field(name='Edited Version', value=after.content)
await logchannel.send(embed=embed)

How to make the discord bot queue local mp3?

I'm new to python, so I wonder if there's a way to do so.
Here's my play mp3 command:
#bot.command()
async def play_song1(ctx):
global voice
channel = ctx.message.author.voice.channel
voice = get(bot.voice_clients, guild=ctx.guild)
if voice and voice.is_connected():
await voice.move_to(channel)
else:
voice = await channel.connect()
voice.play(discord.FFmpegPCMAudio('./mp3/song1.mp3'))
voice.source = discord.PCMVolumeTransformer(voice.source)
voice.source.volume = 0.1
await ctx.send ('playing')
while voice.is_playing():
await asyncio.sleep(.1)
await voice.disconnect()
I made 2 more same commands but for song2 and song3, and now I want to queue the mp3 when someone calls them.
Seen as if you're not using cogs, you could try something like this:
guild_queues = {} # Multi-server support as a dict, just use a list for one server
# EDIT: Map song names in a dict
play_messages = {
"song1": "Now playing something cool!",
"song2": "And something even cooler just started playing!",
"song3": "THE COOLEST!"
}
async def handle_queue(ctx, song):
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
channel = ctx.author.voice.channel
if voice and channel and voice.channel != channel:
await voice.move_to(channel)
elif not voice and channel:
voice = await channel.connect()
if not voice.is_playing():
audio = discord.FFmpegPCMAudio(f"./{song}.mp3")
source = discord.PCMVolumeTransformer(audio)
source.volume = 0.1
voice.play(audio)
await ctx.send(play_messages[song])
while voice.is_playing():
await asyncio.sleep(.1)
if len(guild_queues[ctx.guild.id]) > 0:
next_song = guild_queues[ctx.guild.id].pop(0)
await handle_queue(ctx, next_song)
else:
await voice.disconnect()
#bot.command()
async def play(ctx, *, song): # I'd recommend adding the filenames as an arg, but it's up to you
# Feel free to add a check if the filename exists
try:
# Get the current guild's queue
queue = guild_queues[ctx.guild.id]
except KeyError:
# Create a queue if it doesn't already exist
guild_queues[ctx.guild.id] = []
queue = guild_queues[ctx.guild.id]
voice = discord.utils.get(bot.voice_clients, guild=ctx.guild)
queue.append(song)
# The one song would be the one currently playing
if voice and len(queue) > 0:
await ctx.send("Added to queue!")
else:
current_song = queue.pop(0)
await handle_queue(ctx, current_song)
References:
utils.get()
Client.voice_clients
Context.guild
Member.voice
VoiceState.channel
VoiceClient.move_to()
VoiceClient.is_playing()
VoiceClient.play()
discord.FFmpegPCMAudio()
discord.PCMVolumeTransformer()
Context.send()

A strange bug in discord.py music bot

I'm making a discord.py bot that can play music. When I run it on my local machine sometimes everything works just fine but sometimes when I try running it with a playlist, it just cant find songs in the playlist.
Here is the play command:
#commands.command(name='play',aliases=["p","sing"])
async def _play(self, ctx: commands.Context, *, song1: str):
"""Plays a song.
If there are songs in the queue, this will be queued until the
other songs finished playing.
This command automatically searches from various sites if no URL is provided.
A list of these sites can be found here: https://rg3.github.io/youtube-dl/supportedsites.html
"""
if not ctx.voice_state.voice:
await ctx.invoke(self._join)
x = False
if "playlist?" not in song1:
songs = [song1]
else:
x = True
"""html_content = urllib.request.urlopen(song1)
html_content = html_content.read().decode()"""
r = requests.get(song1)
html_content = r.text
pattern = "href=\"\/watch\?v=(.{11})"
songs = re.findall(pattern,html_content)
print(f"Before: {songs}")
songs = list(dict.fromkeys(songs))
print(f"after: {songs}")
#songs.append(f"https://www.youtube.com/watch?v={code}")
for search in songs:
s = search if not x else f"https://www.youtube.com/watch?v={search}"
try:
source = await YTDLSource.create_source(ctx, s, loop=self.bot.loop)
except YTDLError as e:
await ctx.send('An error occurred while processing this request: {}'.format(str(e)))
else:
song = Song(source)
await ctx.voice_state.songs.put(song)
if not x:
await ctx.send('Enqueued {}'.format(str(source)))
if x:
await ctx.send(f"Succesfully queued the playlist `{song1}`")
Here what is in the console
Command that I run I discord:
pplay https://www.youtube.com/playlist?list=PL2n_fVXKImKlfv3PlcHZTzLk3CoGUV9Hm
output when it works:
Before: ['gEbRqpFkTBk', 'gEbRqpFkTBk', 'gEbRqpFkTBk', 'gEbRqpFkTBk', '4ZvnbsfXRk0', '4ZvnbsfXRk0', 'besNDPvEwQw', 'besNDPvEwQw', 'QglaLzo_aPk', 'QglaLzo_aPk', 'YJTae5ScvQA', 'YJTae5ScvQA', '9Va88Kt0NN0', '9Va88Kt0NN0']
after: ['gEbRqpFkTBk', '4ZvnbsfXRk0', 'besNDPvEwQw', 'QglaLzo_aPk', 'YJTae5ScvQA', '9Va88Kt0NN0']
output most of the time:
Before: []
after: []
It doesn't give any error.
You can use the extract_info method from youtube-dl :
video_list = []
with youtube_dl.YoutubeDL() as ydl:
playlist = ydl.extract_info(url='playlist url', download=False)['entries']
for video in playlist:
video_list.append(video['webpage_url'])
video_list will contain every URLs from your playlist's videos.

Discord.py Play Voice From PyAudio

I'm trying to play live-audio from my microphone into my Discord bot, but I can't convert the stream from PyAudio into an acceptable format Discord can play.
I have tried:
async def play_audio_in_voice():
input_stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = CHUNK)
while True:
await voice_commands.play_voice(input_stream)
async def play_voice(self, input_stream):
guild = #got guild here
if guild.voice_client:
data = input_stream.read(CHUNK, exception_on_overflow = False)
voice_client = guild.voice_client
voice_client.send_audio_packet(data)
and,
async def play_voice(self, input_stream):
guild = #got guild here
if guild.voice_client:
data = input_stream.read(CHUNK, exception_on_overflow = False)
stream = await discord.FFmpegPCMAudio(data)
voice_client = guild.voice_client
voice_client.send_audio_packet(data)
In the first code, I'm getting:
File "D:\Program Files\lib\site-packages\discord\voice_client.py", line 454, in send_audio_packet
encoded_data = self.encoder.encode(data, self.encoder.SAMPLES_PER_FRAME)
AttributeError: 'NoneType' object has no attribute 'encode'
I am aware that I'm getting guild every time I call the function here, I'll fix that now :)
discord.FFmpegPCMAudio automatically does Opus encoding to your sound data. But send_audio_packet does not know if you are passing encoded or non-encoded data.
Just add encode argument when calling send_audio_packet method:
async def play_voice(self, input_stream):
...
voice_client.send_audio_packet(data, encode=input_stream.is_opus())

Resources