From 9b870cd9af7e8418e578d08270d4328cac7fa906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= <76261501+zecakeh@users.noreply.github.com> Date: Fri, 18 Mar 2022 12:13:26 +0100 Subject: [PATCH] common: Add support for voice message events --- crates/ruma-common/Cargo.toml | 1 + crates/ruma-common/src/events.rs | 2 + crates/ruma-common/src/events/enums.rs | 4 + crates/ruma-common/src/events/voice.rs | 72 ++++++++++++ crates/ruma-common/tests/events/mod.rs | 1 + crates/ruma-common/tests/events/voice.rs | 143 +++++++++++++++++++++++ crates/ruma/Cargo.toml | 2 + 7 files changed, 225 insertions(+) create mode 100644 crates/ruma-common/src/events/voice.rs create mode 100644 crates/ruma-common/tests/events/voice.rs diff --git a/crates/ruma-common/Cargo.toml b/crates/ruma-common/Cargo.toml index 2617e3a6..2589b105 100644 --- a/crates/ruma-common/Cargo.toml +++ b/crates/ruma-common/Cargo.toml @@ -33,6 +33,7 @@ unstable-msc2448 = [] unstable-msc2675 = [] unstable-msc2676 = [] unstable-msc2677 = [] +unstable-msc3245 = ["unstable-msc3246"] unstable-msc3246 = ["unstable-msc3551", "thiserror"] unstable-msc3440 = [] unstable-msc3488 = ["unstable-msc1767"] diff --git a/crates/ruma-common/src/events.rs b/crates/ruma-common/src/events.rs index 30ec011b..75e2b141 100644 --- a/crates/ruma-common/src/events.rs +++ b/crates/ruma-common/src/events.rs @@ -185,6 +185,8 @@ pub mod tag; pub mod typing; #[cfg(feature = "unstable-msc3553")] pub mod video; +#[cfg(feature = "unstable-msc3245")] +pub mod voice; #[cfg(feature = "unstable-msc2675")] pub use self::relation::Relations; diff --git a/crates/ruma-common/src/events/enums.rs b/crates/ruma-common/src/events/enums.rs index d9e0ffd8..46fbb63e 100644 --- a/crates/ruma-common/src/events/enums.rs +++ b/crates/ruma-common/src/events/enums.rs @@ -67,6 +67,8 @@ event_enum! { "m.sticker", #[cfg(feature = "unstable-msc3553")] "m.video", + #[cfg(feature = "unstable-msc3245")] + "m.voice", } /// Any state event. @@ -374,6 +376,8 @@ impl AnyMessageLikeEventContent { Self::Notice(ev) => ev.relates_to.clone().map(Into::into), #[cfg(feature = "unstable-msc1767")] Self::Emote(ev) => ev.relates_to.clone().map(Into::into), + #[cfg(feature = "unstable-msc3245")] + Self::Voice(ev) => ev.relates_to.clone().map(Into::into), #[cfg(feature = "unstable-msc3246")] Self::Audio(ev) => ev.relates_to.clone().map(Into::into), #[cfg(feature = "unstable-msc3488")] diff --git a/crates/ruma-common/src/events/voice.rs b/crates/ruma-common/src/events/voice.rs new file mode 100644 index 00000000..0cb4b51b --- /dev/null +++ b/crates/ruma-common/src/events/voice.rs @@ -0,0 +1,72 @@ +//! Types for voice message events ([MSC3245]). +//! +//! [MSC3245]: https://github.com/matrix-org/matrix-spec-proposals/pull/3245 + +use ruma_macros::EventContent; +use serde::{Deserialize, Serialize}; + +use super::{ + audio::AudioContent, file::FileContent, message::MessageContent, room::message::Relation, +}; + +/// The payload for an extensible audio message. +#[derive(Clone, Debug, Serialize, Deserialize, EventContent)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +#[ruma_event(type = "m.voice", kind = MessageLike)] +pub struct VoiceEventContent { + /// The text representation of the message. + #[serde(flatten)] + pub message: MessageContent, + + /// The file content of the message. + #[serde(rename = "org.matrix.msc1767.file")] + pub file: FileContent, + + /// The audio content of the message. + #[serde(rename = "org.matrix.msc1767.audio")] + pub audio: AudioContent, + + /// The audio content of the message. + #[serde(rename = "org.matrix.msc3245.voice")] + pub voice: VoiceContent, + + /// Information about related messages. + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub relates_to: Option, +} + +impl VoiceEventContent { + /// Creates a new `VoiceEventContent` with the given plain text representation and file. + pub fn plain(message: impl Into, file: FileContent) -> Self { + Self { + message: MessageContent::plain(message), + file, + audio: Default::default(), + voice: Default::default(), + relates_to: None, + } + } + + /// Creates a new `VoiceEventContent` with the given message and file. + pub fn with_message(message: MessageContent, file: FileContent) -> Self { + Self { + message, + file, + audio: Default::default(), + voice: Default::default(), + relates_to: None, + } + } +} + +/// Voice content. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +pub struct VoiceContent {} + +impl VoiceContent { + /// Creates a new empty `VoiceContent`. + pub fn new() -> Self { + Self::default() + } +} diff --git a/crates/ruma-common/tests/events/mod.rs b/crates/ruma-common/tests/events/mod.rs index ffe8468e..dc500bae 100644 --- a/crates/ruma-common/tests/events/mod.rs +++ b/crates/ruma-common/tests/events/mod.rs @@ -22,3 +22,4 @@ mod state_event; mod stripped; mod to_device; mod video; +mod voice; diff --git a/crates/ruma-common/tests/events/voice.rs b/crates/ruma-common/tests/events/voice.rs new file mode 100644 index 00000000..65ece0e7 --- /dev/null +++ b/crates/ruma-common/tests/events/voice.rs @@ -0,0 +1,143 @@ +#![cfg(feature = "unstable-msc3245")] + +use std::time::Duration; + +use assign::assign; +use js_int::uint; +use matches::assert_matches; +use ruma_common::{ + event_id, + events::{ + audio::AudioContent, + file::{FileContent, FileContentInfo}, + room::message::{InReplyTo, Relation}, + voice::VoiceEventContent, + AnyMessageLikeEvent, MessageLikeEvent, Unsigned, + }, + mxc_uri, room_id, user_id, MilliSecondsSinceUnixEpoch, +}; +use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; + +#[test] +fn event_serialization() { + let event = MessageLikeEvent { + content: assign!( + VoiceEventContent::plain( + "Voice message", + FileContent::plain( + mxc_uri!("mxc://notareal.hs/abcdef").to_owned(), + Some(Box::new(assign!( + FileContentInfo::new(), + { + name: Some("voice_message.ogg".to_owned()), + mimetype: Some("audio/opus".to_owned()), + size: Some(uint!(897_774)), + } + ))), + ) + ), + { + audio: assign!( + AudioContent::new(), + { + duration: Some(Duration::from_secs(23)) + } + ), + relates_to: Some(Relation::Reply { + in_reply_to: InReplyTo::new(event_id!("$replyevent:example.com").to_owned()), + }), + } + ), + event_id: event_id!("$event:notareal.hs").to_owned(), + sender: user_id!("@user:notareal.hs").to_owned(), + origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(134_829_848)), + room_id: room_id!("!roomid:notareal.hs").to_owned(), + unsigned: Unsigned::default(), + }; + + assert_eq!( + to_json_value(&event).unwrap(), + json!({ + "content": { + "org.matrix.msc1767.text": "Voice message", + "org.matrix.msc1767.file": { + "url": "mxc://notareal.hs/abcdef", + "name": "voice_message.ogg", + "mimetype": "audio/opus", + "size": 897_774, + }, + "org.matrix.msc1767.audio": { + "duration": 23_000, + }, + "org.matrix.msc3245.voice": {}, + "m.relates_to": { + "m.in_reply_to": { + "event_id": "$replyevent:example.com" + } + } + }, + "event_id": "$event:notareal.hs", + "origin_server_ts": 134_829_848, + "room_id": "!roomid:notareal.hs", + "sender": "@user:notareal.hs", + "type": "m.voice", + }) + ); +} + +#[test] +fn message_event_deserialization() { + let json_data = json!({ + "content": { + "org.matrix.msc1767.text": "Voice message", + "org.matrix.msc1767.file": { + "url": "mxc://notareal.hs/abcdef", + "name": "voice_message.ogg", + "mimetype": "audio/opus", + "size": 123_774, + }, + "org.matrix.msc1767.audio": { + "duration": 5_300, + }, + "org.matrix.msc3245.voice": {}, + }, + "event_id": "$event:notareal.hs", + "origin_server_ts": 134_829_848, + "room_id": "!roomid:notareal.hs", + "sender": "@user:notareal.hs", + "type": "m.voice", + }); + + assert_matches!( + from_json_value::(json_data).unwrap(), + AnyMessageLikeEvent::Voice(MessageLikeEvent { + content: VoiceEventContent { + message, + file: FileContent { + url, + info: Some(info), + .. + }, + audio, + .. + }, + event_id, + origin_server_ts, + room_id, + sender, + unsigned + }) if event_id == event_id!("$event:notareal.hs") + && message.find_plain() == Some("Voice message") + && message.find_html().is_none() + && url == "mxc://notareal.hs/abcdef" + && info.name.as_deref() == Some("voice_message.ogg") + && info.mimetype.as_deref() == Some("audio/opus") + && info.size == Some(uint!(123_774)) + && audio.duration == Some(Duration::from_millis(5_300)) + && audio.waveform.is_none() + && origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(134_829_848)) + && room_id == room_id!("!roomid:notareal.hs") + && sender == user_id!("@user:notareal.hs") + && unsigned.is_empty() + ); +} diff --git a/crates/ruma/Cargo.toml b/crates/ruma/Cargo.toml index 09d1df06..05ef01fa 100644 --- a/crates/ruma/Cargo.toml +++ b/crates/ruma/Cargo.toml @@ -117,6 +117,7 @@ unstable-msc2448 = [ unstable-msc2675 = ["ruma-common/unstable-msc2675"] unstable-msc2676 = ["ruma-common/unstable-msc2676"] unstable-msc2677 = ["ruma-common/unstable-msc2677"] +unstable-msc3245 = ["ruma-common/unstable-msc3245"] unstable-msc3246 = ["ruma-common/unstable-msc3246"] unstable-msc3440 = [ "ruma-client-api/unstable-msc3440", @@ -142,6 +143,7 @@ __ci = [ "unstable-msc2675", "unstable-msc2676", "unstable-msc2677", + "unstable-msc3245", "unstable-msc3246", "unstable-msc3440", "unstable-msc3488",