mirror of
https://github.com/deadcxap/YandexMusicDiscordBot.git
synced 2026-01-08 09:31:47 +03:00
impr: Minor code improvement.
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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__}'")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -4,5 +4,4 @@ PyNaCl
|
||||
pymongo
|
||||
yandex-music
|
||||
pillow
|
||||
python-dotenv
|
||||
wavelink
|
||||
python-dotenv
|
||||
Reference in New Issue
Block a user