whatsapp_rust/
upload.rs

1use anyhow::{Result, anyhow};
2use base64::Engine;
3use serde::Deserialize;
4use wacore::download::MediaType;
5
6use crate::client::Client;
7
8#[derive(Debug, Clone)]
9pub struct UploadResponse {
10    pub url: String,
11    pub direct_path: String,
12    pub media_key: Vec<u8>,
13    pub file_enc_sha256: Vec<u8>,
14    pub file_sha256: Vec<u8>,
15    pub file_length: u64,
16}
17
18#[derive(Deserialize)]
19struct RawUploadResponse {
20    url: String,
21    direct_path: String,
22}
23
24impl Client {
25    pub async fn upload(&self, data: Vec<u8>, media_type: MediaType) -> Result<UploadResponse> {
26        let enc = tokio::task::spawn_blocking({
27            let data = data.clone();
28            move || wacore::upload::encrypt_media(&data, media_type)
29        })
30        .await??;
31
32        let media_conn = self.refresh_media_conn(false).await?;
33        let host = media_conn
34            .hosts
35            .first()
36            .ok_or_else(|| anyhow!("No media hosts"))?;
37
38        let token = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(enc.file_enc_sha256);
39        let mms_type = media_type.mms_type();
40        let url = format!(
41            "https://{}/mms/{}/{}?auth={}&token={}",
42            host.hostname, mms_type, token, media_conn.auth, token
43        );
44
45        // Perform the synchronous upload in a blocking thread
46        let raw: RawUploadResponse =
47            tokio::task::spawn_blocking(move || -> Result<RawUploadResponse> {
48                let mut resp = ureq::post(&url)
49                    // FIX: ureq uses .set() for headers
50                    .header("Content-Type", "application/octet-stream")
51                    .header("Origin", "https://web.whatsapp.com")
52                    .send(&enc.data_to_upload)?;
53
54                if resp.status().as_u16() >= 400 {
55                    return Err(anyhow!(
56                        "Upload failed {} body={}",
57                        resp.status(),
58                        resp.body_mut().read_to_string().unwrap_or_default()
59                    ));
60                }
61
62                Ok(resp.body_mut().read_json()?)
63            })
64            .await??;
65
66        let result = UploadResponse {
67            url: raw.url,
68            direct_path: raw.direct_path,
69            media_key: enc.media_key.to_vec(),
70            file_enc_sha256: enc.file_enc_sha256.to_vec(),
71            file_sha256: enc.file_sha256.to_vec(),
72            file_length: data.len() as u64,
73        };
74        Ok(result)
75    }
76}