impr: Minor code improvement.

This commit is contained in:
Lemon4ksan
2025-01-31 17:26:08 +03:00
parent b3962c8928
commit 956dc4d925
8 changed files with 73 additions and 55 deletions

View File

@@ -25,22 +25,23 @@ async def get_search_suggestions(ctx: discord.AutocompleteContext) -> list[str]:
users_db = BaseUsersDatabase()
token = users_db.get_ym_token(ctx.interaction.user.id)
if not token:
return ['❌ Укажите токен через /account login.']
logging.info(f"[GENERAL] User {ctx.interaction.user.id} has no token")
return []
try:
client = await YMClient(token).init()
except yandex_music.exceptions.UnauthorizedError:
logging.info(f"[GENERAL] User {ctx.interaction.user.id} provided invalid token")
return ['❌ Недействительный токен.']
return []
content_type = ctx.options['тип']
search = await client.search(ctx.value)
if not search:
logging.warning(f"Failed to search for '{ctx.value}' for user {ctx.interaction.user.id}")
return ["❌ Что-то пошло не так. Повторите попытку позже"]
logging.warning(f"[GENERAL] Failed to search for '{ctx.value}' for user {ctx.interaction.user.id}")
return []
res = []
logging.debug(f"Searching for '{ctx.value}' for user {ctx.interaction.user.id}")
logging.debug(f"[GENERAL] Searching for '{ctx.value}' for user {ctx.interaction.user.id}")
if content_type == 'Трек' and search.tracks:
for item in search.tracks.results:
@@ -57,10 +58,9 @@ async def get_search_suggestions(ctx: discord.AutocompleteContext) -> list[str]:
elif content_type == "Свой плейлист":
if not client.me or not client.me.account or not client.me.account.uid:
logging.warning(f"Failed to get playlists for user {ctx.interaction.user.id}")
return ["❌ Что-то пошло не так. Повторите попытку позже"]
playlists_list = await client.users_playlists_list(client.me.account.uid)
res = [playlist.title if playlist.title else 'Без названия' for playlist in playlists_list]
else:
playlists_list = await client.users_playlists_list(client.me.account.uid)
res = [playlist.title if playlist.title else 'Без названия' for playlist in playlists_list]
return res
@@ -95,7 +95,8 @@ class General(Cog):
embed.description = (
"Этот бот позволяет слушать музыку из вашего аккаунта Yandex Music.\n"
"Зарегистрируйте свой токен с помощью /login. Его можно получить [здесь](https://github.com/MarshalX/yandex-music-api/discussions/513).\n"
"Для получения помощи по конкретной команде, введите /help <команда>.\n\n"
"Для получения помощи по конкретной команде, введите /help <команда>.\n"
"Для изменения настроек необходимо иметь права управления каналами на сервере.\n\n"
"**Для дополнительной помощи, присоединяйтесь к [серверу любителей Яндекс Музыки](https://discord.gg/gkmFDaPMeC).**"
)
@@ -152,15 +153,18 @@ class General(Cog):
embed.description += (
"`Примечание`: Если вы один в голосовом канале или имеете разрешение управления каналом, голосование не начинается.\n\n"
"Переключиться на следующий трек в очереди. \n```/track next```\n"
"Приостановить текущий трек.\n ```/track pause```\n"
"Возобновить текущий трек.\n ```/track resume```\n"
"Прервать проигрывание, удалить историю, очередь и текущий плеер.\n ```/track stop```"
"Приостановить текущий трек.\n```/track pause```\n"
"Возобновить текущий трек.\n```/track resume```\n"
"Прервать проигрывание, удалить историю, очередь и текущий плеер.\n ```/track stop```\n"
"Запустить Мою Волну по текущему треку.\n```/track vibe```"
)
elif command == 'voice':
embed.description += (
"Присоединить бота в голосовой канал. Требует разрешения управления каналом.\n ```/voice join```\n"
"`Примечание`: Доступность меню и Моей Волны зависит от настроек сервера.\n\n"
"Присоединить бота в голосовой канал. Требует разрешения управления каналом.\n```/voice join```\n"
"Заставить бота покинуть голосовой канал. Требует разрешения управления каналом.\n ```/voice leave```\n"
"Создать меню проигрывателя. Доступность зависит от настроек сервера. По умолчанию работает только когда в канале один человек.\n```/voice menu```"
"Создать меню проигрывателя. По умолчанию работает только когда в канале один человек.\n```/voice menu```\n"
"Запустить Мою Волну. По умолчанию работает только когда в канале один человек.\n```/vibe```"
)
else:
response_message = '❌ Неизвестная команда.'
@@ -194,16 +198,19 @@ class General(Cog):
@account.command(description="Получить плейлист «Мне нравится»")
async def likes(self, ctx: discord.ApplicationContext) -> None:
logging.info(f"[GENERAL] Likes command invoked by user {ctx.author.id} in guild {ctx.guild.id}")
token = self.users_db.get_ym_token(ctx.user.id)
if not token:
logging.info(f"[GENERAL] No token found for user {ctx.user.id}")
await ctx.respond('❌ Необходимо указать свой токен доступа с помощью команды /login.', delete_after=15, ephemeral=True)
await ctx.respond("❌ Укажите токен через /account login.", delete_after=15, ephemeral=True)
return
client = await YMClient(token).init()
if not client.me or not client.me.account or not client.me.account.uid:
logging.warning(f"Failed to fetch user info for user {ctx.user.id}")
await ctx.respond('❌ Что-то пошло не так. Повторите попытку позже.', delete_after=15, ephemeral=True)
return
likes = await client.users_likes_tracks()
if likes is None:
logging.info(f"[GENERAL] Failed to fetch likes for user {ctx.user.id}")
@@ -226,7 +233,7 @@ class General(Cog):
token = self.users_db.get_ym_token(ctx.user.id)
if not token:
await ctx.respond('❌ Необходимо указать свой токен доступа с помощью команды /login.', delete_after=15, ephemeral=True)
await ctx.respond("❌ Укажите токен через /account login.", delete_after=15, ephemeral=True)
return
client = await YMClient(token).init()
@@ -272,7 +279,7 @@ class General(Cog):
token = self.users_db.get_ym_token(ctx.user.id)
if not token:
logging.info(f"[GENERAL] No token found for user {ctx.user.id}")
await ctx.respond("❌ Укажите токен через /account login.", ephemeral=True)
await ctx.respond("❌ Укажите токен через /account login.", delete_after=15, ephemeral=True)
return
try:
@@ -311,7 +318,7 @@ class General(Cog):
embed = await generate_item_embed(result)
view = ListenView(result)
else:
result = await client.search(name, True)
result = await client.search(name, nocorrect=True)
if not result:
logging.warning(f"Failed to search for '{name}' for user {ctx.user.id}")
@@ -368,4 +375,3 @@ class General(Cog):
logging.info(f"[GENERAL] Successfully generated '{content_type}' message for user {ctx.author.id}")
await ctx.respond(embed=embed, view=view)

View File

@@ -11,7 +11,7 @@ from yandex_music import Track, Album, Artist, Playlist, Label
from discord import Embed
async def generate_item_embed(item: Track | Album | Artist | Playlist | list[Track], vibing: bool = False) -> Embed:
"""Generate item embed.
"""Generate item embed. list[Track] is used for likes.
Args:
item (yandex_music.Track | yandex_music.Album | yandex_music.Artist | yandex_music.Playlist): Item to be processed.

View File

@@ -11,7 +11,7 @@ from discord.ui import View
from discord import Interaction, ApplicationContext, RawReactionActionEvent
from MusicBot.cogs.utils import generate_item_embed
from MusicBot.database import VoiceGuildsDatabase, BaseUsersDatabase
from MusicBot.database import VoiceGuildsDatabase, BaseUsersDatabase, ExplicitGuild
# TODO: RawReactionActionEvent is poorly supported.
@@ -277,7 +277,7 @@ class VoiceExtension:
token = self.users_db.get_ym_token(ctx.user.id)
if not token:
logging.debug(f"[VC_EXT] No token found for user {ctx.user.id}")
await ctx.respond("Необходимо указать свой токен доступа с помощью команды /login.", delete_after=15, ephemeral=True)
await ctx.respond("Укажите токен через /account login.", delete_after=15, ephemeral=True)
return False
if not isinstance(ctx.channel, discord.VoiceChannel):
@@ -389,32 +389,22 @@ class VoiceExtension:
self.db.update(gid, {'current_track': track.to_dict()})
guild = self.db.get_guild(gid)
if guild['current_menu'] and not isinstance(ctx, RawReactionActionEvent):
if menu_message:
try:
if gid in menu_views:
menu_views[gid].stop()
menu_views[gid] = await MenuView(ctx).init()
await menu_message.edit(embed=await generate_item_embed(track, guild['vibing']), view=menu_views[gid])
except discord.errors.NotFound:
logging.warning("[VC_EXT] Menu message not found. Using 'update_menu_embed' instead.")
await self._retry_update_menu_embed(ctx, guild['current_menu'], button_callback)
else:
await self._retry_update_menu_embed(ctx, guild['current_menu'], button_callback)
try:
await track.download_async(f'music/{gid}.mp3')
song = discord.FFmpegPCMAudio(f'music/{gid}.mp3', options='-vn -filter:a "volume=0.15"')
except yandex_music.exceptions.TimedOutError: # sometimes track takes too long to download.
await asyncio.gather(
track.download_async(f'music/{gid}.mp3'),
self._update_menu(ctx, guild, track, menu_message, button_callback)
)
except yandex_music.exceptions.TimedOutError:
logging.warning(f"[VC_EXT] Timed out while downloading track '{track.title}'")
if not isinstance(ctx, RawReactionActionEvent) and ctx.user and ctx.channel:
channel = cast(discord.VoiceChannel, ctx.channel)
if not retry:
channel = cast(discord.VoiceChannel, ctx.channel)
return await self.play_track(ctx, track, vc=vc, button_callback=button_callback, retry=True)
await channel.send(f"😔 Не удалось загрузить трек. Попробуйте сбросить меню.", delete_after=15)
return None
song = discord.FFmpegPCMAudio(f'music/{gid}.mp3', options='-vn -filter:a "volume=0.15"')
vc.play(song, after=lambda exc: asyncio.run_coroutine_threadsafe(self.next_track(ctx, after=True), loop))
logging.info(f"[VC_EXT] Playing track '{track.title}'")
@@ -718,6 +708,30 @@ class VoiceExtension:
break
await asyncio.sleep(0.25)
update = await self.update_menu_embed(ctx, menu_mid, button_callback)
async def _update_menu(
self,
ctx: ApplicationContext | Interaction | RawReactionActionEvent,
guild: ExplicitGuild,
track: Track,
menu_message: discord.Message | None,
button_callback: bool
) -> None:
from MusicBot.ui import MenuView
gid = cast(int, ctx.guild_id)
if guild['current_menu'] and not isinstance(ctx, RawReactionActionEvent):
if menu_message:
try:
if gid in menu_views:
menu_views[gid].stop()
menu_views[gid] = await MenuView(ctx).init()
await menu_message.edit(embed=await generate_item_embed(track, guild['vibing']), view=menu_views[gid])
except discord.errors.NotFound:
logging.warning("[VC_EXT] Menu message not found. Using 'update_menu_embed' instead.")
await self._retry_update_menu_embed(ctx, guild['current_menu'], button_callback)
else:
await self._retry_update_menu_embed(ctx, guild['current_menu'], button_callback)
async def init_ym_client(self, ctx: ApplicationContext | Interaction | RawReactionActionEvent, token: str | None = None) -> YMClient | None:
"""Initialize Yandex Music client. Return client on success. Return None if no token found and respond to the context.
@@ -737,7 +751,7 @@ class VoiceExtension:
if not token:
logging.debug("No token found in 'init_ym_client'")
if not isinstance(ctx, discord.RawReactionActionEvent):
await ctx.respond("❌ Укажите токен через /account login.", ephemeral=True)
await ctx.respond("❌ Укажите токен через /account login.", delete_after=15, ephemeral=True)
return None
try:
@@ -747,4 +761,4 @@ class VoiceExtension:
if not isinstance(ctx, discord.RawReactionActionEvent):
await ctx.respond("❌ Недействительный токен. Если это не так, попробуйте ещё раз.", delete_after=15, ephemeral=True)
return None
return client
return client

View File

@@ -356,15 +356,19 @@ class Voice(Cog, VoiceExtension):
token = user['ym_token']
if not token:
logging.info(f"[VOICE] User {ctx.user.id} has no YM token")
await ctx.respond("❌ Укажите токен через /account login.", ephemeral=True)
await ctx.respond("❌ Укажите токен через /account login.", delete_after=15, ephemeral=True)
return
client = await self.init_ym_client(ctx, user['ym_token'])
if not client:
logging.info(f"[VOICE] Failed to init YM client for user {ctx.user.id}")
await ctx.respond("❌ Что-то пошло не так. Попробуйте позже.", delete_after=15, ephemeral=True)
return
track = guild['current_track']
if not track:
logging.info(f"[VOICE] No current track in guild {ctx.guild.id}")
await ctx.respond("❌ Что-то пошло не так. Попробуйте позже.", delete_after=15, ephemeral=True)
return
res = await client.rotor_station_feedback_track_finished(
@@ -470,7 +474,7 @@ class Voice(Cog, VoiceExtension):
await self.send_menu_message(ctx)
await self.update_vibe(ctx, 'track', guild['current_track']['id'])
@discord.slash_command(name='vibe', description="Запустить Мою Волну.")
@voice.command(name='vibe', description="Запустить Мою Волну.")
async def user_vibe(self, ctx: discord.ApplicationContext) -> None:
logging.info(f"[VOICE] Vibe (user) command invoked by user {ctx.user.id} in guild {ctx.guild_id}")
if not await self.voice_check(ctx):

View File

@@ -165,7 +165,7 @@ class MyVibeButton(Button, VoiceExtension):
)
class ListenView(View):
def __init__(self, item: Track | Album | Artist | Playlist | list[Track], *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = False):
def __init__(self, item: Track | Album | Artist | Playlist | list[Track], *items: Item, timeout: float | None = 360, disable_on_timeout: bool = True):
super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout)
logging.debug(f"Creating view for type: '{type(item).__name__}'")

View File

@@ -234,7 +234,7 @@ class MyVibeSelect(Select, VoiceExtension):
await interaction.edit(view=view)
class MyVibeSettingsView(View, VoiceExtension):
def __init__(self, interaction: Interaction, *items: Item, timeout: float | None = 360, disable_on_timeout: bool = False):
def __init__(self, interaction: Interaction, *items: Item, timeout: float | None = 360, disable_on_timeout: bool = True):
View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout)
VoiceExtension.__init__(self, None)

View File

@@ -2,12 +2,7 @@ from math import ceil
from typing import Any
from discord.ui import View, Button, Item
from discord import ButtonStyle, Interaction, Embed
from MusicBot.cogs.utils.voice_extension import VoiceExtension
from discord.ui import View, Button, Item
from discord import ButtonStyle, Interaction, ApplicationContext
from discord import ApplicationContext, ButtonStyle, Interaction, Embed
from MusicBot.cogs.utils.voice_extension import VoiceExtension
@@ -71,7 +66,7 @@ class MPPrevButton(Button, VoiceExtension):
await interaction.edit(embed=embed, view=MyPlaylists(interaction))
class MyPlaylists(View, VoiceExtension):
def __init__(self, ctx: ApplicationContext | Interaction, *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = True):
def __init__(self, ctx: ApplicationContext | Interaction, *items: Item, timeout: float | None = 360, disable_on_timeout: bool = True):
View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout)
VoiceExtension.__init__(self, None)
if not ctx.user:
@@ -121,7 +116,7 @@ class QueuePrevButton(Button, VoiceExtension):
await interaction.edit(embed=embed, view=QueueView(interaction))
class QueueView(View, VoiceExtension):
def __init__(self, ctx: ApplicationContext | Interaction, *items: Item, timeout: float | None = 3600, disable_on_timeout: bool = True):
def __init__(self, ctx: ApplicationContext | Interaction, *items: Item, timeout: float | None = 360, disable_on_timeout: bool = True):
View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout)
VoiceExtension.__init__(self, None)
if not ctx.user or not ctx.guild:

View File

@@ -4,5 +4,4 @@ PyNaCl
pymongo
yandex-music
pillow
python-dotenv
wavelink
python-dotenv