serenity-rs / songbird

An async Rust library for the Discord voice API
ISC License
395 stars 116 forks source link

TrackHandle::get_info hanging when bot is disconnected from VC #259

Open hrolfurgylfa opened 3 weeks ago

hrolfurgylfa commented 3 weeks ago

Songbird version: next branch, commit 92314a1c9fb4

Rust version (rustc -V): rustc 1.81.0 (eeb90cda1 2024-09-04)

Serenity/Twilight version: Serenity v0.12.2

Description: After manually disconnecting the bot from a voice channel and then running TrackHandle::get_info, the bot freezes until you get the bot to join the voice channel again.

Minimal reproduction bot: https://github.com/hrolfurgylfa/musicbot-rs/blob/adf6a7f4c1008382e89bc2f9642c4a9979c4c93f/src/main.rs

Steps to reproduce:

  1. Run the join command.
  2. Run the play command with any url.
  3. Disconnect the bot from the voice channel.
  4. Run the queue command, the bot doesn't respond.
  5. Run the join command again, the bot now responds to the old queue command you ran previously and re-joins the channel.
hrolfurgylfa commented 3 weeks ago

There has been some discussion on Discord regarding this: https://discord.com/channels/381880193251409931/919310407569641492/1295879582809063505

hrolfurgylfa commented 2 weeks ago

For anyone else experiencing this, a workaround is to call songbird.remove when the bot gets disconnected from a voice channel.

Just create a simple disconnect handler:

pub struct TrackDisconnectNotifier {
    songbird: Arc<Songbird>,
    guild_id: GuildId,
}

impl TrackDisconnectNotifier {
    pub fn new(songbird: Arc<Songbird>, guild_id: GuildId) -> Self {
        Self { songbird, guild_id }
    }
}

#[async_trait]
impl VoiceEventHandler for TrackDisconnectNotifier {
    async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> {
        info!("Disconnected from voice channel, removing queue for guild {}", self.guild_id);
        self.songbird
            .remove(self.guild_id)
            .await
            .expect("Failed to leave voice channel on disconnect.");

        None
    }
}

And then add this event to every track when /play is run:

handler.add_global_event(
    CoreEvent::DriverDisconnect.into(),
    TrackDisconnectNotifier::new(manager.clone(), guild_id),
);