Skip to main content

tuwunel_admin/media/
delete_by_event.rs

1use ruma::{CanonicalJsonObject, CanonicalJsonValue, Mxc, OwnedEventId};
2use tuwunel_core::{Err, Result, debug, err, info, warn};
3
4use crate::admin_command;
5
6#[admin_command]
7pub(super) async fn delete_by_event(&self, event_id: OwnedEventId) -> Result {
8	let event_json = self
9		.services
10		.timeline
11		.get_pdu_json(&event_id)
12		.await
13		.map_err(|_| err!("Event ID does not exist or is not known to us."))?;
14
15	let content = event_json
16		.get("content")
17		.and_then(CanonicalJsonValue::as_object)
18		.ok_or_else(|| {
19			err!(
20				"Event ID does not have a \"content\" key, this is not a message or an event \
21				 type that contains media.",
22			)
23		})?;
24
25	let mut mxc_urls = Vec::with_capacity(3);
26	mxc_urls.extend(url_mxc_from_content(content));
27	mxc_urls.extend(thumbnail_mxc_from_content(content));
28	mxc_urls.extend(file_mxc_from_content(content));
29
30	if mxc_urls.is_empty() {
31		return Err!("Parsed event ID but found no MXC URLs.",);
32	}
33
34	let mut mxc_deletion_count: usize = 0;
35
36	for mxc_url in mxc_urls {
37		if !mxc_url.starts_with("mxc://") {
38			warn!("Ignoring non-mxc url {mxc_url}");
39			continue;
40		}
41
42		let mxc: Mxc<'_> = mxc_url.as_str().try_into()?;
43
44		match self.services.media.delete(&mxc).await {
45			| Ok(()) => {
46				info!("Successfully deleted {mxc_url} from filesystem and database");
47				mxc_deletion_count = mxc_deletion_count.saturating_add(1);
48			},
49			| Err(e) => {
50				warn!("Failed to delete {mxc_url}, ignoring error and skipping: {e}");
51			},
52		}
53	}
54
55	write!(
56		self,
57		"Deleted {mxc_deletion_count} total MXCs from our database and the filesystem from \
58		 event ID {event_id}."
59	)
60	.await
61}
62
63fn url_mxc_from_content(content: &CanonicalJsonObject) -> Option<String> {
64	debug!("Attempting to go into \"url\" key for main media file");
65	let url = content
66		.get("url")
67		.and_then(CanonicalJsonValue::as_str)?;
68
69	debug!("Got main media URL: {url}");
70	Some(url.to_owned())
71}
72
73fn thumbnail_mxc_from_content(content: &CanonicalJsonObject) -> Option<String> {
74	debug!("Attempting to go into \"info\" key for thumbnails");
75	let thumbnail_url = content
76		.get("info")
77		.and_then(CanonicalJsonValue::as_object)
78		.and_then(|info| info.get("thumbnail_url"))
79		.and_then(CanonicalJsonValue::as_str)?;
80
81	debug!("Found a thumbnail_url in info key: {thumbnail_url}");
82	Some(thumbnail_url.to_owned())
83}
84
85fn file_mxc_from_content(content: &CanonicalJsonObject) -> Option<String> {
86	debug!("Attempting to go into \"file\" key");
87	let url = content
88		.get("file")
89		.and_then(CanonicalJsonValue::as_object)
90		.and_then(|file| file.get("url"))
91		.and_then(CanonicalJsonValue::as_str)?;
92
93	debug!("Found url in file key: {url}");
94	Some(url.to_owned())
95}