I'm writing an article in MarkDown for the first time, so I think there are various strange parts, but I hope you can see it with warm eyes (; ´Д `) Please let me know if you have any corrections ... And since discord.py is also a beginner, I hope you can see it with warm eyes, but if you make a mistake, please let me know if you find it.
Python 3.9.1
Discord.py 1.5.1
PyNaCl 1.4.0
youtube_dl 2020.12.26
Windows10
First of all, it is necessary to prepare the necessary packages, but it is a prerequisite that ** the environment for running Bot is ready **. So Python and Discord.py are probably included, so I'll omit them. I want to use youtube_dl this time
python
$ pip install youtube_dl
Let's run youtube_dl in the terminal.
First, go to Site, and I think there are two git Links in the middle. Either one is fine, so download it (click the URL to start the download).
After downloading, unzip it and it will contain various items.
Downloaded ffmpeg file
bin
┗ ffmpeg.exe
Since it should be, copy or cut ffmpeg.exe and ** Paste it into the directory that contains the bot's main files **.
Now that you're ready, here's the code.
import asyncio
import discord
import youtube_dl
from discord.ext import commands
# Suppress noise about console usage from errors
youtube_dl.utils.bug_reports_message = lambda: ''
ytdl_format_options = {
'format': 'bestaudio/best',
'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
'restrictfilenames': True,
'noplaylist': True,
'nocheckcertificate': True,
'ignoreerrors': False,
'logtostderr': False,
'quiet': True,
'no_warnings': True,
'default_search': 'auto',
'source_address': '0.0.0.0' # bind to ipv4 since ipv6 addresses cause issues sometimes
}
ffmpeg_options = {
'options': '-vn'
}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)
class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, volume=0.5):
super().__init__(source, volume)
self.data = data
self.title = data.get('title')
self.url = data.get('url')
@classmethod
async def from_url(cls, url, *, loop=None, stream=False):
loop = loop or asyncio.get_event_loop()
data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=not stream))
if 'entries' in data:
# take first item from a playlist
data = data['entries'][0]
filename = data['url'] if stream else ytdl.prepare_filename(data)
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
class Music(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.command()
async def join(self, ctx, *, channel: discord.VoiceChannel):
"""Joins a voice channel"""
if ctx.voice_client is not None:
return await ctx.voice_client.move_to(channel)
await channel.connect()
@commands.command()
async def play(self, ctx, *, query):
"""Plays a file from the local filesystem"""
source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(query))
ctx.voice_client.play(source, after=lambda e: print('Player error: %s' % e) if e else None)
await ctx.send('Now playing: {}'.format(query))
@commands.command()
async def yt(self, ctx, *, url):
"""Plays from a url (almost anything youtube_dl supports)"""
async with ctx.typing():
player = await YTDLSource.from_url(url, loop=self.bot.loop)
ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
await ctx.send('Now playing: {}'.format(player.title))
@commands.command()
async def stream(self, ctx, *, url):
"""Streams from a url (same as yt, but doesn't predownload)"""
async with ctx.typing():
player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
await ctx.send('Now playing: {}'.format(player.title))
@commands.command()
async def volume(self, ctx, volume: int):
"""Changes the player's volume"""
if ctx.voice_client is None:
return await ctx.send("Not connected to a voice channel.")
ctx.voice_client.source.volume = volume / 100
await ctx.send("Changed volume to {}%".format(volume))
@commands.command()
async def stop(self, ctx):
"""Stops and disconnects the bot from voice"""
await ctx.voice_client.disconnect()
@play.before_invoke
@yt.before_invoke
@stream.before_invoke
async def ensure_voice(self, ctx):
if ctx.voice_client is None:
if ctx.author.voice:
await ctx.author.voice.channel.connect()
else:
await ctx.send("You are not connected to a voice channel.")
raise commands.CommandError("Author not connected to a voice channel.")
elif ctx.voice_client.is_playing():
ctx.voice_client.stop()
bot = commands.Bot(command_prefix=commands.when_mentioned_or("!"),
description='Relatively simple music bot example')
@bot.event
async def on_ready():
print('Logged in as {0} ({0.id})'.format(bot.user))
print('------')
bot.add_cog(Music(bot))
bot.run('token')
This time it is taken from github. From here, you may want to make it easier to see with embed. (Sorry for throwing)
When executed, the following commands can be used.
Connect to join [channel]
/ [channel]
Play the link specified by yt [url]
/ [url] * [url] is also searched by song title etc.
Play the link specified by stream [url]
/ [url] * I don't know this
Adjust to the volume specified by volume [volume]
/ [volume]
stop
/ Disconnect from VC
The previous throwing makes it meaningless to write an article (?) I rewrote the code a little to make it easier to use.
@commands.command(aliases=["p"])
async def play(self, ctx, *, url):
channel = ctx.author.voice.channel
if channel is None:
return await ctx.send("Not connected to VC.")
await channel.connect()
async with ctx.typing():
player = await YTDLSource.from_url(url, loop=self.bot.loop)
ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None)
await ctx.send("Playing:{}".format(player.title))
@commands.command(aliases=["vol"])
async def volume(self, ctx, volume: int):
channel = ctx.author.voice.channel
if channel is None:
return await ctx.send(Not connected to VC)
ctx.voice_client.source.volume = volume / 100
await ctx.send("Volume change:{}".format(volume))
@commands.command(aliases=["bye","disconnect"])
async def stop(self, ctx):
channel = ctx.author.voice.channel
if channel is None:
return await ctx.send(Not connected to VC)
await ctx.voice_client.disconnect()
await ctx.send("I left the VC.")
As an improvement of the above code ・ Abolish join ・ Connect to VC with play ・ Check if all are connected to VC I made some improvements.
How was it? I wasn't used to it yet, but I was able to finish writing it safely! Nowadays, bots such as Rythm and MEE6 are famous, but we are aiming for a bot that is comparable to that! It will be an advertisement, but I would be grateful if you could introduce my bot! You can try out bot features and have invitation links on the Support Server (https://discord.gg/bTSHEUsjmy). I hope you will take a look. I'm thinking of making articles gradually, but I haven't written anything so please let me know if there is anything.
Have a nice year!
Recommended Posts