diff --git a/MusicBot/cogs/general.py b/MusicBot/cogs/general.py index bb41d52..627652a 100644 --- a/MusicBot/cogs/general.py +++ b/MusicBot/cogs/general.py @@ -12,9 +12,9 @@ from yandex_music import Track, Album, Artist, Playlist from MusicBot.database import BaseUsersDatabase, BaseGuildsDatabase from MusicBot.cogs.utils.find import ( process_album, process_track, process_artist, process_playlist, - ListenAlbum, ListenTrack, ListenArtist, ListenPlaylist + ListenAlbum, ListenTrack, ListenArtist, ListenPlaylist, ListenLikesPlaylist ) -from MusicBot.cogs.utils.misc import MyPlalists, ListenLikesPlaylist, generate_playlist_embed, generate_likes_embed +from MusicBot.cogs.utils.misc import MyPlaylists, generate_playlist_embed, generate_likes_embed def setup(bot): bot.add_cog(General(bot)) @@ -161,7 +161,7 @@ class General(Cog): ] self.users_db.update(ctx.user.id, {'playlists': playlists, 'playlists_page': 0}) embed = generate_playlist_embed(0, playlists) - await ctx.respond(embed=embed, view=MyPlalists(ctx), ephemeral=True) + await ctx.respond(embed=embed, view=MyPlaylists(ctx), ephemeral=True) @discord.slash_command(description="Найти контент и отправить информацию о нём. Возвращается лучшее совпадение.") @discord.option( diff --git a/MusicBot/cogs/settings.py b/MusicBot/cogs/settings.py index 54832b0..463ffa0 100644 --- a/MusicBot/cogs/settings.py +++ b/MusicBot/cogs/settings.py @@ -23,7 +23,7 @@ class Settings(Cog): embed = discord.Embed(title="Настройки бота", color=0xfed42b) explicit = "✅ - Разрешены" if guild['allow_explicit'] else "❌ - Запрещены" - menu = "✅ - Всегда доступно" if guild['always_allow_menu'] else "❌ - Если в канале 1 пользователь." + menu = "✅ - Всегда доступно" if guild['always_allow_menu'] else "❌ - Если в канале 1 человек." vote = "✅ - Переключение" if guild['vote_next_track'] else "❌ - Переключение" vote += "\n✅ - Добавление треков" if guild['vote_add_track'] else "\n❌ - Добавление треков" @@ -32,7 +32,7 @@ class Settings(Cog): vote += "\n✅ - Добавление плейлистов" if guild['vote_add_playlist'] else "\n❌ - Добавление плейлистов" embed.add_field(name="__Explicit треки__", value=explicit, inline=False) - embed.add_field(name="__Проигрыватель__", value=menu, inline=False) + embed.add_field(name="__Меню проигрывателя__", value=menu, inline=False) embed.add_field(name="__Голосование__", value=vote, inline=False) await ctx.respond(embed=embed, ephemeral=True) @@ -64,8 +64,8 @@ class Settings(Cog): "vote_type", description="Тип голосования.", type=discord.SlashCommandOptionType.string, - choices=['+Всё', '-Всё', 'Переключение', '+Трек', '+Альбом', '+Плейлист'], - default='Всё' + choices=['+Всё', '-Всё', 'Переключение', 'Трек', 'Альбом', 'Плейлист'], + default='+Всё' ) async def vote(self, ctx: discord.ApplicationContext, vote_type: Literal['+Всё', '-Всё', 'Переключение', 'Трек', 'Альбом', 'Плейлист']) -> None: member = cast(discord.Member, ctx.author) @@ -97,18 +97,18 @@ class Settings(Cog): response_message = "Голосование включено." elif vote_type == 'Переключение': self.db.update(ctx.guild.id, {'vote_next_track': not guild['vote_next_track']}) - response_message = "Голосование за переключение трека " + ("включено." if guild['vote_next_track'] else "выключено.") + response_message = "Голосование за переключение трека " + ("выключено." if guild['vote_next_track'] else "включено.") elif vote_type == 'Трек': self.db.update(ctx.guild.id, {'vote_add_track': not guild['vote_add_track']}) - response_message = "Голосование за добавление трека " + ("включено." if guild['vote_add_track'] else "выключено.") + response_message = "Голосование за добавление трека " + ("выключено." if guild['vote_add_track'] else "включено.") elif vote_type == 'Альбом': self.db.update(ctx.guild.id, {'vote_add_album': not guild['vote_add_album']}) - response_message = "Голосование за добавление альбома " + ("включено." if guild['vote_add_album'] else "выключено.") + response_message = "Голосование за добавление альбома " + ("выключено." if guild['vote_add_album'] else "включено.") elif vote_type == 'Артист': self.db.update(ctx.guild.id, {'vote_add_artist': not guild['vote_add_artist']}) - response_message = "Голосование за добавление артиста " + ("включено." if guild['vote_add_artist'] else "выключено.") + response_message = "Голосование за добавление артиста " + ("выключено." if guild['vote_add_artist'] else "включено.") elif vote_type == 'Плейлист': self.db.update(ctx.guild.id, {'vote_add_playlist': not guild['vote_add_playlist']}) - response_message = "Голосование за добавление плейлиста " + ("включено." if guild['vote_add_playlist'] else "выключено.") + response_message = "Голосование за добавление плейлиста " + ("выключено." if guild['vote_add_playlist'] else "включено.") await ctx.respond(response_message, delete_after=15, ephemeral=True) diff --git a/MusicBot/cogs/utils/find.py b/MusicBot/cogs/utils/find.py index 524adfe..4f12dea 100644 --- a/MusicBot/cogs/utils/find.py +++ b/MusicBot/cogs/utils/find.py @@ -23,18 +23,37 @@ class PlayTrackButton(Button, VoiceExtension): gid = interaction.guild.id guild = self.db.get_guild(gid) - - if guild['current_track']: - self.db.modify_track(gid, self.track, 'next', 'append') - response_message = f"Трек **{self.track.title}** был добавлен в очередь." + channel = cast(discord.VoiceChannel, interaction.channel) + member = cast(discord.Member, interaction.user) + + if guild['vote_add_track'] and len(channel.members) > 2 and not member.guild_permissions.manage_channels: + message = cast(discord.Interaction, await interaction.respond(f"{member.mention} хочет добавить трек **{self.track.title}** в очередь.\n\n Голосуйте за добавление.", delete_after=30)) + response = await message.original_response() + await response.add_reaction('✅') + await response.add_reaction('❌') + self.db.update_vote( + gid, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'add_track', + 'vote_content': self.track.to_dict() + } + ) else: - await self.play_track(interaction, self.track) - response_message = f"Сейчас играет: **{self.track.title}**!" + if guild['current_track']: + self.db.modify_track(gid, self.track, 'next', 'append') + response_message = f"Трек **{self.track.title}** был добавлен в очередь." + else: + await self.play_track(interaction, self.track) + response_message = f"Сейчас играет: **{self.track.title}**!" - if guild['current_player'] is not None and interaction.message: - await interaction.message.delete() + if guild['current_player'] is not None and interaction.message: + await interaction.message.delete() - await interaction.respond(response_message, delete_after=15) + await interaction.respond(response_message, delete_after=15) class PlayAlbumButton(Button, VoiceExtension): @@ -53,22 +72,41 @@ class PlayAlbumButton(Button, VoiceExtension): gid = interaction.guild.id guild = self.db.get_guild(gid) + channel = cast(discord.VoiceChannel, interaction.channel) + member = cast(discord.Member, interaction.user) tracks: list[Track] = [track for volume in album.volumes for track in volume] - if guild['current_track'] is not None: - self.db.modify_track(gid, tracks, 'next', 'extend') - response_message = f"Альбом **{album.title}** был добавлен в очередь." + if guild['vote_add_album'] and len(channel.members) > 2 and not member.guild_permissions.manage_channels: + message = cast(discord.Interaction, await interaction.respond(f"{member.mention} хочет добавить альбом **{self.album.title}** в очередь.\n\n Голосуйте за добавление.", delete_after=30)) + response = await message.original_response() + await response.add_reaction('✅') + await response.add_reaction('❌') + self.db.update_vote( + gid, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'add_album', + 'vote_content': [track.to_dict() for track in tracks] + } + ) else: - track = tracks.pop(0) - self.db.modify_track(gid, tracks, 'next', 'extend') - await self.play_track(interaction, track) - response_message = f"Сейчас играет: **{track.title}**!" + if guild['current_track'] is not None: + self.db.modify_track(gid, tracks, 'next', 'extend') + response_message = f"Альбом **{album.title}** был добавлен в очередь." + else: + track = tracks.pop(0) + self.db.modify_track(gid, tracks, 'next', 'extend') + await self.play_track(interaction, track) + response_message = f"Сейчас играет: **{track.title}**!" - if guild['current_player'] is not None and interaction.message: - await interaction.message.delete() - else: - await interaction.respond(response_message, delete_after=15) + if guild['current_player'] is not None and interaction.message: + await interaction.message.delete() + else: + await interaction.respond(response_message, delete_after=15) class PlayArtistButton(Button, VoiceExtension): def __init__(self, artist: Artist, **kwargs): @@ -86,24 +124,44 @@ class PlayArtistButton(Button, VoiceExtension): gid = interaction.guild.id guild = self.db.get_guild(gid) + channel = cast(discord.VoiceChannel, interaction.channel) + member = cast(discord.Member, interaction.user) tracks: list[Track] = artist_tracks.tracks.copy() - if guild['current_track'] is not None: - self.db.modify_track(gid, tracks, 'next', 'extend') - response_message = f"Песни артиста **{self.artist.name}** были добавлены в очередь." + if guild['vote_add_artist'] and len(channel.members) > 2 and not member.guild_permissions.manage_channels: + message = cast(discord.Interaction, await interaction.respond(f"{member.mention} хочет добавить треки от **{self.artist.name}** в очередь.\n\n Голосуйте за добавление.", delete_after=30)) + response = await message.original_response() + await response.add_reaction('✅') + await response.add_reaction('❌') + self.db.update_vote( + gid, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'add_album', + 'vote_content': [track.to_dict() for track in tracks] + } + ) else: - track = tracks.pop(0) - self.db.modify_track(gid, tracks, 'next', 'extend') - await self.play_track(interaction, track) - response_message = f"Сейчас играет: **{track.title}**!" + if guild['current_track'] is not None: + self.db.modify_track(gid, tracks, 'next', 'extend') + response_message = f"Песни артиста **{self.artist.name}** были добавлены в очередь." + else: + track = tracks.pop(0) + self.db.modify_track(gid, tracks, 'next', 'extend') + await self.play_track(interaction, track) + response_message = f"Сейчас играет: **{track.title}**!" - if guild['current_player'] is not None and interaction.message: - await interaction.message.delete() + if guild['current_player'] is not None and interaction.message: + await interaction.message.delete() - await interaction.respond(response_message, delete_after=15) + await interaction.respond(response_message, delete_after=15) class PlayPlaylistButton(Button, VoiceExtension): + def __init__(self, playlist: Playlist, **kwargs): Button.__init__(self, **kwargs) VoiceExtension.__init__(self, None) @@ -120,22 +178,93 @@ class PlayPlaylistButton(Button, VoiceExtension): gid = interaction.guild.id guild = self.db.get_guild(gid) + channel = cast(discord.VoiceChannel, interaction.channel) + member = cast(discord.Member, interaction.user) tracks: list[Track] = [cast(Track, short_track.track) for short_track in short_tracks] - if guild['current_track'] is not None: - self.db.modify_track(gid, tracks, 'next', 'extend') - response_message = f"Плейлист **{self.playlist.title}** был добавлен в очередь." + if guild['vote_add_playlist'] and len(channel.members) > 2 and not member.guild_permissions.manage_channels: + message = cast(discord.Interaction, await interaction.respond(f"{member.mention} хочет добавить плейлист **{self.playlist.title}** в очередь.\n\n Голосуйте за добавление.", delete_after=30)) + response = await message.original_response() + await response.add_reaction('✅') + await response.add_reaction('❌') + self.db.update_vote( + gid, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'add_playlist', + 'vote_content': [track.to_dict() for track in tracks] + } + ) else: - track = tracks.pop(0) - self.db.modify_track(gid, tracks, 'next', 'extend') - await self.play_track(interaction, track) - response_message = f"Сейчас играет: **{self.playlist.title}**!" + if guild['current_track'] is not None: + self.db.modify_track(gid, tracks, 'next', 'extend') + response_message = f"Плейлист **{self.playlist.title}** был добавлен в очередь." + else: + track = tracks.pop(0) + self.db.modify_track(gid, tracks, 'next', 'extend') + await self.play_track(interaction, track) + response_message = f"Сейчас играет: **{self.playlist.title}**!" - if guild['current_player'] is not None and interaction.message: - await interaction.message.delete() + if guild['current_player'] is not None and interaction.message: + await interaction.message.delete() - await interaction.respond(response_message, delete_after=15) + await interaction.respond(response_message, delete_after=15) + +class PlayLikesButton(Button, VoiceExtension): + def __init__(self, playlist: list[Track], **kwargs): + Button.__init__(self, **kwargs) + VoiceExtension.__init__(self, None) + self.playlist = playlist + + async def callback(self, interaction: Interaction): + if not interaction.guild or not await self.voice_check(interaction): + return + + playlist = self.playlist.copy() + gid = interaction.guild.id + guild = self.db.get_guild(gid) + channel = cast(discord.VoiceChannel, interaction.channel) + member = cast(discord.Member, interaction.user) + + if guild['vote_add_playlist'] and len(channel.members) > 2 and not member.guild_permissions.manage_channels: + message = cast(discord.Interaction, await interaction.respond(f"{member.mention} хочет добавить плейлист **«Мне нравится»** в очередь.\n\n Голосуйте за добавление.", delete_after=30)) + response = await message.original_response() + await response.add_reaction('✅') + await response.add_reaction('❌') + self.db.update_vote( + gid, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'add_playlist', + 'vote_content': [track.to_dict() for track in playlist] + } + ) + else: + if guild['current_track'] is not None: + self.db.modify_track(gid, playlist, 'next', 'extend') + response_message = f"Плейлист **«Мне нравится»** был добавлен в очередь." + else: + track = playlist.pop(0) + self.db.modify_track(gid, playlist, 'next', 'extend') + await self.play_track(interaction, track) + response_message = f"Сейчас играет: **{track.title}**!" + + if guild['current_player'] is not None and interaction.message: + await interaction.message.delete() + + await interaction.respond(response_message, delete_after=15) + +class ListenLikesPlaylist(View): + def __init__(self, playlist: list[Track], *items: Item, timeout: float | None = None, disable_on_timeout: bool = False): + super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout) + self.add_item(PlayLikesButton(playlist, label="Слушать в голосовом канале", style=ButtonStyle.gray)) class ListenTrack(View): diff --git a/MusicBot/cogs/utils/misc.py b/MusicBot/cogs/utils/misc.py index 27b54eb..4a69130 100644 --- a/MusicBot/cogs/utils/misc.py +++ b/MusicBot/cogs/utils/misc.py @@ -62,39 +62,6 @@ def generate_queue_embed(page: int, tracks_list: list[dict[str, Any]]) -> Embed: embed.add_field(name=f"{i} - {track['title']} - {duration_m}:{duration_s:02d}", value="", inline=False) return embed -class PlayLikesButton(Button, VoiceExtension): - def __init__(self, playlist: list[Track], **kwargs): - Button.__init__(self, **kwargs) - VoiceExtension.__init__(self, None) - self.playlist = playlist - - async def callback(self, interaction: Interaction): - if not interaction.guild or not await self.voice_check(interaction): - return - - playlist = self.playlist.copy() - gid = interaction.guild.id - guild = self.db.get_guild(gid) - - if guild['current_track'] is not None: - self.db.modify_track(gid, playlist, 'next', 'extend') - response_message = f"Плейлист **«Мне нравится»** был добавлен в очередь." - else: - track = playlist.pop(0) - self.db.modify_track(gid, playlist, 'next', 'extend') - await self.play_track(interaction, track) - response_message = f"Сейчас играет плейлист **{track.title}**!" - - if guild['current_player'] is not None and interaction.message: - await interaction.message.delete() - - await interaction.respond(response_message, delete_after=15) - -class ListenLikesPlaylist(View): - def __init__(self, playlist: list[Track], *items: Item, timeout: float | None = None, disable_on_timeout: bool = False): - super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout) - self.add_item(PlayLikesButton(playlist, label="Слушать в голосовом канале", style=ButtonStyle.gray)) - class MPNextButton(Button, VoiceExtension): def __init__(self, **kwargs): Button.__init__(self, **kwargs) @@ -107,7 +74,7 @@ class MPNextButton(Button, VoiceExtension): page = user['playlists_page'] + 1 self.users_db.update(interaction.user.id, {'playlists_page': page}) embed = generate_playlist_embed(page, user['playlists']) - await interaction.edit(embed=embed, view=MyPlalists(interaction)) + await interaction.edit(embed=embed, view=MyPlaylists(interaction)) class MPPrevButton(Button, VoiceExtension): def __init__(self, **kwargs): @@ -121,9 +88,9 @@ class MPPrevButton(Button, VoiceExtension): page = user['playlists_page'] - 1 self.users_db.update(interaction.user.id, {'playlists_page': page}) embed = generate_playlist_embed(page, user['playlists']) - await interaction.edit(embed=embed, view=MyPlalists(interaction)) + await interaction.edit(embed=embed, view=MyPlaylists(interaction)) -class MyPlalists(View, VoiceExtension): +class MyPlaylists(View, VoiceExtension): def __init__(self, ctx: ApplicationContext | Interaction, *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = True): View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout) VoiceExtension.__init__(self, None) diff --git a/MusicBot/cogs/voice.py b/MusicBot/cogs/voice.py index fc2e24a..08c6113 100644 --- a/MusicBot/cogs/voice.py +++ b/MusicBot/cogs/voice.py @@ -1,4 +1,4 @@ -from typing import cast, TypedDict, Literal +from typing import cast import discord from discord.ext.commands import Cog @@ -21,32 +21,26 @@ class Voice(Cog, VoiceExtension): def __init__(self, bot: discord.Bot): VoiceExtension.__init__(self, bot) self.bot = bot - MessageVotes = TypedDict('MessageVotes', {'positive_votes': set[int], 'negative_votes': set[int], 'total_members': int, 'action': Literal['next']}) - self.vote_messages: dict[int, dict[int, MessageVotes]] = {} @Cog.listener() async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState) -> None: gid = member.guild.id guild = self.db.get_guild(gid) - if after.channel: - channel = cast(discord.VoiceChannel, after.channel) - else: - channel = cast(discord.VoiceChannel, before.channel) - + channel = after.channel or before.channel if not channel: return discord_guild = await self.bot.fetch_guild(gid) - vc = cast((discord.VoiceClient | None), discord.utils.get(self.bot.voice_clients, guild=discord_guild)) + vc = cast(discord.VoiceClient | None, discord.utils.get(self.bot.voice_clients, guild=discord_guild)) if len(channel.members) == 1 and vc: self.db.clear_history(gid) self.db.update(gid, {'current_track': None, 'is_stopped': True}) vc.stop() - if len(channel.members) > 2 and not guild['always_allow_menu']: + elif len(channel.members) > 2 and not guild['always_allow_menu']: current_player = self.db.get_current_player(gid) - if current_player is not None: + if current_player: self.db.update(gid, {'current_player': None, 'repeat': False, 'shuffle': False}) try: message = await channel.fetch_message(current_player) @@ -78,42 +72,58 @@ class Voice(Cog, VoiceExtension): return guild_id = payload.guild_id - if guild_id not in self.vote_messages: + if not guild_id: return + guild = self.db.get_guild(guild_id) + votes = guild['votes'] - if payload.message_id not in self.vote_messages[guild_id]: - return - - vote_data = self.vote_messages[guild_id][payload.message_id] + vote_data = votes[str(payload.message_id)] if payload.emoji.name == '✅': - vote_data['positive_votes'].add(payload.user_id) + vote_data['positive_votes'].append(payload.user_id) elif payload.emoji.name == '❌': - vote_data['negative_votes'].add(payload.user_id) + vote_data['negative_votes'].append(payload.user_id) total_members = len(channel.members) - if total_members <= 5: - required_votes = 2 - elif total_members <= 10: - required_votes = 4 - elif total_members <= 15: - required_votes = 6 - else: - required_votes = 9 - + required_votes = 2 if total_members <= 5 else 4 if total_members <= 10 else 6 if total_members <= 15 else 9 if len(vote_data['positive_votes']) >= required_votes: if vote_data['action'] == 'next': self.db.update(guild_id, {'is_stopped': False}) title = await self.next_track(payload) await message.clear_reactions() - if title is not None: + await message.edit(content=f"Сейчас играет: **{title}**!", delete_after=15) + del votes[str(payload.message_id)] + elif vote_data['action'] == 'add_track': + await message.clear_reactions() + track = vote_data['vote_content'] + if not track: + return + self.db.update(guild_id, {'is_stopped': False}) + self.db.modify_track(guild_id, track, 'next', 'append') + if guild['current_track']: + await message.edit(content=f"Трек был добавлен в очередь!", delete_after=15) + else: + title = await self.next_track(payload) await message.edit(content=f"Сейчас играет: **{title}**!", delete_after=15) - del self.vote_messages[guild_id][payload.message_id] + del votes[str(payload.message_id)] + elif vote_data['action'] in ('add_album', 'add_artist', 'add_playlist'): + tracks = vote_data['vote_content'] + await message.clear_reactions() + if not tracks: + return + self.db.update(guild_id, {'is_stopped': False}) + self.db.modify_track(guild_id, tracks, 'next', 'extend') + if guild['current_track']: + await message.edit(content=f"Контент был добавлен в очередь!", delete_after=15) + else: + title = await self.next_track(payload) + await message.edit(content=f"Сейчас играет: **{title}**!", delete_after=15) + del votes[str(payload.message_id)] elif len(vote_data['negative_votes']) >= required_votes: - channel = cast(discord.VoiceChannel, self.bot.get_channel(payload.channel_id)) - message = await channel.fetch_message(payload.message_id) await message.clear_reactions() await message.edit(content='Запрос был отклонён.', delete_after=15) - del self.vote_messages[guild_id][payload.message_id] + del votes[str(payload.message_id)] + + self.db.update(guild_id, {'votes': votes}) @Cog.listener() async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent) -> None: @@ -121,11 +131,10 @@ class Voice(Cog, VoiceExtension): return guild_id = payload.guild_id - if guild_id not in self.vote_messages: - return - - if payload.message_id not in self.vote_messages[guild_id]: + if not guild_id: return + guild = self.db.get_guild(guild_id) + votes = guild['votes'] channel = cast(discord.VoiceChannel, self.bot.get_channel(payload.channel_id)) if not channel: @@ -135,11 +144,13 @@ class Voice(Cog, VoiceExtension): if not message or message.author.id != self.bot.user.id: return - vote_data = self.vote_messages[guild_id][payload.message_id] + vote_data = votes[str(payload.message_id)] if payload.emoji.name == '✔️': - vote_data['positive_votes'].discard(payload.user_id) + del vote_data['positive_votes'][payload.user_id] elif payload.emoji.name == '❌': - vote_data['negative_votes'].discard(payload.user_id) + del vote_data['negative_votes'][payload.user_id] + + self.db.update(guild_id, {'votes': votes}) @voice.command(name="menu", description="Создать меню проигрывателя. Доступно только если вы единственный в голосовом канале.") async def menu(self, ctx: discord.ApplicationContext) -> None: @@ -280,33 +291,33 @@ class Voice(Cog, VoiceExtension): if not await self.voice_check(ctx): return gid = ctx.guild.id - tracks_list = self.db.get_tracks_list(gid, 'next') - if not tracks_list: + guild = self.db.get_guild(gid) + if not guild['next_tracks']: await ctx.respond("❌ Нет песенен в очереди.", delete_after=15, ephemeral=True) return member = cast(discord.Member, ctx.author) channel = cast(discord.VoiceChannel, ctx.channel) - if self.db.get_track(gid, 'current') and len(channel.members) > 2 and not member.guild_permissions.manage_channels: - message = cast(discord.Interaction, await ctx.respond(f"{ctx.user.mention} хочет пропустить текущий трек.\n\nВыполнить переход?", delete_after=30)) + if guild['vote_next_track'] and len(channel.members) > 2 and not member.guild_permissions.manage_channels: + message = cast(discord.Interaction, await ctx.respond(f"{member.mention} хочет пропустить текущий трек.\n\nВыполнить переход?", delete_after=30)) response = await message.original_response() await response.add_reaction('✅') await response.add_reaction('❌') - self.vote_messages[ctx.guild.id] = { - response.id: { - 'positive_votes': set(), - 'negative_votes': set(), + self.db.update_vote( + gid, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), 'total_members': len(channel.members), - 'action': 'next' + 'action': 'next', + 'vote_content': None } - } + ) else: self.db.update(gid, {'is_stopped': False}) title = await self.next_track(ctx) - if title is not None: - await ctx.respond(f"Сейчас играет: **{title}**!", delete_after=15) - else: - await ctx.respond(f"Нет треков в очереди.", delete_after=15, ephemeral=True) + await ctx.respond(f"Сейчас играет: **{title}**!", delete_after=15) @track.command(description="Добавить трек в избранное или убрать, если он уже там.") async def like(self, ctx: discord.ApplicationContext) -> None: diff --git a/MusicBot/database/__init__.py b/MusicBot/database/__init__.py index 9d5ff12..9612837 100644 --- a/MusicBot/database/__init__.py +++ b/MusicBot/database/__init__.py @@ -2,7 +2,7 @@ from .base import BaseGuildsDatabase, BaseUsersDatabase from .extensions import VoiceGuildsDatabase from .user import User, ExplicitUser -from .guild import Guild, ExplicitGuild +from .guild import Guild, ExplicitGuild, MessageVotes __all__ = [ 'BaseGuildsDatabase', @@ -12,4 +12,5 @@ __all__ = [ 'ExplicitUser', 'Guild', 'ExplicitGuild', + 'MessageVotes' ] \ No newline at end of file diff --git a/MusicBot/database/base.py b/MusicBot/database/base.py index 1730de8..58b123c 100644 --- a/MusicBot/database/base.py +++ b/MusicBot/database/base.py @@ -5,8 +5,8 @@ from typing import cast from pymongo import MongoClient from pymongo.collection import Collection -from MusicBot.database.user import User, ExplicitUser -from MusicBot.database.guild import Guild, ExplicitGuild +from .user import User, ExplicitUser +from .guild import Guild, ExplicitGuild, MessageVotes client: MongoClient = MongoClient("mongodb://localhost:27017/") users: Collection[ExplicitUser] = client.YandexMusicBot.users @@ -91,14 +91,14 @@ class BaseGuildsDatabase: is_stopped=True, allow_explicit=True, always_allow_menu=False, - vote_add=True, vote_next_track=True, vote_add_track=True, vote_add_album=True, vote_add_artist=True, vote_add_playlist=True, shuffle=False, - repeat=False + repeat=False, + votes={} )) def update(self, gid: int, data: Guild) -> None: @@ -135,14 +135,14 @@ class BaseGuildsDatabase: is_stopped=True, allow_explicit=True, always_allow_menu=False, - vote_add=True, vote_next_track=True, vote_add_track=True, vote_add_album=True, vote_add_artist=True, vote_add_playlist=True, shuffle=False, - repeat=False + repeat=False, + votes={} ) for field, default_value in fields.items(): if field not in existing_fields and field != '_id': @@ -151,3 +151,14 @@ class BaseGuildsDatabase: return guild + def update_vote(self, gid: int, mid: int, data: MessageVotes) -> None: + """Update vote for a message in a guild. + + Args: + gid (int): Guild id. + mid (int): Message id. + vote (bool): Vote value. + """ + guild = self.get_guild(gid) + guild['votes'][str(mid)] = data + guilds.update_one({'_id': gid}, {"$set": {'votes': guild['votes']}}) \ No newline at end of file diff --git a/MusicBot/database/guild.py b/MusicBot/database/guild.py index 3ae9eb4..10dfa0b 100644 --- a/MusicBot/database/guild.py +++ b/MusicBot/database/guild.py @@ -1,4 +1,11 @@ -from typing import TypedDict, Any +from typing import TypedDict, Literal, Any + +class MessageVotes(TypedDict): + positive_votes: list[int] + negative_votes: list[int] + total_members: int + action: Literal['next', 'add_track', 'add_album', 'add_artist', 'add_playlist'] + vote_content: dict[str, Any] | list[dict[str, Any]] | None class Guild(TypedDict, total=False): next_tracks: list[dict[str, Any]] @@ -8,7 +15,6 @@ class Guild(TypedDict, total=False): is_stopped: bool allow_explicit: bool always_allow_menu: bool - vote_add: bool vote_next_track: bool vote_add_track: bool vote_add_album: bool @@ -16,6 +22,7 @@ class Guild(TypedDict, total=False): vote_add_playlist: bool shuffle: bool repeat: bool + votes: dict[str, MessageVotes] class ExplicitGuild(TypedDict): _id: int @@ -26,11 +33,11 @@ class ExplicitGuild(TypedDict): is_stopped: bool # Prevents the `after` callback of play_track allow_explicit: bool always_allow_menu: bool - vote_add: bool vote_next_track: bool vote_add_track: bool vote_add_album: bool vote_add_artist: bool vote_add_playlist: bool shuffle: bool - repeat: bool \ No newline at end of file + repeat: bool + votes: dict[str, MessageVotes] \ No newline at end of file