diff --git a/DiscordUtils/InviteTracker.py b/DiscordUtils/InviteTracker.py index 7faa1a6..8b7fa1e 100644 --- a/DiscordUtils/InviteTracker.py +++ b/DiscordUtils/InviteTracker.py @@ -12,7 +12,7 @@ async def cache_invites(self): try: invs = await guild.invites() for invite in invs: - if invite.inviter not in self._cache[guild.id].keys(): + if invite.inviter not in self._cache[guild.id]: self._cache[guild.id][invite.inviter] = [] self._cache[guild.id][invite.inviter].append(invite) except discord.errors.Forbidden: @@ -20,9 +20,9 @@ async def cache_invites(self): async def update_invite_cache(self, invite): try: - if not invite.guild.id in self._cache.keys(): + if not invite.guild.id in self._cache: self._cache[invite.guild.id] = {} - if not invite.inviter in self._cache[invite.guild.id].keys(): + if not invite.inviter in self._cache[invite.guild.id]: self._cache[invite.guild.id][invite.inviter] = [] self._cache[invite.guild.id][invite.inviter].append(invite) except discord.errors.Forbidden: @@ -37,7 +37,7 @@ async def remove_invite_cache(self, invite): break async def remove_guild_cache(self, guild): - if guild.id in self._cache.keys(): + if guild.id in self._cache: del self._cache[guild.id] async def update_guild_cache(self, guild): @@ -45,7 +45,7 @@ async def update_guild_cache(self, guild): invs = await guild.invites() self._cache[guild.id] = {} for invite in invs: - if not invite.inviter in self._cache[guild.id].keys(): + if not invite.inviter in self._cache[guild.id]: self._cache[guild.id][invite.inviter] = [] self._cache[guild.id][invite.inviter].append(invite) except discord.errors.Forbidden: @@ -59,7 +59,7 @@ async def fetch_inviter(self, member): except discord.errors.Forbidden: return for invite in new_invites: - if not invite.inviter in invs.keys(): + if not invite.inviter in invs: invs[invite.inviter] = [] invs[invite.inviter].append(invite) for new_invite_key in invs: @@ -73,6 +73,4 @@ async def fetch_inviter(self, member): cached_invite_list.remove(old_invite) cached_invite_list.append(new_invite) return new_invite_key - break - else: - return None \ No newline at end of file + break \ No newline at end of file diff --git a/DiscordUtils/Music.py b/DiscordUtils/Music.py index 3491c2c..aa7af3e 100644 --- a/DiscordUtils/Music.py +++ b/DiscordUtils/Music.py @@ -33,56 +33,32 @@ async def ytbettersearch(query): return url async def get_video_data(url, search, bettersearch, loop): - if not search and not bettersearch: + if bettersearch: + url = await ytbettersearch(url) data = await loop.run_in_executor(None, lambda: ydl.extract_info(url, download=False)) - source = data["url"] - url = "https://www.youtube.com/watch?v="+data["id"] - title = data["title"] - description = data["description"] - likes = data["like_count"] - dislikes = data["dislike_count"] - views = data["view_count"] - duration = data["duration"] - thumbnail = data["thumbnail"] - channel = data["uploader"] - channel_url = data["uploader_url"] - return Song(source, url, title, description, views, duration, thumbnail, channel, channel_url, False) + elif search: + ytdl = youtube_dl.YoutubeDL({"format": "bestaudio/best", "restrictfilenames": True, "noplaylist": True, "nocheckcertificate": True, "ignoreerrors": True, "logtostderr": False, "quiet": True, "no_warnings": True, "default_search": "auto", "source_address": "0.0.0.0"}) + data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=False)) + try: + data = data["entries"][0] + except (KeyError, TypeError): + pass + del ytdl else: - if bettersearch: - url = await ytbettersearch(url) - data = await loop.run_in_executor(None, lambda: ydl.extract_info(url, download=False)) - source = data["url"] - url = "https://www.youtube.com/watch?v="+data["id"] - title = data["title"] - description = data["description"] - likes = data["like_count"] - dislikes = data["dislike_count"] - views = data["view_count"] - duration = data["duration"] - thumbnail = data["thumbnail"] - channel = data["uploader"] - channel_url = data["uploader_url"] - return Song(source, url, title, description, views, duration, thumbnail, channel, channel_url, False) - elif search: - ytdl = youtube_dl.YoutubeDL({"format": "bestaudio/best", "restrictfilenames": True, "noplaylist": True, "nocheckcertificate": True, "ignoreerrors": True, "logtostderr": False, "quiet": True, "no_warnings": True, "default_search": "auto", "source_address": "0.0.0.0"}) - data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=False)) - try: - data = data["entries"][0] - except KeyError or TypeError: - pass - del ytdl - source = data["url"] - url = "https://www.youtube.com/watch?v="+data["id"] - title = data["title"] - description = data["description"] - likes = data["like_count"] - dislikes = data["dislike_count"] - views = data["view_count"] - duration = data["duration"] - thumbnail = data["thumbnail"] - channel = data["uploader"] - channel_url = data["uploader_url"] - return Song(source, url, title, description, views, duration, thumbnail, channel, channel_url, False) + data = await loop.run_in_executor(None, lambda: ydl.extract_info(url, download=False)) + + source = data["url"] + url = f"https://www.youtube.com/watch?v={data['id']}" + title = data["title"] + description = data["description"] + likes = data["like_count"] + dislikes = data["dislike_count"] + views = data["view_count"] + duration = data["duration"] + thumbnail = data["thumbnail"] + channel = data["uploader"] + channel_url = data["uploader_url"] + return Song(source, url, title, description, views, duration, thumbnail, channel, channel_url, False) def check_queue(ctx, opts, music, after, on_play, loop): try: @@ -91,10 +67,10 @@ def check_queue(ctx, opts, music, after, on_play, loop): return if not song.is_looping: try: - music.queue[ctx.guild.id].pop(0) + music.queue[ctx.guild.id].pop() except IndexError: return - if len(music.queue[ctx.guild.id]) > 0: + if len(music.queue[ctx.guild.id]): source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(music.queue[ctx.guild.id][0].source, **opts)) ctx.voice_client.play(source, after=lambda error: after(ctx, opts, music, after, on_play, loop)) song = music.queue[ctx.guild.id][0] @@ -111,24 +87,18 @@ class Music(object): def __init__(self): self.queue = {} self.players = [] + def create_player(self, ctx, **kwargs): if not ctx.voice_client: raise NotConnectedToVoice("Cannot create the player because bot is not connected to voice") player = MusicPlayer(ctx, self, **kwargs) self.players.append(player) return player - def get_player(self, **kwargs): - guild = kwargs.get("guild_id") - channel = kwargs.get("channel_id") + + def get_player(self, guild_id=None, channel_id=None): for player in self.players: - if guild and channel and player.ctx.guild.id == guild and player.voice.channel.id == channel: - return player - elif not guild and channel and player.voice.channel.id == channel: - return player - elif not channel and guild and player.ctx.guild.id == guild: + if (guild_id and channel_id and player.ctx.guild.id == guild_id and player.voice.channel.id == channel_id) or (not guild_id and channel_id and player.voice.channel.id == channel_id) or (not channel_id and guild_id and player.ctx.guild.id == guild_id): return player - else: - return None class MusicPlayer(object): def __init__(self, ctx, music, **kwargs): @@ -136,43 +106,55 @@ def __init__(self, ctx, music, **kwargs): self.voice = ctx.voice_client self.loop = ctx.bot.loop self.music = music - if self.ctx.guild.id not in self.music.queue.keys(): + if self.ctx.guild.id not in self.music.queue: self.music.queue[self.ctx.guild.id] = [] self.after_func = check_queue self.on_play_func = self.on_queue_func = self.on_skip_func = self.on_stop_func = self.on_pause_func = self.on_resume_func = self.on_loop_toggle_func = self.on_volume_change_func = self.on_remove_from_queue_func = None ffmpeg_error = kwargs.get("ffmpeg_error_betterfix", kwargs.get("ffmpeg_error_fix")) - if ffmpeg_error and "ffmpeg_error_betterfix" in kwargs.keys(): + if ffmpeg_error and "ffmpeg_error_betterfix" in kwargs: self.ffmpeg_opts = {"options": "-vn -loglevel quiet -hide_banner -nostats", "before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 0 -nostdin"} elif ffmpeg_error: self.ffmpeg_opts = {"options": "-vn", "before_options": "-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 0 -nostdin"} else: self.ffmpeg_opts = {"options": "-vn", "before_options": "-nostdin"} + def disable(self): self.music.players.remove(self) + def on_queue(self, func): self.on_queue_func = func + def on_play(self, func): self.on_play_func = func + def on_skip(self, func): self.on_skip_func = func + def on_stop(self, func): self.on_stop_func = func + def on_pause(self, func): self.on_pause_func = func + def on_resume(self, func): self.on_resume_func = func + def on_loop_toggle(self, func): self.on_loop_toggle_func = func + def on_volume_change(self, func): self.on_volume_change_func = func + def on_remove_from_queue(self, func): self.on_remove_from_queue_func = func + async def queue(self, url, search=False, bettersearch=False): song = await get_video_data(url, search, bettersearch, self.loop) self.music.queue[self.ctx.guild.id].append(song) if self.on_queue_func: await self.on_queue_func(self.ctx, song) return song + async def play(self): source = discord.PCMVolumeTransformer(discord.FFmpegPCMAudio(self.music.queue[self.ctx.guild.id][0].source, **self.ffmpeg_opts)) self.voice.play(source, after=lambda error: self.after_func(self.ctx, self.ffmpeg_opts, self.music, self.after_func, self.on_play_func, self.loop)) @@ -180,24 +162,26 @@ async def play(self): if self.on_play_func: await self.on_play_func(self.ctx, song) return song + async def skip(self, force=False): if len(self.music.queue[self.ctx.guild.id]) == 0: raise NotPlaying("Cannot loop because nothing is being played") elif not len(self.music.queue[self.ctx.guild.id]) > 1 and not force: raise EmptyQueue("Cannot skip because queue is empty") - else: - old = self.music.queue[self.ctx.guild.id][0] - old.is_looping = False if old.is_looping else False - self.voice.stop() - try: - new = self.music.queue[self.ctx.guild.id][0] - if self.on_skip_func: - await self.on_skip_func(self.ctx, old, new) - return (old, new) - except IndexError: - if self.on_skip_func: - await self.on_skip_func(self.ctx, old) - return old + + old = self.music.queue[self.ctx.guild.id][0] + old.is_looping = False + self.voice.stop() + try: + new = self.music.queue[self.ctx.guild.id][0] + if self.on_skip_func: + await self.on_skip_func(self.ctx, old, new) + return (old, new) + except IndexError: + if self.on_skip_func: + await self.on_skip_func(self.ctx, old) + return old + async def stop(self): try: self.music.queue[self.ctx.guild.id] = [] @@ -207,6 +191,7 @@ async def stop(self): raise NotPlaying("Cannot loop because nothing is being played") if self.on_stop_func: await self.on_stop_func(self.ctx) + async def pause(self): try: self.voice.pause() @@ -216,6 +201,7 @@ async def pause(self): if self.on_pause_func: await self.on_pause_func(self.ctx, song) return song + async def resume(self): try: self.voice.resume() @@ -225,28 +211,30 @@ async def resume(self): if self.on_resume_func: await self.on_resume_func(self.ctx, song) return song + def current_queue(self): try: return self.music.queue[self.ctx.guild.id] except KeyError: raise EmptyQueue("Queue is empty") + def now_playing(self): try: return self.music.queue[self.ctx.guild.id][0] except: - return None + pass + async def toggle_song_loop(self): try: song = self.music.queue[self.ctx.guild.id][0] except: raise NotPlaying("Cannot loop because nothing is being played") - if not song.is_looping: - song.is_looping = True - else: - song.is_looping = False + + song.is_looping = not song.is_looping if self.on_loop_toggle_func: await self.on_loop_toggle_func(self.ctx, song) return song + async def change_volume(self, vol): self.voice.source.volume = vol try: @@ -256,6 +244,7 @@ async def change_volume(self, vol): if self.on_volume_change_func: await self.on_volume_change_func(self.ctx, song, vol) return (song, vol) + async def remove_from_queue(self, index): if index == 0: try: @@ -269,6 +258,7 @@ async def remove_from_queue(self, index): if self.on_remove_from_queue_func: await self.on_remove_from_queue_func(self.ctx, song) return song + def delete(self): self.music.players.remove(self) @@ -284,4 +274,4 @@ def __init__(self, source, url, title, description, views, duration, thumbnail, self.thumbnail = thumbnail self.channel = channel self.channel_url = channel_url - self.is_looping = loop + self.is_looping = loop \ No newline at end of file diff --git a/DiscordUtils/Pagination.py b/DiscordUtils/Pagination.py index b73533e..fafada3 100644 --- a/DiscordUtils/Pagination.py +++ b/DiscordUtils/Pagination.py @@ -12,39 +12,41 @@ def __init__(self, ctx, **kwargs): self.remove_reactions = kwargs.get("remove_reactions", False) self.control_emojis = ('⏮️', '⏪', '🔐', '⏩', '⏭️') self.timeout = int(kwargs.get("timeout", 60)) + async def run(self, embeds, send_to=None): if not send_to: send_to = self.ctx wait_for = self.ctx.author if send_to == self.ctx else send_to if not self.embeds: self.embeds = embeds + if self.auto_footer: - self.embeds[0].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') + for i in range(len(self.embeds)): + self.embeds[i].set_footer(text=f'({i + 1}/{len(self.embeds)})') + msg = await send_to.send(embed=self.embeds[0]) for emoji in self.control_emojis: try: await msg.add_reaction(emoji) except: pass - msg = await msg.channel.fetch_message(msg.id) + def check(reaction, user): return user == wait_for and reaction.message.id == msg.id and str(reaction.emoji) in self.control_emojis + while True: - if self.timeout > 0: - try: - reaction, user = await self.bot.wait_for("reaction_add",check=check, timeout=self.timeout) - except asyncio.TimeoutError: - self.current_page = 0 - for reaction in msg.reactions: - if reaction.message.author.id == self.bot.user.id: - try: - await msg.remove_reaction(str(reaction.emoji), reaction.message.author) - except: - pass - return msg - break - else: - reaction, user = await self.bot.wait_for("reaction_add",check=check) + try: + reaction, user = await self.bot.wait_for("reaction_add", check=check, timeout=self.timeout if self.timeout > 0 else None) + except asyncio.TimeoutError: + self.current_page = 0 + for reaction in msg.reactions: + if reaction.message.author.id == self.bot.user.id: + try: + await msg.remove_reaction(str(reaction.emoji), reaction.message.author) + except: + pass + return msg + if str(reaction.emoji) == self.control_emojis[0]: self.current_page = 0 if self.remove_reactions: @@ -52,19 +54,15 @@ def check(reaction, user): await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[0].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') await msg.edit(embed=self.embeds[0]) elif str(reaction.emoji) == self.control_emojis[1]: - self.current_page = self.current_page-1 - self.current_page = 0 if self.current_page<0 else self.current_page + self.current_page -= 1 + self.current_page = 0 if self.current_page < 0 else self.current_page if self.remove_reactions: try: await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[self.current_page].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') await msg.edit(embed=self.embeds[self.current_page]) elif str(reaction.emoji) == self.control_emojis[2]: self.current_page = 0 @@ -75,17 +73,14 @@ def check(reaction, user): except: pass return msg - break elif str(reaction.emoji) == self.control_emojis[3]: - self.current_page = self.current_page + 1 + self.current_page += 1 self.current_page = len(self.embeds)-1 if self.current_page > len(self.embeds)-1 else self.current_page if self.remove_reactions: try: await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[self.current_page].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') await msg.edit(embed=self.embeds[self.current_page]) elif str(reaction.emoji) == self.control_emojis[4]: self.current_page = len(self.embeds)-1 @@ -94,9 +89,7 @@ def check(reaction, user): await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[len(self.embeds)-1].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') - await msg.edit(embed=self.embeds[len(self.embeds)-1]) + await msg.edit(embed=self.embeds[self.current_page]) class CustomEmbedPaginator(object): def __init__(self, ctx, **kwargs): @@ -109,62 +102,67 @@ def __init__(self, ctx, **kwargs): self.control_commands = [] self.auto_footer = kwargs.get("auto_footer", False) self.remove_reactions = kwargs.get("remove_reactions", False) + def add_reaction(self, emoji, command): self.control_emojis.append(emoji) self.control_commands.append(command) + def insert_reaction(self, index, emoji, command): self.control_emojis.insert(index, emoji) self.control_commands.insert(index, command) + def remove_reaction(self, emoji): if emoji in self.control_emojis: index = self.control_emojis.index(emoji) self.control_emojis.remove(emoji) self.control_commands.pop(index) + def remove_reaction_at(self, index): if index > len(self.control_emojis)-1: index = len(self.control_emojis)-1 elif index < 0: index = 0 - try: - self.control_emojis.pop(index) - self.control_commands.pop(index) - except: - pass + + self.control_emojis.pop(index, None) + self.control_commands.pop(index, None) + def clear_reactions(self): self.control_emojis = [] self.control_commands = [] + async def run(self, embeds, send_to=None): self.embeds = embeds if not send_to: send_to = self.ctx wait_for = self.ctx.author if send_to == self.ctx else send_to + if self.auto_footer: - self.embeds[0].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') + for i in range(len(self.embeds)): + self.embeds[i].set_footer(text=f'({i + 1}/{len(self.embeds)})') + msg = await send_to.send(embed=self.embeds[0]) for emoji in self.control_emojis: try: await msg.add_reaction(emoji) except: pass - msg = await msg.channel.fetch_message(msg.id) + def check(reaction, user): return user == wait_for and reaction.message.id == msg.id and str(reaction.emoji) in self.control_emojis + while True: - if self.timeout > 0: - try: - reaction, user = await self.bot.wait_for("reaction_add",check=check, timeout=self.timeout) - except asyncio.TimeoutError: - for reaction in msg.reactions: - if reaction.message.author.id == self.bot.user.id and self.remove_reactions: - try: - await msg.remove_reaction(str(reaction.emoji), reaction.message.author) - except: - pass - self.current_page = 0 - return msg - break - else: - reaction, user = await self.bot.wait_for("reaction_add",check=check) + try: + reaction, user = await self.bot.wait_for("reaction_add", check=check, timeout=self.timeout if self.timeout > 0 else None) + except asyncio.TimeoutError: + for reaction in msg.reactions: + if reaction.message.author.id == self.bot.user.id and self.remove_reactions: + try: + await msg.remove_reaction(str(reaction.emoji), reaction.message.author) + except: + pass + self.current_page = 0 + return msg + for emoji in self.control_emojis: if emoji == str(reaction.emoji): index = self.control_emojis.index(emoji) @@ -176,19 +174,15 @@ def check(reaction, user): await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[0].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') await msg.edit(embed=self.embeds[0]) elif cmd.lower() == "last": - self.current_page = len(self.embeds)-1 + self.current_page = len(self.embeds) - 1 if self.remove_reactions: try: await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[len(self.embeds)-1].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') - await msg.edit(embed=self.embeds[len(self.embeds)-1]) + await msg.edit(embed=self.embeds[self.current_page]) elif cmd.lower() == "next": self.current_page += 1 self.current_page = len(self.embeds)-1 if self.current_page > len(self.embeds)-1 else self.current_page @@ -197,25 +191,20 @@ def check(reaction, user): await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[self.current_page].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') await msg.edit(embed=self.embeds[self.current_page]) elif cmd.lower() == "back": - self.current_page = self.current_page-1 + self.current_page -= 1 self.current_page = 0 if self.current_page<0 else self.current_page if self.remove_reactions: try: await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[self.current_page].set_footer(text=f'({self.current_page+1}/{len(self.embeds)})') await msg.edit(embed=self.embeds[self.current_page]) elif cmd.lower() == "delete": self.current_page = 0 await msg.delete() return msg - break elif cmd.lower() == "clear" or cmd.lower() == "lock": self.current_page = 0 for reaction in msg.reactions: @@ -229,7 +218,6 @@ def check(reaction, user): except: pass return msg - break elif cmd.startswith("page"): shit = cmd.split() pg = int(shit[1]) @@ -243,12 +231,9 @@ def check(reaction, user): await msg.remove_reaction(str(reaction.emoji), user) except: pass - if self.auto_footer: - self.embeds[self.current_page].set_footer(text=f'({pg+1}/{len(self.embeds)})') await msg.edit(embed=self.embeds[pg]) elif cmd.startswith("remove"): - things = cmd.split() - things.pop(0) + things = cmd.split()[1:] something = things[0] if something.isdigit(): index = int(something) @@ -278,6 +263,7 @@ def check(reaction, user): await msg.remove_reaction(emoji, self.bot.user) except: pass + index = self.control_emojis.index(emoji) self.control_emojis.remove(emoji) - self.control_commands.pop(index) + self.control_commands.pop(index) \ No newline at end of file diff --git a/DiscordUtils/__init__.py b/DiscordUtils/__init__.py index 6daf1ed..cb8cb63 100644 --- a/DiscordUtils/__init__.py +++ b/DiscordUtils/__init__.py @@ -1,6 +1,6 @@ -from DiscordUtils import Pagination -from DiscordUtils.InviteTracker import InviteTracker -from DiscordUtils.Music import Music +from . import Pagination +from .InviteTracker import InviteTracker +from .Music import Music __title__ = "DiscordUtils" __version__ = "1.2.9.1"