MSC2530: added the ability to send media with captions (#3226)
Now that there is some support for [MSC2530](https://github.com/matrix-org/matrix-spec-proposals/pull/2530), I gave adding sending captions a try. ( This is my first time with Rust 😄 ) I tried it on Element X with a hardcoded caption and it seems to work well  (It even got forwarded through mautrix-whatsapp and the caption was visible on the Whatsapp side) --- * ffi: Expose filename and formatted body fields for media captions In relevance to MSC2530 * MSC2530: added the ability to send media with captions Signed-off-by: Marco Antonio Alvarez <surakin@gmail.com> * signoff Signed-off-by: Marco Antonio Alvarez <surakin@gmail.com> * fixing the import messup * fix missing parameters in documentation * fix formatting * move optional parameters to the end * more formatting fixes * more formatting fixes * rename url parameter to filename in send_attachment and helpers * fix send_attachment documentation example * move caption and formatted_caption into attachmentconfig * fix formatting * fix formatting * fix formatting (hopefully the last one) * updated stale comments * simplify attachment message comments --------- Signed-off-by: Marco Antonio Alvarez <surakin@gmail.com> Co-authored-by: SpiritCroc <dev@spiritcroc.de>
This commit is contained in:
committed by
GitHub
parent
56da4a31a4
commit
10069fbead
@@ -52,19 +52,21 @@ use tokio::{
|
||||
use tracing::{error, info, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
use self::content::{Reaction, ReactionSenderData, TimelineItemContent};
|
||||
use crate::{
|
||||
client::ProgressWatcher,
|
||||
error::{ClientError, RoomError},
|
||||
helpers::unwrap_or_clone_arc,
|
||||
ruma::{AssetType, AudioInfo, FileInfo, ImageInfo, PollKind, ThumbnailInfo, VideoInfo},
|
||||
ruma::{
|
||||
AssetType, AudioInfo, FileInfo, FormattedBody, ImageInfo, PollKind, ThumbnailInfo,
|
||||
VideoInfo,
|
||||
},
|
||||
task_handle::TaskHandle,
|
||||
RUNTIME,
|
||||
};
|
||||
|
||||
mod content;
|
||||
|
||||
pub use self::content::{Reaction, ReactionSenderData, TimelineItemContent};
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
#[repr(transparent)]
|
||||
pub struct Timeline {
|
||||
@@ -106,12 +108,12 @@ impl Timeline {
|
||||
|
||||
async fn send_attachment(
|
||||
&self,
|
||||
url: String,
|
||||
filename: String,
|
||||
mime_type: Mime,
|
||||
attachment_config: AttachmentConfig,
|
||||
progress_watcher: Option<Box<dyn ProgressWatcher>>,
|
||||
) -> Result<(), RoomError> {
|
||||
let request = self.inner.send_attachment(url, mime_type, attachment_config);
|
||||
let request = self.inner.send_attachment(filename, mime_type, attachment_config);
|
||||
if let Some(progress_watcher) = progress_watcher {
|
||||
let mut subscriber = request.subscribe_to_send_progress();
|
||||
RUNTIME.spawn(async move {
|
||||
@@ -218,6 +220,8 @@ impl Timeline {
|
||||
url: String,
|
||||
thumbnail_url: Option<String>,
|
||||
image_info: ImageInfo,
|
||||
caption: Option<String>,
|
||||
formatted_caption: Option<FormattedBody>,
|
||||
progress_watcher: Option<Box<dyn ProgressWatcher>>,
|
||||
) -> Arc<SendAttachmentJoinHandle> {
|
||||
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
|
||||
@@ -238,7 +242,9 @@ impl Timeline {
|
||||
AttachmentConfig::with_thumbnail(thumbnail).info(attachment_info)
|
||||
}
|
||||
_ => AttachmentConfig::new().info(attachment_info),
|
||||
};
|
||||
}
|
||||
.caption(caption)
|
||||
.formatted_caption(formatted_caption.map(Into::into));
|
||||
|
||||
self.send_attachment(url, mime_type, attachment_config, progress_watcher).await
|
||||
}))
|
||||
@@ -249,6 +255,8 @@ impl Timeline {
|
||||
url: String,
|
||||
thumbnail_url: Option<String>,
|
||||
video_info: VideoInfo,
|
||||
caption: Option<String>,
|
||||
formatted_caption: Option<FormattedBody>,
|
||||
progress_watcher: Option<Box<dyn ProgressWatcher>>,
|
||||
) -> Arc<SendAttachmentJoinHandle> {
|
||||
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
|
||||
@@ -269,7 +277,9 @@ impl Timeline {
|
||||
AttachmentConfig::with_thumbnail(thumbnail).info(attachment_info)
|
||||
}
|
||||
_ => AttachmentConfig::new().info(attachment_info),
|
||||
};
|
||||
}
|
||||
.caption(caption)
|
||||
.formatted_caption(formatted_caption.map(Into::into));
|
||||
|
||||
self.send_attachment(url, mime_type, attachment_config, progress_watcher).await
|
||||
}))
|
||||
@@ -279,6 +289,8 @@ impl Timeline {
|
||||
self: Arc<Self>,
|
||||
url: String,
|
||||
audio_info: AudioInfo,
|
||||
caption: Option<String>,
|
||||
formatted_caption: Option<FormattedBody>,
|
||||
progress_watcher: Option<Box<dyn ProgressWatcher>>,
|
||||
) -> Arc<SendAttachmentJoinHandle> {
|
||||
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
|
||||
@@ -291,7 +303,10 @@ impl Timeline {
|
||||
.map_err(|_| RoomError::InvalidAttachmentData)?;
|
||||
|
||||
let attachment_info = AttachmentInfo::Audio(base_audio_info);
|
||||
let attachment_config = AttachmentConfig::new().info(attachment_info);
|
||||
let attachment_config = AttachmentConfig::new()
|
||||
.info(attachment_info)
|
||||
.caption(caption)
|
||||
.formatted_caption(formatted_caption.map(Into::into));
|
||||
|
||||
self.send_attachment(url, mime_type, attachment_config, progress_watcher).await
|
||||
}))
|
||||
@@ -302,6 +317,8 @@ impl Timeline {
|
||||
url: String,
|
||||
audio_info: AudioInfo,
|
||||
waveform: Vec<u16>,
|
||||
caption: Option<String>,
|
||||
formatted_caption: Option<FormattedBody>,
|
||||
progress_watcher: Option<Box<dyn ProgressWatcher>>,
|
||||
) -> Arc<SendAttachmentJoinHandle> {
|
||||
SendAttachmentJoinHandle::new(RUNTIME.spawn(async move {
|
||||
@@ -315,7 +332,10 @@ impl Timeline {
|
||||
|
||||
let attachment_info =
|
||||
AttachmentInfo::Voice { audio_info: base_audio_info, waveform: Some(waveform) };
|
||||
let attachment_config = AttachmentConfig::new().info(attachment_info);
|
||||
let attachment_config = AttachmentConfig::new()
|
||||
.info(attachment_info)
|
||||
.caption(caption)
|
||||
.formatted_caption(formatted_caption.map(Into::into));
|
||||
|
||||
self.send_attachment(url, mime_type, attachment_config, progress_watcher).await
|
||||
}))
|
||||
|
||||
@@ -10,7 +10,7 @@ use super::{Error, Timeline};
|
||||
|
||||
pub struct SendAttachment<'a> {
|
||||
timeline: &'a Timeline,
|
||||
url: String,
|
||||
filename: String,
|
||||
mime_type: Mime,
|
||||
config: AttachmentConfig,
|
||||
tracing_span: Span,
|
||||
@@ -20,13 +20,13 @@ pub struct SendAttachment<'a> {
|
||||
impl<'a> SendAttachment<'a> {
|
||||
pub(crate) fn new(
|
||||
timeline: &'a Timeline,
|
||||
url: String,
|
||||
filename: String,
|
||||
mime_type: Mime,
|
||||
config: AttachmentConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
timeline,
|
||||
url,
|
||||
filename,
|
||||
mime_type,
|
||||
config,
|
||||
tracing_span: Span::current(),
|
||||
@@ -47,14 +47,14 @@ impl<'a> IntoFuture for SendAttachment<'a> {
|
||||
boxed_into_future!(extra_bounds: 'a);
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
let Self { timeline, url, mime_type, config, tracing_span, send_progress } = self;
|
||||
let Self { timeline, filename, mime_type, config, tracing_span, send_progress } = self;
|
||||
let fut = async move {
|
||||
let body = Path::new(&url)
|
||||
let body = Path::new(&filename)
|
||||
.file_name()
|
||||
.ok_or(Error::InvalidAttachmentFileName)?
|
||||
.to_str()
|
||||
.expect("path was created from UTF-8 string, hence filename part is UTF-8 too");
|
||||
let data = fs::read(&url).map_err(|_| Error::InvalidAttachmentData)?;
|
||||
let data = fs::read(&filename).map_err(|_| Error::InvalidAttachmentData)?;
|
||||
|
||||
timeline
|
||||
.room()
|
||||
|
||||
@@ -513,21 +513,22 @@ impl Timeline {
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `url` - The url for the file to be sent
|
||||
/// * `filename` - The filename of the file to be sent
|
||||
///
|
||||
/// * `mime_type` - The attachment's mime type
|
||||
///
|
||||
/// * `config` - An attachment configuration object containing details about
|
||||
/// the attachment
|
||||
///
|
||||
/// like a thumbnail, its size, duration etc.
|
||||
#[instrument(skip_all)]
|
||||
pub fn send_attachment(
|
||||
&self,
|
||||
url: String,
|
||||
filename: String,
|
||||
mime_type: Mime,
|
||||
config: AttachmentConfig,
|
||||
) -> SendAttachment<'_> {
|
||||
SendAttachment::new(self, url, mime_type, config)
|
||||
SendAttachment::new(self, filename, mime_type, config)
|
||||
}
|
||||
|
||||
/// Retry sending a message that previously failed to send.
|
||||
|
||||
@@ -23,7 +23,7 @@ use image::GenericImageView;
|
||||
use ruma::{
|
||||
assign,
|
||||
events::room::{
|
||||
message::{AudioInfo, FileInfo, VideoInfo},
|
||||
message::{AudioInfo, FileInfo, FormattedBody, VideoInfo},
|
||||
ImageInfo, ThumbnailInfo,
|
||||
},
|
||||
OwnedTransactionId, TransactionId, UInt,
|
||||
@@ -190,6 +190,8 @@ pub struct AttachmentConfig {
|
||||
pub(crate) txn_id: Option<OwnedTransactionId>,
|
||||
pub(crate) info: Option<AttachmentInfo>,
|
||||
pub(crate) thumbnail: Option<Thumbnail>,
|
||||
pub(crate) caption: Option<String>,
|
||||
pub(crate) formatted_caption: Option<FormattedBody>,
|
||||
#[cfg(feature = "image-proc")]
|
||||
pub(crate) generate_thumbnail: bool,
|
||||
#[cfg(feature = "image-proc")]
|
||||
@@ -205,6 +207,8 @@ impl AttachmentConfig {
|
||||
txn_id: Default::default(),
|
||||
info: Default::default(),
|
||||
thumbnail: None,
|
||||
caption: None,
|
||||
formatted_caption: None,
|
||||
#[cfg(feature = "image-proc")]
|
||||
generate_thumbnail: Default::default(),
|
||||
#[cfg(feature = "image-proc")]
|
||||
@@ -247,6 +251,8 @@ impl AttachmentConfig {
|
||||
txn_id: Default::default(),
|
||||
info: Default::default(),
|
||||
thumbnail: Some(thumbnail),
|
||||
caption: None,
|
||||
formatted_caption: None,
|
||||
#[cfg(feature = "image-proc")]
|
||||
generate_thumbnail: Default::default(),
|
||||
#[cfg(feature = "image-proc")]
|
||||
@@ -278,6 +284,26 @@ impl AttachmentConfig {
|
||||
self.info = Some(info);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the optional caption
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `caption` - The optional caption
|
||||
pub fn caption(mut self, caption: Option<String>) -> Self {
|
||||
self.caption = caption;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the optional formatted caption
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `formatted_caption` - The optional formatted caption
|
||||
pub fn formatted_caption(mut self, formatted_caption: Option<FormattedBody>) -> Self {
|
||||
self.formatted_caption = formatted_caption;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AttachmentConfig {
|
||||
@@ -331,7 +357,7 @@ impl Default for AttachmentConfig {
|
||||
///
|
||||
/// if let Some(room) = client.get_room(&room_id) {
|
||||
/// room.send_attachment(
|
||||
/// "My favorite cat",
|
||||
/// "my_favorite_cat.jpg",
|
||||
/// &mime::IMAGE_JPEG,
|
||||
/// image,
|
||||
/// config,
|
||||
|
||||
@@ -68,7 +68,7 @@ use self::{
|
||||
tasks::{BackupDownloadTask, BackupUploadingTask, ClientTasks},
|
||||
};
|
||||
use crate::{
|
||||
attachment::{AttachmentInfo, Thumbnail},
|
||||
attachment::{AttachmentConfig, Thumbnail},
|
||||
client::ClientInner,
|
||||
encryption::{
|
||||
identities::{Device, UserDevices},
|
||||
@@ -298,18 +298,17 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Encrypt and upload the file to be read from `reader` and construct an
|
||||
/// attachment message with `body`, `content_type`, `info` and `thumbnail`.
|
||||
/// attachment message.
|
||||
pub(crate) async fn prepare_encrypted_attachment_message(
|
||||
&self,
|
||||
body: &str,
|
||||
filename: &str,
|
||||
content_type: &mime::Mime,
|
||||
data: Vec<u8>,
|
||||
info: Option<AttachmentInfo>,
|
||||
thumbnail: Option<Thumbnail>,
|
||||
config: AttachmentConfig,
|
||||
send_progress: SharedObservable<TransmissionProgress>,
|
||||
) -> Result<MessageType> {
|
||||
let upload_thumbnail =
|
||||
self.upload_encrypted_thumbnail(thumbnail, content_type, send_progress.clone());
|
||||
self.upload_encrypted_thumbnail(config.thumbnail, content_type, send_progress.clone());
|
||||
|
||||
let upload_attachment = async {
|
||||
let mut cursor = Cursor::new(data);
|
||||
@@ -321,15 +320,25 @@ impl Client {
|
||||
let ((thumbnail_source, thumbnail_info), file) =
|
||||
try_join(upload_thumbnail, upload_attachment).await?;
|
||||
|
||||
// if config.caption is set, use it as body, and filename as the file name
|
||||
// otherwise, body is the filename, and the filename is not set
|
||||
// https://github.com/tulir/matrix-spec-proposals/blob/body-as-caption/proposals/2530-body-as-caption.md
|
||||
let (body, filename) = match config.caption {
|
||||
Some(caption) => (caption, Some(filename.to_owned())),
|
||||
None => (filename.to_owned(), None),
|
||||
};
|
||||
|
||||
Ok(match content_type.type_() {
|
||||
mime::IMAGE => {
|
||||
let info = assign!(info.map(ImageInfo::from).unwrap_or_default(), {
|
||||
let info = assign!(config.info.map(ImageInfo::from).unwrap_or_default(), {
|
||||
mimetype: Some(content_type.as_ref().to_owned()),
|
||||
thumbnail_source,
|
||||
thumbnail_info
|
||||
});
|
||||
let content = assign!(ImageMessageEventContent::encrypted(body.to_owned(), file), {
|
||||
info: Some(Box::new(info))
|
||||
info: Some(Box::new(info)),
|
||||
formatted: config.formatted_caption,
|
||||
filename
|
||||
});
|
||||
MessageType::Image(content)
|
||||
}
|
||||
@@ -339,22 +348,24 @@ impl Client {
|
||||
MessageType::Audio(crate::media::update_audio_message_event(
|
||||
audio_message_event_content,
|
||||
content_type,
|
||||
info,
|
||||
config.info,
|
||||
))
|
||||
}
|
||||
mime::VIDEO => {
|
||||
let info = assign!(info.map(VideoInfo::from).unwrap_or_default(), {
|
||||
let info = assign!(config.info.map(VideoInfo::from).unwrap_or_default(), {
|
||||
mimetype: Some(content_type.as_ref().to_owned()),
|
||||
thumbnail_source,
|
||||
thumbnail_info
|
||||
});
|
||||
let content = assign!(VideoMessageEventContent::encrypted(body.to_owned(), file), {
|
||||
info: Some(Box::new(info))
|
||||
info: Some(Box::new(info)),
|
||||
formatted: config.formatted_caption,
|
||||
filename
|
||||
});
|
||||
MessageType::Video(content)
|
||||
}
|
||||
_ => {
|
||||
let info = assign!(info.map(FileInfo::from).unwrap_or_default(), {
|
||||
let info = assign!(config.info.map(FileInfo::from).unwrap_or_default(), {
|
||||
mimetype: Some(content_type.as_ref().to_owned()),
|
||||
thumbnail_source,
|
||||
thumbnail_info
|
||||
|
||||
@@ -44,7 +44,7 @@ use tempfile::{Builder as TempFileBuilder, NamedTempFile, TempDir};
|
||||
use tokio::{fs::File as TokioFile, io::AsyncWriteExt};
|
||||
|
||||
use crate::{
|
||||
attachment::{AttachmentInfo, Thumbnail},
|
||||
attachment::{AttachmentConfig, AttachmentInfo, Thumbnail},
|
||||
futures::SendRequest,
|
||||
Client, Result, TransmissionProgress,
|
||||
};
|
||||
@@ -437,17 +437,16 @@ impl Media {
|
||||
}
|
||||
|
||||
/// Upload the file bytes in `data` and construct an attachment
|
||||
/// message with `body`, `content_type`, `info` and `thumbnail`.
|
||||
/// message.
|
||||
pub(crate) async fn prepare_attachment_message(
|
||||
&self,
|
||||
body: &str,
|
||||
filename: &str,
|
||||
content_type: &Mime,
|
||||
data: Vec<u8>,
|
||||
info: Option<AttachmentInfo>,
|
||||
thumbnail: Option<Thumbnail>,
|
||||
config: AttachmentConfig,
|
||||
send_progress: SharedObservable<TransmissionProgress>,
|
||||
) -> Result<MessageType> {
|
||||
let upload_thumbnail = self.upload_thumbnail(thumbnail, send_progress.clone());
|
||||
let upload_thumbnail = self.upload_thumbnail(config.thumbnail, send_progress.clone());
|
||||
|
||||
let upload_attachment = async move {
|
||||
self.upload(content_type, data)
|
||||
@@ -459,46 +458,65 @@ impl Media {
|
||||
let ((thumbnail_source, thumbnail_info), response) =
|
||||
try_join(upload_thumbnail, upload_attachment).await?;
|
||||
|
||||
// if config.caption is set, use it as body, and filename as the file name
|
||||
// otherwise, body is the filename, and the filename is not set
|
||||
// https://github.com/tulir/matrix-spec-proposals/blob/body-as-caption/proposals/2530-body-as-caption.md
|
||||
let (body, filename) = match config.caption {
|
||||
Some(caption) => (caption, Some(filename.to_owned())),
|
||||
None => (filename.to_owned(), None),
|
||||
};
|
||||
|
||||
let url = response.content_uri;
|
||||
|
||||
Ok(match content_type.type_() {
|
||||
mime::IMAGE => {
|
||||
let info = assign!(info.map(ImageInfo::from).unwrap_or_default(), {
|
||||
let info = assign!(config.info.map(ImageInfo::from).unwrap_or_default(), {
|
||||
mimetype: Some(content_type.as_ref().to_owned()),
|
||||
thumbnail_source,
|
||||
thumbnail_info,
|
||||
});
|
||||
MessageType::Image(
|
||||
ImageMessageEventContent::plain(body.to_owned(), url).info(Box::new(info)),
|
||||
ImageMessageEventContent::plain(body.to_owned(), url)
|
||||
.info(Box::new(info))
|
||||
.filename(filename)
|
||||
.formatted(config.formatted_caption),
|
||||
)
|
||||
}
|
||||
mime::AUDIO => {
|
||||
let audio_message_event_content =
|
||||
message::AudioMessageEventContent::plain(body.to_owned(), url);
|
||||
message::AudioMessageEventContent::plain(body.to_owned(), url)
|
||||
.filename(filename)
|
||||
.formatted(config.formatted_caption);
|
||||
MessageType::Audio(update_audio_message_event(
|
||||
audio_message_event_content,
|
||||
content_type,
|
||||
info,
|
||||
config.info,
|
||||
))
|
||||
}
|
||||
mime::VIDEO => {
|
||||
let info = assign!(info.map(VideoInfo::from).unwrap_or_default(), {
|
||||
let info = assign!(config.info.map(VideoInfo::from).unwrap_or_default(), {
|
||||
mimetype: Some(content_type.as_ref().to_owned()),
|
||||
thumbnail_source,
|
||||
thumbnail_info
|
||||
});
|
||||
MessageType::Video(
|
||||
VideoMessageEventContent::plain(body.to_owned(), url).info(Box::new(info)),
|
||||
VideoMessageEventContent::plain(body.to_owned(), url)
|
||||
.info(Box::new(info))
|
||||
.filename(filename)
|
||||
.formatted(config.formatted_caption),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let info = assign!(info.map(FileInfo::from).unwrap_or_default(), {
|
||||
let info = assign!(config.info.map(FileInfo::from).unwrap_or_default(), {
|
||||
mimetype: Some(content_type.as_ref().to_owned()),
|
||||
thumbnail_source,
|
||||
thumbnail_info
|
||||
});
|
||||
MessageType::File(
|
||||
FileMessageEventContent::plain(body.to_owned(), url).info(Box::new(info)),
|
||||
FileMessageEventContent::plain(body.to_owned(), url)
|
||||
.info(Box::new(info))
|
||||
.filename(filename)
|
||||
.formatted(config.formatted_caption),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -216,7 +216,7 @@ impl<'a> IntoFuture for SendRawMessageLikeEvent<'a> {
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct SendAttachment<'a> {
|
||||
room: &'a Room,
|
||||
body: &'a str,
|
||||
url: &'a str,
|
||||
content_type: &'a Mime,
|
||||
data: Vec<u8>,
|
||||
config: AttachmentConfig,
|
||||
@@ -227,14 +227,14 @@ pub struct SendAttachment<'a> {
|
||||
impl<'a> SendAttachment<'a> {
|
||||
pub(crate) fn new(
|
||||
room: &'a Room,
|
||||
body: &'a str,
|
||||
url: &'a str,
|
||||
content_type: &'a Mime,
|
||||
data: Vec<u8>,
|
||||
config: AttachmentConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
room,
|
||||
body,
|
||||
url,
|
||||
content_type,
|
||||
data,
|
||||
config,
|
||||
@@ -260,10 +260,10 @@ impl<'a> IntoFuture for SendAttachment<'a> {
|
||||
boxed_into_future!(extra_bounds: 'a);
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
let Self { room, body, content_type, data, config, tracing_span, send_progress } = self;
|
||||
let Self { room, url, content_type, data, config, tracing_span, send_progress } = self;
|
||||
let fut = async move {
|
||||
if config.thumbnail.is_some() {
|
||||
room.prepare_and_send_attachment(body, content_type, data, config, send_progress)
|
||||
room.prepare_and_send_attachment(url, content_type, data, config, send_progress)
|
||||
.await
|
||||
} else {
|
||||
#[cfg(not(feature = "image-proc"))]
|
||||
@@ -316,13 +316,15 @@ impl<'a> IntoFuture for SendAttachment<'a> {
|
||||
txn_id: config.txn_id,
|
||||
info: config.info,
|
||||
thumbnail,
|
||||
caption: config.caption,
|
||||
formatted_caption: config.formatted_caption,
|
||||
#[cfg(feature = "image-proc")]
|
||||
generate_thumbnail: false,
|
||||
#[cfg(feature = "image-proc")]
|
||||
thumbnail_size: None,
|
||||
};
|
||||
|
||||
room.prepare_and_send_attachment(body, content_type, data, config, send_progress)
|
||||
room.prepare_and_send_attachment(url, content_type, data, config, send_progress)
|
||||
.await
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1692,8 +1692,7 @@ impl Room {
|
||||
/// [`upload()`] and afterwards the [`send()`].
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `body` - A textual representation of the media that is going to be
|
||||
/// uploaded. Usually the file name.
|
||||
/// * `filename` - The file name.
|
||||
///
|
||||
/// * `content_type` - The type of the media, this will be used as the
|
||||
/// content-type header.
|
||||
@@ -1718,7 +1717,7 @@ impl Room {
|
||||
///
|
||||
/// if let Some(room) = client.get_room(&room_id) {
|
||||
/// room.send_attachment(
|
||||
/// "My favorite cat",
|
||||
/// "my_favorite_cat.jpg",
|
||||
/// &mime::IMAGE_JPEG,
|
||||
/// image,
|
||||
/// AttachmentConfig::new(),
|
||||
@@ -1732,12 +1731,12 @@ impl Room {
|
||||
#[instrument(skip_all)]
|
||||
pub fn send_attachment<'a>(
|
||||
&'a self,
|
||||
body: &'a str,
|
||||
filename: &'a str,
|
||||
content_type: &'a Mime,
|
||||
data: Vec<u8>,
|
||||
config: AttachmentConfig,
|
||||
) -> SendAttachment<'a> {
|
||||
SendAttachment::new(self, body, content_type, data, config)
|
||||
SendAttachment::new(self, filename, content_type, data, config)
|
||||
}
|
||||
|
||||
/// Prepare and send an attachment to this room.
|
||||
@@ -1752,8 +1751,7 @@ impl Room {
|
||||
/// [`send()`](#method.send).
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `body` - A textual representation of the media that is going to be
|
||||
/// uploaded. Usually the file name.
|
||||
/// * `filename` - The file name.
|
||||
///
|
||||
/// * `content_type` - The type of the media, this will be used as the
|
||||
/// content-type header.
|
||||
@@ -1764,7 +1762,7 @@ impl Room {
|
||||
/// * `config` - Metadata and configuration for the attachment.
|
||||
pub(super) async fn prepare_and_send_attachment<'a>(
|
||||
&'a self,
|
||||
body: &'a str,
|
||||
filename: &'a str,
|
||||
content_type: &'a Mime,
|
||||
data: Vec<u8>,
|
||||
config: AttachmentConfig,
|
||||
@@ -1772,29 +1770,22 @@ impl Room {
|
||||
) -> Result<send_message_event::v3::Response> {
|
||||
self.ensure_room_joined()?;
|
||||
|
||||
let txn_id = config.txn_id.clone();
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
let content = if self.is_encrypted().await? {
|
||||
self.client
|
||||
.prepare_encrypted_attachment_message(
|
||||
body,
|
||||
filename,
|
||||
content_type,
|
||||
data,
|
||||
config.info,
|
||||
config.thumbnail,
|
||||
config,
|
||||
send_progress,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
self.client
|
||||
.media()
|
||||
.prepare_attachment_message(
|
||||
body,
|
||||
content_type,
|
||||
data,
|
||||
config.info,
|
||||
config.thumbnail,
|
||||
send_progress,
|
||||
)
|
||||
.prepare_attachment_message(filename, content_type, data, config, send_progress)
|
||||
.await?
|
||||
};
|
||||
|
||||
@@ -1802,18 +1793,11 @@ impl Room {
|
||||
let content = self
|
||||
.client
|
||||
.media()
|
||||
.prepare_attachment_message(
|
||||
body,
|
||||
content_type,
|
||||
data,
|
||||
config.info,
|
||||
config.thumbnail,
|
||||
send_progress,
|
||||
)
|
||||
.prepare_attachment_message(filename, content_type, data, config, send_progress)
|
||||
.await?;
|
||||
|
||||
let mut fut = self.send(RoomMessageEventContent::new(content));
|
||||
if let Some(txn_id) = &config.txn_id {
|
||||
if let Some(txn_id) = &txn_id {
|
||||
fut = fut.with_transaction_id(txn_id);
|
||||
}
|
||||
fut.await
|
||||
|
||||
@@ -418,15 +418,17 @@ async fn room_attachment_send_info() {
|
||||
|
||||
let room = client.get_room(&DEFAULT_TEST_ROOM_ID).unwrap();
|
||||
|
||||
let config = AttachmentConfig::new().info(AttachmentInfo::Image(BaseImageInfo {
|
||||
height: Some(uint!(600)),
|
||||
width: Some(uint!(800)),
|
||||
size: None,
|
||||
blurhash: None,
|
||||
}));
|
||||
let config = AttachmentConfig::new()
|
||||
.info(AttachmentInfo::Image(BaseImageInfo {
|
||||
height: Some(uint!(600)),
|
||||
width: Some(uint!(800)),
|
||||
size: None,
|
||||
blurhash: None,
|
||||
}))
|
||||
.caption(Some("image caption".to_owned()));
|
||||
|
||||
let response = room
|
||||
.send_attachment("image", &mime::IMAGE_JPEG, b"Hello world".to_vec(), config)
|
||||
.send_attachment("image.jpg", &mime::IMAGE_JPEG, b"Hello world".to_vec(), config)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -470,16 +472,18 @@ async fn room_attachment_send_wrong_info() {
|
||||
|
||||
let room = client.get_room(&DEFAULT_TEST_ROOM_ID).unwrap();
|
||||
|
||||
let config = AttachmentConfig::new().info(AttachmentInfo::Video(BaseVideoInfo {
|
||||
height: Some(uint!(600)),
|
||||
width: Some(uint!(800)),
|
||||
duration: Some(Duration::from_millis(3600)),
|
||||
size: None,
|
||||
blurhash: None,
|
||||
}));
|
||||
let config = AttachmentConfig::new()
|
||||
.info(AttachmentInfo::Video(BaseVideoInfo {
|
||||
height: Some(uint!(600)),
|
||||
width: Some(uint!(800)),
|
||||
duration: Some(Duration::from_millis(3600)),
|
||||
size: None,
|
||||
blurhash: None,
|
||||
}))
|
||||
.caption(Some("image caption".to_owned()));
|
||||
|
||||
let response =
|
||||
room.send_attachment("image", &mime::IMAGE_JPEG, b"Hello world".to_vec(), config).await;
|
||||
room.send_attachment("image.jpg", &mime::IMAGE_JPEG, b"Hello world".to_vec(), config).await;
|
||||
|
||||
response.unwrap_err();
|
||||
}
|
||||
|
||||
@@ -17,9 +17,14 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room, image:
|
||||
|
||||
if text_content.body.contains("!image") {
|
||||
println!("sending image");
|
||||
room.send_attachment("cat", &mime::IMAGE_JPEG, image, AttachmentConfig::new())
|
||||
.await
|
||||
.unwrap();
|
||||
room.send_attachment(
|
||||
"cat.jpg",
|
||||
&mime::IMAGE_JPEG,
|
||||
image,
|
||||
AttachmentConfig::new().caption(Some("my pretty cat".to_owned())),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
println!("message sent");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user