From 2ecd3f5ae812c3fc6872624d1ec1c13643083ed8 Mon Sep 17 00:00:00 2001 From: Lemon4ksan Date: Fri, 21 Feb 2025 18:52:55 +0300 Subject: [PATCH] impr: Add new votings. --- MusicBot/cogs/general.py | 2 +- MusicBot/cogs/utils/voice_extension.py | 58 ++++++++++--- MusicBot/cogs/voice.py | 108 +++++++++++++++++++++---- MusicBot/database/guild.py | 5 +- 4 files changed, 143 insertions(+), 30 deletions(-) diff --git a/MusicBot/cogs/general.py b/MusicBot/cogs/general.py index e27b1ab..4867aec 100644 --- a/MusicBot/cogs/general.py +++ b/MusicBot/cogs/general.py @@ -155,7 +155,7 @@ class General(Cog): "`Примечание`: Доступность меню и Моей Волны зависит от настроек сервера.\n\n" "Присоединить бота в голосовой канал.\n```/voice join```\n" "Заставить бота покинуть голосовой канал.\n ```/voice leave```\n" - "Прервать проигрывание, удалить историю, очередь и текущий плеер.\n ```/track stop```\n" + "Прервать проигрывание, удалить историю, очередь и текущий плеер.\n ```/voice stop```\n" "Создать меню проигрывателя. \n```/voice menu```\n" "Запустить станцию. Без уточнения станции, запускает Мою Волну.\n```/voice vibe <название станции>```" ) diff --git a/MusicBot/cogs/utils/voice_extension.py b/MusicBot/cogs/utils/voice_extension.py index 3f614e2..b127a5a 100644 --- a/MusicBot/cogs/utils/voice_extension.py +++ b/MusicBot/cogs/utils/voice_extension.py @@ -259,10 +259,11 @@ class VoiceExtension: async def update_vibe( self, - ctx: ApplicationContext | Interaction, + ctx: ApplicationContext | Interaction | RawReactionActionEvent, type: str, id: str | int, *, + viber_id: int | None = None, update_settings: bool = False ) -> bool: """Update vibe state or initialize it if not `guild['vibing']` and replace queue with next tracks. @@ -272,6 +273,7 @@ class VoiceExtension: ctx (ApplicationContext | Interaction): Context. type (str): Type of the item. id (str | int): ID of the item. + viber_id (int | None, optional): ID of the user who started vibe. If None, uses user id in context. Defaults to None. update_settings (bool, optional): Update vibe settings by sending feedack usind data from database. Defaults to False. Returns: @@ -280,7 +282,7 @@ class VoiceExtension: logging.info(f"[VC_EXT] Updating vibe for guild {ctx.guild_id} with type '{type}' and id '{id}'") gid = ctx.guild_id - uid = ctx.user_id if isinstance(ctx, discord.RawReactionActionEvent) else ctx.user.id if ctx.user else None + uid = viber_id if viber_id else ctx.user_id if isinstance(ctx, discord.RawReactionActionEvent) else ctx.user.id if ctx.user else None if not uid or not gid: logging.warning("[VC_EXT] Guild ID or User ID not found in context inside 'vibe_update'") @@ -385,8 +387,7 @@ class VoiceExtension: if check_vibe_privilage: guild = await self.db.get_guild(ctx.guild.id, projection={'current_viber_id': 1, 'vibing': 1}) - member = cast(discord.Member, ctx.user) - if guild['vibing'] and ctx.user.id != guild['current_viber_id'] and not member.guild_permissions.manage_channels: + if guild['vibing'] and ctx.user.id != guild['current_viber_id']: logging.debug("[VIBE] Context user is not the current viber") await ctx.respond("❌ Вы не можете взаимодействовать с чужой волной!", delete_after=15, ephemeral=True) return False @@ -826,23 +827,27 @@ class VoiceExtension: """ logging.info(f"[VOICE] Performing '{vote_data['action']}' action for message {ctx.message_id}") + if not ctx.guild_id: + logging.warning("[VOICE] Guild not found") + return False + if not guild['current_menu']: await self.send_menu_message(ctx) if vote_data['action'] in ('next', 'previous'): if not guild.get(f'{vote_data['action']}_tracks'): + logging.info(f"[VOICE] No {vote_data['action']} tracks found for message {ctx.message_id}") await channel.send(content=f"❌ Очередь пуста!", delete_after=15) elif not (await self.next_track(ctx) if vote_data['action'] == 'next' else await self.previous_track(ctx)): await channel.send(content=f"❌ Ошибка при смене трека! Попробуйте ещё раз.", delete_after=15) elif vote_data['action'] == 'add_track': - track = vote_data['vote_content'] - if not track: + if not vote_data['vote_content']: logging.info(f"[VOICE] Recieved empty vote context for message {ctx.message_id}") return False - await self.db.modify_track(guild['_id'], track, 'next', 'append') + await self.db.modify_track(guild['_id'], vote_data['vote_content'], 'next', 'append') if guild['current_track']: await channel.send(content=f"✅ Трек был добавлен в очередь!", delete_after=15) @@ -851,13 +856,12 @@ class VoiceExtension: await channel.send(content=f"❌ Ошибка при воспроизведении! Попробуйте ещё раз.", delete_after=15) elif vote_data['action'] in ('add_album', 'add_artist', 'add_playlist'): - tracks = vote_data['vote_content'] - if not tracks: + if not vote_data['vote_content']: logging.info(f"[VOICE] Recieved empty vote context for message {ctx.message_id}") return False await self.db.update(guild['_id'], {'is_stopped': False}) - await self.db.modify_track(guild['_id'], tracks, 'next', 'extend') + await self.db.modify_track(guild['_id'], vote_data['vote_content'], 'next', 'extend') if guild['current_track']: await channel.send(content=f"✅ Контент был добавлен в очередь!", delete_after=15) @@ -866,8 +870,7 @@ class VoiceExtension: await channel.send(content=f"❌ Ошибка при воспроизведении! Попробуйте ещё раз.", delete_after=15) elif vote_data['action'] == 'play/pause': - vc = await self.get_voice_client(ctx) - if not vc: + if not (vc := await self.get_voice_client(ctx)): await channel.send(content=f"❌ Ошибка при изменении воспроизведения! Попробуйте ещё раз.", delete_after=15) return False @@ -882,8 +885,37 @@ class VoiceExtension: await self.db.update(guild['_id'], {vote_data['action']: not guild[vote_data['action']]}) await self.update_menu_view(ctx) + elif vote_data['action'] == 'clear_queue': + await self.db.update(ctx.guild_id, {'previous_tracks': [], 'next_tracks': []}) + await channel.send("✅ Очередь и история сброшены.", delete_after=15) + + elif vote_data['action'] == 'stop': + res = await self.stop_playing(ctx, full=True) + if res: + await channel.send("✅ Воспроизведение остановлено.", delete_after=15) + else: + await channel.send("❌ Произошла ошибка при остановке воспроизведения.", delete_after=15) + + elif vote_data['action'] == 'vibe_station': + _type, _id, viber_id = vote_data['vote_content'] if isinstance(vote_data['vote_content'], list) else (None, None, None) + + if not _type or not _id or not viber_id: + logging.warning(f"[VOICE] Recieved empty vote context for message {ctx.message_id}") + await channel.send("❌ Произошла ошибка при обновлении станции.", delete_after=15) + return False + + feedback = await self.update_vibe(ctx, _type, _id, viber_id=viber_id) + + if not feedback: + await channel.send("❌ Операция не удалась. Возможно, у вес нет подписки на Яндекс Музыку.", delete_after=15) + return False + + next_track = await self.db.get_track(ctx.guild_id, 'next') + if next_track: + await self._play_track(ctx, next_track) + else: - logging.warning(f"[VOICE] Unknown action '{vote_data['action']}' for message {ctx.message_id}") + logging.error(f"[VOICE] Unknown action '{vote_data['action']}' for message {ctx.message_id}") return False return True diff --git a/MusicBot/cogs/voice.py b/MusicBot/cogs/voice.py index 65c8b56..55ccef3 100644 --- a/MusicBot/cogs/voice.py +++ b/MusicBot/cogs/voice.py @@ -260,16 +260,38 @@ class Voice(Cog, VoiceExtension): async def clear(self, ctx: discord.ApplicationContext) -> None: logging.info(f"[VOICE] Clear queue command invoked by user {ctx.author.id} in guild {ctx.guild.id}") + if not await self.voice_check(ctx): + return + member = cast(discord.Member, ctx.author) channel = cast(discord.VoiceChannel, ctx.channel) if len(channel.members) > 2 and not member.guild_permissions.manage_channels: - logging.info(f"[VOICE] User {ctx.author.id} does not have permissions to execute leave command in guild {ctx.guild.id}") - await ctx.respond("❌ У вас нет прав для выполнения этой команды.", delete_after=15, ephemeral=True) - elif await self.voice_check(ctx): - await self.db.update(ctx.guild.id, {'previous_tracks': [], 'next_tracks': []}) - await ctx.respond("✅ Очередь и история сброшены.", delete_after=15, ephemeral=True) - logging.info(f"[VOICE] Queue and history cleared in guild {ctx.guild.id}") + logging.info(f"Starting vote for stopping playback in guild {ctx.guild.id}") + + response_message = f"{member.mention} хочет очистить историю прослушивания и очередь треков.\n\n Выполнить действие?." + message = cast(discord.Interaction, await ctx.respond(response_message, delete_after=60)) + response = await message.original_response() + + await response.add_reaction('✅') + await response.add_reaction('❌') + + await self.db.update_vote( + ctx.guild_id, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'clear_queue', + 'vote_content': None + } + ) + return + + await self.db.update(ctx.guild.id, {'previous_tracks': [], 'next_tracks': []}) + await ctx.respond("✅ Очередь и история сброшены.", delete_after=15, ephemeral=True) + logging.info(f"[VOICE] Queue and history cleared in guild {ctx.guild.id}") @queue.command(description="Получить очередь треков.") async def get(self, ctx: discord.ApplicationContext) -> None: @@ -289,19 +311,40 @@ class Voice(Cog, VoiceExtension): async def stop(self, ctx: discord.ApplicationContext) -> None: logging.info(f"[VOICE] Stop command invoked by user {ctx.author.id} in guild {ctx.guild.id}") + if not await self.voice_check(ctx): + return + member = cast(discord.Member, ctx.author) channel = cast(discord.VoiceChannel, ctx.channel) if len(channel.members) > 2 and not member.guild_permissions.manage_channels: - logging.info(f"[VOICE] User {ctx.author.id} tried to stop playback in guild {ctx.guild.id} but there are other users in the channel") - await ctx.respond("❌ Вы не можете остановить воспроизведение, пока в канале находятся другие пользователи.", delete_after=15, ephemeral=True) + logging.info(f"Starting vote for stopping playback in guild {ctx.guild.id}") - elif await self.voice_check(ctx): - res = await self.stop_playing(ctx, full=True) - if res: - await ctx.respond("✅ Воспроизведение остановлено.", delete_after=15, ephemeral=True) - else: - await ctx.respond("❌ Произошла ошибка при остановке воспроизведения.", delete_after=15, ephemeral=True) + response_message = f"{member.mention} хочет полностью остановить проигрывание.\n\n Выполнить действие?." + message = cast(discord.Interaction, await ctx.respond(response_message, delete_after=60)) + response = await message.original_response() + + await response.add_reaction('✅') + await response.add_reaction('❌') + + await self.db.update_vote( + ctx.guild_id, + response.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'stop', + 'vote_content': None + } + ) + return + + res = await self.stop_playing(ctx, full=True) + if res: + await ctx.respond("✅ Воспроизведение остановлено.", delete_after=15, ephemeral=True) + else: + await ctx.respond("❌ Произошла ошибка при остановке воспроизведения.", delete_after=15, ephemeral=True) @voice.command(name='vibe', description="Запустить Мою Волну.") @discord.option( @@ -321,7 +364,7 @@ class Voice(Cog, VoiceExtension): if guild['vibing']: logging.info(f"[VOICE] Action declined: vibing is already enabled in guild {ctx.guild.id}") - await ctx.respond("❌ Моя Волна уже включена. Используйте /track stop, чтобы остановить воспроизведение.", delete_after=15, ephemeral=True) + await ctx.respond("❌ Моя Волна уже включена. Используйте /voice stop, чтобы остановить воспроизведение.", delete_after=15, ephemeral=True) return await ctx.defer(invisible=False) @@ -357,7 +400,42 @@ class Voice(Cog, VoiceExtension): return else: _type, _id = 'user', 'onyourwave' + content = None + + member = cast(discord.Member, ctx.author) + channel = cast(discord.VoiceChannel, ctx.channel) + + if len(channel.members) > 2 and not member.guild_permissions.manage_channels: + logging.info(f"Starting vote for stopping playback in guild {ctx.guild.id}") + if _type == 'user' and _id == 'onyourwave': + station = "Моя Волна" + elif content and content.station: + station = content.station.name + else: + logging.warning(f"[VOICE] Station {name} not found") + await ctx.respond("❌ Станция не найдена.", delete_after=15, ephemeral=True) + return + + response_message = f"{member.mention} хочет запустить станцию **{station}**.\n\n Выполнить действие?" + message = cast(discord.WebhookMessage, await ctx.respond(response_message, delete_after=60)) + + await message.add_reaction('✅') + await message.add_reaction('❌') + + await self.db.update_vote( + ctx.guild_id, + message.id, + { + 'positive_votes': list(), + 'negative_votes': list(), + 'total_members': len(channel.members), + 'action': 'vibe_station', + 'vote_content': [_type, _id, ctx.user.id] + } + ) + return + feedback = await self.update_vibe(ctx, _type, _id) if not feedback: diff --git a/MusicBot/database/guild.py b/MusicBot/database/guild.py index b915b74..bc26157 100644 --- a/MusicBot/database/guild.py +++ b/MusicBot/database/guild.py @@ -4,7 +4,10 @@ class MessageVotes(TypedDict): positive_votes: list[int] negative_votes: list[int] total_members: int - action: Literal['next', 'play/pause', 'repeat', 'shuffle', 'previous', 'add_track', 'add_album', 'add_artist', 'add_playlist'] + action: Literal[ + 'next', 'play/pause', 'stop', 'repeat', 'shuffle', 'previous', 'add_track', + 'add_album', 'add_artist', 'add_playlist', 'vibe_station', 'clear_queue' + ] vote_content: Any | None class Guild(TypedDict, total=False):