223 lines
7.4 KiB
Rust
223 lines
7.4 KiB
Rust
use std::sync::LazyLock;
|
|
|
|
use async_compression::futures::bufread::BrotliDecoder;
|
|
use capnp::message::ReaderOptions;
|
|
use futures::{AsyncReadExt, TryStreamExt};
|
|
use opendal::ErrorKind;
|
|
use snafu::{OptionExt, Snafu};
|
|
use twilight_model::{
|
|
application::{
|
|
command::{Command, CommandType},
|
|
interaction::Interaction,
|
|
},
|
|
channel::message::{Embed, MessageFlags},
|
|
http::interaction::{InteractionResponse, InteractionResponseType},
|
|
id::{Id, marker::UserMarker},
|
|
};
|
|
use twilight_util::builder::{
|
|
InteractionResponseDataBuilder,
|
|
command::CommandBuilder,
|
|
embed::{EmbedAuthorBuilder, EmbedBuilder, EmbedFieldBuilder},
|
|
};
|
|
|
|
use crate::{bot_capnp, command::State};
|
|
|
|
const NAME: &str = "debug";
|
|
const DESCRIPTION: &str =
|
|
"(Only the bot owner can use this) Show various information for debugging purposes";
|
|
|
|
pub static COMMAND: LazyLock<Command> = LazyLock::new(|| {
|
|
CommandBuilder::new(NAME, DESCRIPTION, CommandType::ChatInput)
|
|
.validate()
|
|
.expect("command wasn't correct")
|
|
.build()
|
|
});
|
|
|
|
#[derive(Debug, Snafu)]
|
|
enum NoPermission {
|
|
/// there isn't a user who invoked this command
|
|
NoUser,
|
|
|
|
/// the user isn't allowed to use this command because they're not the bot owner
|
|
NotInvokedByBotOwner,
|
|
}
|
|
|
|
fn no_permission_to_embed(error: NoPermission) -> Embed {
|
|
match error {
|
|
NoPermission::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()
|
|
},
|
|
NoPermission::NotInvokedByBotOwner => {
|
|
EmbedBuilder::new().title("No permission to see debug info").description("Only the owner of this bot is allowed to see its information for debugging purposes.").validate().unwrap().build()
|
|
},
|
|
}
|
|
}
|
|
|
|
fn check_permission(
|
|
interaction: &Interaction,
|
|
bot_owner_user_id: Id<UserMarker>,
|
|
) -> Result<(), NoPermission> {
|
|
let user_id = interaction
|
|
.member
|
|
.as_ref()
|
|
.and_then(|member| member.user.as_ref().map(|user| user.id))
|
|
.context(NoUserSnafu)?;
|
|
|
|
if user_id != bot_owner_user_id {
|
|
return Err(NoPermission::NotInvokedByBotOwner);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tracing::instrument]
|
|
pub async fn handle(state: State, interaction: Interaction) {
|
|
if let Err(no_permission) = check_permission(&interaction, state.discord_bot_owner_user_id) {
|
|
state
|
|
.discord_client
|
|
.interaction(state.discord_application_id)
|
|
.create_response(
|
|
interaction.id,
|
|
&interaction.token,
|
|
&InteractionResponse {
|
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
|
data: Some(
|
|
InteractionResponseDataBuilder::new()
|
|
.embeds([no_permission_to_embed(no_permission)])
|
|
.flags(MessageFlags::EPHEMERAL)
|
|
.build(),
|
|
),
|
|
},
|
|
)
|
|
.await
|
|
.expect("TODO");
|
|
|
|
return;
|
|
}
|
|
|
|
state
|
|
.discord_client
|
|
.interaction(state.discord_application_id)
|
|
.create_response(
|
|
interaction.id,
|
|
&interaction.token,
|
|
&InteractionResponse {
|
|
kind: InteractionResponseType::ChannelMessageWithSource,
|
|
data: Some(
|
|
InteractionResponseDataBuilder::new()
|
|
.flags(MessageFlags::EPHEMERAL)
|
|
.content("some debug info is coming your way!")
|
|
.build(),
|
|
),
|
|
},
|
|
)
|
|
.await
|
|
.expect("TODO");
|
|
|
|
let heat_script_description = {
|
|
let compressed_result = state
|
|
.bot_data
|
|
.reader("data.bin.brotli")
|
|
.await
|
|
.expect("TODO")
|
|
.into_futures_async_read(..)
|
|
.await;
|
|
|
|
let mut buf = Vec::default();
|
|
let mut message = capnp::message::TypedBuilder::<bot_capnp::bot::Owned>::new_default();
|
|
let fallback = message.init_root();
|
|
|
|
let message_reader;
|
|
let mut bot_data = fallback.into_reader();
|
|
match compressed_result {
|
|
Ok(compressed) => {
|
|
let mut decompressed = BrotliDecoder::new(compressed);
|
|
decompressed.read_to_end(&mut buf).await.expect("TODO");
|
|
|
|
message_reader =
|
|
capnp::serialize_packed::read_message(&*buf, ReaderOptions::default())
|
|
.expect("TODO");
|
|
|
|
bot_data = message_reader
|
|
.get_root::<bot_capnp::bot::Reader>()
|
|
.expect("TODO");
|
|
}
|
|
Err(error) if error.kind() == ErrorKind::NotFound => {
|
|
tracing::error!("TODO: proceeding with fallback");
|
|
}
|
|
Err(error) => {
|
|
tracing::error!(?error, "TODO");
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
let heat_script_option = bot_data
|
|
.has_heat_script()
|
|
.then(|| bot_data.get_heat_script().expect("TODO"));
|
|
let heat_script_option =
|
|
heat_script_option.map(|heat_script| heat_script.to_str().expect("TODO"));
|
|
|
|
heat_script_option.map_or("none set yet".into(), |heat_script| {
|
|
format!("```\n{heat_script}\n```")
|
|
})
|
|
};
|
|
|
|
state
|
|
.discord_client
|
|
.interaction(state.discord_application_id)
|
|
.create_followup(&interaction.token)
|
|
.embeds(&[EmbedBuilder::new()
|
|
.field(EmbedFieldBuilder::new("Heat Script", heat_script_description).build())
|
|
.validate()
|
|
.unwrap()
|
|
.build()])
|
|
.flags(MessageFlags::EPHEMERAL)
|
|
.await
|
|
.expect("TODO");
|
|
|
|
let mut user_id_stream = state.user_data_manager.list().await.expect("TODO");
|
|
|
|
while let Some(user_id) = user_id_stream.try_next().await.expect("TODO") {
|
|
let (consent, notification_script) = state
|
|
.user_data_manager
|
|
.with(user_id, |user_data| {
|
|
let consent = user_data.get_voice_recording_consent().unwrap();
|
|
let notification_script = user_data.has_notification_script().then_some(
|
|
user_data
|
|
.get_notification_script()
|
|
.expect("TODO")
|
|
.to_string()
|
|
.expect("TODO"),
|
|
);
|
|
|
|
(consent, notification_script)
|
|
})
|
|
.await
|
|
.expect("TODO");
|
|
|
|
let user_mention = format!("<@{user_id}>");
|
|
|
|
state
|
|
.discord_client
|
|
.interaction(state.discord_application_id)
|
|
.create_followup(&interaction.token)
|
|
.embeds(&[EmbedBuilder::new()
|
|
.author(EmbedAuthorBuilder::new(user_mention))
|
|
.field(EmbedFieldBuilder::new("Consent", format!("{consent:?}")).build())
|
|
.field(
|
|
EmbedFieldBuilder::new(
|
|
"Notification Script",
|
|
format!("{notification_script:?}"),
|
|
)
|
|
.build(),
|
|
)
|
|
.validate()
|
|
.unwrap()
|
|
.build()])
|
|
.flags(MessageFlags::EPHEMERAL)
|
|
.await
|
|
.expect("TODO");
|
|
}
|
|
}
|