From cdc644946dfeef77b2f78e9cad27db74b7769f1c Mon Sep 17 00:00:00 2001 From: strawberry Date: Sun, 25 Feb 2024 16:10:07 -0500 Subject: [PATCH] admin cmd to delete MXCs via event_id this can be used as a way to deal with the thumbnail and the media file at the same time without knowing the thumbnail MXC URL. Signed-off-by: strawberry --- src/service/admin/mod.rs | 147 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 9 deletions(-) diff --git a/src/service/admin/mod.rs b/src/service/admin/mod.rs index 65271ef2..10184a35 100644 --- a/src/service/admin/mod.rs +++ b/src/service/admin/mod.rs @@ -84,10 +84,15 @@ enum AdminCommand { #[cfg_attr(test, derive(Debug))] #[derive(Subcommand)] enum MediaCommand { - /// - Deletes a single media file from our database and on the filesystem via a single MXC URI + /// - Deletes a single media file from our database and on the filesystem via a single MXC URL Delete { - /// The MXC URI to delete - mxc: Box, + /// The MXC URL to delete + #[arg(long)] + mxc: Option>, + + /// - The message event ID which contains the media and thumbnail MXC URLs + #[arg(long)] + event_id: Option>, }, /// - Deletes a codeblock list of MXC URLs from our database and on the filesystem @@ -624,13 +629,137 @@ impl Service { } }, AdminCommand::Media(command) => match command { - MediaCommand::Delete { mxc } => { - debug!("Got MXC URI: {}", mxc); - services().media.delete(mxc.to_string()).await?; + MediaCommand::Delete { mxc, event_id } => { + if event_id.is_some() && mxc.is_some() { + return Ok(RoomMessageEventContent::text_plain( + "Please specify either an MXC or an event ID, not both.", + )); + } - return Ok(RoomMessageEventContent::text_plain( - "Deleted the MXC from our database and on our filesystem.", - )); + if let Some(mxc) = mxc { + if !mxc.to_string().starts_with("mxc://") { + return Ok(RoomMessageEventContent::text_plain( + "MXC provided is not valid.", + )); + } + + debug!("Got MXC URL: {}", mxc); + services().media.delete(mxc.to_string()).await?; + + return Ok(RoomMessageEventContent::text_plain( + "Deleted the MXC from our database and on our filesystem.", + )); + } else if let Some(event_id) = event_id { + debug!("Got event ID to delete media from: {}", event_id); + + let mut mxc_urls: Vec = vec![]; + let mut mxc_deletion_count = 0; + + // parsing the PDU for any MXC URLs begins here + if let Some(event_json) = + services().rooms.timeline.get_pdu_json(&event_id)? + { + if let Some(content_key) = event_json.get("content") { + debug!("Event ID has \"content\"."); + let content_obj = content_key.as_object(); + + if let Some(content) = content_obj { + // 1. attempts to parse the "url" key + debug!("Attempting to go into \"url\" key for main media file"); + if let Some(url) = content.get("url") { + debug!("Got a URL in the event ID {event_id}: {url}"); + + if url.to_string().starts_with("\"mxc://") { + debug!("Pushing URL {} to list of MXCs to delete", url); + let final_url = url.to_string().replace('"', ""); + mxc_urls.push(final_url); + } else { + info!("Found a URL in the event ID {event_id} but did not start with mxc://, ignoring"); + } + } + + // 2. attempts to parse the "info" key + debug!("Attempting to go into \"info\" key for thumbnails"); + if let Some(info_key) = content.get("info") { + debug!("Event ID has \"info\"."); + let info_obj = info_key.as_object(); + + if let Some(info) = info_obj { + if let Some(thumbnail_url) = info.get("thumbnail_url") { + debug!("Found a thumbnail_url in info key: {thumbnail_url}"); + + if thumbnail_url.to_string().starts_with("\"mxc://") + { + debug!("Pushing thumbnail URL {} to list of MXCs to delete", thumbnail_url); + let final_thumbnail_url = + thumbnail_url.to_string().replace('"', ""); + mxc_urls.push(final_thumbnail_url); + } else { + info!("Found a thumbnail URL in the event ID {event_id} but did not start with mxc://, ignoring"); + } + } else { + info!("No \"thumbnail_url\" key in \"info\" key, assuming no thumbnails."); + } + } + } + + // 3. attempts to parse the "file" key + debug!("Attempting to go into \"file\" key"); + if let Some(file_key) = content.get("file") { + debug!("Event ID has \"file\"."); + let file_obj = file_key.as_object(); + + if let Some(file) = file_obj { + if let Some(url) = file.get("url") { + debug!("Found url in file key: {url}"); + + if url.to_string().starts_with("\"mxc://") { + debug!( + "Pushing URL {} to list of MXCs to delete", + url + ); + let final_url = + url.to_string().replace('"', ""); + mxc_urls.push(final_url); + } else { + info!("Found a URL in the event ID {event_id} but did not start with mxc://, ignoring"); + } + } else { + info!("No \"url\" key in \"file\" key."); + } + } + } + } else { + return Ok(RoomMessageEventContent::text_plain("Event ID does not have a \"content\" key or failed parsing the event ID JSON.")); + } + } else { + return Ok(RoomMessageEventContent::text_plain("Event ID does not have a \"content\" key, this is not a message or an event type that contains media.")); + } + } else { + return Ok(RoomMessageEventContent::text_plain( + "Event ID does not exist or is not known to us.", + )); + } + + if mxc_urls.is_empty() { + // we shouldn't get here (should have errored earlier) but just in case for whatever reason we do... + info!("Parsed event ID {event_id} but did not contain any MXC URLs."); + return Ok(RoomMessageEventContent::text_plain( + "Parsed event ID but found no MXC URLs.", + )); + } + + for mxc_url in mxc_urls { + services().media.delete(mxc_url).await?; + mxc_deletion_count += 1; + } + + return Ok(RoomMessageEventContent::text_plain(format!("Deleted {} total MXCs from our database and the filesystem from event ID {event_id}.", mxc_deletion_count))); + } else { + return Ok(RoomMessageEventContent::text_plain( + "Please specify either an MXC using --mxc or an event ID using --event-id of the message containing an image. See --help for details.", + )); + } } MediaCommand::DeleteList => { if body.len() > 2