use crate::client::{Client, ClientError, NodeFilter};
use anyhow::Result;
use log::debug;
use std::time::Duration;
pub use wacore::media_retry::MediaRetryResult;
use wacore::media_retry::{
build_media_retry_receipt, encrypt_media_retry_receipt, parse_media_retry_notification,
};
use wacore_binary::jid::{Jid, JidExt as _};
const MEDIA_RETRY_TIMEOUT: Duration = Duration::from_secs(30);
pub struct MediaReuploadRequest<'a> {
pub msg_id: &'a str,
pub chat_jid: &'a Jid,
pub media_key: &'a [u8],
pub is_from_me: bool,
pub participant: Option<&'a Jid>,
}
pub struct MediaReupload<'a> {
client: &'a Client,
}
impl<'a> MediaReupload<'a> {
pub(crate) fn new(client: &'a Client) -> Self {
Self { client }
}
pub async fn request(&self, req: &MediaReuploadRequest<'_>) -> Result<MediaRetryResult> {
anyhow::ensure!(
!req.chat_jid.is_newsletter(),
"media reupload is not supported for newsletter messages"
);
debug!(
"[media][rmr] Requesting media reupload for msg {} in chat {}",
req.msg_id, req.chat_jid
);
let (ciphertext, iv) = encrypt_media_retry_receipt(req.media_key, req.msg_id)?;
let device_snapshot = self.client.persistence_manager.get_device_snapshot().await;
let own_jid = device_snapshot.pn.clone().ok_or(ClientError::NotLoggedIn)?;
let waiter = self.client.wait_for_node(
NodeFilter::tag("notification")
.attr("type", "mediaretry")
.attr("id", req.msg_id),
);
let receipt_node = build_media_retry_receipt(
&own_jid,
req.msg_id,
req.chat_jid,
req.is_from_me,
req.participant,
&ciphertext,
&iv,
);
self.client.send_node(receipt_node).await?;
debug!(
"[media][rmr] Sent server-error receipt for {}, waiting for response",
req.msg_id
);
let notification_node =
wacore::runtime::timeout(&*self.client.runtime, MEDIA_RETRY_TIMEOUT, waiter)
.await
.map_err(|_| anyhow::anyhow!("media retry notification timed out after 30s"))?
.map_err(|_| anyhow::anyhow!("media retry waiter cancelled"))?;
debug!(
"[media][rmr] Received mediaretry notification for {}",
req.msg_id
);
parse_media_retry_notification(¬ification_node, req.media_key)
}
}
impl Client {
pub fn media_reupload(&self) -> MediaReupload<'_> {
MediaReupload::new(self)
}
}