2022-10-05 20:34:31 +02:00
|
|
|
use std::collections::BTreeMap;
|
2024-03-05 19:48:54 -05:00
|
|
|
|
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
|
|
|
};
|
2024-03-24 12:53:32 -04:00
|
|
|
use tracing::debug;
|
2020-08-18 12:15:27 +02:00
|
|
|
|
|
|
|
use crate::{services, Error, Result, Ruma};
|
|
|
|
|
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-04-22 23:48:57 -04:00
|
|
|
pub(crate) async fn search_events_route(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-03-25 17:05:11 -04:00
|
|
|
let room_ids = filter.rooms.clone().unwrap_or_else(|| {
|
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.rooms_joined(sender_user)
|
|
|
|
.filter_map(Result::ok)
|
|
|
|
.collect()
|
|
|
|
});
|
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-03-25 17:05:11 -04:00
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.is_joined(sender_user, room_id)?
|
|
|
|
{
|
2024-03-24 12:53:32 -04:00
|
|
|
return Err(Error::BadRequest(
|
2024-04-03 15:59:03 -04:00
|
|
|
ErrorKind::forbidden(),
|
2024-03-24 12:53:32 -04:00
|
|
|
"You don't have permission to view this room.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if sender_user can see state events
|
2024-03-25 17:05:11 -04:00
|
|
|
if services()
|
|
|
|
.rooms
|
|
|
|
.state_accessor
|
|
|
|
.user_can_see_state_events(sender_user, room_id)?
|
|
|
|
{
|
2024-03-24 12:53:32 -04:00
|
|
|
let room_state = services()
|
|
|
|
.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(
|
2024-04-03 15:59:03 -04:00
|
|
|
ErrorKind::forbidden(),
|
2024-03-24 12:53:32 -04:00
|
|
|
"You don't have permission to view this room.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 15:20:37 +02:00
|
|
|
let mut searches = Vec::new();
|
|
|
|
|
2024-03-24 12:53:32 -04:00
|
|
|
for room_id in &room_ids {
|
2024-03-25 17:05:11 -04:00
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.is_joined(sender_user, room_id)?
|
|
|
|
{
|
2021-06-21 15:20:37 +02:00
|
|
|
return Err(Error::BadRequest(
|
2024-04-03 15:59:03 -04:00
|
|
|
ErrorKind::forbidden(),
|
2021-06-21 15:20:37 +02:00
|
|
|
"You don't have permission to view this room.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2024-03-25 17:05:11 -04:00
|
|
|
if let Some(search) = services()
|
|
|
|
.rooms
|
|
|
|
.search
|
|
|
|
.search_pdus(room_id, &search_criteria.search_term)?
|
|
|
|
{
|
2022-02-04 17:15:21 +01:00
|
|
|
searches.push(search.0.peekable());
|
|
|
|
}
|
2020-08-18 12:15:27 +02:00
|
|
|
}
|
|
|
|
|
2024-05-03 21:42:47 -04:00
|
|
|
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
|
|
|
|
};
|
|
|
|
|
2021-06-21 15:20:37 +02:00
|
|
|
let mut results = Vec::new();
|
2024-05-06 02:40:16 -04:00
|
|
|
let next_batch: usize = skip.saturating_add(limit);
|
|
|
|
|
|
|
|
for _ in 0..next_batch {
|
2021-06-21 15:20:37 +02:00
|
|
|
if let Some(s) = searches
|
|
|
|
.iter_mut()
|
|
|
|
.map(|s| (s.peek().cloned(), s))
|
|
|
|
.max_by_key(|(peek, _)| peek.clone())
|
|
|
|
.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
|
2021-06-21 15:20:37 +02:00
|
|
|
.iter()
|
2023-02-21 16:38:50 +01:00
|
|
|
.filter_map(|result| {
|
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.timeline
|
|
|
|
.get_pdu_from_id(result)
|
|
|
|
.ok()?
|
2023-02-22 15:49:55 +01:00
|
|
|
.filter(|pdu| {
|
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.state_accessor
|
|
|
|
.user_can_see_event(sender_user, &pdu.room_id, &pdu.event_id)
|
|
|
|
.unwrap_or(false)
|
|
|
|
})
|
2023-02-21 16:38:50 +01:00
|
|
|
.map(|pdu| pdu.to_room_event())
|
|
|
|
})
|
2020-08-18 12:15:27 +02:00
|
|
|
.map(|result| {
|
|
|
|
Ok::<_, Error>(SearchResult {
|
2020-09-08 17:32:03 +02:00
|
|
|
context: EventContextResult {
|
|
|
|
end: None,
|
|
|
|
events_after: Vec::new(),
|
|
|
|
events_before: Vec::new(),
|
|
|
|
profile_info: BTreeMap::new(),
|
|
|
|
start: None,
|
|
|
|
},
|
2020-08-18 12:15:27 +02:00
|
|
|
rank: None,
|
2023-02-21 16:38:50 +01:00
|
|
|
result: Some(result),
|
2020-08-18 12:15:27 +02:00
|
|
|
})
|
|
|
|
})
|
2024-03-22 21:51:21 -04:00
|
|
|
.filter_map(Result::ok)
|
2020-08-18 12:15:27 +02:00
|
|
|
.skip(skip)
|
|
|
|
.take(limit)
|
2021-10-13 11:51:30 +02:00
|
|
|
.collect();
|
2020-08-18 12:15:27 +02:00
|
|
|
|
2022-10-31 09:31:17 +01:00
|
|
|
let next_batch = if results.len() < limit {
|
2020-08-18 12:15:27 +02:00
|
|
|
None
|
|
|
|
} else {
|
2024-05-06 02:40:16 -04:00
|
|
|
Some(next_batch.to_string())
|
2020-08-18 12:15:27 +02:00
|
|
|
};
|
|
|
|
|
2022-02-18 19:54:26 +01: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
|
2020-08-19 18:26:39 +02:00
|
|
|
next_batch,
|
|
|
|
results,
|
2024-03-24 12:53:32 -04:00
|
|
|
state: room_states,
|
2021-06-21 15:20:37 +02:00
|
|
|
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
|
|
|
}
|