tuwunel_api/client/report/
report_event.rs1use axum::extract::State;
2use ruma::{EventId, RoomId, UserId, api::client::room::report_content};
3use tuwunel_core::{Err, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
4use tuwunel_service::Services;
5
6use super::REASON_MAX_LEN;
7use crate::{ClientIp, Ruma};
8
9#[tracing::instrument(skip_all, fields(%client), name = "report_event")]
13pub(crate) async fn report_event_route(
14 State(services): State<crate::State>,
15 ClientIp(client): ClientIp,
16 body: Ruma<report_content::v3::Request>,
17) -> Result<report_content::v3::Response> {
18 let sender_user = body.sender_user();
19 let reason = body.reason.as_deref().unwrap_or("");
20
21 info!(
22 "Received event report by user {sender_user} for room {} and event ID {}, with reason: \
23 \"{}\"",
24 body.room_id, body.event_id, reason,
25 );
26
27 let Ok(pdu) = services.timeline.get_pdu(&body.event_id).await else {
29 return Err!(Request(NotFound("Event ID is not known to us or Event ID is invalid")));
30 };
31
32 is_event_report_valid(
33 &services,
34 &pdu.event_id,
35 &body.room_id,
36 sender_user,
37 body.reason.as_ref(),
38 &pdu,
39 )
40 .await?;
41
42 services
43 .admin
44 .send_report(&format!(
45 "@room Event report received from {}\nReport Reason: {}\n\nEvent ID: {}\nRoom ID: \
46 {}\nSent By: {}",
47 sender_user, reason, pdu.event_id, pdu.room_id, pdu.sender,
48 ))
49 .await;
50
51 Ok(report_content::v3::Response {})
52}
53
54async fn is_event_report_valid(
60 services: &Services,
61 event_id: &EventId,
62 room_id: &RoomId,
63 sender_user: &UserId,
64 reason: Option<&String>,
65 pdu: &PduEvent,
66) -> Result {
67 debug_info!(
68 "Checking if report from user {sender_user} for event {event_id} in room {room_id} is \
69 valid"
70 );
71
72 if room_id != pdu.room_id {
73 return Err!(Request(NotFound("Event ID does not belong to the reported room",)));
74 }
75
76 if reason
77 .as_ref()
78 .is_some_and(|s| s.len() > REASON_MAX_LEN)
79 {
80 return Err!(Request(InvalidParam(
81 "Reason too long, should be {REASON_MAX_LEN} characters or fewer"
82 )));
83 }
84
85 if !services
86 .state_cache
87 .room_members(room_id)
88 .ready_any(|user_id| user_id == sender_user)
89 .await
90 {
91 return Err!(Request(NotFound("You are not in the room you are reporting.",)));
92 }
93
94 Ok(())
95}