mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-06-21 22:58:32 -04:00
sdk: basic support for sending live location beacons
This commit is contained in:
@@ -521,9 +521,14 @@ pub enum BeaconError {
|
||||
#[error("Must join the room to access beacon information.")]
|
||||
Stripped,
|
||||
|
||||
// The beacon event could not be deserialized.
|
||||
#[error("Deserialization error: {0}")]
|
||||
Deserialization(#[from] serde_json::Error),
|
||||
|
||||
// The beacon event is expired.
|
||||
#[error("The beacon event has expired.")]
|
||||
NotLive,
|
||||
|
||||
// Allow for other errors to be wrapped.
|
||||
#[error("Other error: {0}")]
|
||||
Other(Box<Error>),
|
||||
|
||||
@@ -52,6 +52,7 @@ use ruma::{
|
||||
},
|
||||
assign,
|
||||
events::{
|
||||
beacon::BeaconEventContent,
|
||||
beacon_info::BeaconInfoEventContent,
|
||||
call::notify::{ApplicationType, CallNotifyEventContent, NotifyType},
|
||||
direct::DirectEventContent,
|
||||
@@ -72,10 +73,10 @@ use ruma::{
|
||||
tag::{TagInfo, TagName},
|
||||
typing::SyncTypingEvent,
|
||||
AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent, AnyTimelineEvent, EmptyStateKey,
|
||||
Mentions, MessageLikeEventContent, MessageLikeEventType, RedactContent,
|
||||
RedactedStateEventContent, RoomAccountDataEvent, RoomAccountDataEventContent,
|
||||
RoomAccountDataEventType, StateEventContent, StateEventType, StaticEventContent,
|
||||
StaticStateEventContent, SyncStateEvent,
|
||||
Mentions, MessageLikeEventContent, MessageLikeEventType, OriginalSyncStateEvent,
|
||||
RedactContent, RedactedStateEventContent, RoomAccountDataEvent,
|
||||
RoomAccountDataEventContent, RoomAccountDataEventType, StateEventContent, StateEventType,
|
||||
StaticEventContent, StaticStateEventContent, SyncStateEvent,
|
||||
},
|
||||
push::{Action, PushConditionRoomCtx},
|
||||
serde::Raw,
|
||||
@@ -2753,6 +2754,27 @@ impl Room {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the beacon information event in the room for the current user.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the event is redacted, stripped, not found or could
|
||||
/// not be deserialized.
|
||||
async fn get_user_beacon_info(
|
||||
&self,
|
||||
) -> Result<OriginalSyncStateEvent<BeaconInfoEventContent>, BeaconError> {
|
||||
let raw_event = self
|
||||
.get_state_event_static_for_key::<BeaconInfoEventContent, _>(self.own_user_id())
|
||||
.await?
|
||||
.ok_or(BeaconError::NotFound)?;
|
||||
|
||||
match raw_event.deserialize()? {
|
||||
SyncOrStrippedState::Sync(SyncStateEvent::Original(beacon_info)) => Ok(beacon_info),
|
||||
SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_)) => Err(BeaconError::Redacted),
|
||||
SyncOrStrippedState::Stripped(_) => Err(BeaconError::Stripped),
|
||||
}
|
||||
}
|
||||
|
||||
/// Start sharing live location in the room.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -2795,24 +2817,35 @@ impl Room {
|
||||
) -> Result<send_state_event::v3::Response, BeaconError> {
|
||||
self.ensure_room_joined()?;
|
||||
|
||||
if let Some(raw_event) = self
|
||||
.get_state_event_static_for_key::<BeaconInfoEventContent, _>(self.own_user_id())
|
||||
.await?
|
||||
{
|
||||
match raw_event.deserialize() {
|
||||
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Original(beacon_info))) => {
|
||||
let mut content = beacon_info.content.clone();
|
||||
content.stop();
|
||||
Ok(self.send_state_event_for_key(self.own_user_id(), content).await?)
|
||||
}
|
||||
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_))) => {
|
||||
Err(BeaconError::Redacted)
|
||||
}
|
||||
Ok(SyncOrStrippedState::Stripped(_)) => Err(BeaconError::Stripped),
|
||||
Err(e) => Err(BeaconError::Deserialization(e)),
|
||||
}
|
||||
let mut beacon_info_event = self.get_user_beacon_info().await?;
|
||||
beacon_info_event.content.stop();
|
||||
Ok(self.send_state_event_for_key(self.own_user_id(), beacon_info_event.content).await?)
|
||||
}
|
||||
|
||||
/// Send a location beacon event in the current room.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `geo_uri` - The geo URI of the location beacon.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the room is not joined, if the beacon information
|
||||
/// is redacted or stripped, if the location share is no longer live,
|
||||
/// or if the state event is not found.
|
||||
pub async fn send_location_beacon(
|
||||
&self,
|
||||
geo_uri: String,
|
||||
) -> Result<send_message_event::v3::Response, BeaconError> {
|
||||
self.ensure_room_joined()?;
|
||||
|
||||
let beacon_info_event = self.get_user_beacon_info().await?;
|
||||
|
||||
if beacon_info_event.content.is_live() {
|
||||
let content = BeaconEventContent::new(beacon_info_event.event_id, geo_uri, None);
|
||||
Ok(self.send(content).await?)
|
||||
} else {
|
||||
Err(BeaconError::NotFound)
|
||||
Err(BeaconError::NotLive)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
time::Duration,
|
||||
time::{Duration, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use futures_util::future::join_all;
|
||||
use js_int::uint;
|
||||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
instant::SystemTime,
|
||||
room::{edit::EditedContent, Receipts, ReportedContentScore, RoomMemberRole},
|
||||
test_utils::events::EventFactory,
|
||||
};
|
||||
@@ -37,7 +38,6 @@ use crate::{
|
||||
logged_in_client_with_server, mock_encryption_state, mock_sync, mock_sync_with_new_room,
|
||||
synced_client,
|
||||
};
|
||||
|
||||
#[async_test]
|
||||
async fn test_invite_user_by_id() {
|
||||
let (client, server) = logged_in_client_with_server().await;
|
||||
@@ -1003,3 +1003,95 @@ async fn test_stop_sharing_live_location() {
|
||||
|
||||
assert!(!content.live);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_send_location_beacon() {
|
||||
let (client, server) = logged_in_client_with_server().await;
|
||||
|
||||
// Validate request body and response, partial body matching due to
|
||||
// auto-generated `org.matrix.msc3488.ts`.
|
||||
Mock::given(method("PUT"))
|
||||
.and(path_regex(r"^/_matrix/client/r0/rooms/.*/send/org.matrix.msc3672.beacon/.*"))
|
||||
.and(header("authorization", "Bearer 1234"))
|
||||
.and(body_partial_json(json!({
|
||||
"m.relates_to": {
|
||||
"event_id": "$15139375514XsgmR:localhost",
|
||||
"rel_type": "m.reference"
|
||||
},
|
||||
"org.matrix.msc3488.location": {
|
||||
"uri": "geo:48.8588448,2.2943506"
|
||||
}
|
||||
})))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(&*test_json::EVENT_ID))
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let current_timestamp =
|
||||
SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_millis()
|
||||
as u64;
|
||||
|
||||
mock_sync(
|
||||
&server,
|
||||
json!({
|
||||
"next_batch": "s526_47314_0_7_1_1_1_1_1",
|
||||
"rooms": {
|
||||
"join": {
|
||||
*DEFAULT_TEST_ROOM_ID: {
|
||||
"state": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"description": "Live Share",
|
||||
"live": true,
|
||||
"org.matrix.msc3488.ts": current_timestamp,
|
||||
"timeout": 600_000,
|
||||
"org.matrix.msc3488.asset": { "type": "m.self" }
|
||||
},
|
||||
"event_id": "$15139375514XsgmR:localhost",
|
||||
"origin_server_ts": 1_636_829_458,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "@example:localhost",
|
||||
"type": "org.matrix.msc3672.beacon_info",
|
||||
"unsigned": {
|
||||
"age": 7034220
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
mock_encryption_state(&server, false).await;
|
||||
|
||||
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
||||
|
||||
client.sync_once(sync_settings).await.unwrap();
|
||||
|
||||
let room = client.get_room(&DEFAULT_TEST_ROOM_ID).unwrap();
|
||||
|
||||
let response = room.send_location_beacon("geo:48.8588448,2.2943506".to_owned()).await.unwrap();
|
||||
|
||||
assert_eq!(event_id!("$h29iv0s8:example.com"), response.event_id)
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_send_location_beacon_fails_without_starting_live_share() {
|
||||
let (client, server) = logged_in_client_with_server().await;
|
||||
|
||||
mock_sync(&server, &*test_json::SYNC, None).await;
|
||||
|
||||
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
||||
client.sync_once(sync_settings).await.unwrap();
|
||||
|
||||
let room = client.get_room(&DEFAULT_TEST_ROOM_ID).unwrap();
|
||||
|
||||
let result = room.send_location_beacon("geo:48.8588448,2.2943506".to_owned()).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user