feat: Move room state commands to the room group

This commit is contained in:
timedout committed 2026-10-31 20:57:49 +00:00
1 parent cb27a2082d
commit 7871ce9190
4 files changed
+74 -118

No files matched your search

+4 -91
View File
@@ -6,17 +6,15 @@ use std::{
};
use conduwuit::{
Err, Result, at, debug_error, err, info,
Err, Result, debug_error, err, info,
matrix::{
Event, StateKey,
Event,
pdu::{PduEvent, PduId, RawPduId},
},
state_res::EventTypeExt,
trace, utils,
utils::{
stream::{BroadbandExt, IterStream, ReadyExt},
string::EMPTY,
to_canonical_object,
},
warn,
};
@@ -27,11 +25,8 @@ use resolvematrix::{
server::{MatrixResolver, MatrixResolverBuilder},
};
use ruma::{
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
OwnedRoomOrAliasId, OwnedServerName, RoomId, RoomVersionId, UInt,
api::federation::event::get_room_state,
events::{AnyStateEvent, StateEventType},
serde::Raw,
CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId, OwnedServerName,
RoomId, RoomVersionId, UInt, api::federation::event::get_room_state,
};
use service::rooms::{
short::{ShortEventId, ShortRoomId},
@@ -518,88 +513,6 @@ impl crate::Context<'_> {
.await
}
pub(super) async fn get_state_at(&self, event_id: OwnedEventId) -> Result {
self.bail_restricted()?;
let shortstatehash = self
.services
.rooms
.state_accessor
.pdu_shortstatehash(&event_id)
.await?;
let state_ids: Vec<OwnedEventId> = self
.services
.rooms
.state_accessor
.state_full_ids(shortstatehash)
.map(at!(1))
.collect()
.await;
let mut pdus: HashMap<(StateEventType, StateKey), CanonicalJsonObject> = state_ids
.iter()
.try_stream::<conduwuit::Error>()
.and_then(|id| async {
let pdu_json = self.services.rooms.timeline.get_pdu_json(id).await?;
let event_type = pdu_json
.get("type")
.and_then(CanonicalJsonValue::as_str)
.expect("event type must be present in events");
let state_key = pdu_json
.get("state_key")
.and_then(CanonicalJsonValue::as_str)
.expect("state keys must be present for events in the room state");
Ok(((event_type.into(), state_key.into()), pdu_json))
})
.try_collect()
.await?;
let pdu = self.services.rooms.timeline.get_pdu(&event_id).await?;
if let Some(state_key) = pdu.state_key() {
pdus.insert(pdu.kind().with_state_key(state_key), to_canonical_object(pdu)?);
}
let json =
serde_json::to_string_pretty(&pdus.values().collect::<Vec<_>>()).map_err(|e| {
err!(Database(
"Failed to convert room state events to pretty JSON, possible invalid room \
state events in our database {e}",
))
})?;
let out = format!("```json\n{json}\n```");
self.write_str(&out).await
}
pub(super) async fn get_room_state(&self, room: OwnedRoomOrAliasId) -> Result {
self.bail_restricted()?;
let room_id = self.services.rooms.alias.resolve(&room).await?;
let room_state: Vec<Raw<AnyStateEvent>> = self
.services
.rooms
.state_accessor
.room_state_full_pdus(&room_id)
.map_ok(Event::into_format)
.try_collect()
.await?;
if room_state.is_empty() {
return Err!("Unable to find room state in our database (vector is empty)",);
}
let json = serde_json::to_string_pretty(&room_state).map_err(|e| {
err!(Database(
"Failed to convert room state events to pretty JSON, possible invalid room \
state events in our database {e}",
))
})?;
let out = format!("```json\n{json}\n```");
self.write_str(&out).await
}
pub(super) async fn ping(&self, server: OwnedServerName) -> Result {
if server == self.services.globals.server_name() {
return Err!("Not allowed to send federation requests to ourselves.");
+1 -24
View File
@@ -3,7 +3,7 @@ pub(crate) mod tester;
use clap::Subcommand;
use conduwuit::Result;
use ruma::{OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId, OwnedServerName};
use ruma::{OwnedEventId, OwnedRoomId, OwnedServerName};
use service::rooms::short::{ShortEventId, ShortRoomId};
use self::tester::TesterCommand;
@@ -80,29 +80,6 @@ pub enum DebugCommand {
force: bool,
},
/// Gets all the room state events for the specified room.
///
/// This is functionally equivalent to `GET
/// /_matrix/client/v3/rooms/{roomid}/state`, except the admin command does
/// *not* check if the sender user is allowed to see state events. This is
/// done because it's implied that server admins here have database access
/// and can see/get room info themselves anyways if they were malicious
/// admins.
///
/// Of course the check is still done on the actual client API.
GetRoomState {
/// Room ID
room_id: OwnedRoomOrAliasId,
},
/// Gets all the room state events at the specified event.
///
/// State at event might not be available for some PDUs, such as rejected
/// ones.
GetStateAt {
event_id: OwnedEventId,
},
/// Sends a federation request to the remote server's
/// `/_matrix/federation/v1/version` endpoint and measures the latency it
/// took for the server to respond
+57 -2
View File
@@ -1,6 +1,8 @@
use conduwuit::{Err, Result};
use conduwuit::{Err, Event, Result, err};
use futures::StreamExt;
use ruma::OwnedRoomId;
#[allow(unused_imports)]
use ruma::state_res::Event as _;
use ruma::{OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId};
use crate::{PAGE_SIZE, get_room_info};
@@ -82,4 +84,57 @@ impl crate::Context<'_> {
self.write_str(&format!("{result}")).await
}
pub(super) async fn export_state(
&self,
room: OwnedRoomOrAliasId,
event_id: Option<OwnedEventId>,
) -> Result {
self.bail_restricted()?;
let room_id = self.services.rooms.alias.resolve(&room).await?;
let shortstatehash = match &event_id {
| Some(event_id) =>
self.services
.rooms
.state_accessor
.pdu_shortstatehash(event_id)
.await?,
| None =>
self.services
.rooms
.state
.get_room_shortstatehash(&room_id)
.await?,
};
let mut state_at: Vec<_> = self
.services
.rooms
.state_accessor
.state_full_pdus(shortstatehash)
.map(Event::into_pdu)
.collect()
.await;
if let Some(event_id) = event_id {
let pdu = self.services.rooms.timeline.get_pdu(&event_id).await?;
if let Some(state_key) = pdu.state_key() {
state_at.retain(|p| p.kind() != pdu.kind() || p.state_key() != Some(state_key));
state_at.push(pdu);
}
}
if state_at.is_empty() {
return Err!(Request(NotFound("No state found.")));
}
let json = serde_json::to_string_pretty(&state_at).map_err(|e| {
err!(Database(
"Failed to convert room state events to pretty JSON, possible invalid room \
state events in the database: {e:?}",
))
})?;
self.write_str(&format!("```json\n{json}\n```")).await
}
}
+12 -1
View File
@@ -6,7 +6,7 @@ mod moderation;
use clap::Subcommand;
use conduwuit::Result;
use ruma::OwnedRoomId;
use ruma::{OwnedEventId, OwnedRoomId, OwnedRoomOrAliasId};
use self::{
alias::RoomAliasCommand, directory::RoomDirectoryCommand, info::RoomInfoCommand,
@@ -60,4 +60,15 @@ pub enum RoomCommand {
Exists {
room_id: OwnedRoomId,
},
/// Exports the room's current state as a JSON file.
/// If an event ID is supplied, the state at that event (the state preceding
/// it, plus any change made by the named event) is returned instead, if it
/// is known.
ExportState {
/// The room ID or alias to get the state for.
room: OwnedRoomOrAliasId,
/// The event ID to get the state at, if any.
event_id: Option<OwnedEventId>,
},
}