Skip to main content

matrix_bot_sdk/
content_scanner.rs

1use serde::{Deserialize, Serialize};
2
3use crate::client::MatrixClient;
4
5#[derive(Serialize, Deserialize, Debug, Clone)]
6pub struct ContentScannerResult {
7    pub info: String,
8    pub clean: bool,
9}
10
11#[derive(Serialize, Deserialize, Debug, Clone)]
12pub struct EncryptedFile {
13    pub url: String,
14    // Other fields omitted for brevity; this matches common matrix spec
15}
16
17/// API client for https://github.com/element-hq/matrix-content-scanner-python.
18pub struct MatrixContentScannerClient<'a> {
19    pub client: &'a MatrixClient,
20}
21
22impl<'a> MatrixContentScannerClient<'a> {
23    pub fn new(client: &'a MatrixClient) -> Self {
24        Self { client }
25    }
26
27    pub async fn scan_content(
28        &self,
29        domain: &str,
30        media_id: &str,
31    ) -> anyhow::Result<ContentScannerResult> {
32        let path = format!("/_matrix/media_proxy/unstable/scan/{}/{}", domain, media_id);
33        let resp = self
34            .client
35            .do_request(reqwest::Method::GET, &path, None, None)
36            .await?;
37        Ok(serde_json::from_value(resp)?)
38    }
39
40    pub async fn scan_content_encrypted(
41        &self,
42        file: &EncryptedFile,
43    ) -> anyhow::Result<ContentScannerResult> {
44        let path = "/_matrix/media_proxy/unstable/scan_encrypted".to_string();
45        let body = serde_json::json!({ "file": file });
46        let resp = self
47            .client
48            .do_request(reqwest::Method::POST, &path, None, Some(&body))
49            .await?;
50        Ok(serde_json::from_value(resp)?)
51    }
52
53    pub async fn download_content(&self, domain: &str, media_id: &str) -> anyhow::Result<Vec<u8>> {
54        let endpoint = format!(
55            "/_matrix/media_proxy/unstable/download/{}/{}",
56            domain, media_id
57        );
58
59        let url = self.client.homeserver().join(&endpoint)?;
60        let client = reqwest::Client::new();
61        let mut req = client.request(reqwest::Method::GET, url);
62        if let Some(token) = self.client.access_token().await {
63            req = req.header("Authorization", format!("Bearer {}", token));
64        }
65
66        let resp = req.send().await?;
67        if !resp.status().is_success() {
68            anyhow::bail!("Error fetching file: {}", resp.status());
69        }
70
71        Ok(resp.bytes().await?.to_vec())
72    }
73
74    pub async fn download_encrypted_content(
75        &self,
76        file: &EncryptedFile,
77    ) -> anyhow::Result<Vec<u8>> {
78        let endpoint = "/_matrix/media_proxy/unstable/download_encrypted";
79        let url = self.client.homeserver().join(endpoint)?;
80
81        let client = reqwest::Client::new();
82        let mut req = client.request(reqwest::Method::POST, url);
83        if let Some(token) = self.client.access_token().await {
84            req = req.header("Authorization", format!("Bearer {}", token));
85        }
86
87        let body = serde_json::json!({ "file": file });
88        req = req.json(&body);
89
90        let resp = req.send().await?;
91        if !resp.status().is_success() {
92            anyhow::bail!("Error fetching encrypted file: {}", resp.status());
93        }
94
95        Ok(resp.bytes().await?.to_vec())
96    }
97}