From c0053ca98912901b5d387d58fbc57f9b7da91f38 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 18 Mar 2026 15:29:51 +0100 Subject: voice doesn't work due to DAVE, but we got copyparty search --- cogs/voice.py | 140 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 111 insertions(+), 29 deletions(-) (limited to 'cogs/voice.py') diff --git a/cogs/voice.py b/cogs/voice.py index dd5b74a..8ee3699 100644 --- a/cogs/voice.py +++ b/cogs/voice.py @@ -10,33 +10,117 @@ import asyncio logger = logging.getLogger(__name__) +_voice_manager = None + +def get_voice_manager(bot): + global _voice_manager + if _voice_manager is None: + _voice_manager = VoiceManager(bot) + return _voice_manager + + +class VoiceManager: + def __init__(self, bot): + self.bot = bot + self._voice_lock = asyncio.Lock() + + async def join_channel(self, channel): + """Join a voice channel by channel object.""" + async with self._voice_lock: + guild = channel.guild + voice_client = discord.utils.get(self.bot.voice_clients, guild=guild) + + if voice_client and voice_client.is_connected(): + if voice_client.channel == channel: + logger.info(f"Already connected to {channel}") + return voice_client + try: + await voice_client.move_to(channel) + logger.info(f"Moved to voice channel: {channel}") + return voice_client + except Exception as e: + logger.error(f"Failed to move voice client: {e}") + try: + await voice_client.disconnect() + except Exception: + pass + + await asyncio.sleep(0.3) + try: + voice_client = await channel.connect(timeout=30.0, reconnect=False) + logger.info(f"Connected to voice channel: {channel}") + return voice_client + except Exception as e: + logger.error(f"Failed to connect to voice channel: {e}") + return None + + async def join_by_channel_id(self, channel_id): + """Join a voice channel by channel ID.""" + channel = self.bot.get_channel(channel_id) + if not channel: + logger.error(f"Channel {channel_id} not found") + return None + if not isinstance(channel, discord.VoiceChannel): + logger.error(f"Channel {channel_id} is not a voice channel") + return None + return await self.join_channel(channel) + + async def get_voice_client(self, guild): + """Get existing voice client for a guild.""" + return discord.utils.get(self.bot.voice_clients, guild=guild) + + async def play_audio(self, channel, audio_source, volume=1.0): + """Play audio in a voice channel.""" + voice_client = await self.join_channel(channel) + if not voice_client: + return False + + if volume != 1.0: + audio_source = discord.PCMVolumeTransformer(audio_source) + audio_source.volume = volume + + voice_client.play(audio_source) + + while voice_client.is_playing(): + await asyncio.sleep(0.1) + return True + + async def stop(self, guild): + """Stop playing and disconnect from voice.""" + voice_client = await self.get_voice_client(guild) + if voice_client: + voice_client.stop() + await voice_client.disconnect() + logger.info(f"Stopped and disconnected from guild {guild}") + + class Voice(commands.Cog): def __init__(self, bot): self.bot = bot + self._voice_lock = asyncio.Lock() async def _validate_user_voice_state(self, ctx): - """Validate that user is in a voice channel.""" if not ctx.author.voice or not ctx.author.voice.channel: await ctx.send("You need to be in a voice channel first!") return False return True async def _create_voice_client(self, ctx): - """Create a new voice client connection.""" - try: - voice_client = await ctx.author.voice.channel.connect(timeout=30.0, reconnect=True) - logger.info(f"Connected to voice channel: {ctx.author.voice.channel}") - return voice_client - except asyncio.TimeoutError: - await ctx.send("Connection to voice channel timed out.") - return None - except discord.ClientException as e: - logger.error(f"Failed to connect to voice channel: {e}") - await ctx.send("Failed to connect to voice channel. Please try again.", ephemeral=True) - return None + async with self._voice_lock: + try: + await asyncio.sleep(0.5) + voice_client = await ctx.author.voice.channel.connect(timeout=30.0, reconnect=False) + logger.info(f"Connected to voice channel: {ctx.author.voice.channel}") + return voice_client + except asyncio.TimeoutError: + await ctx.send("Connection to voice channel timed out.") + return None + except discord.ClientException as e: + logger.error(f"Failed to connect to voice channel: {e}") + await ctx.send("Failed to connect to voice channel. Please try again.", ephemeral=True) + return None async def _move_voice_client(self, voice_client, ctx): - """Move existing voice client to user's channel.""" try: await voice_client.move_to(ctx.author.voice.channel) logger.info(f"Moved to voice channel: {ctx.author.voice.channel}") @@ -50,10 +134,9 @@ class Voice(commands.Cog): return None async def _verify_connection(self, voice_client): - """Verify and repair voice client connection if needed.""" if not voice_client.is_connected(): try: - await voice_client.connect(reconnect=True, timeout=10.0) + await voice_client.connect(reconnect=False, timeout=10.0) return voice_client except Exception as e: logger.error(f"Failed to reconnect to voice: {e}") @@ -61,31 +144,30 @@ class Voice(commands.Cog): return voice_client async def ensure_voice_client(self, ctx): - """Ensure we have a valid voice client connection.""" if not await self._validate_user_voice_state(ctx): return None - + voice_client = discord.utils.get(self.bot.voice_clients, guild=ctx.guild) - - # If no voice client exists, create one + + if voice_client and not voice_client.is_connected(): + try: + await voice_client.disconnect() + except Exception: + pass + voice_client = None + if not voice_client or not voice_client.is_connected(): + if not await self._validate_user_voice_state(ctx): + return None return await self._create_voice_client(ctx) - # If we're in a different channel, move to the user's channel if voice_client.channel != ctx.author.voice.channel: return await self._move_voice_client(voice_client, ctx) - # Verify the connection is healthy return await self._verify_connection(voice_client) async def connect(self, ctx): - """Connect to the user's voice channel with improved connection handling.""" voice_client = await self.ensure_voice_client(ctx) if not voice_client: return None - - return voice_client - - -def setup(bot): - bot.add_cog(Voice(bot)) \ No newline at end of file + return voice_client \ No newline at end of file -- cgit v1.2.3