diff --git a/justfile b/justfile index fd98b64..4483287 100644 --- a/justfile +++ b/justfile @@ -21,8 +21,8 @@ run: venv {{python}} bot.py # Run with debug logging enabled -debug: run - --debug +debug: venv + {{python}} bot.py --debug clean: rm -rf {{env_name}} diff --git a/musicbot/cogs/music/__init__.py b/musicbot/cogs/music/__init__.py index b837b94..f0aad2d 100644 --- a/musicbot/cogs/music/__init__.py +++ b/musicbot/cogs/music/__init__.py @@ -507,7 +507,7 @@ async def _disconnect(self, ctx): @commands.command(name='reconnect') @require_voice_connection() @voteable(DJ_override=True, react_to_vote=True) - async def _reconnect(self, ctx): + async def _reconnect(self, ctx, force: bool = False): """Tries to disconnect then reconnect the player in case the bot gets stuck on a song.""" player = self.get_player(ctx.guild) current_channel = player.channel_id @@ -515,7 +515,7 @@ async def _reconnect(self, ctx): async def inner_reconnect(): await player.stop() if ctx.voice_client: - await ctx.voice_client.disconnect() + await ctx.voice_client.disconnect(force) await asyncio.sleep(1) # Pretend stuff is happening/give everything some time to reset. channel = ctx.guild.get_channel(current_channel) await channel.connect(cls=BasicVoiceClient) @@ -833,6 +833,7 @@ async def track_hook(self, event): @commands.Cog.listener() async def on_voice_state_update(self, member: discord.Member, _: discord.VoiceState, after: discord.VoiceState): """Updates listeners when the bot or a user changes voice state.""" + self.logger.debug("Voice state update, member: %s, new_state: %s", member, after) if self.bot.user is None: return # Bot not logged in if member.id == self.bot.user.id and after.channel is not None: @@ -846,6 +847,11 @@ async def on_voice_state_update(self, member: discord.Member, _: discord.VoiceSt if not member.bot: player.update_listeners(member, member.voice) + if member.id == self.bot.user.id and after.channel is None: + voice_client: BasicVoiceClient + if voice_client := member.guild.voice_client: + await voice_client.disconnect(force=True) + if not member.bot: try: player = self.get_player(member.guild) diff --git a/musicbot/cogs/music/voice_client.py b/musicbot/cogs/music/voice_client.py index e312877..2c35703 100644 --- a/musicbot/cogs/music/voice_client.py +++ b/musicbot/cogs/music/voice_client.py @@ -4,29 +4,43 @@ from musicbot.cogs.music.music_errors import MusicError -class BasicVoiceClient(discord.VoiceClient): +class BasicVoiceClient(discord.VoiceProtocol): def __init__(self, client: MusicBot, channel: discord.VoiceChannel): # Needs to be named client in order for base class to work # in most cases lavalink handles disconnects, but if we force it then we'll get an error. # during self.cleanup() self.client = client self.channel = channel + self.logger = self.client.main_logger.bot_logger.getChild("VoiceClient") if self.client.lavalink: self.lavalink = self.client.lavalink else: + self.logger.debug("Client did not have defined lavalink before connect.") raise MusicError("client did not have defined lavalink before connect") async def on_voice_server_update(self, data): - await self.lavalink.voice_update_handler({'t': 'VOICE_SERVER_UPDATE', 'd': data}) + self.logger.debug("BasicVoiceClient server update, %s", data) + await self.lavalink.voice_update_handler({"t": "VOICE_SERVER_UPDATE", "d": data}) async def on_voice_state_update(self, data): - await self.lavalink.voice_update_handler({'t': 'VOICE_STATE_UPDATE', 'd': data}) + self.logger.debug("BasicVoiceClient state update, %s", data) + channel_id = data['channel_id'] - async def connect(self, *, timeout: float, reconnect: bool, self_deaf: bool = False, - self_mute: bool = False) -> None: + if not channel_id: + self.cleanup() + return + + self.channel = self.client.get_channel(int(channel_id)) + await self.lavalink.voice_update_handler({"t": "VOICE_STATE_UPDATE", "d": data}) + + async def connect( + self, *, timeout: float, reconnect: bool, self_deaf: bool = False, self_mute: bool = False + ) -> None: + self.logger.debug("Connecting to %s", self.channel) await self.channel.guild.change_voice_state(channel=self.channel, self_mute=self_mute, self_deaf=self_deaf) async def disconnect(self, *, force: bool = False) -> None: + self.logger.debug("Disconnecting from voice. Force: %s", force) player = self.lavalink.player_manager.get(self.channel.guild.id) if player: @@ -35,6 +49,11 @@ async def disconnect(self, *, force: bool = False) -> None: return # None means disconnect + self.logger.debug("Player found, disconnecting and resetting player channel") await self.channel.guild.change_voice_state(channel=None) player.channel_id = None self.cleanup() + elif force: + self.logger.debug("No player found, disconnecting") + await self.channel.guild.change_voice_state(channel=None) + self.cleanup()