Skip to main content

whatsapp_rust/
upload.rs

1use anyhow::{Result, anyhow};
2use base64::Engine;
3use serde::Deserialize;
4use wacore::download::MediaType;
5
6use crate::client::Client;
7use crate::http::HttpRequest;
8
9#[derive(Debug, Clone)]
10pub struct UploadResponse {
11    pub url: String,
12    pub direct_path: String,
13    pub media_key: Vec<u8>,
14    pub file_enc_sha256: Vec<u8>,
15    pub file_sha256: Vec<u8>,
16    pub file_length: u64,
17}
18
19#[derive(Deserialize)]
20struct RawUploadResponse {
21    url: String,
22    direct_path: String,
23}
24
25impl Client {
26    pub async fn upload(&self, data: Vec<u8>, media_type: MediaType) -> Result<UploadResponse> {
27        let enc = tokio::task::spawn_blocking({
28            let data = data.clone();
29            move || wacore::upload::encrypt_media(&data, media_type)
30        })
31        .await??;
32
33        let media_conn = self.refresh_media_conn(false).await?;
34        let host = media_conn
35            .hosts
36            .first()
37            .ok_or_else(|| anyhow!("No media hosts"))?;
38
39        let token = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(enc.file_enc_sha256);
40        let mms_type = media_type.mms_type();
41        let scheme = "https";
42        let url = format!(
43            "{}://{}/mms/{}/{}?auth={}&token={}",
44            scheme, host.hostname, mms_type, token, media_conn.auth, token
45        );
46
47        let request = HttpRequest::post(url)
48            .with_header("Content-Type", "application/octet-stream")
49            .with_header("Origin", "https://web.whatsapp.com")
50            .with_body(enc.data_to_upload);
51
52        let response = self.http_client.execute(request).await?;
53
54        if response.status_code >= 400 {
55            let body_str = match response.body_string() {
56                Ok(body) => body,
57                Err(body_err) => {
58                    return Err(anyhow!(
59                        "Upload failed {} and failed to read response body: {}",
60                        response.status_code,
61                        body_err
62                    ));
63                }
64            };
65            return Err(anyhow!(
66                "Upload failed {} body={}",
67                response.status_code,
68                body_str
69            ));
70        }
71
72        let raw: RawUploadResponse = serde_json::from_slice(&response.body)?;
73
74        let result = UploadResponse {
75            url: raw.url,
76            direct_path: raw.direct_path,
77            media_key: enc.media_key.to_vec(),
78            file_enc_sha256: enc.file_enc_sha256.to_vec(),
79            file_sha256: enc.file_sha256.to_vec(),
80            file_length: data.len() as u64,
81        };
82        Ok(result)
83    }
84}