feat: graceful shutdown, try making join and leave work (but some bug fixes are still needed)

This commit is contained in:
2026-04-08 22:18:32 -04:00
parent 288a784870
commit d2511f7a55
6 changed files with 272 additions and 97 deletions

View File

@@ -1,6 +1,6 @@
use std::sync::LazyLock;
use crate::{VCs, command::State};
use snafu::{OptionExt, Snafu};
use std::sync::LazyLock;
use twilight_model::{
application::{
command::{Command, CommandType},
@@ -17,8 +17,6 @@ use twilight_util::builder::{
InteractionResponseDataBuilder, command::CommandBuilder, embed::EmbedBuilder,
};
use crate::{VCs, command::State};
const NAME: &str = "join";
const DESCRIPTION: &str = "The bot will join the same VC as you (with intention to record)";
@@ -30,7 +28,7 @@ pub static COMMAND: LazyLock<Command> = LazyLock::new(|| {
});
#[derive(Debug, Snafu)]
enum GetGuildAndChannelIdError {
enum GetGuildAndVoiceChannelIdError {
/// this command was not used inside a guild (Discord server)
NotInGuild,
@@ -44,29 +42,11 @@ enum GetGuildAndChannelIdError {
UserNotInVC,
}
impl From<GetGuildAndChannelIdError> for Embed {
fn from(error: GetGuildAndChannelIdError) -> Embed {
match error {
GetGuildAndChannelIdError::NotInGuild => {
EmbedBuilder::new().title("Use this in a server").description("This bot can't find a VC to join if the command is used outside of a server (you might've used it in a DM?).").validate().unwrap().build()
}
GetGuildAndChannelIdError::NoUser => EmbedBuilder::new().title("Not invoked by a user").description("This command works by joining the same VC as the user, but this bot didn't receive any user data. So did no user invoke it?! (This error should be impossible!)").validate().unwrap().build(),
GetGuildAndChannelIdError::NoVCsInGuild => {
EmbedBuilder::new().title("No VCs in this server").description("This bot can't find a VC to join because there aren't any in this server right now.").validate().unwrap().build()
},
GetGuildAndChannelIdError::UserNotInVC => {
EmbedBuilder::new().title("You're not in a VC").description("This bot can't follow you into VC if you aren't in one in this server.").validate().unwrap().build()
},
}
}
}
#[tracing::instrument]
fn get_guild_and_channel_id(
fn get_guild_and_voice_channel_id(
interaction: &Interaction,
vcs: &VCs,
) -> Result<(Id<GuildMarker>, Id<ChannelMarker>), GetGuildAndChannelIdError> {
) -> Result<(Id<GuildMarker>, Id<ChannelMarker>), GetGuildAndVoiceChannelIdError> {
let guild_id = interaction.guild_id.context(NotInGuildSnafu)?;
let user_id = interaction
@@ -77,17 +57,34 @@ fn get_guild_and_channel_id(
let guild_vcs = vcs.get(&guild_id).context(NoVCsInGuildSnafu)?;
let &channel_id = guild_vcs.get_left_for(&user_id).context(UserNotInVCSnafu)?;
let &voice_channel_id = guild_vcs.get_left_for(&user_id).context(UserNotInVCSnafu)?;
Ok((guild_id, channel_id))
Ok((guild_id, voice_channel_id))
}
fn get_guild_and_vc_error_to_embed(error: GetGuildAndVoiceChannelIdError) -> Embed {
match error {
GetGuildAndVoiceChannelIdError::NotInGuild => {
EmbedBuilder::new().title("Use this in a server").description("This bot can't find a VC to join if the command is used outside of a server (you might've used it in a DM?).").validate().unwrap().build()
}
GetGuildAndVoiceChannelIdError::NoUser => {
EmbedBuilder::new().title("Not invoked by a user").description("This command works by joining the same VC as the user, but this bot didn't receive any user data. So did no user invoke it?! (This error should be impossible!)").validate().unwrap().build()
},
GetGuildAndVoiceChannelIdError::NoVCsInGuild => {
EmbedBuilder::new().title("No VCs in this server").description("This bot can't find a VC to join because there aren't any in this server right now.").validate().unwrap().build()
},
GetGuildAndVoiceChannelIdError::UserNotInVC => {
EmbedBuilder::new().title("You're not in a VC").description("This bot can't follow you into VC if you aren't in one in this server.").validate().unwrap().build()
},
}
}
#[tracing::instrument(skip(state))]
pub async fn handle(state: State, interaction: Interaction) {
let vcs = state.vcs;
let (guild_id, channel_id) = match get_guild_and_channel_id(&interaction, &vcs) {
Ok((guild_id, channel_id)) => (guild_id, channel_id),
let (guild_id, voice_channel_id) = match get_guild_and_voice_channel_id(&interaction, &vcs) {
Ok((guild_id, voice_channel_id)) => (guild_id, voice_channel_id),
Err(error) => {
state
.discord_client
@@ -99,7 +96,7 @@ pub async fn handle(state: State, interaction: Interaction) {
kind: InteractionResponseType::ChannelMessageWithSource,
data: Some(
InteractionResponseDataBuilder::new()
.embeds([error.into()])
.embeds([get_guild_and_vc_error_to_embed(error)])
.flags(MessageFlags::EPHEMERAL)
.build(),
),
@@ -126,24 +123,15 @@ pub async fn handle(state: State, interaction: Interaction) {
.await
.expect("TODO");
let call = loop {
tracing::error!("TODO: about to try joining");
match state.songbird.join(guild_id, channel_id).await {
Ok(call) => break call,
Err(error) => {
tracing::error!(?error, "I'm still here");
let call = state
.songbird
.join(guild_id, voice_channel_id)
.await
.expect("TODO");
if error.should_leave_server() {
state.songbird.leave(guild_id).await.expect("TODO");
} else if error.should_reconnect_driver() {
todo!();
}
}
}
};
tracing::error!(?call, "successfully joined");
let channel_mention = format!("<#{channel_id}>");
let channel_mention = format!("<#{voice_channel_id}>");
state
.discord_client
@@ -155,9 +143,4 @@ pub async fn handle(state: State, interaction: Interaction) {
]))
.await
.expect("TODO");
tracing::error!(?call, "TODO");
let call_guard = call.lock().await;
tracing::error!(?call_guard, "TODO");
}