Compare commits

..

No commits in common. "0d1620d0bd0f4a5887c51ca846b6839d9768f0c7" and "908619838c9bf8a3c06a33242046be136839b581" have entirely different histories.

5 changed files with 162 additions and 155 deletions

1
.gitignore vendored
View file

@ -1 +0,0 @@
.env

View file

@ -5,18 +5,15 @@ from yt_dlp import YoutubeDL
from asyncio import sleep
from discord.ext import commands
from StringProgressBar import progressBar
import uuid
import logging
import json
from pprint import pprint
## LOCAL IMPORTS ##
import utils
logging.basicConfig(level=logging.DEBUG)
intents = discord.Intents.default()
intents.message_content = True
# Dictionary to store queues between servers
queue_list = {}
server_info = {}
downloading = 0
paused = False
@ -24,25 +21,45 @@ paused = False
# Create a new Discord client
bot = commands.Bot(command_prefix="!",intents=discord.Intents.all(), shard_count=1)
@bot.event
async def on_ready():
print("Bot is ready!")
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="ななひら"))
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="Nanahira"))
async def delete_after_delay(ctx, delay):
await sleep(delay)
await ctx.message.delete()
async def get_ids(ctx):
await ctx.message.delete(delay=3)
server_id = ctx.message.guild.id
voice_channel = ctx.message.guild.voice_client
user_voice_channel = ctx.author.voice
return server_id, voice_channel, user_voice_channel
async def getFileNames(server_id):
downloading = 1
# Get the current unix timestamp to the nearest millisecond for the filename
uuid_stamp = uuid.uuid1()
audioname = str(uuid_stamp) + "audio"
thumbname = str(uuid_stamp) + "image"
# Create the id and thumbnail of the attachment as "tmp<timestamp>.flac" and "tmp<timestamp>.png" respectively
# And add the server ID as the path, making it unique
audioname = os.path.join(str(server_id), audioname)
thumbname = os.path.join(str(server_id), thumbname)
return audioname, thumbname
@bot.command()
async def play(ctx, *, query: str = None):
# Get all the id and channel info
server_id, voice_channel, user_voice_channel = await utils.get_ids(ctx)
server_id, voice_channel, user_voice_channel = await get_ids(ctx)
# Create a queue and info dictionary for the current server
if server_id not in server_info:
if server_id not in queue_list:
queue_list[server_id] = [1]
server_info[server_id] = {
"loop": False,
"paused": False,
"queue_position": 0,
"queue": [],
"loop": False
}
# Create the server ID folder for storing downloads
@ -53,7 +70,7 @@ async def play(ctx, *, query: str = None):
# If the user is not in a voice channel, return an error message
if user_voice_channel is None:
await ctx.send(":no_entry_sign: You must be in a voice channel to use this command.")
await ctx.send("You must be in a voice channel to use this command.", delete_after=3)
return
# Connect to the voice channel if not already connected
@ -62,13 +79,13 @@ async def play(ctx, *, query: str = None):
# If the user is not in a voice channel or the voice channel is not the same one as the bot, return an error message
if user_voice_channel.channel != voice_channel.channel:
await ctx.send(":no_entry_sign: You must be in the same voice channel as the bot to use this command.")
await ctx.send("You must be in the same voice channel as the bot to use this command.", delete_after=3)
return
if query == None and not ctx.message.attachments:
# If the bot is already playing, return an error message
if voice_channel.is_playing():
await ctx.send(":no_entry_sign: The bot is already playing")
await ctx.send(":no_entry_sign: The bot is already playing", delete_after=3)
return
# If the bot is paused, resume playback
@ -84,11 +101,13 @@ async def play(ctx, *, query: str = None):
if voice_channel.is_playing():
await ctx.send("The bot is already playing, adding song to queue", delete_after=3)
print("1");
if ctx.message.attachments:
downloading = 1
notice = await ctx.send(":arrow_double_up: Uploading...", suppress_embeds=True)
for song in ctx.message.attachments:
filename, thumbname = await utils.getFileNames(server_id)
filename, thumbname = await getFileNames(server_id)
# Make sure the file is either audio or video
filetype = song.content_type
@ -99,38 +118,33 @@ async def play(ctx, *, query: str = None):
await song.save(filename)
# Grab thumbnail from file
ffmpeg_cli = ffmpeg\
.input(
filename,
t=1,
)\
.output(
thumbname,
f="image2"
)\
.overwrite_output()
print(ffmpeg_cli.get_args())
ffmpeg_cli.run()
f = open("tmp", "w")
subprocess.run(["ffmpeg", "-y", "-i", filename, "-map", "0:v", "-map", "-0:V", "-c", "copy", thumbname], stderr=subprocess.STDOUT, stdout=f)
# Grab metadata from file
try:
metadata = ffmpeg.probe(filename)
except:
metadata = {}
f = open("tmp", "w")
subprocess.run(["ffmpeg", "-y", "-i", filename, "-f", "ffmetadata", str(server_id) + "/meta.txt"], stderr=subprocess.STDOUT, stdout=f)
file_title = song.filename
if "TITLE" in metadata["format"]["tags"]:
file_title = metadata["format"]["tags"]["TITLE"]
file_artist = None
file_album = None
file_artist = ""
if "ARTIST" in metadata["format"]["tags"]:
file_artist = metadata["format"]["tags"]["ARTIST"]
file_album = ""
if "ALBUM" in metadata["format"]["tags"]:
file_album = metadata["format"]["tags"]["ALBUM"]
with open(str(server_id) + '/meta.txt', 'r') as m:
lines = m.readlines()
file_artist = "Unknown"
file_album = "Unknown"
for line in lines:
if re.search(r'TITLE=', line, re.IGNORECASE):
file_title = line.split('=')[1]
break
for line in lines:
if re.search(r'ARTIST=', line, re.IGNORECASE):
file_artist = line.split('=')[1]
break
for line in lines:
if re.search(r'ALBUM=', line, re.IGNORECASE):
file_album = line.split('=')[1]
break
if os.path.exists(thumbname):
@ -139,7 +153,7 @@ async def play(ctx, *, query: str = None):
thumbnail = "assets/unknown.png"
try:
duration = metadata['format']['duration']
duration = ffmpeg.probe(filename)['format']['duration']
except:
duration = None
@ -151,16 +165,16 @@ async def play(ctx, *, query: str = None):
"url": song.url,
"id": filename,
"thumbnail": thumbnail,
"thumbnail_url": None,
"duration": duration
}
await notice.edit(content=":white_check_mark: Successfully uploaded \"" + song.filename + "\"")
server_info[server_id]["queue"].append(item)
queue_list[server_id].append(item)
downloading = 0
await notice.edit(content=":white_check_mark: Successfully uploaded \"" + song.filename + "\"", delete_after=3)
elif query[0:4] != "http" and query[0:3] != "www":
filename, thumbname = await utils.getFileNames(server_id)
filename, thumbname = await getFileNames(server_id)
# Let the user know the bot is searching for a video
notice = await ctx.send(":mag_right: Searching for \"" + query + "\" ...", suppress_embeds=True)
@ -187,11 +201,11 @@ async def play(ctx, *, query: str = None):
"duration": duration
}
await notice.edit(content=":white_check_mark: Found " + title + ": " + audio_url, suppress=True, delete_after=3)
server_info[server_id]["queue"].append(item)
await notice.edit(content=":white_check_mark: Downloaded " + title + ": " + audio_url, suppress=True, delete_after=3)
queue_list[server_id].append(item)
downloading = 0
elif query[0:4] == "http" or query[0:3] == "www":
filename, thumbname = await utils.getFileNames(server_id)
filename, thumbname = await getFileNames(server_id)
# Let the user know the bot is searching for a video
notice = await ctx.send(":mag_right: Searching for \"" + query + "\" ...", suppress_embeds=True)
@ -222,8 +236,8 @@ async def play(ctx, *, query: str = None):
"duration": duration
}
await notice.edit(content=":white_check_mark: Found \"" + title + "\": " + query, suppress=True, delete_after=3)
server_info[server_id]["queue"].append(item)
await notice.edit(content=":white_check_mark: Added \"" + title + "\": " + query, suppress=True, delete_after=3)
queue_list[server_id].append(item)
downloading = 0
else:
print("Error")
@ -239,27 +253,26 @@ async def play(ctx, *, query: str = None):
playing = await ctx.send(embed=embed)
# Loop that repeats as long as the queue position has not reached the length of the queue
while len(server_info[server_id]["queue"]) >= server_info[server_id]["queue_position"]:
while len(queue_list[server_id]) - 1 >= queue_list[server_id][0]:
# Get the current queue position
queue_position = server_info[server_id]["queue_position"]
queue = server_info[server_id]["queue"]
queue_position = queue_list[server_id][0]
# Set song variables
song_id = queue[queue_position]['id']
song_url = queue[queue_position]['url']
song_name = queue[queue_position]['name']
song_thumb = queue[queue_position]['thumbnail']
song_duration = queue[queue_position]['duration']
song_thumb_url = queue[queue_position]['thumbnail_url']
song_id = queue_list[server_id][queue_position]['id']
song_url = queue_list[server_id][queue_position]['url']
song_name = queue_list[server_id][queue_position]['name']
song_thumb = queue_list[server_id][queue_position]['thumbnail']
song_duration = queue_list[server_id][queue_position]['duration']
song_thumb_url = queue_list[server_id][queue_position]['thumbnail_url']
song_thumbname = str(int(time.time())) + ".png"
# Create the embed
if queue[queue_position]['artist'] and queue[queue_position]['album']:
song_desc = "Artist: " + queue[queue_position]['artist'] + "\nAlbum: " + queue[queue_position]['album']
if queue_list[server_id][queue_position]['artist'] and queue_list[server_id][queue_position]['album']:
song_desc = "Name: " + song_name + "\nArtist: " + queue_list[server_id][queue_position]['artist'] + "\nAlbum: " + queue_list[server_id][queue_position]['album']
else:
song_desc = ""
song_desc = "Name: " + song_name + "\nURL: " + song_url
embed=discord.Embed(title=":arrow_forward: Playing: " + song_name, url=song_url, description=song_desc, color=0x42f5a7)
embed=discord.Embed(title="▶️ Playing: " + song_name, url=song_url, description=song_desc, color=0x42f5a7)
if song_thumb is not None:
await playing.add_files(discord.File(song_thumb, filename=song_thumbname))
embed.set_thumbnail(url="attachment://" + song_thumbname)
@ -315,15 +328,22 @@ async def play(ctx, *, query: str = None):
)
await playing.edit(embed=embed)
if not server_info[server_id]["loop"]:
# Increment the queue position by 1
try:
server_info[server_id]["queue_position"] += 1
except:
print(str(server_id) + " | " + "Queue position out of range.")
break
if server_info[server_id]["loop"]:
continue
# Increment the queue position by 1
try:
queue_list[server_id][0] += 1
except:
print(str(server_id) + " | " + "Queue position out of range.")
break
print(str(server_id) + " | " + "Play position: " + str(queue_position))
try:
await q(ctx)
except:
pass
# Display the stop embed
try:
await q(ctx, "hide")
@ -338,8 +358,8 @@ async def play(ctx, *, query: str = None):
# Disconnect from the voice channel if the loop finishes
await voice_channel.disconnect()
server_info[server_id]["queue"].clear()
server_info[server_id]["queue_position"] = 0
queue_list[server_id].clear()
queue_list[server_id].insert(0, 1)
# Remove all queued files and folders
fileList = glob.glob(os.path.join(str(server_id),'*'))
@ -350,11 +370,10 @@ async def play(ctx, *, query: str = None):
print("Error while deleting file")
os.rmdir(str(server_id))
@bot.command()
async def skip(ctx, direction = None, number = None):
# Get all the id and channel info
server_id, voice_channel, user_voice_channel = await utils.get_ids(ctx)
server_id, voice_channel, user_voice_channel = await get_ids(ctx)
if voice_channel is None:
await ctx.send(":no_entry_sign: Bot must be playing to skip!", delete_after=3)
@ -375,15 +394,16 @@ async def skip(ctx, direction = None, number = None):
try:
if direction.isnumeric():
if int(direction) > 0:
server_info[server_id]["queue_position"] += int(direction) - 1
queue_list[server_id][0] += int(direction) - 1
except:
pass
if number != None and number > 0:
server_info[server_id]["queue_position"] += number - 1
queue_list[server_id][0] += number - 1
# Stop the audio playback
voice_channel.stop()
elif direction == "back" and not server_info[server_id]["queue_position"] == 1:
elif direction == "back" and not queue_list[server_id][0] == 1:
# Decrement the queue position
back = 2
@ -392,16 +412,19 @@ async def skip(ctx, direction = None, number = None):
if number != None and number > 0:
back = number + 1
server_info[server_id]["queue_position"] -= back
queue_list[server_id][0] -= back
# Stop the audio playback of the current track
voice_channel.stop()
elif direction == "to" and number is not None:
server_info[server_id]["queue_position"] = number
queue_list[server_id][0] = number - 1
voice_channel.stop()
elif server_info[server_id]["queue_position"] == 0:
elif queue_list[server_id][0] == 1:
await ctx.send(":no_entry_sign: Already at first song in queue.", delete_after=3)
else:
await ctx.send(":no_entry_sign: Invalid argument.", delete_after=3)
@ -411,11 +434,10 @@ async def skip(ctx, direction = None, number = None):
except:
pass
@bot.command()
async def stop(ctx):
# Get all the id and channel info
server_id, voice_channel, user_voice_channel = await utils.get_ids(ctx)
server_id, voice_channel, user_voice_channel = await get_ids(ctx)
if voice_channel is None:
await ctx.send(":no_entry_sign: Bot must be playing to stop!", delete_after=3)
@ -425,17 +447,16 @@ async def stop(ctx):
await ctx.send(":no_entry_sign: You must be in the same voice channel as the bot to use this command.", delete_after=3)
return
server_info[server_id]["queue"].clear()
server_info[server_id]["queue_position"] = 0
queue_list[server_id].clear()
queue_list[server_id].insert(0, 1)
await sleep(0.5)
voice_channel.stop()
await voice_channel.disconnect()
@bot.command()
async def pause(ctx):
# Get all the id and channel info
server_id, voice_channel, user_voice_channel = await utils.get_ids(ctx)
server_id, voice_channel, user_voice_channel = await get_ids(ctx)
if voice_channel is None or not voice_channel.is_playing():
await ctx.send(":no_entry_sign: Bot must be playing to pause!", delete_after=3)
@ -448,16 +469,28 @@ async def pause(ctx):
global paused
paused = True
@bot.command()
async def queue(ctx, action = None, selection = None):
await q(ctx, action, selection)
@bot.command()
async def q(ctx, action = None, selection = None):
# Get all the id and channel info
server_id, voice_channel, user_voice_channel = await utils.get_ids(ctx)
server_id, voice_channel, user_voice_channel = await get_ids(ctx)
global queue_embed
if action == "hide":
try:
queue_embed
except:
pass
else:
if queue_embed is None:
pass
else:
await queue_embed.delete()
queue_embed = None
del queue_embed
return
if voice_channel is None:
await ctx.send(":no_entry_sign: Bot must be in a channel to view the queue!", delete_after=3)
@ -468,34 +501,40 @@ async def q(ctx, action = None, selection = None):
return
if action == "show" or action == "list" or action == None:
print(str(server_id) + " | " + "Updating queue, position: " + str(server_info[server_id]["queue_position"]))
index = 0
if action == "show":
try:
await q(ctx, "hide")
except:
pass
print(str(server_id) + " | " + "Updating queue, position: " + str(queue_list[server_id][0]))
x = 0
qu = ""
d = ""
p = ""
now_playing = ""
for entry in server_info[server_id]["queue"]:
if index == server_info[server_id]["queue_position"]:
now_playing = ":arrow_right:"
else:
now_playing = ""
for entry in queue_list[server_id]:
if x != 0:
if x == queue_list[server_id][0]:
now_playing = ":arrow_right:"
else:
now_playing = ""
if len(entry['name']) >= 30:
entry_cut = entry['name'][0:30]
else:
entry_cut = entry['name']
if len(entry['name']) >= 30:
entry_cut = entry['name'][0:30]
else:
entry_cut = entry['name']
p += now_playing + "\n"
qu += "**" + str(index + 1) + ":** " + entry_cut + "\n"
if str(strftime("%H", gmtime(int(float(entry['duration'])))))[0:1] == "00":
d += str(strftime("%M:%S", gmtime(int(float(entry['duration']))))) + "\n"
else:
d += str(strftime("%H:%M:%S", gmtime(int(float(entry['duration']))))) + "\n"
p += now_playing + "\n"
qu += "**" + str(x) + ":** " + entry_cut + "\n"
if str(strftime("%H", gmtime(int(float(entry['duration'])))))[0:1] == "00":
d += str(strftime("%M:%S", gmtime(int(float(entry['duration']))))) + "\n"
else:
d += str(strftime("%H:%M:%S", gmtime(int(float(entry['duration']))))) + "\n"
x += 1
index += 1
embed = discord.Embed(title="Queue:", description="", color=0xa032a8)
embed=discord.Embed(title="Queue:", description="", color=0xa032a8)
try:
embed.add_field(name="", value=p, inline=True)
embed.add_field(name="List", value=qu, inline=True)
@ -516,20 +555,19 @@ async def q(ctx, action = None, selection = None):
if action == "remove":
print(str(server_id) + " | " + "Removing item #" + str(selection) + " from queue")
selection = selection - 1
selection = int(selection)
position = server_info[server_id]["queue_position"]
id = server_info[server_id]["queue"][selection]['id']
if server_info[server_id]["queue"][selection]['thumbnail'] != "/assets/unknown.png":
position = queue_list[server_id][0]
id = queue_list[server_id][selection]['id']
if queue_list[server_id][selection]['thumbnail'] != "/assets/unknown.png":
thumbnail = None
else:
thumbnail = server_info[server_id]["queue"][selection]['thumbnail']
thumbnail = queue_list[server_id][selection]['thumbnail']
if selection is position:
await ctx.send(":no_entry_sign: Error, cannot remove currently playing item", delete_after=3)
return
if selection != 0 and not int(selection) > len(server_info[server_id]["queue"]):
if selection != 0 and not int(selection) > len(queue_list[server_id]):
try:
os.remove(id)
if not thumbnail is None:
@ -542,22 +580,21 @@ async def q(ctx, action = None, selection = None):
if selection < position and position > 1:
try:
server_info[server_id]["queue_position"] -= 1
queue_list[server_id][0] -= 1
except:
print(str(server_id) + " | " + "Queue position out of range.")
pass
await ctx.send(":white_check_mark: Removed item #" + str(selection) + " from queue.", delete_after=3)
server_info[server_id]["queue"].pop(selection)
queue_list[server_id].pop(selection)
await q(ctx)
else:
await ctx.send(":no_entry_sign: Error, item #" + str(selection) + "not a valid queue item", delete_after=3)
return
@bot.command()
async def loop(ctx, number = None):
server_id, voice_channel, user_voice_channel = await utils.get_ids(ctx)
server_id, voice_channel, user_voice_channel = await get_ids(ctx)
if voice_channel is None or not voice_channel.is_playing() and not voice_channel.is_paused():
await ctx.send(":no_entry_sign: Bot must be playing to loop!", delete_after=3)
@ -579,14 +616,11 @@ async def loop(ctx, number = None):
print(server_info[server_id]["loop"])
return
@bot.event
async def on_command_error(ctx, error):
server_id, voice_channel, user_voice_channel = await utils.get_ids(ctx)
server_id, voice_channel, user_voice_channel = await get_ids(ctx)
if isinstance(error, commands.CommandNotFound):
await ctx.send("Unknown command", delete_after=3)
else:
print(error)
# Run the bot using the Discord bot token
bot.run(os.environ['DISCORD_SECRET'])
bot.run("<>")

View file

@ -1,5 +1,5 @@
discord==2.3
ffmpeg-python==0.2
ffmpeg==1.4
stringprogressbar==1.1
pynacl==1.5
yt-dlp==2024.8.6

View file

@ -1,26 +0,0 @@
import uuid, os
async def delete_after_delay(ctx, delay):
await sleep(delay)
await ctx.message.delete()
async def get_ids(ctx):
server_id = ctx.message.guild.id
voice_channel = ctx.message.guild.voice_client
user_voice_channel = ctx.author.voice
return server_id, voice_channel, user_voice_channel
async def getFileNames(server_id):
downloading = 1
# Get the current unix timestamp to the nearest millisecond for the filename
uuid_stamp = uuid.uuid1()
audioname = str(uuid_stamp) + "audio"
thumbname = str(uuid_stamp) + "image"
# Create the id and thumbnail of the attachment as "tmp<timestamp>.flac" and "tmp<timestamp>.png" respectively
# And add the server ID as the path, making it unique
audioname = os.path.join(str(server_id), audioname)
thumbname = os.path.join(str(server_id), thumbname)
return audioname, thumbname