diff --git a/tests/test_uploads_repository.py b/tests/test_uploads_repository.py index f006c7e..d5a1f93 100644 --- a/tests/test_uploads_repository.py +++ b/tests/test_uploads_repository.py @@ -3,9 +3,16 @@ import unittest from pathlib import Path from zyrarbot.storage.uploads_repository import UploadsRepository +from zyrarbot.ui.bot.cogs.playback_cog import upload_choices class UploadsRepositoryTest(unittest.TestCase): + def test_upload_autocomplete_ignores_empty_names_and_limits_labels(self): + choices = upload_choices(["", " ", "song.mp3", "x" * 120]) + + self.assertEqual([choice.value for choice in choices], ["song.mp3", "x" * 120]) + self.assertEqual(len(choices[1].name), 100) + def test_only_audio_and_video_extensions_are_supported(self): self.assertTrue(UploadsRepository.is_supported_filename("song.MP3")) self.assertTrue(UploadsRepository.is_supported_filename("video.webm")) diff --git a/zyrarbot/ui/bot/cogs/playback_cog.py b/zyrarbot/ui/bot/cogs/playback_cog.py index dca7870..7526ccf 100644 --- a/zyrarbot/ui/bot/cogs/playback_cog.py +++ b/zyrarbot/ui/bot/cogs/playback_cog.py @@ -1,5 +1,6 @@ from __future__ import annotations +import hashlib import logging import os import tempfile @@ -20,6 +21,12 @@ AUTOCOMPLETE_LIMIT = 25 HISTORY_PREVIEW_LIMIT = 25 +def upload_choices(names: list[str]) -> list[app_commands.Choice[str]]: + return [ + app_commands.Choice(name=name[:100], value=name) for name in names if name and name.strip() + ] + + class HistoryView(discord.ui.View): def __init__(self, cog: PlaybackCog, guild_id: int): super().__init__(timeout=900) @@ -46,7 +53,7 @@ class FileView(discord.ui.View): def __init__(self, cog: PlaybackCog, guild_id: int): super().__init__(timeout=900) self.cog = cog - files = cog._uploads.list(guild_id)[:25] + files = [item for item in cog._uploads.list(guild_id) if item.filename.strip()][:25] self.add_item(FileSelect([item.filename for item in files])) @@ -74,6 +81,7 @@ class PlaybackCog(MusicCogBase, name="Playback"): super().__init__(bot, player) self._tracks = tracks self._uploads = uploads + self._upload_tokens: dict[tuple[int, str], str] = {} async def _uploads_autocomplete( self, interaction: discord.Interaction, current: str @@ -81,7 +89,18 @@ class PlaybackCog(MusicCogBase, name="Playback"): names = self._uploads.search( current, limit=AUTOCOMPLETE_LIMIT, guild_id=interaction.guild.id ) - return [app_commands.Choice(name=name, value=name) for name in names] + choices = upload_choices(names) + result = [] + for choice in choices: + token = hashlib.sha256(f"{interaction.guild.id}:{choice.value}".encode()).hexdigest()[ + :16 + ] + self._upload_tokens[(interaction.guild.id, token)] = choice.value + result.append(app_commands.Choice(name=choice.name, value=token)) + return result + + def _resolve_upload_name(self, guild_id: int, value: str) -> str: + return self._upload_tokens.get((guild_id, value), value) @app_commands.command(name="files", description="Wybierz plik z biblioteki do odtworzenia") async def files(self, interaction: discord.Interaction) -> None: @@ -130,6 +149,7 @@ class PlaybackCog(MusicCogBase, name="Playback"): @app_commands.describe(filename="Plik z biblioteki") @app_commands.autocomplete(filename=_uploads_autocomplete) async def delete_file(self, interaction: discord.Interaction, filename: str) -> None: + filename = self._resolve_upload_name(interaction.guild.id, filename) if not self._uploads.delete(filename, interaction.guild.id): await interaction.response.send_message( embed=message_embed("Biblioteka plików", "Nie znaleziono pliku."), ephemeral=True @@ -197,6 +217,7 @@ class PlaybackCog(MusicCogBase, name="Playback"): await self.play_filename(interaction, filename) async def play_filename(self, interaction: discord.Interaction, filename: str) -> None: + filename = self._resolve_upload_name(interaction.guild.id, filename) if not await self._ensure_voice(interaction): return await interaction.response.defer()