use ruma::{events::room::message::RoomMessageEventContent, EventId}; use tracing::{debug, info}; use crate::{service::admin::MxcUri, services, Result}; pub(crate) async fn delete( _body: Vec<&str>, mxc: Option>, event_id: Option>, ) -> Result { if event_id.is_some() && mxc.is_some() { return Ok(RoomMessageEventContent::text_plain( "Please specify either an MXC or an event ID, not both.", )); } if let Some(mxc) = mxc { 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![]; 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 {url} to list of MXCs to delete"); 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 {thumbnail_url} to list of MXCs to delete"); 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 {url} to list of MXCs to delete"); 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 {mxc_deletion_count} total MXCs from our database and the filesystem from event ID {event_id}." ))); } 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.", )) } pub(crate) async fn delete_list(body: Vec<&str>) -> Result { if body.len() > 2 && body[0].trim().starts_with("```") && body.last().unwrap().trim() == "```" { let mxc_list = body .clone() .drain(1..body.len().checked_sub(1).unwrap()) .collect::>(); let mut mxc_deletion_count: usize = 0; for mxc in mxc_list { debug!("Deleting MXC {mxc} in bulk"); services().media.delete(mxc.to_owned()).await?; mxc_deletion_count = mxc_deletion_count .checked_add(1) .expect("mxc_deletion_count should not get this high"); } return Ok(RoomMessageEventContent::text_plain(format!( "Finished bulk MXC deletion, deleted {mxc_deletion_count} total MXCs from our database and the filesystem.", ))); } Ok(RoomMessageEventContent::text_plain( "Expected code block in command body. Add --help for details.", )) } pub(crate) async fn delete_past_remote_media(_body: Vec<&str>, duration: String) -> Result { let deleted_count = services() .media .delete_all_remote_media_at_after_time(duration) .await?; Ok(RoomMessageEventContent::text_plain(format!( "Deleted {deleted_count} total files.", ))) }