Browse Source

Fix call connection management

The bot can get into a state where it never connects to a channel due to
old audio states persisting when the bot is kicked by a server or
Discord itself.

Fix this by checking the channel_id of the current call, and
reconnecting as necessary.
Frans Bergman 3 years ago
parent
commit
03c95597b7
1 changed files with 54 additions and 30 deletions
  1. 54 30
      src/audio/audio.rs

+ 54 - 30
src/audio/audio.rs

@@ -38,6 +38,57 @@ async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<AudioState>
     let guild_id = guild.id;
     let guild_id = guild.id;
 
 
     let mut audio_states = AUDIO_STATES.lock().await;
     let mut audio_states = AUDIO_STATES.lock().await;
+    let manager = songbird::get(ctx).await.unwrap();
+    let channel_id = guild
+        .voice_states
+        .get(&msg.author.id)
+        .and_then(|voice_state| voice_state.channel_id);
+
+    let channel_id = match channel_id {
+        Some(channel_id) => channel_id,
+        None => {
+            send_embed(ctx, msg, "Error: please be in a voice channel").await;
+            return None;
+        }
+    };
+
+    if let Some(call_locked) = manager.get(guild_id) {
+        let call = call_locked.lock().await;
+
+        if call.current_channel().is_none() {
+            drop(call);
+            match manager.remove(guild_id).await {
+                Ok(_) => {}
+                Err(err) => {
+                    println!("Error leaving call: {:?}", err);
+                    return None;
+                }
+            }
+            audio_states.remove(&guild_id);
+        }
+    }
+
+    let call_lock = match manager.get(guild_id) {
+        Some(call) => call,
+        None => {
+            audio_states.remove(&guild_id);
+            match manager.join(guild_id, channel_id).await {
+                (call, Ok(_)) => call,
+                (_, Err(err)) => {
+                    println!("Error joining call: {:?}", err);
+                    return None;
+                }
+            }
+        }
+    };
+
+    let mut call = call_lock.lock().await;
+
+    if call.current_channel() != Some(channel_id.into()) {
+        if let Err(err) = call.join(channel_id.into()).await {
+            println!("Error joining call: {:?}", err);
+        }
+    }
 
 
     match audio_states.get(&guild_id) {
     match audio_states.get(&guild_id) {
         Some(state) => {
         Some(state) => {
@@ -46,27 +97,8 @@ async fn get_audio_state(ctx: &Context, msg: &Message) -> Option<Arc<AudioState>
             Some(state)
             Some(state)
         }
         }
         None => {
         None => {
-            let channel_id = guild
-                .voice_states
-                .get(&msg.author.id)
-                .and_then(|voice_state| voice_state.channel_id);
-            let channel_id = match channel_id {
-                Some(channel_id) => channel_id,
-                None => {
-                    send_embed(ctx, msg, "Error: please be in a voice channel").await;
-                    return None;
-                }
-            };
-            let manager = songbird::get(ctx).await.unwrap().clone();
-            let (handle_lock, success) = manager.join(guild_id, channel_id).await;
-            if let Err(err) = success {
-                println!("Error: {:?}", err);
-                return None;
-            }
-            let audio_state = AudioState::new(handle_lock, ctx, msg);
-            {
-                audio_states.insert(guild_id, audio_state.clone());
-            }
+            let audio_state = AudioState::new(manager.get(guild_id).unwrap(), ctx, msg);
+            audio_states.insert(guild_id, audio_state.clone());
             Some(audio_state)
             Some(audio_state)
         }
         }
     }
     }
@@ -78,15 +110,7 @@ async fn remove_audio_state(ctx: &Context, msg: &Message) -> Result<(), String>
 
 
     let mut audio_states = AUDIO_STATES.lock().await;
     let mut audio_states = AUDIO_STATES.lock().await;
     let manager = songbird::get(ctx).await.unwrap();
     let manager = songbird::get(ctx).await.unwrap();
-    if manager
-        .get(guild_id)
-        .unwrap()
-        .lock()
-        .await
-        .leave()
-        .await
-        .is_err()
-    {
+    if let Err(_) = manager.remove(guild_id).await {
         return Err("Could not leave channel".to_string());
         return Err("Could not leave channel".to_string());
     }
     }