feat: inspect user data
This commit is contained in:
@@ -2,7 +2,7 @@ use std::sync::LazyLock;
|
||||
|
||||
use async_compression::futures::bufread::BrotliDecoder;
|
||||
use capnp::message::ReaderOptions;
|
||||
use futures::AsyncReadExt;
|
||||
use futures::{AsyncReadExt, TryStreamExt};
|
||||
use opendal::ErrorKind;
|
||||
use snafu::{OptionExt, Snafu};
|
||||
use twilight_model::{
|
||||
@@ -175,4 +175,48 @@ pub async fn handle(state: State, interaction: Interaction) {
|
||||
.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()
|
||||
.title(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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use async_compression::futures::{bufread::BrotliDecoder, write::BrotliEncoder};
|
||||
use capnp::message::{TypedBuilder, TypedReader};
|
||||
use futures::{AsyncReadExt, AsyncWriteExt};
|
||||
use futures::{AsyncReadExt, AsyncWriteExt, TryStream, TryStreamExt};
|
||||
use opendal::Operator;
|
||||
use snafu::{ResultExt, Snafu};
|
||||
use snafu::{OptionExt as _, ResultExt as _, Snafu, ensure};
|
||||
use twilight_model::id::{Id, marker::UserMarker};
|
||||
|
||||
use crate::{OperatorExt, option_ext::OptionExt, user_capnp};
|
||||
use crate::{OperatorExt, option_ext::OptionExt as _, user_capnp};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UserDataManager {
|
||||
@@ -16,9 +18,78 @@ impl UserDataManager {
|
||||
pub fn new(operator: Operator) -> Self {
|
||||
Self { operator }
|
||||
}
|
||||
}
|
||||
|
||||
fn path(id: Id<UserMarker>) -> String {
|
||||
fn path(id: Id<UserMarker>) -> String {
|
||||
format!("{id}/data.bin.brotli")
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(module)]
|
||||
pub enum ParsePathError {
|
||||
/// paths must have a / in them because that's how user data is stored, but this one doesn't have one
|
||||
MissingSlashError,
|
||||
|
||||
/// if this isn't a directory, then the file must be "data.bin.brotli" but it was actually {actual:?}
|
||||
WrongFileError { actual: String },
|
||||
|
||||
/// couldn't parse the directory as a user ID
|
||||
ParseUserIdError {
|
||||
source: <Id<UserMarker> as FromStr>::Err,
|
||||
},
|
||||
}
|
||||
|
||||
fn parse(path: &str) -> Result<Id<UserMarker>, ParsePathError> {
|
||||
let (directory, file) = path
|
||||
.rsplit_once("/")
|
||||
.context(parse_path_error::MissingSlashSnafu)?;
|
||||
|
||||
ensure!(
|
||||
file == "" || file == "data.bin.brotli",
|
||||
parse_path_error::WrongFileSnafu {
|
||||
actual: file.to_owned()
|
||||
}
|
||||
);
|
||||
|
||||
let user_id = directory
|
||||
.parse()
|
||||
.context(parse_path_error::ParseUserIdSnafu)?;
|
||||
|
||||
Ok(user_id)
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(module)]
|
||||
pub enum ListError {
|
||||
/// error creating a lister through the storage operator
|
||||
CreateListerError { source: opendal::Error },
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(module)]
|
||||
pub enum EntryError {
|
||||
/// failed to get an entry from the storage operator's lister
|
||||
ReceiveEntryError { source: opendal::Error },
|
||||
|
||||
/// failed to parse the entry as an acceptable path
|
||||
ParsePathError { source: ParsePathError },
|
||||
}
|
||||
|
||||
impl UserDataManager {
|
||||
pub async fn list(
|
||||
&self,
|
||||
) -> Result<impl TryStream<Ok = Id<UserMarker>, Error = EntryError> + Unpin, ListError> {
|
||||
let lister = self
|
||||
.operator
|
||||
.lister("")
|
||||
.await
|
||||
.context(list_error::CreateListerSnafu)?;
|
||||
|
||||
Ok(lister
|
||||
.map_err(|error| EntryError::ReceiveEntryError { source: error })
|
||||
.and_then(|entry| {
|
||||
std::future::ready(parse(entry.path()).context(entry_error::ParsePathSnafu))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +114,7 @@ impl UserDataManager {
|
||||
) -> Result<R, WithError> {
|
||||
let compressed_buffer = self
|
||||
.operator
|
||||
.async_reader_if_exists(&UserDataManager::path(id))
|
||||
.async_reader_if_exists(&path(id))
|
||||
.await
|
||||
.context(with_error::ReadSnafu)?;
|
||||
|
||||
@@ -108,7 +179,7 @@ impl UserDataManager {
|
||||
id: Id<UserMarker>,
|
||||
f: impl FnOnce(user_capnp::user::Builder<'_>) -> R,
|
||||
) -> Result<R, UpdateError> {
|
||||
let path = UserDataManager::path(id);
|
||||
let path = path(id);
|
||||
let compressed_buffer = self
|
||||
.operator
|
||||
.async_reader_if_exists(&path)
|
||||
|
||||
Reference in New Issue
Block a user