mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-15 13:16:56 +00:00
feat: Add timestamp to event endpoint
This commit is contained in:
1 parent
272f93a634
commit
d330930bb5
5 files changed
+138
-5
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
Added the `GET /_matrix/client/v1/rooms/{roomId}/timestamp_to_event` endpoint as per MSC3030, letting clients find the event closest to a given timestamp. Contributed by @eleboucher.
|
||||||
@@ -3,10 +3,11 @@ mod create;
|
|||||||
mod event;
|
mod event;
|
||||||
mod initial_sync;
|
mod initial_sync;
|
||||||
mod summary;
|
mod summary;
|
||||||
|
mod timestamp_to_event;
|
||||||
mod upgrade;
|
mod upgrade;
|
||||||
|
|
||||||
pub(crate) use self::{
|
pub(crate) use self::{
|
||||||
aliases::get_room_aliases_route, create::create_room_route, event::get_room_event_route,
|
aliases::get_room_aliases_route, create::create_room_route, event::get_room_event_route,
|
||||||
initial_sync::room_initial_sync_route, summary::get_room_summary,
|
initial_sync::room_initial_sync_route, summary::get_room_summary,
|
||||||
upgrade::upgrade_room_route,
|
timestamp_to_event::get_event_by_timestamp_route, upgrade::upgrade_room_route,
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduwuit::{Err, Event, Result, err};
|
||||||
|
use ruma::api::{Direction, client::room::get_event_by_timestamp};
|
||||||
|
|
||||||
|
use crate::Ruma;
|
||||||
|
|
||||||
|
/// # `GET /_matrix/client/v1/rooms/{roomId}/timestamp_to_event`
|
||||||
|
///
|
||||||
|
/// Gets the ID of the event closest to the given timestamp, in the direction
|
||||||
|
/// specified by `dir`.
|
||||||
|
pub(crate) async fn get_event_by_timestamp_route(
|
||||||
|
State(ref services): State<crate::State>,
|
||||||
|
body: Ruma<get_event_by_timestamp::v1::Request>,
|
||||||
|
) -> Result<get_event_by_timestamp::v1::Response> {
|
||||||
|
let sender_user = body.identity.expect_sender_user()?;
|
||||||
|
let room_id = &body.room_id;
|
||||||
|
|
||||||
|
if !services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.user_can_see_state_events(sender_user, room_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return Err!(Request(Forbidden("You don't have permission to view this room.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
let dir = match body.dir {
|
||||||
|
| Direction::Forward => "f",
|
||||||
|
| Direction::Backward => "b",
|
||||||
|
};
|
||||||
|
|
||||||
|
let not_found = || {
|
||||||
|
err!(Request(NotFound(
|
||||||
|
"Unable to find event from {} in direction {}",
|
||||||
|
body.ts.get(),
|
||||||
|
dir
|
||||||
|
)))
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some((_, event)) = services
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.event_by_timestamp(room_id, body.ts, body.dir)
|
||||||
|
.await
|
||||||
|
else {
|
||||||
|
return Err(not_found());
|
||||||
|
};
|
||||||
|
|
||||||
|
if !services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.user_can_see_event(sender_user, room_id, event.event_id())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
return Err!(Request(Forbidden("You don't have permission to view this event.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(get_event_by_timestamp::v1::Response::new(
|
||||||
|
event.event_id().to_owned(),
|
||||||
|
event.origin_server_ts(),
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ pub fn build(router: Router<State>, state: State) -> Router<State> {
|
|||||||
.ruma_route(&client::set_pushrule_actions_route)
|
.ruma_route(&client::set_pushrule_actions_route)
|
||||||
.ruma_route(&client::delete_pushrule_route)
|
.ruma_route(&client::delete_pushrule_route)
|
||||||
.ruma_route(&client::get_room_event_route)
|
.ruma_route(&client::get_room_event_route)
|
||||||
|
.ruma_route(&client::get_event_by_timestamp_route)
|
||||||
.ruma_route(&client::get_room_aliases_route)
|
.ruma_route(&client::get_room_aliases_route)
|
||||||
.ruma_route(&client::get_filter_route)
|
.ruma_route(&client::get_filter_route)
|
||||||
.ruma_route(&client::create_filter_route)
|
.ruma_route(&client::create_filter_route)
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ use conduwuit_core::{
|
|||||||
event::Event,
|
event::Event,
|
||||||
pdu::{PduCount, PduEvent},
|
pdu::{PduCount, PduEvent},
|
||||||
},
|
},
|
||||||
utils::{MutexMap, MutexMapGuard, future::TryExtExt, stream::TryIgnore},
|
utils::{MutexMap, MutexMapGuard, ReadyExt, future::TryExtExt, stream::TryIgnore},
|
||||||
warn,
|
warn,
|
||||||
};
|
};
|
||||||
use futures::{Future, Stream, TryStreamExt, pin_mut};
|
use futures::{Future, Stream, StreamExt, TryStreamExt, pin_mut};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, EventId, OwnedEventId, OwnedRoomId, RoomId,
|
CanonicalJsonObject, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, RoomId,
|
||||||
events::room::encrypted::Relation,
|
api::Direction, events::room::encrypted::Relation,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
@@ -272,4 +272,72 @@ impl Service {
|
|||||||
) -> impl Stream<Item = Result<PdusIterItem>> + Send + 'a {
|
) -> impl Stream<Item = Result<PdusIterItem>> + Send + 'a {
|
||||||
self.db.pdus(room_id, from.unwrap_or_else(PduCount::min))
|
self.db.pdus(room_id, from.unwrap_or_else(PduCount::min))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn pdu_at_or_after(&self, room_id: &RoomId, count: PduCount) -> Option<PdusIterItem> {
|
||||||
|
let pdus = self
|
||||||
|
.pdus(room_id, Some(count.saturating_sub(1)))
|
||||||
|
.ignore_err()
|
||||||
|
.ready_filter(move |(at, _)| *at >= count);
|
||||||
|
|
||||||
|
pin_mut!(pdus);
|
||||||
|
pdus.next().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn pdu_at_or_before(&self, room_id: &RoomId, count: PduCount) -> Option<PdusIterItem> {
|
||||||
|
let pdus = self
|
||||||
|
.pdus_rev(room_id, Some(count.saturating_add(1)))
|
||||||
|
.ignore_err()
|
||||||
|
.ready_filter(move |(at, _)| *at <= count);
|
||||||
|
|
||||||
|
pin_mut!(pdus);
|
||||||
|
pdus.next().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn event_by_timestamp(
|
||||||
|
&self,
|
||||||
|
room_id: &RoomId,
|
||||||
|
ts: MilliSecondsSinceUnixEpoch,
|
||||||
|
dir: Direction,
|
||||||
|
) -> Option<PdusIterItem> {
|
||||||
|
let first = self.pdu_at_or_after(room_id, PduCount::min()).await?.0;
|
||||||
|
let last = self.pdu_at_or_before(room_id, PduCount::max()).await?.0;
|
||||||
|
let mut low = first.into_signed();
|
||||||
|
let mut high = last.into_signed();
|
||||||
|
let mut result = None;
|
||||||
|
|
||||||
|
match dir {
|
||||||
|
| Direction::Forward =>
|
||||||
|
while low <= high {
|
||||||
|
let count = PduCount::from_signed(low.midpoint(high));
|
||||||
|
let Some((at, pdu)) = self.pdu_at_or_after(room_id, count).await else {
|
||||||
|
high = count.into_signed().saturating_sub(1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if pdu.origin_server_ts() >= ts {
|
||||||
|
result = Some((at, pdu));
|
||||||
|
high = at.into_signed().saturating_sub(1);
|
||||||
|
} else {
|
||||||
|
low = at.into_signed().saturating_add(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
| Direction::Backward =>
|
||||||
|
while low <= high {
|
||||||
|
let count = PduCount::from_signed(low.midpoint(high));
|
||||||
|
let Some((at, pdu)) = self.pdu_at_or_before(room_id, count).await else {
|
||||||
|
low = count.into_signed().saturating_add(1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if pdu.origin_server_ts() <= ts {
|
||||||
|
result = Some((at, pdu));
|
||||||
|
low = at.into_signed().saturating_add(1);
|
||||||
|
} else {
|
||||||
|
high = at.into_signed().saturating_sub(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user