1use arminius_lib_component::{
8 Component, ServerState,
9 error::{
10 FeatureNotImplemented, InputJsonError, InternalServerError, MatrixApiError, ResultExt as _,
11 },
12 ruma_requests::ArminiusResponse,
13};
14use arminius_lib_db::DefaultDb;
15use arminius_lib_db_room::RoomQueries as _;
16use arminius_lib_db_user::UserQueries as _;
17use arminius_lib_macros::FromNatsError;
18use arminius_lib_room_service_messages::EventProvenance;
19use arminius_lib_roomservice_client::{
20 requests::{
21 AddNewEvent, AddNewEventError, AddRoomMemberEvent, AddRoomMemberEventError,
22 AddRoomMemberEventRequest, CreateRoom, CreateRoomRequest, ErrorOf, ForgetRoom,
23 GetRoomEvent, GetRoomMembers, GetStateEvents, GetStateEventsForKey, NewEvent,
24 ReadMarkerError, ReadMarkerRequest, ReadReceipt, ReadReceiptError, ReadReceiptRequest,
25 RedactEvent, SetFullyReadMarker, TransactionInformation,
26 },
27 roomserver_connection::RoomServerConnection,
28 ruma_request_wrapper::SerializingRumaRequestWrapper,
29};
30use axum::{
31 Router,
32 extract::State,
33 routing::{get, post, put},
34};
35use ruma::{
36 OwnedRoomId, OwnedServerName, OwnedUserId, RoomId,
37 api::client::{
38 knock::knock_room,
39 membership::{
40 ban_user, forget_room, get_member_events,
41 invite_user::{
42 self,
43 v3::{InvitationRecipient, InviteUserId},
44 },
45 join_room_by_id, join_room_by_id_or_alias, joined_rooms, kick_user, leave_room,
46 unban_user,
47 },
48 message::send_message_event,
49 read_marker::set_read_marker,
50 receipt::create_receipt,
51 redact::redact_event,
52 room::{create_room, get_room_event},
53 state::{get_state_event_for_key, get_state_events, send_state_event},
54 },
55 events::{
56 receipt::ReceiptType,
57 room::member::{MembershipState, RoomMemberEventContent},
58 },
59};
60use serde::{Deserialize, Serialize};
61
62use crate::client_request::AuthenticatedClientRequest;
63
64fn append_server_to_via(mut via: Vec<OwnedServerName>, room_id: &RoomId) -> Vec<OwnedServerName> {
66 if let Some(room_server_name) = room_id.server_name()
67 && !via.iter().any(|server_name| server_name == room_server_name)
68 {
69 via.push(room_server_name.to_owned());
70 }
71 via
72}
73
74async fn create_member_event_content(
78 mut database: DefaultDb,
79 membership: MembershipState,
80 user_id: OwnedUserId,
81 reason: Option<String>,
82) -> Result<RoomMemberEventContent, InternalServerError> {
83 let mut event_content = RoomMemberEventContent::new(membership);
84 if let Some(user_info) = database
88 .fetch_user(&user_id)
89 .await
90 .unrecoverable_error("Could not fetch user from database")?
91 {
92 event_content.displayname = user_info.display_name;
93 }
94
95 event_content.reason = reason;
96 Ok(event_content)
97}
98
99async fn create_room_handler(
104 State(state): State<ServerState>,
105 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
106 create_room::v3::Request,
107 >,
108) -> Result<ArminiusResponse<create_room::v3::Response>, ErrorOf<CreateRoom>> {
109 let request = CreateRoomRequest {
110 creator: claims.user_id.clone(),
111 request: SerializingRumaRequestWrapper(request),
112 creator_join_event_contents: create_member_event_content(
113 state.database,
114 MembershipState::Join,
115 claims.user_id,
116 None,
117 )
118 .await?,
119 };
120 let room_id = RoomServerConnection::new(&state.nats_connection)
121 .make_roomserver_request::<CreateRoom>(None, request)
122 .await??;
123 Ok(create_room::v3::Response::new(room_id).into())
124}
125
126async fn get_room_event_handler(
131 room_server: RoomServerConnection,
132 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
133 get_room_event::v3::Request,
134 >,
135) -> Result<ArminiusResponse<get_room_event::v3::Response>, ErrorOf<GetRoomEvent>> {
136 Ok(get_room_event::v3::Response::new(
137 room_server
138 .make_roomserver_request::<GetRoomEvent>(
139 Some(request.room_id.clone()),
140 (claims.user_id, claims.device_id, request.event_id),
141 )
142 .await
143 .map_err(InternalServerError::from)??,
144 )
145 .into())
146}
147
148#[derive(thiserror::Error, MatrixApiError, Debug, Serialize, Deserialize, FromNatsError)]
150enum JoinRoomByIdError {
151 #[error(transparent)]
154 InternalServerError(#[from] InternalServerError),
155 #[error(transparent)]
157 AddRoomMemberEventError(#[from] AddRoomMemberEventError),
158 #[error(transparent)]
160 FeatureNotImplemented(#[from] FeatureNotImplemented),
161}
162
163async fn join_room(
165 state: ServerState,
166 user_id: OwnedUserId,
167 reason: Option<String>,
168 room_id: OwnedRoomId,
169 via: Vec<OwnedServerName>,
170) -> Result<(), JoinRoomByIdError> {
171 RoomServerConnection::new(&state.nats_connection)
172 .make_roomserver_request::<AddRoomMemberEvent>(
173 Some(room_id),
174 AddRoomMemberEventRequest {
175 via,
176 member_event_content: create_member_event_content(
177 state.database,
178 MembershipState::Join,
179 user_id.clone(),
180 reason,
181 )
182 .await?,
183 member: user_id.clone(),
184 sender: user_id,
185 },
186 )
187 .await??;
188 Ok(())
189}
190
191async fn join_room_by_id_or_alias_handler(
199 State(state): State<ServerState>,
200 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
201 join_room_by_id_or_alias::v3::Request,
202 >,
203) -> Result<ArminiusResponse<join_room_by_id_or_alias::v3::Response>, JoinRoomByIdError> {
204 let room_id = RoomId::parse(&request.room_id_or_alias)
205 .map_err(|_| FeatureNotImplemented("Room Aliases".to_owned()))?;
207
208 join_room(
209 state,
210 claims.user_id,
211 request.reason,
212 room_id.clone(),
213 append_server_to_via(request.via, &room_id),
214 )
215 .await?;
216 Ok(join_room_by_id_or_alias::v3::Response::new(room_id).into())
217}
218
219async fn join_room_by_id_handler(
224 State(state): State<ServerState>,
225 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
226 join_room_by_id::v3::Request,
227 >,
228) -> Result<ArminiusResponse<join_room_by_id::v3::Response>, JoinRoomByIdError> {
229 join_room(
230 state,
231 claims.user_id,
232 request.reason,
233 request.room_id.clone(),
234 append_server_to_via(vec![], &request.room_id),
235 )
236 .await?;
237 Ok(join_room_by_id::v3::Response::new(request.room_id).into())
238}
239
240async fn get_member_events_handler(
245 room_server: RoomServerConnection,
246 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
247 get_member_events::v3::Request,
248 >,
249) -> Result<ArminiusResponse<get_member_events::v3::Response>, ErrorOf<GetRoomMembers>> {
250 Ok(room_server
251 .make_roomserver_request::<GetRoomMembers>(
252 Some(request.room_id.clone()),
253 (SerializingRumaRequestWrapper(request), claims.user_id),
254 )
255 .await??
256 .0
257 .into())
258}
259
260async fn leave_room_handler(
265 State(state): State<ServerState>,
266 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
267 leave_room::v3::Request,
268 >,
269) -> Result<ArminiusResponse<leave_room::v3::Response>, AddRoomMemberEventError> {
270 RoomServerConnection::new(&state.nats_connection)
271 .make_roomserver_request::<AddRoomMemberEvent>(
272 Some(request.room_id.clone()),
273 AddRoomMemberEventRequest {
274 via: append_server_to_via(vec![], &request.room_id),
275 member_event_content: create_member_event_content(
276 state.database,
277 MembershipState::Leave,
278 claims.user_id.clone(),
279 request.reason,
280 )
281 .await?,
282 member: claims.user_id.clone(),
283 sender: claims.user_id,
284 },
285 )
286 .await??;
287
288 Ok(leave_room::v3::Response::new().into())
289}
290
291async fn send_state_event_handler(
297 room_server: RoomServerConnection,
298 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
299 send_state_event::v3::Request,
300 >,
301) -> Result<ArminiusResponse<send_state_event::v3::Response>, AddNewEventError> {
302 let event_id = room_server
303 .make_roomserver_request::<AddNewEvent>(
304 Some(request.room_id.clone()),
305 NewEvent {
306 event_type: request.event_type.into(),
307 sender: claims.user_id,
308 body: request
309 .body
310 .deserialize_as()
311 .map_err(InputJsonError::from)
312 .map_err(AddNewEventError::from)?,
313 state_key: Some(request.state_key),
314 timestamp: None,
315 transaction_information: None,
316 },
317 )
318 .await??;
319
320 Ok(send_state_event::v3::Response::new(event_id).into())
321}
322
323async fn send_message_event_handler(
328 room_server: RoomServerConnection,
329 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
330 send_message_event::v3::Request,
331 >,
332) -> Result<ArminiusResponse<send_message_event::v3::Response>, AddNewEventError> {
333 let event_id = room_server
334 .make_roomserver_request::<AddNewEvent>(
335 Some(request.room_id.clone()),
336 NewEvent {
337 event_type: request.event_type.into(),
338 sender: claims.user_id,
339 body: request.body.deserialize_as().map_err(InputJsonError::from)?,
340 state_key: None,
341 timestamp: None,
342 transaction_information: Some(TransactionInformation {
343 device_id: claims.device_id,
344 transaction_id: request.txn_id,
345 }),
346 },
347 )
348 .await??;
349
350 Ok(send_message_event::v3::Response::new(event_id).into())
351}
352
353async fn get_state_events_handler(
358 room_server: RoomServerConnection,
359 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
360 get_state_events::v3::Request,
361 >,
362) -> Result<ArminiusResponse<get_state_events::v3::Response>, ErrorOf<GetStateEvents>> {
363 Ok(room_server
364 .make_roomserver_request::<GetStateEvents>(
365 Some(request.room_id.clone()),
366 (SerializingRumaRequestWrapper(request), claims.user_id),
367 )
368 .await??
369 .0
370 .into())
371}
372
373async fn get_state_events_for_key_handler(
380 room_server: RoomServerConnection,
381 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
382 get_state_event_for_key::v3::Request,
383 >,
384) -> Result<ArminiusResponse<get_state_event_for_key::v3::Response>, ErrorOf<GetStateEventsForKey>>
385{
386 Ok(room_server
387 .make_roomserver_request::<GetStateEventsForKey>(
388 Some(request.room_id.clone()),
389 (SerializingRumaRequestWrapper(request), claims.user_id),
390 )
391 .await??
392 .0
393 .into())
394}
395
396#[derive(thiserror::Error, Debug, MatrixApiError, FromNatsError)]
398enum InviteUserError {
399 #[error(transparent)]
402 InternalServerError(#[from] InternalServerError),
403 #[error(transparent)]
405 AddRoomMemberEventError(#[from] AddRoomMemberEventError),
406 #[error(transparent)]
408 FeatureNotImplemented(#[from] FeatureNotImplemented),
409}
410
411async fn invite_user_handler(
416 State(state): State<ServerState>,
417 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
418 invite_user::v3::Request,
419 >,
420) -> Result<ArminiusResponse<invite_user::v3::Response>, InviteUserError> {
421 let (user_id, reason) = match request.recipient {
422 InvitationRecipient::UserId(InviteUserId { user_id, reason, .. }) => Ok((user_id, reason)),
423 _ => Err(FeatureNotImplemented("Non-user-id invite subjects".to_owned())),
424 }?;
425 RoomServerConnection::new(&state.nats_connection)
426 .make_roomserver_request::<AddRoomMemberEvent>(
427 Some(request.room_id.clone()),
428 AddRoomMemberEventRequest {
429 via: vec![],
430 member_event_content: create_member_event_content(
431 state.database,
432 MembershipState::Invite,
433 user_id.clone(),
434 reason,
435 )
436 .await?,
437 member: user_id,
438 sender: claims.user_id,
439 },
440 )
441 .await??;
442
443 Ok(invite_user::v3::Response::new().into())
444}
445
446async fn kick_user_handler(
451 State(state): State<ServerState>,
452 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<kick_user::v3::Request>,
453) -> Result<ArminiusResponse<kick_user::v3::Response>, InviteUserError> {
454 RoomServerConnection::new(&state.nats_connection)
455 .make_roomserver_request::<AddRoomMemberEvent>(
456 Some(request.room_id.clone()),
457 AddRoomMemberEventRequest {
458 via: vec![],
459 member_event_content: create_member_event_content(
460 state.database,
461 MembershipState::Leave,
462 request.user_id.clone(),
463 request.reason,
464 )
465 .await?,
466 member: request.user_id,
467 sender: claims.user_id,
468 },
469 )
470 .await??;
471
472 Ok(kick_user::v3::Response::new().into())
473}
474
475async fn ban_user_handler(
480 State(state): State<ServerState>,
481 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<ban_user::v3::Request>,
482) -> Result<ArminiusResponse<ban_user::v3::Response>, InviteUserError> {
483 RoomServerConnection::new(&state.nats_connection)
484 .make_roomserver_request::<AddRoomMemberEvent>(
485 Some(request.room_id.clone()),
486 AddRoomMemberEventRequest {
487 via: vec![],
488 member_event_content: create_member_event_content(
489 state.database,
490 MembershipState::Ban,
491 request.user_id.clone(),
492 request.reason,
493 )
494 .await?,
495 member: request.user_id,
496 sender: claims.user_id,
497 },
498 )
499 .await??;
500
501 Ok(ban_user::v3::Response::new().into())
502}
503
504async fn unban_user_handler(
509 State(state): State<ServerState>,
510 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
511 unban_user::v3::Request,
512 >,
513) -> Result<ArminiusResponse<unban_user::v3::Response>, InviteUserError> {
514 RoomServerConnection::new(&state.nats_connection)
515 .make_roomserver_request::<AddRoomMemberEvent>(
516 Some(request.room_id.clone()),
517 AddRoomMemberEventRequest {
518 via: vec![],
519 member_event_content: create_member_event_content(
520 state.database,
521 MembershipState::Leave,
522 request.user_id.clone(),
523 request.reason,
524 )
525 .await?,
526 member: request.user_id,
527 sender: claims.user_id,
528 },
529 )
530 .await??;
531
532 Ok(unban_user::v3::Response::new().into())
533}
534
535async fn knock_room_handler(
543 State(state): State<ServerState>,
544 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
545 knock_room::v3::Request,
546 >,
547) -> Result<ArminiusResponse<knock_room::v3::Response>, JoinRoomByIdError> {
548 let room_id = OwnedRoomId::try_from(request.room_id_or_alias.clone())
549 .map_err(|_| FeatureNotImplemented("Room aliases".to_owned()))?;
551
552 RoomServerConnection::new(&state.nats_connection)
553 .make_roomserver_request::<AddRoomMemberEvent>(
554 Some(room_id.clone()),
555 AddRoomMemberEventRequest {
556 via: append_server_to_via(request.via, &room_id),
557 member_event_content: create_member_event_content(
558 state.database,
559 MembershipState::Knock,
560 claims.user_id.clone(),
561 request.reason,
562 )
563 .await?,
564 member: claims.user_id.clone(),
565 sender: claims.user_id,
566 },
567 )
568 .await??;
569
570 Ok(knock_room::v3::Response::new(room_id).into())
571}
572
573async fn redact_event_handler(
578 room_server: RoomServerConnection,
579 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
580 redact_event::v3::Request,
581 >,
582) -> Result<ArminiusResponse<redact_event::v3::Response>, ErrorOf<RedactEvent>> {
583 Ok(room_server
584 .make_roomserver_request::<RedactEvent>(
585 Some(request.room_id.clone()),
586 (SerializingRumaRequestWrapper(request), claims.user_id),
587 )
588 .await??
589 .0
590 .into())
591}
592
593async fn forget_room_handler(
598 room_server: RoomServerConnection,
599 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
600 forget_room::v3::Request,
601 >,
602) -> Result<ArminiusResponse<forget_room::v3::Response>, ErrorOf<ForgetRoom>> {
603 room_server
604 .make_roomserver_request::<ForgetRoom>(Some(request.room_id.clone()), claims.user_id)
605 .await??;
606 Ok(forget_room::v3::Response::new().into())
607}
608
609async fn joined_rooms_handler(
614 State(mut state): State<ServerState>,
615 AuthenticatedClientRequest(claims, _): AuthenticatedClientRequest<joined_rooms::v3::Request>,
616) -> Result<ArminiusResponse<joined_rooms::v3::Response>, InternalServerError> {
617 Ok(joined_rooms::v3::Response::new(state.database.fetch_joined_rooms(&claims.user_id).await?)
618 .into())
619}
620
621#[derive(Debug, thiserror::Error, MatrixApiError, Serialize, Deserialize, FromNatsError)]
627enum ReadReceiptOrReadMarkerError {
628 #[error(transparent)]
631 InternalServerError(#[from] InternalServerError),
632 #[error(transparent)]
634 ReadReceipt(#[from] ReadReceiptError),
635 #[error(transparent)]
637 ReadMarker(#[from] ReadMarkerError),
638 #[error(transparent)]
640 FeatureNotImplemented(#[from] FeatureNotImplemented),
641}
642
643async fn create_receipt_handler(
648 state: State<ServerState>,
649 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
650 create_receipt::v3::Request,
651 >,
652) -> Result<ArminiusResponse<create_receipt::v3::Response>, ReadReceiptOrReadMarkerError> {
653 let response = create_receipt::v3::Response::new().into();
654 if request.receipt_type == create_receipt::v3::ReceiptType::FullyRead {
655 RoomServerConnection::new(&state.nats_connection)
656 .make_roomserver_request::<SetFullyReadMarker>(
657 Some(request.room_id.clone()),
658 ReadMarkerRequest { user_id: claims.user_id.clone(), event_id: request.event_id },
659 )
660 .await??;
661
662 return Ok(response);
663 }
664
665 let receipt_type = match request.receipt_type {
666 create_receipt::v3::ReceiptType::Read => ReceiptType::Read,
667 create_receipt::v3::ReceiptType::ReadPrivate => ReceiptType::ReadPrivate,
668 custom => {
669 return Err(FeatureNotImplemented(format!(
670 "Custom receipt type '{custom}' is not handled by this server",
671 ))
672 .into());
673 }
674 };
675
676 RoomServerConnection::new(&state.nats_connection)
677 .make_roomserver_request::<ReadReceipt>(
678 Some(request.room_id),
679 ReadReceiptRequest {
680 user_id: claims.user_id,
681 receipt_type,
682 event_id: request.event_id,
683 thread: request.thread,
684 ts: chrono::Utc::now(),
685 provenance: EventProvenance::Local,
686 },
687 )
688 .await??;
689
690 Ok(response)
691}
692
693async fn set_read_marker_handler(
699 state: State<ServerState>,
700 AuthenticatedClientRequest(claims, request): AuthenticatedClientRequest<
701 set_read_marker::v3::Request,
702 >,
703) -> Result<ArminiusResponse<set_read_marker::v3::Response>, ReadReceiptOrReadMarkerError> {
704 if let Some(fully_read_marker_event) = request.fully_read {
705 RoomServerConnection::new(&state.nats_connection)
706 .make_roomserver_request::<SetFullyReadMarker>(
707 Some(request.room_id.clone()),
708 ReadMarkerRequest {
709 user_id: claims.user_id.clone(),
710 event_id: fully_read_marker_event,
711 },
712 )
713 .await??;
714 };
715
716 if let Some(read_receipt_event) = request.read_receipt {
717 RoomServerConnection::new(&state.nats_connection)
718 .make_roomserver_request::<ReadReceipt>(
719 Some(request.room_id.clone()),
720 ReadReceiptRequest {
721 user_id: claims.user_id.clone(),
722 receipt_type: ReceiptType::Read,
723 event_id: read_receipt_event,
724 thread: ruma::events::receipt::ReceiptThread::Unthreaded,
725 ts: chrono::Utc::now(),
726 provenance: EventProvenance::Local,
727 },
728 )
729 .await??;
730 };
731
732 if let Some(private_read_receipt_event) = request.private_read_receipt {
733 RoomServerConnection::new(&state.nats_connection)
734 .make_roomserver_request::<ReadReceipt>(
735 Some(request.room_id),
736 ReadReceiptRequest {
737 user_id: claims.user_id.clone(),
738 receipt_type: ReceiptType::ReadPrivate,
739 event_id: private_read_receipt_event,
740 thread: ruma::events::receipt::ReceiptThread::Unthreaded,
741 ts: chrono::Utc::now(),
742 provenance: EventProvenance::Local,
743 },
744 )
745 .await??;
746 };
747
748 Ok(set_read_marker::v3::Response::new().into())
749}
750
751#[derive(Debug)]
753pub struct RoomComponent;
754
755impl Component for RoomComponent {
756 fn router() -> Router<ServerState> {
757 Router::new()
758 .route("/_matrix/client/v3/createRoom", post(create_room_handler))
759 .route("/_matrix/client/v3/joined_rooms", get(joined_rooms_handler))
760 .route(
761 "/_matrix/client/v3/join/{roomIdOrAlias}",
762 post(join_room_by_id_or_alias_handler),
763 )
764 .route("/_matrix/client/v3/knock/{roomIdOrAlias}", post(knock_room_handler))
765 .route("/_matrix/client/v3/rooms/{roomId}/ban", post(ban_user_handler))
766 .route("/_matrix/client/v3/rooms/{roomId}/event/{eventId}", get(get_room_event_handler))
767 .route("/_matrix/client/v3/rooms/{roomId}/forget", post(forget_room_handler))
768 .route("/_matrix/client/v3/rooms/{roomId}/invite", post(invite_user_handler))
769 .route("/_matrix/client/v3/rooms/{roomId}/join", post(join_room_by_id_handler))
770 .route("/_matrix/client/v3/rooms/{roomId}/kick", post(kick_user_handler))
771 .route("/_matrix/client/v3/rooms/{roomId}/leave", post(leave_room_handler))
772 .route("/_matrix/client/v3/rooms/{roomId}/members", get(get_member_events_handler))
773 .route("/_matrix/client/v3/rooms/{roomId}/read_markers", post(set_read_marker_handler))
774 .route(
775 "/_matrix/client/v3/rooms/{roomId}/receipt/{receiptType}/{eventId}",
776 post(create_receipt_handler),
777 )
778 .route(
779 "/_matrix/client/v3/rooms/{roomId}/redact/{eventId}/{txnId}",
780 put(redact_event_handler),
781 )
782 .route(
783 "/_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}",
784 put(send_message_event_handler),
785 )
786 .route(
787 "/_matrix/client/v3/rooms/{roomId}/state/{eventType}/",
788 put(send_state_event_handler).get(get_state_events_for_key_handler),
789 )
790 .route(
791 "/_matrix/client/v3/rooms/{roomId}/state/{eventType}",
792 put(send_state_event_handler).get(get_state_events_for_key_handler),
793 )
794 .route(
795 "/_matrix/client/v3/rooms/{roomId}/state/{eventType}/{stateKey}",
796 put(send_state_event_handler).get(get_state_events_for_key_handler),
797 )
798 .route("/_matrix/client/v3/rooms/{roomId}/state", get(get_state_events_handler))
799 .route("/_matrix/client/v3/rooms/{roomId}/unban", post(unban_user_handler))
800 }
801}