Files
continuwuity/src/api/client/search.rs
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

201 lines
4.6 KiB
Rust
Raw Normal View History

2022-10-05 20:34:31 +02:00
use std::collections::BTreeMap;
2024-03-05 19:48:54 -05:00
2024-07-16 08:05:25 +00:00
use axum::extract::State;
2024-08-08 17:18:30 +00:00
use conduit::{
debug,
utils::{IterStream, ReadyExt},
Err,
};
use futures::{FutureExt, StreamExt};
2024-03-24 12:53:32 -04:00
use ruma::{
api::client::{
error::ErrorKind,
search::search_events::{
self,
v3::{EventContextResult, ResultCategories, ResultRoomEvents, SearchResult},
},
2022-02-18 15:33:14 +01:00
},
2024-03-24 12:53:32 -04:00
events::AnyStateEvent,
serde::Raw,
2024-05-04 09:45:37 -04:00
uint, OwnedRoomId,
2022-02-18 15:33:14 +01:00
};
2020-08-18 12:15:27 +02:00
2024-07-16 08:05:25 +00:00
use crate::{Error, Result, Ruma};
2020-08-18 12:15:27 +02:00
2021-08-31 19:14:37 +02:00
/// # `POST /_matrix/client/r0/search`
///
/// Searches rooms for messages.
///
/// - Only works if the user is currently joined to the room (TODO: Respect
/// history visibility)
2024-07-16 08:05:25 +00:00
pub(crate) async fn search_events_route(
State(services): State<crate::State>, body: Ruma<search_events::v3::Request>,
) -> Result<search_events::v3::Response> {
2020-10-18 20:33:12 +02:00
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
2020-08-18 12:15:27 +02:00
let search_criteria = body.search_categories.room_events.as_ref().unwrap();
2022-02-12 02:06:30 +01:00
let filter = &search_criteria.filter;
2024-03-24 12:53:32 -04:00
let include_state = &search_criteria.include_state;
2020-08-21 21:22:59 +02:00
2024-08-08 17:18:30 +00:00
let room_ids = if let Some(room_ids) = &filter.rooms {
room_ids.clone()
} else {
2024-07-16 08:05:25 +00:00
services
2024-03-25 17:05:11 -04:00
.rooms
.state_cache
.rooms_joined(sender_user)
2024-08-08 17:18:30 +00:00
.map(ToOwned::to_owned)
2024-03-25 17:05:11 -04:00
.collect()
2024-08-08 17:18:30 +00:00
.await
};
2020-08-18 12:15:27 +02:00
2023-07-02 16:06:54 +02:00
// Use limit or else 10, with maximum 100
2024-05-04 09:45:37 -04:00
let limit: usize = filter
.limit
.unwrap_or_else(|| uint!(10))
.try_into()
.unwrap_or(10)
.min(100);
2020-08-18 12:15:27 +02:00
2024-03-24 12:53:32 -04:00
let mut room_states: BTreeMap<OwnedRoomId, Vec<Raw<AnyStateEvent>>> = BTreeMap::new();
if include_state.is_some_and(|include_state| include_state) {
for room_id in &room_ids {
2024-08-08 17:18:30 +00:00
if !services
.rooms
.state_cache
.is_joined(sender_user, room_id)
.await
{
return Err!(Request(Forbidden("You don't have permission to view this room.")));
2024-03-24 12:53:32 -04:00
}
// check if sender_user can see state events
2024-07-16 08:05:25 +00:00
if services
2024-03-25 17:05:11 -04:00
.rooms
.state_accessor
2024-08-08 17:18:30 +00:00
.user_can_see_state_events(sender_user, room_id)
.await
2024-03-25 17:05:11 -04:00
{
2024-07-16 08:05:25 +00:00
let room_state = services
2024-03-24 12:53:32 -04:00
.rooms
.state_accessor
.room_state_full(room_id)
.await?
.values()
.map(|pdu| pdu.to_state_event())
.collect::<Vec<_>>();
debug!("Room state: {:?}", room_state);
room_states.insert(room_id.clone(), room_state);
} else {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
2024-03-24 12:53:32 -04:00
"You don't have permission to view this room.",
));
}
}
}
2024-08-08 17:18:30 +00:00
let mut search_vecs = Vec::new();
2024-03-24 12:53:32 -04:00
for room_id in &room_ids {
2024-08-08 17:18:30 +00:00
if !services
.rooms
.state_cache
.is_joined(sender_user, room_id)
.await
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"You don't have permission to view this room.",
));
}
2024-07-16 08:05:25 +00:00
if let Some(search) = services
2024-03-25 17:05:11 -04:00
.rooms
.search
2024-08-08 17:18:30 +00:00
.search_pdus(room_id, &search_criteria.search_term)
.await
2024-03-25 17:05:11 -04:00
{
2024-08-08 17:18:30 +00:00
search_vecs.push(search.0);
2022-02-04 17:15:21 +01:00
}
2020-08-18 12:15:27 +02:00
}
2024-08-08 17:18:30 +00:00
let mut searches: Vec<_> = search_vecs
.iter()
.map(|vec| vec.iter().peekable())
.collect();
let skip: usize = match body.next_batch.as_ref().map(|s| s.parse()) {
2020-08-18 12:15:27 +02:00
Some(Ok(s)) => s,
Some(Err(_)) => return Err(Error::BadRequest(ErrorKind::InvalidParam, "Invalid next_batch token.")),
None => 0, // Default to the start
};
let mut results = Vec::new();
2024-06-30 23:26:59 -04:00
let next_batch = skip.saturating_add(limit);
for _ in 0..next_batch {
if let Some(s) = searches
.iter_mut()
2024-08-08 17:18:30 +00:00
.map(|s| (s.peek().copied(), s))
.max_by_key(|(peek, _)| *peek)
.and_then(|(_, i)| i.next())
{
results.push(s);
}
}
2020-08-18 12:15:27 +02:00
2021-10-13 11:51:30 +02:00
let results: Vec<_> = results
2024-08-08 17:18:30 +00:00
.into_iter()
2024-06-12 00:17:03 -04:00
.skip(skip)
2024-08-08 17:18:30 +00:00
.stream()
.filter_map(|id| services.rooms.timeline.get_pdu_from_id(id).map(Result::ok))
.ready_filter(|pdu| !pdu.is_redacted())
.filter_map(|pdu| async move {
2024-07-16 08:05:25 +00:00
services
.rooms
2024-08-08 17:18:30 +00:00
.state_accessor
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
.await
.then_some(pdu)
2020-08-18 12:15:27 +02:00
})
.take(limit)
2024-08-08 17:18:30 +00:00
.map(|pdu| pdu.to_room_event())
.map(|result| SearchResult {
context: EventContextResult {
end: None,
events_after: Vec::new(),
events_before: Vec::new(),
profile_info: BTreeMap::new(),
start: None,
},
rank: None,
result: Some(result),
})
.collect()
.boxed()
.await;
2020-08-18 12:15:27 +02:00
2024-06-12 00:14:36 -04:00
let more_unloaded_results = searches.iter_mut().any(|s| s.peek().is_some());
2024-08-08 17:18:30 +00:00
2024-06-12 00:14:36 -04:00
let next_batch = more_unloaded_results.then(|| next_batch.to_string());
2020-08-18 12:15:27 +02:00
Ok(search_events::v3::Response::new(ResultCategories {
2020-09-08 17:32:03 +02:00
room_events: ResultRoomEvents {
2024-05-04 09:45:37 -04:00
count: Some(results.len().try_into().unwrap_or_else(|_| uint!(0))),
2024-03-23 23:40:09 -04:00
groups: BTreeMap::new(), // TODO
next_batch,
results,
2024-03-24 12:53:32 -04:00
state: room_states,
highlights: search_criteria
.search_term
.split_terminator(|c: char| !c.is_alphanumeric())
.map(str::to_lowercase)
2021-10-13 11:51:30 +02:00
.collect(),
2020-09-08 17:32:03 +02:00
},
2022-01-22 16:58:32 +01:00
}))
2020-08-18 12:15:27 +02:00
}