events: Move msgtype types in separate files
This commit is contained in:
parent
f1079c94ee
commit
9ea6c3c6ab
File diff suppressed because it is too large
Load Diff
203
crates/ruma-common/src/events/room/message/audio.rs
Normal file
203
crates/ruma-common/src/events/room/message/audio.rs
Normal file
@ -0,0 +1,203 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use js_int::UInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
use crate::events::voice::VoiceContent;
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
use crate::events::{
|
||||
audio::AudioContent,
|
||||
file::{FileContent, FileContentInfo},
|
||||
message::MessageContent,
|
||||
};
|
||||
use crate::{
|
||||
events::room::{EncryptedFile, MediaSource},
|
||||
OwnedMxcUri,
|
||||
};
|
||||
|
||||
/// The payload for an audio message.
|
||||
///
|
||||
/// With the `unstable-msc3246` feature, this type contains the transitional format of
|
||||
/// [`AudioEventContent`] and with the `unstable-msc3245` feature, this type also contains the
|
||||
/// transitional format of [`VoiceEventContent`]. See the documentation of the [`message`] module
|
||||
/// for more information.
|
||||
///
|
||||
/// [`AudioEventContent`]: crate::events::audio::AudioEventContent
|
||||
/// [`VoiceEventContent`]: crate::events::voice::VoiceEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.audio")]
|
||||
#[cfg_attr(
|
||||
feature = "unstable-msc3246",
|
||||
serde(from = "super::content_serde::AudioMessageEventContentDeHelper")
|
||||
)]
|
||||
pub struct AudioMessageEventContent {
|
||||
/// The textual representation of this message.
|
||||
pub body: String,
|
||||
|
||||
/// The source of the audio clip.
|
||||
#[serde(flatten)]
|
||||
pub source: MediaSource,
|
||||
|
||||
/// Metadata for the audio clip referred to in `source`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub info: Option<Box<AudioInfo>>,
|
||||
|
||||
/// Extensible-event text representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `body` field.
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
|
||||
/// Extensible-event file content of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `source` and `info` fields.
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
#[serde(rename = "org.matrix.msc1767.file", skip_serializing_if = "Option::is_none")]
|
||||
pub file: Option<FileContent>,
|
||||
|
||||
/// Extensible-event audio info of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `info` field.
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
#[serde(rename = "org.matrix.msc1767.audio", skip_serializing_if = "Option::is_none")]
|
||||
pub audio: Option<AudioContent>,
|
||||
|
||||
/// Extensible-event voice flag of the message.
|
||||
///
|
||||
/// If present, this should be represented as a voice message.
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
#[serde(rename = "org.matrix.msc3245.voice", skip_serializing_if = "Option::is_none")]
|
||||
pub voice: Option<VoiceContent>,
|
||||
}
|
||||
|
||||
impl AudioMessageEventContent {
|
||||
/// Creates a new non-encrypted `AudioMessageEventContent` with the given body, url and
|
||||
/// optional extra info.
|
||||
pub fn plain(body: String, url: OwnedMxcUri, info: Option<Box<AudioInfo>>) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
file: Some(FileContent::plain(
|
||||
url.clone(),
|
||||
info.as_deref().map(|info| Box::new(info.into())),
|
||||
)),
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
audio: Some(info.as_deref().map_or_else(AudioContent::default, Into::into)),
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
voice: None,
|
||||
body,
|
||||
source: MediaSource::Plain(url),
|
||||
info,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new encrypted `AudioMessageEventContent` with the given body and encrypted
|
||||
/// file.
|
||||
pub fn encrypted(body: String, file: EncryptedFile) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
file: Some(FileContent::encrypted(file.url.clone(), (&file).into(), None)),
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
audio: Some(AudioContent::default()),
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
voice: None,
|
||||
body,
|
||||
source: MediaSource::Encrypted(Box::new(file)),
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `AudioMessageEventContent` with the given message, file info and audio info.
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
pub fn from_extensible_content(
|
||||
message: MessageContent,
|
||||
file: FileContent,
|
||||
audio: AudioContent,
|
||||
) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() {
|
||||
body.to_owned()
|
||||
} else {
|
||||
message[0].body.clone()
|
||||
};
|
||||
let source = (&file).into();
|
||||
let info = AudioInfo::from_extensible_content(file.info.as_deref(), &audio).map(Box::new);
|
||||
|
||||
Self {
|
||||
message: Some(message),
|
||||
file: Some(file),
|
||||
audio: Some(audio),
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
voice: None,
|
||||
body,
|
||||
source,
|
||||
info,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `AudioMessageEventContent` with the given message, file info, audio info and
|
||||
/// voice flag.
|
||||
#[cfg(feature = "unstable-msc3245")]
|
||||
pub fn from_extensible_voice_content(
|
||||
message: MessageContent,
|
||||
file: FileContent,
|
||||
audio: AudioContent,
|
||||
voice: VoiceContent,
|
||||
) -> Self {
|
||||
let mut content = Self::from_extensible_content(message, file, audio);
|
||||
content.voice = Some(voice);
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata about an audio clip.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct AudioInfo {
|
||||
/// The duration of the audio in milliseconds.
|
||||
#[serde(
|
||||
with = "crate::serde::duration::opt_ms",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub duration: Option<Duration>,
|
||||
|
||||
/// The mimetype of the audio, e.g. "audio/aac".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mimetype: Option<String>,
|
||||
|
||||
/// The size of the audio clip in bytes.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub size: Option<UInt>,
|
||||
}
|
||||
|
||||
impl AudioInfo {
|
||||
/// Creates an empty `AudioInfo`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Create an `AudioInfo` from the given file info and audio info.
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
pub fn from_extensible_content(
|
||||
file_info: Option<&FileContentInfo>,
|
||||
audio: &AudioContent,
|
||||
) -> Option<Self> {
|
||||
if file_info.is_none() && audio.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let (mimetype, size) = file_info
|
||||
.map(|info| (info.mimetype.to_owned(), info.size.to_owned()))
|
||||
.unwrap_or_default();
|
||||
let AudioContent { duration, .. } = audio;
|
||||
|
||||
Some(Self { duration: duration.to_owned(), mimetype, size })
|
||||
}
|
||||
}
|
||||
}
|
@ -13,12 +13,16 @@ use super::{
|
||||
#[cfg(feature = "unstable-msc3246")]
|
||||
use super::{AudioContent, AudioInfo, AudioMessageEventContent};
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
use super::{FileContent, FileInfo, FileMessageEventContent, MediaSource, MessageContent};
|
||||
use super::{FileContent, FileInfo, FileMessageEventContent, MessageContent};
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
use super::{ImageContent, ImageInfo, ImageMessageEventContent, ThumbnailContent};
|
||||
use super::{ImageContent, ImageMessageEventContent, ThumbnailContent};
|
||||
use super::{MessageType, Relation, RoomMessageEventContent};
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
use super::{VideoContent, VideoInfo, VideoMessageEventContent};
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
use crate::events::room::ImageInfo;
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
use crate::events::room::MediaSource;
|
||||
use crate::serde::from_raw_json_value;
|
||||
|
||||
impl<'de> Deserialize<'de> for RoomMessageEventContent {
|
||||
|
79
crates/ruma-common/src/events/room/message/emote.rs
Normal file
79
crates/ruma-common/src/events/room/message/emote.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::FormattedBody;
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
use crate::events::message::MessageContent;
|
||||
|
||||
/// The payload for an emote message.
|
||||
///
|
||||
/// With the `unstable-msc1767` feature, this type contains the transitional format of
|
||||
/// [`EmoteEventContent`]. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [`EmoteEventContent`]: crate::events::emote::EmoteEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.emote")]
|
||||
pub struct EmoteMessageEventContent {
|
||||
/// The emote action to perform.
|
||||
pub body: String,
|
||||
|
||||
/// Formatted form of the message `body`.
|
||||
#[serde(flatten)]
|
||||
pub formatted: Option<FormattedBody>,
|
||||
|
||||
/// Extensible-event representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the other fields.
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
}
|
||||
|
||||
impl EmoteMessageEventContent {
|
||||
/// A convenience constructor to create a plain-text emote.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
let body = body.into();
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
body,
|
||||
formatted: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an html emote message.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
let body = body.into();
|
||||
let html_body = html_body.into();
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
message: Some(MessageContent::html(body.clone(), html_body.clone())),
|
||||
body,
|
||||
formatted: Some(FormattedBody::html(html_body)),
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a markdown emote.
|
||||
///
|
||||
/// Returns an html emote message if some markdown formatting was detected, otherwise returns a
|
||||
/// plain-text emote.
|
||||
#[cfg(feature = "markdown")]
|
||||
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||
if let Some(formatted) = FormattedBody::markdown(&body) {
|
||||
Self::html(body, formatted.body)
|
||||
} else {
|
||||
Self::plain(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
impl From<MessageContent> for EmoteMessageEventContent {
|
||||
fn from(message: MessageContent) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() { body } else { &message[0].body };
|
||||
let formatted = message.find_html().map(FormattedBody::html);
|
||||
|
||||
Self { body: body.to_owned(), formatted, message: Some(message) }
|
||||
}
|
||||
}
|
149
crates/ruma-common/src/events/room/message/file.rs
Normal file
149
crates/ruma-common/src/events/room/message/file.rs
Normal file
@ -0,0 +1,149 @@
|
||||
use js_int::UInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
use crate::events::{
|
||||
file::{FileContent, FileContentInfo},
|
||||
message::MessageContent,
|
||||
};
|
||||
use crate::{
|
||||
events::room::{EncryptedFile, MediaSource, ThumbnailInfo},
|
||||
OwnedMxcUri,
|
||||
};
|
||||
|
||||
/// The payload for a file message.
|
||||
///
|
||||
/// With the `unstable-msc3551` feature, this type contains the transitional format of
|
||||
/// [`FileEventContent`]. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [`FileEventContent`]: crate::events::file::FileEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.file")]
|
||||
#[cfg_attr(
|
||||
feature = "unstable-msc3551",
|
||||
serde(from = "super::content_serde::FileMessageEventContentDeHelper")
|
||||
)]
|
||||
pub struct FileMessageEventContent {
|
||||
/// A human-readable description of the file.
|
||||
///
|
||||
/// This is recommended to be the filename of the original upload.
|
||||
pub body: String,
|
||||
|
||||
/// The original filename of the uploaded file.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub filename: Option<String>,
|
||||
|
||||
/// The source of the file.
|
||||
#[serde(flatten)]
|
||||
pub source: MediaSource,
|
||||
|
||||
/// Metadata about the file referred to in `source`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub info: Option<Box<FileInfo>>,
|
||||
|
||||
/// Extensible-event text representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `body` field.
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
|
||||
/// Extensible-event file content of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `source` and `info` fields.
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
#[serde(rename = "org.matrix.msc1767.file", skip_serializing_if = "Option::is_none")]
|
||||
pub file: Option<FileContent>,
|
||||
}
|
||||
|
||||
impl FileMessageEventContent {
|
||||
/// Creates a new non-encrypted `FileMessageEventContent` with the given body, url and
|
||||
/// optional extra info.
|
||||
pub fn plain(body: String, url: OwnedMxcUri, info: Option<Box<FileInfo>>) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
file: Some(FileContent::plain(
|
||||
url.clone(),
|
||||
info.as_deref().map(|info| Box::new(info.into())),
|
||||
)),
|
||||
body,
|
||||
filename: None,
|
||||
source: MediaSource::Plain(url),
|
||||
info,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new encrypted `FileMessageEventContent` with the given body and encrypted
|
||||
/// file.
|
||||
pub fn encrypted(body: String, file: EncryptedFile) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
file: Some(FileContent::encrypted(file.url.clone(), (&file).into(), None)),
|
||||
body,
|
||||
filename: None,
|
||||
source: MediaSource::Encrypted(Box::new(file)),
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `FileMessageEventContent` with the given message and file info.
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
pub fn from_extensible_content(message: MessageContent, file: FileContent) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() {
|
||||
body.to_owned()
|
||||
} else {
|
||||
message[0].body.clone()
|
||||
};
|
||||
let filename = file.info.as_deref().and_then(|info| info.name.clone());
|
||||
let info = file.info.as_deref().map(|info| Box::new(info.into()));
|
||||
let source = (&file).into();
|
||||
|
||||
Self { message: Some(message), file: Some(file), body, filename, source, info }
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata about a file.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct FileInfo {
|
||||
/// The mimetype of the file, e.g. "application/msword".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mimetype: Option<String>,
|
||||
|
||||
/// The size of the file in bytes.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub size: Option<UInt>,
|
||||
|
||||
/// Metadata about the image referred to in `thumbnail_source`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thumbnail_info: Option<Box<ThumbnailInfo>>,
|
||||
|
||||
/// The source of the thumbnail of the file.
|
||||
#[serde(
|
||||
flatten,
|
||||
with = "crate::events::room::thumbnail_source_serde",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub thumbnail_source: Option<MediaSource>,
|
||||
}
|
||||
|
||||
impl FileInfo {
|
||||
/// Creates an empty `FileInfo`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-msc3551")]
|
||||
impl From<&FileContentInfo> for FileInfo {
|
||||
fn from(info: &FileContentInfo) -> Self {
|
||||
let FileContentInfo { mimetype, size, .. } = info;
|
||||
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() }
|
||||
}
|
||||
}
|
165
crates/ruma-common/src/events/room/message/image.rs
Normal file
165
crates/ruma-common/src/events/room/message/image.rs
Normal file
@ -0,0 +1,165 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
use crate::events::{
|
||||
file::FileContent,
|
||||
image::{ImageContent, ThumbnailContent},
|
||||
message::MessageContent,
|
||||
};
|
||||
use crate::{
|
||||
events::room::{EncryptedFile, ImageInfo, MediaSource},
|
||||
OwnedMxcUri,
|
||||
};
|
||||
|
||||
/// The payload for an image message.
|
||||
///
|
||||
/// With the `unstable-msc3552` feature, this type contains the transitional format of
|
||||
/// [`ImageEventContent`]. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [`ImageEventContent`]: crate::events::image::ImageEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.image")]
|
||||
#[cfg_attr(
|
||||
feature = "unstable-msc3552",
|
||||
serde(from = "super::content_serde::ImageMessageEventContentDeHelper")
|
||||
)]
|
||||
pub struct ImageMessageEventContent {
|
||||
/// A textual representation of the image.
|
||||
///
|
||||
/// Could be the alt text of the image, the filename of the image, or some kind of content
|
||||
/// description for accessibility e.g. "image attachment".
|
||||
pub body: String,
|
||||
|
||||
/// The source of the image.
|
||||
#[serde(flatten)]
|
||||
pub source: MediaSource,
|
||||
|
||||
/// Metadata about the image referred to in `source`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub info: Option<Box<ImageInfo>>,
|
||||
|
||||
/// Extensible-event text representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `body` field.
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
|
||||
/// Extensible-event file content of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `source` and `info` fields.
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
#[serde(rename = "org.matrix.msc1767.file", skip_serializing_if = "Option::is_none")]
|
||||
pub file: Option<FileContent>,
|
||||
|
||||
/// Extensible-event image info of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `info` field.
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
#[serde(rename = "org.matrix.msc1767.image", skip_serializing_if = "Option::is_none")]
|
||||
pub image: Option<Box<ImageContent>>,
|
||||
|
||||
/// Extensible-event thumbnails of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `info` field.
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
#[serde(rename = "org.matrix.msc1767.thumbnail", skip_serializing_if = "Option::is_none")]
|
||||
pub thumbnail: Option<Vec<ThumbnailContent>>,
|
||||
|
||||
/// Extensible-event captions of the message.
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
#[serde(
|
||||
rename = "org.matrix.msc1767.caption",
|
||||
with = "crate::events::message::content_serde::as_vec",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub caption: Option<MessageContent>,
|
||||
}
|
||||
|
||||
impl ImageMessageEventContent {
|
||||
/// Creates a new non-encrypted `ImageMessageEventContent` with the given body, url and
|
||||
/// optional extra info.
|
||||
pub fn plain(body: String, url: OwnedMxcUri, info: Option<Box<ImageInfo>>) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
file: Some(FileContent::plain(
|
||||
url.clone(),
|
||||
info.as_deref().map(|info| Box::new(info.into())),
|
||||
)),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
image: Some(Box::new(info.as_deref().map_or_else(ImageContent::default, Into::into))),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
thumbnail: info
|
||||
.as_deref()
|
||||
.and_then(|info| {
|
||||
ThumbnailContent::from_room_message_content(
|
||||
info.thumbnail_source.as_ref(),
|
||||
info.thumbnail_info.as_deref(),
|
||||
)
|
||||
})
|
||||
.map(|thumbnail| vec![thumbnail]),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
caption: None,
|
||||
body,
|
||||
source: MediaSource::Plain(url),
|
||||
info,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new encrypted `ImageMessageEventContent` with the given body and encrypted
|
||||
/// file.
|
||||
pub fn encrypted(body: String, file: EncryptedFile) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
file: Some(FileContent::encrypted(file.url.clone(), (&file).into(), None)),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
image: Some(Box::new(ImageContent::default())),
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
thumbnail: None,
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
caption: None,
|
||||
body,
|
||||
source: MediaSource::Encrypted(Box::new(file)),
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `ImageMessageEventContent` with the given message, file info, image info,
|
||||
/// thumbnails and captions.
|
||||
#[cfg(feature = "unstable-msc3552")]
|
||||
pub fn from_extensible_content(
|
||||
message: MessageContent,
|
||||
file: FileContent,
|
||||
image: Box<ImageContent>,
|
||||
thumbnail: Vec<ThumbnailContent>,
|
||||
caption: Option<MessageContent>,
|
||||
) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() {
|
||||
body.to_owned()
|
||||
} else {
|
||||
message[0].body.clone()
|
||||
};
|
||||
let source = (&file).into();
|
||||
let info = ImageInfo::from_extensible_content(file.info.as_deref(), &image, &thumbnail)
|
||||
.map(Box::new);
|
||||
let thumbnail = if thumbnail.is_empty() { None } else { Some(thumbnail) };
|
||||
|
||||
Self {
|
||||
message: Some(message),
|
||||
file: Some(file),
|
||||
image: Some(image),
|
||||
thumbnail,
|
||||
caption,
|
||||
body,
|
||||
source,
|
||||
info,
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{events::key::verification::VerificationMethod, OwnedDeviceId, OwnedUserId};
|
||||
|
||||
/// The payload for a key verification request message.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.key.verification.request")]
|
||||
pub struct KeyVerificationRequestEventContent {
|
||||
/// A fallback message to alert users that their client does not support the key verification
|
||||
/// framework.
|
||||
pub body: String,
|
||||
|
||||
/// The verification methods supported by the sender.
|
||||
pub methods: Vec<VerificationMethod>,
|
||||
|
||||
/// The device ID which is initiating the request.
|
||||
pub from_device: OwnedDeviceId,
|
||||
|
||||
/// The user ID which should receive the request.
|
||||
///
|
||||
/// Users should only respond to verification requests if they are named in this field. Users
|
||||
/// who are not named in this field and who did not send this event should ignore all other
|
||||
/// events that have a `m.reference` relationship with this event.
|
||||
pub to: OwnedUserId,
|
||||
}
|
||||
|
||||
impl KeyVerificationRequestEventContent {
|
||||
/// Creates a new `KeyVerificationRequestEventContent` with the given body, method, device
|
||||
/// and user ID.
|
||||
pub fn new(
|
||||
body: String,
|
||||
methods: Vec<VerificationMethod>,
|
||||
from_device: OwnedDeviceId,
|
||||
to: OwnedUserId,
|
||||
) -> Self {
|
||||
Self { body, methods, from_device, to }
|
||||
}
|
||||
}
|
132
crates/ruma-common/src/events/room/message/location.rs
Normal file
132
crates/ruma-common/src/events/room/message/location.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::events::room::{MediaSource, ThumbnailInfo};
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
use crate::{
|
||||
events::{
|
||||
location::{AssetContent, LocationContent},
|
||||
message::MessageContent,
|
||||
},
|
||||
MilliSecondsSinceUnixEpoch,
|
||||
};
|
||||
|
||||
/// The payload for a location message.
|
||||
///
|
||||
/// With the `unstable-msc3488` feature, this type contains the transitional format of
|
||||
/// [`LocationEventContent`]. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [`LocationEventContent`]: crate::events::location::LocationEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.location")]
|
||||
#[cfg_attr(
|
||||
feature = "unstable-msc3488",
|
||||
serde(from = "super::content_serde::LocationMessageEventContentDeHelper")
|
||||
)]
|
||||
pub struct LocationMessageEventContent {
|
||||
/// A description of the location e.g. "Big Ben, London, UK", or some kind of content
|
||||
/// description for accessibility, e.g. "location attachment".
|
||||
pub body: String,
|
||||
|
||||
/// A geo URI representing the location.
|
||||
pub geo_uri: String,
|
||||
|
||||
/// Info about the location being represented.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub info: Option<Box<LocationInfo>>,
|
||||
|
||||
/// Extensible-event text representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `body` field.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
|
||||
/// Extensible-event location info of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `geo_uri` field.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
#[serde(rename = "org.matrix.msc3488.location", skip_serializing_if = "Option::is_none")]
|
||||
pub location: Option<LocationContent>,
|
||||
|
||||
/// Extensible-event asset this message refers to.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
#[serde(rename = "org.matrix.msc3488.asset", skip_serializing_if = "Option::is_none")]
|
||||
pub asset: Option<AssetContent>,
|
||||
|
||||
/// Extensible-event timestamp this message refers to.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
#[serde(rename = "org.matrix.msc3488.ts", skip_serializing_if = "Option::is_none")]
|
||||
pub ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||
}
|
||||
|
||||
impl LocationMessageEventContent {
|
||||
/// Creates a new `LocationMessageEventContent` with the given body and geo URI.
|
||||
pub fn new(body: String, geo_uri: String) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
location: Some(LocationContent::new(geo_uri.clone())),
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
asset: None,
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
ts: None,
|
||||
body,
|
||||
geo_uri,
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `LocationMessageEventContent` with the given message, location info, asset and
|
||||
/// timestamp.
|
||||
#[cfg(feature = "unstable-msc3488")]
|
||||
pub fn from_extensible_content(
|
||||
message: MessageContent,
|
||||
location: LocationContent,
|
||||
asset: AssetContent,
|
||||
ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||
) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() {
|
||||
body.to_owned()
|
||||
} else {
|
||||
message[0].body.clone()
|
||||
};
|
||||
let geo_uri = location.uri.clone();
|
||||
|
||||
Self {
|
||||
message: Some(message),
|
||||
location: Some(location),
|
||||
asset: Some(asset),
|
||||
ts,
|
||||
body,
|
||||
geo_uri,
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Thumbnail info associated with a location.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct LocationInfo {
|
||||
/// The source of a thumbnail of the location.
|
||||
#[serde(
|
||||
flatten,
|
||||
with = "crate::events::room::thumbnail_source_serde",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub thumbnail_source: Option<MediaSource>,
|
||||
|
||||
/// Metadata about the image referred to in `thumbnail_source.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thumbnail_info: Option<Box<ThumbnailInfo>>,
|
||||
}
|
||||
|
||||
impl LocationInfo {
|
||||
/// Creates an empty `LocationInfo`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
80
crates/ruma-common/src/events/room/message/notice.rs
Normal file
80
crates/ruma-common/src/events/room/message/notice.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
use crate::events::message::MessageContent;
|
||||
|
||||
use super::FormattedBody;
|
||||
|
||||
/// The payload for a notice message.
|
||||
///
|
||||
/// With the `unstable-msc1767` feature, this type contains the transitional format of
|
||||
/// [`NoticeEventContent`]. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [`NoticeEventContent`]: crate::events::notice::NoticeEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.notice")]
|
||||
pub struct NoticeMessageEventContent {
|
||||
/// The notice text.
|
||||
pub body: String,
|
||||
|
||||
/// Formatted form of the message `body`.
|
||||
#[serde(flatten)]
|
||||
pub formatted: Option<FormattedBody>,
|
||||
|
||||
/// Extensible-event representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the other fields.
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
}
|
||||
|
||||
impl NoticeMessageEventContent {
|
||||
/// A convenience constructor to create a plain text notice.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
let body = body.into();
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
body,
|
||||
formatted: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an html notice.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
let body = body.into();
|
||||
let html_body = html_body.into();
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
message: Some(MessageContent::html(body.clone(), html_body.clone())),
|
||||
body,
|
||||
formatted: Some(FormattedBody::html(html_body)),
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a markdown notice.
|
||||
///
|
||||
/// Returns an html notice if some markdown formatting was detected, otherwise returns a plain
|
||||
/// text notice.
|
||||
#[cfg(feature = "markdown")]
|
||||
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||
if let Some(formatted) = FormattedBody::markdown(&body) {
|
||||
Self::html(body, formatted.body)
|
||||
} else {
|
||||
Self::plain(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
impl From<MessageContent> for NoticeMessageEventContent {
|
||||
fn from(message: MessageContent) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() { body } else { &message[0].body };
|
||||
let formatted = message.find_html().map(FormattedBody::html);
|
||||
|
||||
Self { body: body.to_owned(), formatted, message: Some(message) }
|
||||
}
|
||||
}
|
63
crates/ruma-common/src/events/room/message/server_notice.rs
Normal file
63
crates/ruma-common/src/events/room/message/server_notice.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{serde::StringEnum, PrivOwnedStr};
|
||||
|
||||
/// The payload for a server notice message.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.server_notice")]
|
||||
pub struct ServerNoticeMessageEventContent {
|
||||
/// A human-readable description of the notice.
|
||||
pub body: String,
|
||||
|
||||
/// The type of notice being represented.
|
||||
pub server_notice_type: ServerNoticeType,
|
||||
|
||||
/// A URI giving a contact method for the server administrator.
|
||||
///
|
||||
/// Required if the notice type is `m.server_notice.usage_limit_reached`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub admin_contact: Option<String>,
|
||||
|
||||
/// The kind of usage limit the server has exceeded.
|
||||
///
|
||||
/// Required if the notice type is `m.server_notice.usage_limit_reached`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub limit_type: Option<LimitType>,
|
||||
}
|
||||
|
||||
impl ServerNoticeMessageEventContent {
|
||||
/// Creates a new `ServerNoticeMessageEventContent` with the given body and notice type.
|
||||
pub fn new(body: String, server_notice_type: ServerNoticeType) -> Self {
|
||||
Self { body, server_notice_type, admin_contact: None, limit_type: None }
|
||||
}
|
||||
}
|
||||
|
||||
/// Types of server notices.
|
||||
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
|
||||
#[non_exhaustive]
|
||||
pub enum ServerNoticeType {
|
||||
/// The server has exceeded some limit which requires the server administrator to intervene.
|
||||
#[ruma_enum(rename = "m.server_notice.usage_limit_reached")]
|
||||
UsageLimitReached,
|
||||
|
||||
#[doc(hidden)]
|
||||
_Custom(PrivOwnedStr),
|
||||
}
|
||||
|
||||
/// Types of usage limits.
|
||||
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
|
||||
#[ruma_enum(rename_all = "snake_case")]
|
||||
#[non_exhaustive]
|
||||
pub enum LimitType {
|
||||
/// The server's number of active users in the last 30 days has exceeded the maximum.
|
||||
///
|
||||
/// New connections are being refused by the server. What defines "active" is left as an
|
||||
/// implementation detail, however servers are encouraged to treat syncing users as "active".
|
||||
MonthlyActiveUser,
|
||||
|
||||
#[doc(hidden)]
|
||||
_Custom(PrivOwnedStr),
|
||||
}
|
80
crates/ruma-common/src/events/room/message/text.rs
Normal file
80
crates/ruma-common/src/events/room/message/text.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
use crate::events::message::MessageContent;
|
||||
|
||||
use super::FormattedBody;
|
||||
|
||||
/// The payload for a text message.
|
||||
///
|
||||
/// With the `unstable-msc1767` feature, this type contains the transitional format of
|
||||
/// [`MessageEventContent`]. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [`MessageEventContent`]: crate::events::message::MessageEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.text")]
|
||||
pub struct TextMessageEventContent {
|
||||
/// The body of the message.
|
||||
pub body: String,
|
||||
|
||||
/// Formatted form of the message `body`.
|
||||
#[serde(flatten)]
|
||||
pub formatted: Option<FormattedBody>,
|
||||
|
||||
/// Extensible-event representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the other fields.
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
}
|
||||
|
||||
impl TextMessageEventContent {
|
||||
/// A convenience constructor to create a plain text message.
|
||||
pub fn plain(body: impl Into<String>) -> Self {
|
||||
let body = body.into();
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
body,
|
||||
formatted: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create an HTML message.
|
||||
pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
|
||||
let body = body.into();
|
||||
let html_body = html_body.into();
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
message: Some(MessageContent::html(body.clone(), html_body.clone())),
|
||||
body,
|
||||
formatted: Some(FormattedBody::html(html_body)),
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience constructor to create a Markdown message.
|
||||
///
|
||||
/// Returns an HTML message if some Markdown formatting was detected, otherwise returns a plain
|
||||
/// text message.
|
||||
#[cfg(feature = "markdown")]
|
||||
pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
|
||||
if let Some(formatted) = FormattedBody::markdown(&body) {
|
||||
Self::html(body, formatted.body)
|
||||
} else {
|
||||
Self::plain(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-msc1767")]
|
||||
impl From<MessageContent> for TextMessageEventContent {
|
||||
fn from(message: MessageContent) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() { body } else { &message[0].body };
|
||||
let formatted = message.find_html().map(FormattedBody::html);
|
||||
|
||||
Self { body: body.to_owned(), formatted, message: Some(message) }
|
||||
}
|
||||
}
|
264
crates/ruma-common/src/events/room/message/video.rs
Normal file
264
crates/ruma-common/src/events/room/message/video.rs
Normal file
@ -0,0 +1,264 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use js_int::UInt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
use crate::events::{
|
||||
file::{FileContent, FileContentInfo},
|
||||
image::ThumbnailContent,
|
||||
message::MessageContent,
|
||||
video::VideoContent,
|
||||
};
|
||||
use crate::{
|
||||
events::room::{EncryptedFile, MediaSource, ThumbnailInfo},
|
||||
OwnedMxcUri,
|
||||
};
|
||||
|
||||
/// The payload for a video message.
|
||||
///
|
||||
/// With the `unstable-msc3553` feature, this type contains the transitional format of
|
||||
/// [`VideoEventContent`]. See the documentation of the [`message`] module for more information.
|
||||
///
|
||||
/// [`VideoEventContent`]: crate::events::video::VideoEventContent
|
||||
/// [`message`]: crate::events::message
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[serde(tag = "msgtype", rename = "m.video")]
|
||||
#[cfg_attr(
|
||||
feature = "unstable-msc3553",
|
||||
serde(from = "super::content_serde::VideoMessageEventContentDeHelper")
|
||||
)]
|
||||
pub struct VideoMessageEventContent {
|
||||
/// A description of the video, e.g. "Gangnam Style", or some kind of content description for
|
||||
/// accessibility, e.g. "video attachment".
|
||||
pub body: String,
|
||||
|
||||
/// The source of the video clip.
|
||||
#[serde(flatten)]
|
||||
pub source: MediaSource,
|
||||
|
||||
/// Metadata about the video clip referred to in `source`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub info: Option<Box<VideoInfo>>,
|
||||
|
||||
/// Extensible-event text representation of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `body` field.
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||
pub message: Option<MessageContent>,
|
||||
|
||||
/// Extensible-event file content of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `source` and `info` fields.
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
#[serde(rename = "org.matrix.msc1767.file", skip_serializing_if = "Option::is_none")]
|
||||
pub file: Option<FileContent>,
|
||||
|
||||
/// Extensible-event video info of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `info` field.
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
#[serde(rename = "org.matrix.msc1767.video", skip_serializing_if = "Option::is_none")]
|
||||
pub video: Option<Box<VideoContent>>,
|
||||
|
||||
/// Extensible-event thumbnails of the message.
|
||||
///
|
||||
/// If present, this should be preferred over the `info` field.
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
#[serde(rename = "org.matrix.msc1767.thumbnail", skip_serializing_if = "Option::is_none")]
|
||||
pub thumbnail: Option<Vec<ThumbnailContent>>,
|
||||
|
||||
/// Extensible-event captions of the message.
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
#[serde(
|
||||
rename = "org.matrix.msc1767.caption",
|
||||
with = "crate::events::message::content_serde::as_vec",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub caption: Option<MessageContent>,
|
||||
}
|
||||
|
||||
impl VideoMessageEventContent {
|
||||
/// Creates a new non-encrypted `VideoMessageEventContent` with the given body, url and
|
||||
/// optional extra info.
|
||||
pub fn plain(body: String, url: OwnedMxcUri, info: Option<Box<VideoInfo>>) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
file: Some(FileContent::plain(
|
||||
url.clone(),
|
||||
info.as_deref().map(|info| Box::new(info.into())),
|
||||
)),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
video: Some(Box::new(info.as_deref().map_or_else(VideoContent::default, Into::into))),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
thumbnail: info
|
||||
.as_deref()
|
||||
.and_then(|info| {
|
||||
ThumbnailContent::from_room_message_content(
|
||||
info.thumbnail_source.as_ref(),
|
||||
info.thumbnail_info.as_deref(),
|
||||
)
|
||||
})
|
||||
.map(|thumbnail| vec![thumbnail]),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
caption: None,
|
||||
body,
|
||||
source: MediaSource::Plain(url),
|
||||
info,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new encrypted `VideoMessageEventContent` with the given body and encrypted
|
||||
/// file.
|
||||
pub fn encrypted(body: String, file: EncryptedFile) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
message: Some(MessageContent::plain(body.clone())),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
file: Some(FileContent::encrypted(file.url.clone(), (&file).into(), None)),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
video: Some(Box::new(VideoContent::default())),
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
thumbnail: None,
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
caption: None,
|
||||
body,
|
||||
source: MediaSource::Encrypted(Box::new(file)),
|
||||
info: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `VideoMessageEventContent` with the given message, file info, video info,
|
||||
/// thumbnails and captions.
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
pub fn from_extensible_content(
|
||||
message: MessageContent,
|
||||
file: FileContent,
|
||||
video: Box<VideoContent>,
|
||||
thumbnail: Vec<ThumbnailContent>,
|
||||
caption: Option<MessageContent>,
|
||||
) -> Self {
|
||||
let body = if let Some(body) = message.find_plain() {
|
||||
body.to_owned()
|
||||
} else {
|
||||
message[0].body.clone()
|
||||
};
|
||||
let source = (&file).into();
|
||||
let info = VideoInfo::from_extensible_content(file.info.as_deref(), &video, &thumbnail)
|
||||
.map(Box::new);
|
||||
let thumbnail = if thumbnail.is_empty() { None } else { Some(thumbnail) };
|
||||
|
||||
Self {
|
||||
message: Some(message),
|
||||
file: Some(file),
|
||||
video: Some(video),
|
||||
thumbnail,
|
||||
caption,
|
||||
body,
|
||||
source,
|
||||
info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata about a video.
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub struct VideoInfo {
|
||||
/// The duration of the video in milliseconds.
|
||||
#[serde(
|
||||
with = "crate::serde::duration::opt_ms",
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub duration: Option<Duration>,
|
||||
|
||||
/// The height of the video in pixels.
|
||||
#[serde(rename = "h", skip_serializing_if = "Option::is_none")]
|
||||
pub height: Option<UInt>,
|
||||
|
||||
/// The width of the video in pixels.
|
||||
#[serde(rename = "w", skip_serializing_if = "Option::is_none")]
|
||||
pub width: Option<UInt>,
|
||||
|
||||
/// The mimetype of the video, e.g. "video/mp4".
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mimetype: Option<String>,
|
||||
|
||||
/// The size of the video in bytes.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub size: Option<UInt>,
|
||||
|
||||
/// Metadata about the image referred to in `thumbnail_source`.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub thumbnail_info: Option<Box<ThumbnailInfo>>,
|
||||
|
||||
/// The source of the thumbnail of the video clip.
|
||||
#[serde(
|
||||
flatten,
|
||||
with = "crate::events::room::thumbnail_source_serde",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub thumbnail_source: Option<MediaSource>,
|
||||
|
||||
/// The [BlurHash](https://blurha.sh) for this video.
|
||||
///
|
||||
/// This uses the unstable prefix in
|
||||
/// [MSC2448](https://github.com/matrix-org/matrix-spec-proposals/pull/2448).
|
||||
#[cfg(feature = "unstable-msc2448")]
|
||||
#[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")]
|
||||
pub blurhash: Option<String>,
|
||||
}
|
||||
|
||||
impl VideoInfo {
|
||||
/// Creates an empty `VideoInfo`.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Create a `VideoInfo` from the given file info, video info and thumbnail.
|
||||
#[cfg(feature = "unstable-msc3553")]
|
||||
pub fn from_extensible_content(
|
||||
file_info: Option<&FileContentInfo>,
|
||||
video: &VideoContent,
|
||||
thumbnail: &[ThumbnailContent],
|
||||
) -> Option<Self> {
|
||||
if file_info.is_none() && video.is_empty() && thumbnail.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let (mimetype, size) = file_info
|
||||
.map(|info| (info.mimetype.to_owned(), info.size.to_owned()))
|
||||
.unwrap_or_default();
|
||||
let VideoContent { duration, height, width } = video.to_owned();
|
||||
let (thumbnail_source, thumbnail_info) = thumbnail
|
||||
.get(0)
|
||||
.map(|thumbnail| {
|
||||
let source = (&thumbnail.file).into();
|
||||
let info = ThumbnailInfo::from_extensible_content(
|
||||
thumbnail.file.info.as_deref(),
|
||||
thumbnail.image.as_deref(),
|
||||
)
|
||||
.map(Box::new);
|
||||
(Some(source), info)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Some(Self {
|
||||
duration,
|
||||
height,
|
||||
width,
|
||||
mimetype,
|
||||
size,
|
||||
thumbnail_info,
|
||||
thumbnail_source,
|
||||
#[cfg(feature = "unstable-msc2448")]
|
||||
blurhash: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user