impr: Minor clarifications in voice client.

This commit is contained in:
Lemon4ksan
2025-02-10 22:56:39 +03:00
parent b3e5fe596e
commit a5572cc6d8
3 changed files with 47 additions and 46 deletions

View File

@@ -3,7 +3,6 @@ import aiofiles
import logging import logging
import io import io
from typing import Any, Literal, cast from typing import Any, Literal, cast
from time import time
import yandex_music.exceptions import yandex_music.exceptions
from yandex_music import Track, TrackShort, ClientAsync as YMClient from yandex_music import Track, TrackShort, ClientAsync as YMClient
@@ -24,16 +23,17 @@ class VoiceExtension:
self.db = VoiceGuildsDatabase() self.db = VoiceGuildsDatabase()
self.users_db = BaseUsersDatabase() self.users_db = BaseUsersDatabase()
async def send_menu_message(self, ctx: ApplicationContext | Interaction) -> bool: async def send_menu_message(self, ctx: ApplicationContext | Interaction, *, disable: bool = False) -> bool:
"""Send menu message to the channel and delete old menu message if exists. Return True if sent. """Send menu message to the channel and delete old menu message if exists. Return True if sent.
Args: Args:
ctx (ApplicationContext | Interaction): Context. ctx (ApplicationContext | Interaction): Context.
disable (bool, optional): Disable menu message. Defaults to False.
Returns: Returns:
bool: True if sent, False if not. bool: True if sent, False if not.
""" """
logging.info("[VC_EXT] Sending menu message") logging.info(f"[VC_EXT] Sending menu message to channel {ctx.channel_id} in guild {ctx.guild_id}")
if not ctx.guild_id: if not ctx.guild_id:
logging.warning("[VC_EXT] Guild id not found in context inside 'create_menu'") logging.warning("[VC_EXT] Guild id not found in context inside 'create_menu'")
@@ -42,14 +42,16 @@ class VoiceExtension:
guild = await self.db.get_guild(ctx.guild_id, projection={'current_track': 1, 'current_menu': 1, 'vibing': 1}) guild = await self.db.get_guild(ctx.guild_id, projection={'current_track': 1, 'current_menu': 1, 'vibing': 1})
if guild['current_track']: if guild['current_track']:
if not (vc := await self.get_voice_client(ctx)):
return False
track = cast(Track, Track.de_json( track = cast(Track, Track.de_json(
guild['current_track'], guild['current_track'],
client=YMClient() # type: ignore client=YMClient() # type: ignore
)) ))
embed = await generate_item_embed(track, guild['vibing']) embed = await generate_item_embed(track, guild['vibing'])
vc = await self.get_voice_client(ctx) if vc.is_paused():
if vc and vc.is_paused():
embed.set_footer(text='Приостановлено') embed.set_footer(text='Приостановлено')
else: else:
embed.remove_footer() embed.remove_footer()
@@ -62,7 +64,7 @@ class VoiceExtension:
if message: if message:
await message.delete() await message.delete()
await self._update_menu_views_dict(ctx) await self._update_menu_views_dict(ctx, disable=disable)
interaction = await ctx.respond(view=menu_views[ctx.guild_id], embed=embed) interaction = await ctx.respond(view=menu_views[ctx.guild_id], embed=embed)
response = await interaction.original_response() if isinstance(interaction, discord.Interaction) else interaction response = await interaction.original_response() if isinstance(interaction, discord.Interaction) else interaction
await self.db.update(ctx.guild_id, {'current_menu': response.id}) await self.db.update(ctx.guild_id, {'current_menu': response.id})
@@ -81,7 +83,7 @@ class VoiceExtension:
Returns: Returns:
(discord.Message | None): Menu message or None. (discord.Message | None): Menu message or None.
""" """
logging.debug(f"[VC_EXT] Fetching menu message {menu_mid}...") logging.debug(f"[VC_EXT] Fetching menu message {menu_mid} in guild {ctx.guild_id}")
if not ctx.guild_id: if not ctx.guild_id:
logging.warning("[VC_EXT] Guild ID not found in context") logging.warning("[VC_EXT] Guild ID not found in context")
@@ -104,9 +106,9 @@ class VoiceExtension:
return None return None
if menu: if menu:
logging.debug("[VC_EXT] Menu message found") logging.debug(f"[VC_EXT] Menu message {menu_mid} successfully fetched")
else: else:
logging.debug("[VC_EXT] Menu message not found. Resetting current_menu field.") logging.debug(f"[VC_EXT] Menu message {menu_mid} not found in guild {ctx.guild_id}")
await self.db.update(ctx.guild_id, {'current_menu': None}) await self.db.update(ctx.guild_id, {'current_menu': None})
return menu return menu
@@ -151,11 +153,9 @@ class VoiceExtension:
if not menu_mid: if not menu_mid:
logging.warning("[VC_EXT] No menu message or menu message id provided") logging.warning("[VC_EXT] No menu message or menu message id provided")
return False return False
menu = await self.get_menu_message(ctx, menu_mid) menu_message = await self.get_menu_message(ctx, menu_mid)
else:
menu = menu_message
if not menu: if not menu_message:
return False return False
if not guild['current_track']: if not guild['current_track']:
@@ -164,9 +164,8 @@ class VoiceExtension:
track = cast(Track, Track.de_json( track = cast(Track, Track.de_json(
guild['current_track'], guild['current_track'],
client=YMClient(), # type: ignore client=YMClient() # type: ignore
)) ))
embed = await generate_item_embed(track, guild['vibing']) embed = await generate_item_embed(track, guild['vibing'])
await self._update_menu_views_dict(ctx) await self._update_menu_views_dict(ctx)
@@ -176,12 +175,12 @@ class VoiceExtension:
await ctx.edit(embed=embed, view=menu_views[gid]) await ctx.edit(embed=embed, view=menu_views[gid])
else: else:
# If interaction from other buttons or commands. They should have their own response. # If interaction from other buttons or commands. They should have their own response.
await menu.edit(embed=embed, view=menu_views[gid]) await menu_message.edit(embed=embed, view=menu_views[gid])
except discord.NotFound: except discord.NotFound:
logging.warning("[VC_EXT] Menu message not found") logging.warning("[VC_EXT] Menu message not found")
return False return False
logging.debug("[VC_EXT] Menu embed updated") logging.debug("[VC_EXT] Menu embed updated successfully")
return True return True
async def update_menu_view( async def update_menu_view(
@@ -225,6 +224,8 @@ class VoiceExtension:
except discord.NotFound: except discord.NotFound:
logging.warning("[VC_EXT] Menu message not found") logging.warning("[VC_EXT] Menu message not found")
return False return False
logging.debug("[VC_EXT] Menu view updated successfully")
return True return True
async def update_vibe( async def update_vibe(
@@ -248,7 +249,7 @@ class VoiceExtension:
bool: True if vibe was updated successfully. False otherwise. bool: True if vibe was updated successfully. False otherwise.
""" """
logging.info(f"[VC_EXT] Updating vibe for guild {ctx.guild_id} with type '{type}' and id '{id}'") logging.info(f"[VC_EXT] Updating vibe for guild {ctx.guild_id} with type '{type}' and id '{id}'")
gid = ctx.guild_id gid = ctx.guild_id
uid = ctx.user_id if isinstance(ctx, discord.RawReactionActionEvent) else ctx.user.id if ctx.user else None uid = ctx.user_id if isinstance(ctx, discord.RawReactionActionEvent) else ctx.user.id if ctx.user else None
@@ -275,12 +276,9 @@ class VoiceExtension:
) )
if not guild['vibing']: if not guild['vibing']:
logging.debug(f"[VIBE] Starting radio '{type}:{id}'")
feedback = await client.rotor_station_feedback_radio_started( feedback = await client.rotor_station_feedback_radio_started(
f"{type}:{id}", f"{type}:{id}",
f"desktop-user-{client.me.account.uid}", # type: ignore f"desktop-user-{client.me.account.uid}", # type: ignore # That's made up, but it doesn't do much anyway.
timestamp=time()
) )
if not feedback: if not feedback:
@@ -430,14 +428,15 @@ class VoiceExtension:
logging.warning("Guild ID or User ID not found in context") logging.warning("Guild ID or User ID not found in context")
return None return None
guild = await self.db.get_guild(gid, projection={'current_menu': 1, 'vibing': 1}) guild = await self.db.get_guild(gid, projection={'current_menu': 1, 'vibing': 1, 'current_track': 1})
vc = await self.get_voice_client(ctx) if not vc else vc vc = await self.get_voice_client(ctx) if not vc else vc
if not vc: if not vc:
return None return None
try: try:
await self._download_track(gid, track) if not guild['current_track'] or track.id != guild['current_track']['id']:
await self._download_track(gid, track)
except yandex_music.exceptions.TimedOutError: except yandex_music.exceptions.TimedOutError:
logging.warning(f"[VC_EXT] Timed out while downloading track '{track.title}'") logging.warning(f"[VC_EXT] Timed out while downloading track '{track.title}'")
@@ -457,6 +456,7 @@ class VoiceExtension:
await self.db.set_current_track(gid, track) await self.db.set_current_track(gid, track)
if menu_message or guild['current_menu']: if menu_message or guild['current_menu']:
# Updating menu message before playing to prevent delay and avoid FFMPEG lags.
await self.update_menu_full(ctx, guild['current_menu'], menu_message=menu_message, button_callback=button_callback) await self.update_menu_full(ctx, guild['current_menu'], menu_message=menu_message, button_callback=button_callback)
if not guild['vibing']: if not guild['vibing']:
@@ -503,9 +503,11 @@ class VoiceExtension:
user = await self.users_db.get_user(uid, projection={'vibe_type': 1, 'vibe_id': 1, 'vibe_batch_id': 1, 'ym_token': 1}) user = await self.users_db.get_user(uid, projection={'vibe_type': 1, 'vibe_id': 1, 'vibe_batch_id': 1, 'ym_token': 1})
vc = await self.get_voice_client(ctx) if not vc else vc vc = await self.get_voice_client(ctx) if not vc else vc
if vc: if not vc:
await self.db.update(gid, {'current_track': None, 'is_stopped': True}) return False
vc.stop()
await self.db.update(gid, {'current_track': None, 'is_stopped': True})
vc.stop()
if full: if full:
if not await self._full_stop(ctx, guild, gid): if not await self._full_stop(ctx, guild, gid):
@@ -580,10 +582,10 @@ class VoiceExtension:
logging.debug("[VC_EXT] Repeating current track") logging.debug("[VC_EXT] Repeating current track")
next_track = guild['current_track'] next_track = guild['current_track']
elif guild['shuffle']: elif guild['shuffle']:
logging.debug("[VC_EXT] Shuffling tracks") logging.debug("[VC_EXT] Getting random track from queue")
next_track = await self.db.pop_random_track(gid, 'next') next_track = await self.db.pop_random_track(gid, 'next')
else: else:
logging.debug("[VC_EXT] Getting next track") logging.debug("[VC_EXT] Getting next track from queue")
next_track = await self.db.get_track(gid, 'next') next_track = await self.db.get_track(gid, 'next')
if not next_track and guild['vibing'] and not isinstance(ctx, discord.RawReactionActionEvent): if not next_track and guild['vibing'] and not isinstance(ctx, discord.RawReactionActionEvent):
@@ -854,8 +856,7 @@ class VoiceExtension:
feedback = await client.rotor_station_feedback_track_started( feedback = await client.rotor_station_feedback_track_started(
f"{user['vibe_type']}:{user['vibe_id']}", f"{user['vibe_type']}:{user['vibe_id']}",
track.id, track.id,
user['vibe_batch_id'], # type: ignore # wrong typehints user['vibe_batch_id'] # type: ignore # Wrong typehints
time()
) )
logging.debug(f"[VIBE] Track started feedback: {feedback}") logging.debug(f"[VIBE] Track started feedback: {feedback}")
return True return True
@@ -898,8 +899,7 @@ class VoiceExtension:
f"{user['vibe_type']}:{user['vibe_id']}", f"{user['vibe_type']}:{user['vibe_id']}",
track['id'], track['id'],
track['duration_ms'] // 1000, track['duration_ms'] // 1000,
cast(str, user['vibe_batch_id']), user['vibe_batch_id'] # type: ignore # Wrong typehints
time()
) )
logging.info(f"[VOICE] User {user['_id']} finished vibing with result: {res}") logging.info(f"[VOICE] User {user['_id']} finished vibing with result: {res}")
return True return True
@@ -941,23 +941,21 @@ class VoiceExtension:
f'{user['vibe_type']}:{user['vibe_id']}', f'{user['vibe_type']}:{user['vibe_id']}',
guild['current_track']['id'], guild['current_track']['id'],
guild['current_track']['duration_ms'] // 1000, guild['current_track']['duration_ms'] // 1000,
user['vibe_batch_id'], # type: ignore # Wrong typehints user['vibe_batch_id'] # type: ignore # Wrong typehints
time()
) )
logging.debug(f"[VIBE] Finished track: {feedback}") logging.debug(f"[VIBE] Finished track feeedback: {feedback}")
else: else:
feedback = await client.rotor_station_feedback_skip( feedback = await client.rotor_station_feedback_skip(
f'{user['vibe_type']}:{user['vibe_id']}', f'{user['vibe_type']}:{user['vibe_id']}',
guild['current_track']['id'], guild['current_track']['id'],
guild['current_track']['duration_ms'] // 1000, guild['current_track']['duration_ms'] // 1000,
user['vibe_batch_id'], # type: ignore # Wrong typehints user['vibe_batch_id'] # type: ignore # Wrong typehints
time()
) )
if not feedback: if not feedback:
logging.warning("[VIBE] Failed to send vibe feedback") logging.warning("[VIBE] Failed to send vibe feedback")
return False return False
logging.debug(f"[VIBE] Skipped track: {feedback}") logging.debug(f"[VIBE] Skipped track feeedback: {feedback}")
feedback = await self.update_vibe( feedback = await self.update_vibe(
ctx, ctx,
user['vibe_type'], user['vibe_type'],
@@ -988,18 +986,21 @@ class VoiceExtension:
Returns: Returns:
str | None: Song title or None. str | None: Song title or None.
""" """
logging.debug("[VC_EXT] Playing next track")
client = await self.init_ym_client(ctx) if not client else client client = await self.init_ym_client(ctx) if not client else client
if not client: if not client:
return None return None
if not vc:
vc = await self.get_voice_client(ctx)
if not await self.stop_playing(ctx, vc=vc):
return None
ym_track = cast(Track, Track.de_json( ym_track = cast(Track, Track.de_json(
next_track, next_track,
client=client # type: ignore # Async client can be used here. client=client # type: ignore # Async client can be used here.
)) ))
await self.stop_playing(ctx, vc=vc)
return await self.play_track( return await self.play_track(
ctx, ctx,
ym_track, ym_track,

View File

@@ -454,7 +454,7 @@ class Voice(Cog, VoiceExtension):
return return
if not guild['current_menu']: if not guild['current_menu']:
await self.send_menu_message(ctx) await self.send_menu_message(ctx, disable=True)
next_track = await self.db.get_track(ctx.guild_id, 'next') next_track = await self.db.get_track(ctx.guild_id, 'next')
if next_track: if next_track:
@@ -480,7 +480,7 @@ class Voice(Cog, VoiceExtension):
return return
if not guild['current_menu']: if not guild['current_menu']:
await self.send_menu_message(ctx) await self.send_menu_message(ctx, disable=True)
next_track = await self.db.get_track(ctx.guild_id, 'next') next_track = await self.db.get_track(ctx.guild_id, 'next')
if next_track: if next_track:

View File

@@ -239,11 +239,11 @@ class MyVibeSelect(Select, VoiceExtension):
await interaction.edit(view=view) await interaction.edit(view=view)
class MyVibeSettingsView(View, VoiceExtension): class MyVibeSettingsView(View, VoiceExtension):
def __init__(self, interaction: Interaction, *items: Item, timeout: float | None = 360, disable_on_timeout: bool = True): def __init__(self, interaction: Interaction, *items: Item, timeout: float | None = None, disable_on_timeout: bool = True):
View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout) View.__init__(self, *items, timeout=timeout, disable_on_timeout=disable_on_timeout)
VoiceExtension.__init__(self, None) VoiceExtension.__init__(self, None)
self.interaction = interaction self.interaction = interaction
async def init(self) -> Self: async def init(self) -> Self:
if not self.interaction.user: if not self.interaction.user:
logging.warning('[MENU] No user in settings view') logging.warning('[MENU] No user in settings view')