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())
Related
I am using python 3.6
I am making a discord bot to play music.
Whenever I give it the 'play' command the discord app shows that the bot is producing audio but I don't hear any audio. I checked the settings and maximized all the audio-related settings.
When I go back to pycharm, I see this error in the 'run' tab:
Traceback (most recent call last):
File "/Volumes/Mahmoud-Disk/MyProfile/Library/Python/3.8/lib/python/site-packages/discord/ext/commands/core.py", line 229, in wrapped
ret = await coro(*args, **kwargs)
File "/Volumes/Mahmoud-Disk/MyProfile/Desktop/Discord Bot/Discord-Bot/main.py", line 72, in play
await ctx.send(embed = discord.Embed(
TypeError: __init__() got an unexpected keyword argument 'author'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Volumes/Mahmoud-Disk/MyProfile/Library/Python/3.8/lib/python/site-packages/discord/ext/commands/bot.py", line 1349, in invoke
await ctx.command.invoke(ctx)
File "/Volumes/Mahmoud-Disk/MyProfile/Library/Python/3.8/lib/python/site-packages/discord/ext/commands/core.py", line 1023, in invoke
await injected(*ctx.args, **ctx.kwargs) # type: ignore
File "/Volumes/Mahmoud-Disk/MyProfile/Library/Python/3.8/lib/python/site-packages/discord/ext/commands/core.py", line 238, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: __init__() got an unexpected keyword argument 'author'
Here is my code:
import discord
from discord.ext import commands
import wavelink
client = commands.Bot(command_prefix = ".", intents = discord.Intents.all())
class CustomPlayer (wavelink.Player):
def __init__(self):
super().__init__()
self.queue = wavelink.Queue()
#client.event
async def on_ready():
client.loop.create_task(connect_nodes()) #HTTPS and Websocket operations
async def connect_nodes(): #Helper function
await client.wait_until_ready()
await wavelink.NodePool.create_node(
bot = client ,
host = "127.0.0.1" ,
port = 2333 ,
password = "yk I can't "
)
#client.event
async def on_wavelink_node_ready(node = wavelink.Node):
print(f"Node: <{node.identifier}> is ready!")
#client.command()
async def connect(ctx):
vc = ctx.voice_client
try:
channel = ctx.author.voice.channel
except AttributeError:
return await ctx.send("Please join a channel to connect.")
if not vc:
await ctx.author.voice.channel.connect(cls = CustomPlayer())
else:
await ctx.send("The bot is already connected to a voice channel.")
#client.command()
async def disconnect(ctx):
vc = ctx.voice_client
if vc:
await vc.disconnect()
else:
await ctx.send("Bot is not connected to a voice channel.")
#client.command()
async def play(ctx, *, search: wavelink.YouTubeTrack):
vc = ctx.voice_client #represents a discord voice connection
if not vc:
custom_player = CustomPlayer()
vc: CustomPlayer = await ctx.author.voice.channel.connect(cls = custom_player)
if vc.is_playing():
vc.queue.put(item = search)
await ctx.send(embed = discord.Embed(
title = search.title,
url = search.uri,
author = ctx.author,
description = f"Queued {search.title} in {vc.channel}"
))
else:
await vc.play(search)
await ctx.send(embed = discord.Embed(
title = search.title,
url = search.uri,
author = ctx.author,
description = f"Playing {vc.source.title} in {vc.channel}"))
I have no idea what to do to fix this problem.
Simply, I removed the
author = ctx.author,
That are in the "play()" command both times
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")
So, i tried to make a bot that send embed to specific channel everytime user join my server.
The code is look like this
import discord
import asyncio
import datetime
from discord.ext import commands
intents = discord.Intents()
intents.members = True
intents.messages = True
intents.presences = True
bot = commands.Bot(command_prefix="a!", intents=intents)
#bot.event
async def on_ready():
print('Bot is ready.')
#bot.event
async def on_member_join(ctx, member):
embed = discord.Embed(colour=0x1abc9c, description=f"Welcome {member.name} to {member.guild.name}!")
embed.set_thumbnail(url=f"{member.avatar_url}")
embed.set_author(name=member.name, icon_url=member.avatar_url)
embed.timestamp = datetime.datetime.utcnow()
channel = guild.get_channel(816353040482566164)
await channel.send(embed=embed)
and i got an error
Ignoring exception in on_member_join
Traceback (most recent call last):
File "C:\Users\Piero\AppData\Roaming\Python\Python39\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "C:\Users\Piero\Documents\Discord\a-chan\achan_bot\main.py", line 24, in on_member_join
channel = guild.get_channel(816353040482566164)
NameError: name 'guild' is not defined
Anyone know what is wrong in my code?
First of all, looking at the discord.py documention, ctx is not passed to the on_member_join event reference. However, you can use the attributes of member which is passed in order to get the values which you need.
#bot.event
async def on_member_join(member):
embed = discord.Embed(
colour=0x1abc9c,
description=f"Welcome {member.name} to {member.guild.name}!"
)
embed.set_thumbnail(url=f"{member.avatar_url}")
embed.set_author(name=member.name, icon_url=member.avatar_url)
embed.timestamp = datetime.datetime.utcnow()
channel = member.guild.get_channel(816353040482566164)
await channel.send(embed=embed)
Interestingly enough, you did this perfectly for getting the guild name, but it seems you forgot to do the same when retrieving channel.
You did not define guild.
To define your guild you can do the following:
guild = bot.get_guild(GuildID)
It's the same method you used to define your channel, just for your guild now.
For further information you can have a look at the docs: https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.get_guild
Also take into account that we do not have a paramter like ctx in an on_member_join event.
The event just has the parameter member in your case:
#bot.event
async def on_member_join(member): #Removed ctx
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()
I am trying to get my bot to play an audio file in the voice chat, not a youtube video. I surprisingly can't really find any working help on this.
#client.command()
async def play(ctx, *args):
if len(args) == 0:
await ctx.send("Specify something then")
else:
#server = ctx.message.server
#voice_client = client.voice_client_in(server)
voice_player = await ctx.message.author.voice.channel.connect()
if args[0] == "chulp":
print("Playing chulp")
source = discord.FFmpegPCMAudio("Core files\\Sounds\\chulpy.mp3")
player = voice_player.play(source)
player.start()
I always get this error:
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: AttributeError: 'NoneType' object has no attribute 'start'