feat: Add explicit settings functionality.

This commit is contained in:
Lemon4ksan
2025-01-20 21:34:08 +03:00
parent a1a29aee95
commit 18ff925236
4 changed files with 77 additions and 44 deletions

View File

@@ -7,8 +7,9 @@ from discord.ext.commands import Cog
import yandex_music import yandex_music
import yandex_music.exceptions import yandex_music.exceptions
from yandex_music import ClientAsync as YMClient from yandex_music import ClientAsync as YMClient
from yandex_music import Track, Album, Artist, Playlist
from MusicBot.database import BaseUsersDatabase from MusicBot.database import BaseUsersDatabase, BaseGuildsDatabase
from MusicBot.cogs.utils.find import ( from MusicBot.cogs.utils.find import (
process_album, process_track, process_artist, process_playlist, process_album, process_track, process_artist, process_playlist,
ListenAlbum, ListenTrack, ListenArtist, ListenPlaylist ListenAlbum, ListenTrack, ListenArtist, ListenPlaylist
@@ -22,7 +23,8 @@ class General(Cog):
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
self.db = BaseUsersDatabase() self.db = BaseGuildsDatabase()
self.users_db = BaseUsersDatabase()
account = discord.SlashCommandGroup("account", "Команды, связанные с аккаунтом.") account = discord.SlashCommandGroup("account", "Команды, связанные с аккаунтом.")
@@ -84,7 +86,7 @@ class General(Cog):
"или имеете разрешение управления каналом.\n```/queue clear```\n") "или имеете разрешение управления каналом.\n```/queue clear```\n")
elif command == 'settings': elif command == 'settings':
embed.description += ("Получить текущие настройки.\n```/settings show```\n" embed.description += ("Получить текущие настройки.\n```/settings show```\n"
"Разрешить или запретить воспроизведение Explicit треков.\n```/settings explicit```\n" "Разрешить или запретить воспроизведение Explicit треков и альбомов. Если автор или плейлист содержат Explicit треки, убираются кнопки для доступа к ним.\n```/settings explicit```\n"
"Разрешить или запретить создание меню проигрывателя, даже если в канале больше одного человека.\n```/settings menu```\n" "Разрешить или запретить создание меню проигрывателя, даже если в канале больше одного человека.\n```/settings menu```\n"
"Разрешить или запретить голосование.\n```/settings vote <тип голосования>```\n" "Разрешить или запретить голосование.\n```/settings vote <тип голосования>```\n"
"`Примечание`: Только пользователи с разрешением управления каналом могут менять настройки.") "`Примечание`: Только пользователи с разрешением управления каналом могут менять настройки.")
@@ -115,17 +117,17 @@ class General(Cog):
about = cast(yandex_music.Status, client.me).to_dict() about = cast(yandex_music.Status, client.me).to_dict()
uid = ctx.author.id uid = ctx.author.id
self.db.update(uid, {'ym_token': token}) self.users_db.update(uid, {'ym_token': token})
await ctx.respond(f'Привет, {about['account']['first_name']}!', delete_after=15, ephemeral=True) await ctx.respond(f'Привет, {about['account']['first_name']}!', delete_after=15, ephemeral=True)
@account.command(description="Удалить токен из датабазы бота.") @account.command(description="Удалить токен из датабазы бота.")
async def remove(self, ctx: discord.ApplicationContext) -> None: async def remove(self, ctx: discord.ApplicationContext) -> None:
self.db.update(ctx.user.id, {'ym_token': None}) self.users_db.update(ctx.user.id, {'ym_token': None})
await ctx.respond(f'Токен был удалён.', delete_after=15, ephemeral=True) await ctx.respond(f'Токен был удалён.', delete_after=15, ephemeral=True)
@account.command(description="Получить плейлист «Мне нравится»") @account.command(description="Получить плейлист «Мне нравится»")
async def likes(self, ctx: discord.ApplicationContext) -> None: async def likes(self, ctx: discord.ApplicationContext) -> None:
token = self.db.get_ym_token(ctx.user.id) token = self.users_db.get_ym_token(ctx.user.id)
if not token: if not token:
await ctx.respond('❌ Необходимо указать свой токен доступа с помощью команды /login.', delete_after=15, ephemeral=True) await ctx.respond('❌ Необходимо указать свой токен доступа с помощью команды /login.', delete_after=15, ephemeral=True)
return return
@@ -145,7 +147,7 @@ class General(Cog):
@account.command(description="Получить ваши плейлисты.") @account.command(description="Получить ваши плейлисты.")
async def playlists(self, ctx: discord.ApplicationContext) -> None: async def playlists(self, ctx: discord.ApplicationContext) -> None:
token = self.db.get_ym_token(ctx.user.id) token = self.users_db.get_ym_token(ctx.user.id)
if not token: if not token:
await ctx.respond('❌ Необходимо указать свой токен доступа с помощью команды /login.', delete_after=15, ephemeral=True) await ctx.respond('❌ Необходимо указать свой токен доступа с помощью команды /login.', delete_after=15, ephemeral=True)
return return
@@ -157,7 +159,7 @@ class General(Cog):
playlists: list[tuple[str, int]] = [ playlists: list[tuple[str, int]] = [
(playlist.title if playlist.title else 'Без названия', playlist.track_count if playlist.track_count else 0) for playlist in playlists_list (playlist.title if playlist.title else 'Без названия', playlist.track_count if playlist.track_count else 0) for playlist in playlists_list
] ]
self.db.update(ctx.user.id, {'playlists': playlists, 'playlists_page': 0}) self.users_db.update(ctx.user.id, {'playlists': playlists, 'playlists_page': 0})
embed = generate_playlist_embed(0, playlists) embed = generate_playlist_embed(0, playlists)
await ctx.respond(embed=embed, view=MyPlalists(ctx), ephemeral=True) await ctx.respond(embed=embed, view=MyPlalists(ctx), ephemeral=True)
@@ -184,10 +186,12 @@ class General(Cog):
await ctx.respond("❌ Недопустимый тип.", delete_after=15, ephemeral=True) await ctx.respond("❌ Недопустимый тип.", delete_after=15, ephemeral=True)
return return
token = self.db.get_ym_token(ctx.user.id) guild = self.db.get_guild(ctx.guild_id)
token = self.users_db.get_ym_token(ctx.user.id)
if not token: if not token:
await ctx.respond("❌ Необходимо указать свой токен доступа с помощью комманды /login.", delete_after=15, ephemeral=True) await ctx.respond("❌ Необходимо указать свой токен доступа с помощью команды /login.", delete_after=15, ephemeral=True)
return return
try: try:
client = await YMClient(token).init() client = await YMClient(token).init()
except yandex_music.exceptions.UnauthorizedError: except yandex_music.exceptions.UnauthorizedError:
@@ -198,41 +202,70 @@ class General(Cog):
if not client.me or not client.me.account or not client.me.account.uid: if not client.me or not client.me.account or not client.me.account.uid:
await ctx.respond("Не удалось получить информацию о пользователе.", delete_after=15, ephemeral=True) await ctx.respond("Не удалось получить информацию о пользователе.", delete_after=15, ephemeral=True)
return return
playlists = await client.users_playlists_list(client.me.account.uid) playlists = await client.users_playlists_list(client.me.account.uid)
result = None result = next((playlist for playlist in playlists if playlist.title == name), None)
for playlist in playlists: if not result:
if playlist.title == name:
result = playlist
break
else:
await ctx.respond("❌ Плейлист не найден.", delete_after=15, ephemeral=True) await ctx.respond("❌ Плейлист не найден.", delete_after=15, ephemeral=True)
return return
tracks = await result.fetch_tracks_async()
if not tracks:
await ctx.respond("❌ Плейлист пуст.", delete_after=15, ephemeral=True)
return
for track_short in tracks:
track = cast(Track, track_short.track)
if (track.explicit or track.content_warning) and not guild['allow_explicit']:
await ctx.respond("❌ Explicit контент запрещён на этом сервере.", delete_after=15, ephemeral=True)
return
embed = await process_playlist(result) embed = await process_playlist(result)
await ctx.respond(embed=embed, view=ListenPlaylist(result)) await ctx.respond(embed=embed, view=ListenPlaylist(result))
else: else:
result = await client.search(name, True, content_type.lower()) result = await client.search(name, True)
if not result: if not result:
await ctx.respond("❌ Что-то пошло не так. Повторите попытку позже", delete_after=15, ephemeral=True) await ctx.respond("❌ Что-то пошло не так. Повторите попытку позже", delete_after=15, ephemeral=True)
return return
if content_type == 'Album' and result.albums: content_map = {
album = result.albums.results[0] 'Album': (result.albums, process_album, ListenAlbum),
embed = await process_album(album) 'Track': (result.tracks, process_track, ListenTrack),
await ctx.respond(embed=embed, view=ListenAlbum(album)) 'Artist': (result.artists, process_artist, ListenArtist),
elif content_type == 'Track' and result.tracks: 'Playlist': (result.playlists, process_playlist, ListenPlaylist)
track = result.tracks.results[0] }
album_id = cast(int, track.albums[0].id)
embed = await process_track(track) if content_type in content_map:
await ctx.respond(embed=embed, view=ListenTrack(track, album_id)) content: Album | Track | Artist | Playlist = content_map[content_type][0].results[0]
elif content_type == 'Artist' and result.artists: embed: discord.Embed = await content_map[content_type][1](content)
artist = result.artists.results[0] view = content_map[content_type][2](content)
embed = await process_artist(artist)
await ctx.respond(embed=embed, view=ListenArtist(artist)) if isinstance(content, (Track, Album)) and (content.explicit or content.content_warning) and not guild['allow_explicit']:
elif content_type == 'Playlist' and result.playlists: await ctx.respond("❌ Explicit контент запрещён на этом сервере.", delete_after=15, ephemeral=True)
playlist = result.playlists.results[0] return
embed = await process_playlist(playlist) elif isinstance(content, Artist):
await ctx.respond(embed=embed, view=ListenPlaylist(playlist)) tracks = await content.get_tracks_async()
if not tracks:
await ctx.respond("❌ Треки от этого исполнителя не найдены.", delete_after=15, ephemeral=True)
return
for track in tracks:
if (track.explicit or track.content_warning) and not guild['allow_explicit']:
view = None
embed.set_footer(text="Воспроизведение недоступно, так как у автора присутствуют Explicit треки")
break
elif isinstance(content, Playlist):
tracks = await content.fetch_tracks_async()
if not tracks:
await ctx.respond("❌ Треки в этом плейлисте не найдены.", delete_after=15, ephemeral=True)
return
for track_short in content.tracks:
track = cast(Track, track_short.track)
if (track.explicit or track.content_warning) and not guild['allow_explicit']:
view = None
embed.set_footer(text="Воспроизведение недоступно, так как у автора присутствуют Explicit треки")
break
await ctx.respond(embed=embed, view=view)
else: else:
await ctx.respond("❌ По запросу ничего не найдено.", delete_after=15, ephemeral=True) await ctx.respond("❌ По запросу ничего не найдено.", delete_after=15, ephemeral=True)

View File

@@ -139,10 +139,10 @@ class PlayPlaylistButton(Button, VoiceExtension):
class ListenTrack(View): class ListenTrack(View):
def __init__(self, track: Track, album_id: int, *items: Item, timeout: float | None = None, disable_on_timeout: bool = False): def __init__(self, track: Track, *items: Item, timeout: float | None = None, disable_on_timeout: bool = False):
super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout) super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout)
link_app = f"yandexmusic://album/{album_id}/track/{track.id}" link_app = f"yandexmusic://album/{track.albums[0].id}/track/{track.id}"
link_web = f"https://music.yandex.ru/album/{album_id}/track/{track.id}" link_web = f"https://music.yandex.ru/album/{track.albums[0].id}/track/{track.id}"
self.button1: Button = Button(label="Слушать в приложении", style=ButtonStyle.gray, url=link_app) self.button1: Button = Button(label="Слушать в приложении", style=ButtonStyle.gray, url=link_app)
self.button2: Button = Button(label="Слушать в браузере", style=ButtonStyle.gray, url=link_web) self.button2: Button = Button(label="Слушать в браузере", style=ButtonStyle.gray, url=link_web)
self.button3: PlayTrackButton = PlayTrackButton(track, label="Слушать в голосовом канале", style=ButtonStyle.gray) self.button3: PlayTrackButton = PlayTrackButton(track, label="Слушать в голосовом канале", style=ButtonStyle.gray)
@@ -180,6 +180,7 @@ class ListenArtist(View):
self.add_item(self.button3) self.add_item(self.button3)
class ListenPlaylist(View): class ListenPlaylist(View):
def __init__(self, playlist: Playlist, *items: Item, timeout: float | None = None, disable_on_timeout: bool = False): def __init__(self, playlist: Playlist, *items: Item, timeout: float | None = None, disable_on_timeout: bool = False):
super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout) super().__init__(*items, timeout=timeout, disable_on_timeout=disable_on_timeout)
link_app = f"yandexmusic://playlist/{playlist.playlist_uuid}" link_app = f"yandexmusic://playlist/{playlist.playlist_uuid}"

View File

@@ -458,4 +458,3 @@ class VoiceExtension:
return None return None
await client.users_likes_tracks_remove(ym_track.id, client.me.account.uid) await client.users_likes_tracks_remove(ym_track.id, client.me.account.uid)
return 'TRACK REMOVED' return 'TRACK REMOVED'

View File

@@ -151,7 +151,7 @@ class Voice(Cog, VoiceExtension):
embed = None embed = None
if len(channel.members) > 2 and not guild['always_allow_menu']: if len(channel.members) > 2 and not guild['always_allow_menu']:
await ctx.respond("Вы не единственный в голосовом канале.", ephemeral=True) await ctx.respond("Вы не единственный в голосовом канале.", ephemeral=True)
return return
if guild['current_track']: if guild['current_track']:
@@ -159,8 +159,8 @@ class Voice(Cog, VoiceExtension):
Track.de_json( Track.de_json(
guild['current_track'], guild['current_track'],
client=ClientAsync() # type: ignore # Async client can be used here. client=ClientAsync() # type: ignore # Async client can be used here.
)
) )
)
vc = await self.get_voice_client(ctx) vc = await self.get_voice_client(ctx)
if vc and vc.is_paused(): if vc and vc.is_paused():
embed.set_footer(text='Приостановлено') embed.set_footer(text='Приостановлено')