use matrix_sdk::{
Room,
ruma::events::room::{
ImageInfo,
message::{
AudioInfo, AudioMessageEventContent, FileInfo, FileMessageEventContent,
ImageMessageEventContent, MessageType, RoomMessageEventContent,
UnstableVoiceContentBlock, VideoInfo, VideoMessageEventContent,
},
},
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum MediaAttachmentUploadPrepError {
#[error("Error getting encryption status: {0}")]
EncryptionStatusUnknown(matrix_sdk::Error),
#[error("Error during unencrypted upload: {0}")]
UnencryptedUpload(matrix_sdk::Error),
#[error("Error during encrypted upload: {0}")]
EncryptedUpload(matrix_sdk::Error),
}
#[derive(Clone)]
pub struct Media {}
impl Default for Media {
fn default() -> Self {
Self::new()
}
}
impl Media {
pub(super) fn new() -> Self {
Self {}
}
pub async fn upload_and_prepare_event_content(
&self,
room: &Room,
content_type: &mime::Mime,
data: Vec<u8>,
attachment_body_text: &str,
) -> Result<RoomMessageEventContent, MediaAttachmentUploadPrepError> {
let bytes = data.clone();
let message_type = upload_and_prepare_attachment_message(
room,
content_type,
bytes,
attachment_body_text.to_owned(),
)
.await?;
Ok(RoomMessageEventContent::new(message_type))
}
}
pub async fn upload_and_prepare_attachment_message(
room: &matrix_sdk::Room,
content_type: &mime::Mime,
data: Vec<u8>,
attachment_body: String,
) -> Result<MessageType, MediaAttachmentUploadPrepError> {
let is_encrypted = room
.latest_encryption_state()
.await
.map_err(MediaAttachmentUploadPrepError::EncryptionStatusUnknown)?
.is_encrypted();
if is_encrypted {
upload_and_prepare_attachment_message_encrypted(
room.client(),
content_type,
data,
attachment_body,
)
.await
} else {
upload_and_prepare_attachment_message_unencrypted(
room.client(),
content_type,
data,
attachment_body,
)
.await
}
}
async fn upload_and_prepare_attachment_message_unencrypted(
client: matrix_sdk::Client,
content_type: &mime::Mime,
data: Vec<u8>,
attachment_body: String,
) -> Result<MessageType, MediaAttachmentUploadPrepError> {
let data_size = data.len();
let response = client
.media()
.upload(content_type, data, None)
.await
.map_err(MediaAttachmentUploadPrepError::UnencryptedUpload)?;
let url = response.content_uri;
Ok(match content_type.type_() {
mime::IMAGE => {
let mut image_event_content = ImageMessageEventContent::plain(attachment_body, url);
image_event_content =
inject_info_into_image_content(image_event_content, content_type, data_size);
MessageType::Image(image_event_content)
}
mime::AUDIO => {
let mut audio_message_event_content =
AudioMessageEventContent::plain(attachment_body, url);
audio_message_event_content = inject_info_into_audio_content(
audio_message_event_content,
content_type,
data_size,
);
MessageType::Audio(audio_message_event_content)
}
mime::VIDEO => {
let mut video_message_event_content =
VideoMessageEventContent::plain(attachment_body, url);
video_message_event_content = inject_info_into_video_content(
video_message_event_content,
content_type,
data_size,
);
MessageType::Video(video_message_event_content)
}
_ => {
let mut file_message_event_content =
FileMessageEventContent::plain(attachment_body, url);
file_message_event_content =
inject_info_into_file_content(file_message_event_content, content_type, data_size);
MessageType::File(file_message_event_content)
}
})
}
async fn upload_and_prepare_attachment_message_encrypted(
client: matrix_sdk::Client,
content_type: &mime::Mime,
data: Vec<u8>,
attachment_body: String,
) -> Result<MessageType, MediaAttachmentUploadPrepError> {
let data_size = data.len();
let mut cursor = std::io::Cursor::new(data);
let file = client
.upload_encrypted_file(&mut cursor)
.await
.map_err(MediaAttachmentUploadPrepError::EncryptedUpload)?;
Ok(match content_type.type_() {
mime::IMAGE => {
let mut image_event_content =
ImageMessageEventContent::encrypted(attachment_body, file);
image_event_content =
inject_info_into_image_content(image_event_content, content_type, data_size);
MessageType::Image(image_event_content)
}
mime::AUDIO => {
let mut audio_message_event_content =
AudioMessageEventContent::encrypted(attachment_body, file);
audio_message_event_content = inject_info_into_audio_content(
audio_message_event_content,
content_type,
data_size,
);
MessageType::Audio(audio_message_event_content)
}
mime::VIDEO => {
let mut video_message_event_content =
VideoMessageEventContent::encrypted(attachment_body, file);
video_message_event_content = inject_info_into_video_content(
video_message_event_content,
content_type,
data_size,
);
MessageType::Video(video_message_event_content)
}
_ => {
let mut file_message_event_content =
FileMessageEventContent::encrypted(attachment_body, file);
file_message_event_content =
inject_info_into_file_content(file_message_event_content, content_type, data_size);
MessageType::File(file_message_event_content)
}
})
}
fn inject_info_into_image_content(
content: ImageMessageEventContent,
content_type: &mime::Mime,
size: usize,
) -> ImageMessageEventContent {
let mut info = ImageInfo::new();
info.mimetype = Some(content_type.as_ref().to_owned());
info.size = matrix_sdk::ruma::UInt::new(size as u64);
content.info(Box::new(info))
}
fn inject_info_into_audio_content(
content: AudioMessageEventContent,
content_type: &mime::Mime,
size: usize,
) -> AudioMessageEventContent {
let mut content = content.clone();
if content_type.as_ref() == "audio/ogg" {
content.voice = Some(UnstableVoiceContentBlock::new());
}
let mut info = AudioInfo::new();
info.mimetype = Some(content_type.as_ref().to_owned());
info.size = matrix_sdk::ruma::UInt::new(size as u64);
content.info(Box::new(info))
}
fn inject_info_into_video_content(
content: VideoMessageEventContent,
content_type: &mime::Mime,
size: usize,
) -> VideoMessageEventContent {
let mut info = VideoInfo::new();
info.mimetype = Some(content_type.as_ref().to_owned());
info.size = matrix_sdk::ruma::UInt::new(size as u64);
content.info(Box::new(info))
}
fn inject_info_into_file_content(
content: FileMessageEventContent,
content_type: &mime::Mime,
size: usize,
) -> FileMessageEventContent {
let mut info = FileInfo::new();
info.mimetype = Some(content_type.as_ref().to_owned());
info.size = matrix_sdk::ruma::UInt::new(size as u64);
content.info(Box::new(info))
}