├── .env ├── keep_alive.py ├── lyrics.py ├── LICENSE ├── README.md └── main.py /.env: -------------------------------------------------------------------------------- 1 | TOKEN = 2 | #copy and paste your bots token the above variable 3 | -------------------------------------------------------------------------------- /keep_alive.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from threading import Thread 3 | 4 | app = Flask('') 5 | 6 | @app.route('/') 7 | def home(): 8 | return "Hello. I am alive!" 9 | 10 | def run(): 11 | app.run(host='0.0.0.0',port=8080) 12 | 13 | def keep_alive(): 14 | t = Thread(target=run) 15 | t.start() -------------------------------------------------------------------------------- /lyrics.py: -------------------------------------------------------------------------------- 1 | import lyricsgenius 2 | import os 3 | 4 | 5 | class GeniusAPI: 6 | def __init__(self, api_key): 7 | self.api_key = api_key 8 | self.genius = lyricsgenius.Genius(self.api_key) 9 | 10 | def get_lyrics(self, song, artist = None): 11 | try: 12 | if artist is None: 13 | song = self.genius.search_song(song) 14 | else: 15 | song = self.genius.search_song(song, artist) 16 | return song.lyrics 17 | 18 | except Exception as e: 19 | return e 20 | 21 | #Function to get current song 22 | def get_current_song(): 23 | for file in os.listdir('./'): 24 | if file.endswith('.mp3'): 25 | return file.split('.mp3')[0] 26 | 27 | #Function to get song list 28 | def get_song_list(): 29 | return [file.split('.mp3')[0] for file in os.listdir('./') if file.endswith('.mp3')] 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Divya Kumar Baid 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discord_Music_bot 2 |
5 | 6 | This is a simple Discord Music bot that plays music 24/7 looping thorugh the available ones in directory. 7 | To run this bot we need to several packages such as PyNaCl, discord.py 8 | 9 | ## Installation 10 | 11 | Use the package manager [pip](https://pip.pypa.io/en/stable/) \ 12 | To install discord.py 13 | 14 | ```bash 15 | python3 -m pip install -U discord.py 16 | ``` 17 | To install PyNaCl 18 | ```bash 19 | pip install pynacl 20 | ``` 21 | To install youtube_dl 22 | ```bash 23 | pip install youtube_dl 24 | ``` 25 | You also need to install [FFmpeg](https://www.ffmpeg.org/) 26 | API key for song lyrics should be acquired from [Genius](https://docs.genius.com/) 27 | 28 | ## Pre-Text: 29 | 30 | This is a discord bot made using the lastest discord.py api as of march 2021. This bot plays mp3 file from the storage i.e. all the mp3 files available to play from the file of your bot. I did this program in python language and have used discord.py , youtube_dl , PyNaCl , FFmpeg and several other packages and api's. Initially this bot was build on repl.it IDE and can run for infinite time i.e. it will be online continuously using [Uptimerobot](https://uptimerobot.com/) 31 | 32 | ## How to Install 33 | 34 | 1. Create a ```python``` virtual environment.I did in repl.it 35 | 2. Clone the repo ```git clone https://github.com/DivyaKumarBaid/Discord_Music_bot.git``` or download the repository. 36 | 3. Go to the cloned/downloaded directory ``` cd Discord_Music_bot ``` 37 | 4. Upload it in ```repl.it``` 38 | 5. Create a bot in [discord developers portal]((https://discord.com/developers/docs/game-and-server-management/vanity-perks)) 39 | 6. Copy the ``Token`` of the bot and paste it in the ``.env`` file as ``TOKEN`` 40 | 7. Run the bot on ```repl.it``` 41 | 8. Copy the ``url`` that appears on the right side of window 42 | 9. Go to [Uptimerobot](https://uptimerobot.com/) and create a monitor and paste the copied ``url`` and start the monitor.This will keep the bot alive even after you close it. 43 | 44 | For more precise steps have a look at [FreeCodeCamp](https://www.freecodecamp.org/news/create-a-discord-bot-with-python/) 45 | 46 | ## Commands 47 | 48 | ``` 49 | add This adds a music to the playlist. The url must be of youtube 50 | clear This command clears given number of messages and by default it clears last 5 messages 51 | clear_playlist This command removes every available mp3 file 52 | join This adds bot to the given channel and by default in General 53 | play This command plays song from the available ones.Providing channel would let the bot run there else by default it will run in General 54 | remove This command removes the specified .mp3 file 55 | songs This command lists all the songs available to play 56 | stop This stops the music playing and the bot leaves the voice channel 57 | lyrics This displays the lyrics for the current playing song 58 | ``` 59 | 60 | ## Contributing 61 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 62 | 63 | Please make sure to update tests as appropriate. 64 | 65 | ## License 66 | [MIT](https://choosealicense.com/licenses/mit/) 67 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | from keep_alive import keep_alive 3 | import discord 4 | import nacl 5 | from discord import FFmpegPCMAudio 6 | import os 7 | import youtube_dl 8 | import discord.utils 9 | from discord.ext import commands, tasks 10 | from itertools import cycle 11 | from youtubesearchpython import VideosSearch 12 | from lyrics import * 13 | 14 | 15 | my_secret = os.environ['TOKEN'] 16 | # GeniusAPI = GeniusAPI() 17 | 18 | client = commands.Bot(command_prefix='m.',help_command=None) 19 | song_played=[] 20 | song_url=[] 21 | ch_vc=[] 22 | 23 | #before running install pip install pynacl 24 | #for audio pip install ffmpeg 25 | 26 | FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} #locking options for ffmpeg 27 | 28 | 29 | #infinite loop to play music 24X7 untill closed/stopped 30 | @tasks.loop(seconds=5) 31 | async def play_song(ctx, ch, channel,l): 32 | voice = discord.utils.get(client.voice_clients, guild=ctx.guild) 33 | global song_url 34 | #print(song_url) 35 | url=song_url[0] 36 | if not ch.is_playing() and not voice == None : 37 | try: 38 | ydl_opts = {'format': 'bestaudio/best'} 39 | with youtube_dl.YoutubeDL(ydl_opts) as ydl: 40 | info = ydl.extract_info(url, download=False) 41 | video_title = info.get('title', None) 42 | URL = info['formats'][0]['url'] 43 | ch.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS)) 44 | text = embedding(f" Playing :{video_title}") 45 | await ctx.send(embed=text, delete_after=60.0) 46 | song_played.append(song_url[0]) 47 | song_url.pop(0) 48 | except: 49 | await ctx.send("Connection Error!!") 50 | if len(song_url) == 0: 51 | for i in range(0,len(song_played)): 52 | song_url.append(song_played[i]) 53 | 54 | song_played.clear() 55 | 56 | @client.command(help= "Skip the current song") 57 | async def skip(ctx): 58 | ch=ch_vc[0] 59 | ch.stop() 60 | 61 | #when bot is ready 62 | @client.event 63 | async def on_ready(): 64 | print("I am alive") 65 | await client.change_presence( 66 | status=discord.Status.online, 67 | activity=discord.Game('Music. To know more type m.help')) 68 | 69 | 70 | #sets volume to user defined value 71 | @client.command() 72 | async def volume(ctx, x: int): 73 | y=x/100 74 | vc = discord.utils.get(client.voice_clients, guild=ctx.guild) 75 | vc.source = discord.PCMVolumeTransformer(vc.source) 76 | vc.source.volume = y 77 | text = discord.Embed( 78 | title= "**Volume Control**", 79 | description = f" Volume set to {int(x)} ", 80 | color= 53380, 81 | ) 82 | text.set_author(name= "Discord_music_bot", 83 | icon_url= "https://static.vecteezy.com/system/resources/thumbnails/000/371/212/small/1781.jpg") 84 | text.set_footer(text= "m.help to know commands") 85 | await ctx.send(embed=text) 86 | 87 | 88 | #play command to start an infinite loop 89 | 90 | @client.command(help="Channel name is optional." , brief="This command plays song from the available ones.Providing channel name is optional without which it will play on General") 91 | async def play(ctx, channel='General'): 92 | 93 | #joining the desired channel 94 | voice = discord.utils.get(client.voice_clients, guild=ctx.guild) 95 | channel = discord.utils.get(ctx.guild.voice_channels, name=channel) 96 | if voice == None: 97 | await ctx.send(f"Joined **{channel}**") 98 | else: 99 | await ctx.voice_client.disconnect() 100 | ch = await channel.connect() 101 | if(len(ch_vc)!=0): 102 | ch_vc.pop(0) 103 | ch_vc.append(ch) 104 | print(ch_vc) 105 | await ctx.send(f"Playing on **{channel}** Channel") 106 | 107 | #get the number of songs and if none is present it will show up a message 108 | n = len(song_url) 109 | if not n==0: 110 | n=n-1 111 | play_song.start(ctx, ch, channel,n) 112 | else: 113 | text = discord.Embed( 114 | title= "**No Music**", 115 | description = "There is no music to play\nUse _add [url] to add a music", 116 | color= 53380, 117 | ) 118 | text.set_author(name= "Discord_Music_bot", 119 | icon_url= "https://static.vecteezy.com/system/resources/thumbnails/000/371/212/small/1781.jpg") 120 | text.set_footer(text= "m.help to know commands") 121 | await ctx.send(embed=text) 122 | 123 | 124 | 125 | #add music 126 | @client.command(help='youtube link is required', brief='This adds a music to the playlist. The url must be of youtube') 127 | async def add(ctx, * ,searched_song): 128 | print(searched_song) 129 | 130 | videosSearch = VideosSearch(searched_song, limit = 1) 131 | result_song_list = videosSearch.result() 132 | # print(result_song_list) 133 | title_song = result_song_list['result'][0]['title'] 134 | urllink = result_song_list['result'][0]['link'] 135 | 136 | song_url.append(urllink) 137 | text = discord.Embed( 138 | title= "**Song Added**", 139 | description = f"{title_song} is added to the Queue\nLink : {urllink}", 140 | color= 53380, 141 | ) 142 | # text.add_image(url=f"{result_song_list['result'][0]['thumbnail']['url']}") 143 | text.set_author(name= "Discord_music_bot", 144 | icon_url= "https://static.vecteezy.com/system/resources/thumbnails/000/371/212/small/1781.jpg") 145 | text.set_footer(text= "m.help to know commands") 146 | await ctx.send(embed=text) 147 | # await ctx.send(f"LINK : {urllink} ADDED") 148 | 149 | 150 | #leave vc and stop playing 151 | @client.command(help='This stops the loop' ,brief='This stops the music playing and the bot leaves the voice channel') 152 | async def stop(ctx): 153 | voice = discord.utils.get(client.voice_clients, guild=ctx.guild) 154 | if voice == None: 155 | return 156 | await ctx.voice_client.disconnect() 157 | play_song.stop() 158 | for i in range(0,len(song_played)): 159 | song_url.append(song_played[i]) 160 | await ctx.send("Have left the channel") 161 | 162 | 163 | 164 | #Songs list 165 | @client.command(help="This shows the songs present in the directory" ,brief='This command lists all the songs available to play') 166 | async def songs(ctx): 167 | l=len(song_url) 168 | if(l==0): 169 | await ctx.send("No music to play") 170 | for i in range(0,l): 171 | videosSearch = VideosSearch(song_url[i], limit = 1) 172 | result_song_list = videosSearch.result() 173 | # print(result_song_list) 174 | title_song = result_song_list['result'][0]['title'] 175 | text = discord.Embed( 176 | description = f"{i+1}# Song Name : {title_song} ", 177 | color= 53380, 178 | ) 179 | text.set_author(name= "Discord_music_bot", 180 | icon_url= "https://static.vecteezy.com/system/resources/thumbnails/000/371/212/small/1781.jpg") 181 | await ctx.send(embed=text) 182 | 183 | #removes every song 184 | @client.command(help='The file name should be wiht mp3 extension' , brief='This command removes every0 available song') 185 | async def clear_playlist(ctx): 186 | song_url.clear() 187 | text= discord.Embed( 188 | description="**Playlist cleared**", 189 | color = 53380, 190 | ) 191 | text.set_author(name= "Discord_music_bot", 192 | icon_url= "https://static.vecteezy.com/system/resources/thumbnails/000/371/212/small/1781.jpg") 193 | text.set_footer(text= "m.help to know commands") 194 | await ctx.send(embed=text) 195 | 196 | 197 | #clears msgs 198 | @client.command(help='This command clears text messages', brief='This command clears given number of messages and by default it clears last 5 text messages') 199 | async def clear(ctx, amount=5): 200 | await ctx.channel.purge(limit=amount) 201 | text = embedding("Cleared") 202 | await ctx.send(embed=text) 203 | 204 | #remove a particular song 205 | @client.command(help='The file name should be wiht mp3 extension' , brief='This command removes the specified file') 206 | async def remove(ctx,x: int): 207 | x=x-1 208 | videosSearch = VideosSearch(song_url[x], limit = 1) 209 | result_song_list = videosSearch.result() 210 | title_song = result_song_list['result'][0]['title'] 211 | text= embedding(f"{title_song} Removed") 212 | await ctx.send(embed=text) 213 | song_url.pop(x) 214 | 215 | #custom help command 216 | @client.group(invoke_without_command=True) 217 | async def help(ctx): 218 | text = discord.Embed( 219 | title= "**HELP TAB**", 220 | url= "https://github.com/DivyaKumarBaid/Discord_Music_bot", 221 | description = "***Welcome to Help Tab. Below are definations and how to use commands section*** \n\nm.add** [url]\n\nThis adds the music to queue \n\n**m.play [VoiceChannel(optional)]**\n\nThis command plays music in the desired channel or by default in General\n\n**m.songs** \n\n Lists all the songs in the playlist\n\n**m.volume [integer value]**\n\nSets the volume level\n\n**m.stop**\n\nStops the music player\n\n**m.clear_playlist**\n\nRemoves every song from the playlist\n\n**m.remove [index from the list of songs provided by typing m.songs]**\n\nRemoves the particular song\n\n", 222 | color= 53380, 223 | ) 224 | text.set_author(name= "Discord_music_bot", 225 | icon_url= "https://static.vecteezy.com/system/resources/thumbnails/000/371/212/small/1781.jpg") 226 | text.set_footer(text= "m.help to know commands") 227 | await ctx.send(embed=text) 228 | 229 | #embeds text 230 | def embedding(text: str): 231 | text= discord.Embed( 232 | description=f"**{text}**", 233 | color = 53380, 234 | ) 235 | text.set_author(name= "Discord_music_bot", 236 | icon_url= "https://static.vecteezy.com/system/resources/thumbnails/000/371/212/small/1781.jpg") 237 | text.set_footer(text= "m.help to know commands") 238 | return(text) 239 | 240 | #checks for errors 241 | @client.event 242 | async def on_command_error(ctx, error): 243 | if isinstance(error, commands.CommandNotFound): 244 | await ctx.send('Invalid Command Used. Type //help to know the commands' 245 | ) 246 | if isinstance(error, commands.MissingRequiredArgument): 247 | await ctx.send( 248 | 'Give proper values to the command an argument is missing') 249 | 250 | keep_alive() #this keeps the bot alive 251 | 252 | #runs bot 253 | client.run(my_secret) 254 | --------------------------------------------------------------------------------