refactor more of admin code, add unfinished fsck command
Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
parent
7cbe82668b
commit
6b28bd5ae7
8 changed files with 219 additions and 191 deletions
|
@ -1,91 +1,16 @@
|
||||||
use std::{collections::BTreeMap, sync::Arc, time::Instant};
|
use std::{collections::BTreeMap, sync::Arc, time::Instant};
|
||||||
|
|
||||||
use clap::Subcommand;
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::error::ErrorKind, events::room::message::RoomMessageEventContent, CanonicalJsonObject, EventId,
|
api::client::error::ErrorKind, events::room::message::RoomMessageEventContent, CanonicalJsonObject, EventId,
|
||||||
RoomId, RoomVersionId, ServerName,
|
RoomId, RoomVersionId,
|
||||||
};
|
};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
use super::DebugCommand;
|
||||||
use crate::{api::server_server::parse_incoming_pdu, services, utils::HtmlEscape, Error, PduEvent, Result};
|
use crate::{api::server_server::parse_incoming_pdu, services, utils::HtmlEscape, Error, PduEvent, Result};
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(crate) enum DebugCommand {
|
|
||||||
/// - Get the auth_chain of a PDU
|
|
||||||
GetAuthChain {
|
|
||||||
/// An event ID (the $ character followed by the base64 reference hash)
|
|
||||||
event_id: Box<EventId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Parse and print a PDU from a JSON
|
|
||||||
///
|
|
||||||
/// The PDU event is only checked for validity and is not added to the
|
|
||||||
/// database.
|
|
||||||
///
|
|
||||||
/// This command needs a JSON blob provided in a Markdown code block below
|
|
||||||
/// the command.
|
|
||||||
ParsePdu,
|
|
||||||
|
|
||||||
/// - Retrieve and print a PDU by ID from the conduwuit database
|
|
||||||
GetPdu {
|
|
||||||
/// An event ID (a $ followed by the base64 reference hash)
|
|
||||||
event_id: Box<EventId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Attempts to retrieve a PDU from a remote server. Inserts it into our
|
|
||||||
/// database/timeline if found and we do not have this PDU already
|
|
||||||
/// (following normal event auth rules, handles it as an incoming PDU).
|
|
||||||
GetRemotePdu {
|
|
||||||
/// An event ID (a $ followed by the base64 reference hash)
|
|
||||||
event_id: Box<EventId>,
|
|
||||||
|
|
||||||
/// Argument for us to attempt to fetch the event from the
|
|
||||||
/// specified remote server.
|
|
||||||
server: Box<ServerName>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - 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: Box<RoomId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - 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
|
|
||||||
Ping {
|
|
||||||
server: Box<ServerName>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Forces device lists for all local and remote users to be updated (as
|
|
||||||
/// having new keys available)
|
|
||||||
ForceDeviceListUpdates,
|
|
||||||
|
|
||||||
/// - Change tracing log level/filter on the fly
|
|
||||||
///
|
|
||||||
/// This accepts the same format as the `log` config option.
|
|
||||||
ChangeLogLevel {
|
|
||||||
/// Log level/filter
|
|
||||||
filter: Option<String>,
|
|
||||||
|
|
||||||
/// Resets the log level/filter to the one in your config
|
|
||||||
#[arg(short, long)]
|
|
||||||
reset: bool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
pub(crate) async fn process(command: DebugCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||||
Ok(match command {
|
Ok(match command {
|
||||||
DebugCommand::GetAuthChain {
|
DebugCommand::GetAuthChain {
|
80
src/service/admin/debug/mod.rs
Normal file
80
src/service/admin/debug/mod.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use clap::Subcommand;
|
||||||
|
use ruma::{EventId, RoomId, ServerName};
|
||||||
|
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
|
pub(crate) mod debug;
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub(crate) enum DebugCommand {
|
||||||
|
/// - Get the auth_chain of a PDU
|
||||||
|
GetAuthChain {
|
||||||
|
/// An event ID (the $ character followed by the base64 reference hash)
|
||||||
|
event_id: Box<EventId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Parse and print a PDU from a JSON
|
||||||
|
///
|
||||||
|
/// The PDU event is only checked for validity and is not added to the
|
||||||
|
/// database.
|
||||||
|
///
|
||||||
|
/// This command needs a JSON blob provided in a Markdown code block below
|
||||||
|
/// the command.
|
||||||
|
ParsePdu,
|
||||||
|
|
||||||
|
/// - Retrieve and print a PDU by ID from the conduwuit database
|
||||||
|
GetPdu {
|
||||||
|
/// An event ID (a $ followed by the base64 reference hash)
|
||||||
|
event_id: Box<EventId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Attempts to retrieve a PDU from a remote server. Inserts it into our
|
||||||
|
/// database/timeline if found and we do not have this PDU already
|
||||||
|
/// (following normal event auth rules, handles it as an incoming PDU).
|
||||||
|
GetRemotePdu {
|
||||||
|
/// An event ID (a $ followed by the base64 reference hash)
|
||||||
|
event_id: Box<EventId>,
|
||||||
|
|
||||||
|
/// Argument for us to attempt to fetch the event from the
|
||||||
|
/// specified remote server.
|
||||||
|
server: Box<ServerName>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - 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: Box<RoomId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - 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
|
||||||
|
Ping {
|
||||||
|
server: Box<ServerName>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Forces device lists for all local and remote users to be updated (as
|
||||||
|
/// having new keys available)
|
||||||
|
ForceDeviceListUpdates,
|
||||||
|
|
||||||
|
/// - Change tracing log level/filter on the fly
|
||||||
|
///
|
||||||
|
/// This accepts the same format as the `log` config option.
|
||||||
|
ChangeLogLevel {
|
||||||
|
/// Log level/filter
|
||||||
|
filter: Option<String>,
|
||||||
|
|
||||||
|
/// Resets the log level/filter to the one in your config
|
||||||
|
#[arg(short, long)]
|
||||||
|
reset: bool,
|
||||||
|
},
|
||||||
|
}
|
18
src/service/admin/fsck.rs
Normal file
18
src/service/admin/fsck.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use clap::Subcommand;
|
||||||
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
|
use crate::{services, Result};
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub(crate) enum FsckCommand {
|
||||||
|
Register,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn fsck(command: FsckCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||||
|
match command {
|
||||||
|
FsckCommand::Register => {
|
||||||
|
todo!()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ use super::pdu::PduBuilder;
|
||||||
use crate::{
|
use crate::{
|
||||||
service::admin::{
|
service::admin::{
|
||||||
appservice::AppserviceCommand, debug::DebugCommand, federation::FederationCommand, media::MediaCommand,
|
appservice::AppserviceCommand, debug::DebugCommand, federation::FederationCommand, media::MediaCommand,
|
||||||
query::query::QueryCommand, room::RoomCommand, server::ServerCommand, user::UserCommand,
|
query::QueryCommand, room::RoomCommand, server::ServerCommand, user::UserCommand,
|
||||||
},
|
},
|
||||||
services, Error, Result,
|
services, Error, Result,
|
||||||
};
|
};
|
||||||
|
@ -38,6 +38,7 @@ use crate::{
|
||||||
pub(crate) mod appservice;
|
pub(crate) mod appservice;
|
||||||
pub(crate) mod debug;
|
pub(crate) mod debug;
|
||||||
pub(crate) mod federation;
|
pub(crate) mod federation;
|
||||||
|
pub(crate) mod fsck;
|
||||||
pub(crate) mod media;
|
pub(crate) mod media;
|
||||||
pub(crate) mod query;
|
pub(crate) mod query;
|
||||||
pub(crate) mod room;
|
pub(crate) mod room;
|
||||||
|
@ -279,12 +280,12 @@ impl Service {
|
||||||
let reply_message_content = match command {
|
let reply_message_content = match command {
|
||||||
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
|
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
|
||||||
AdminCommand::Media(command) => media::process(command, body).await?,
|
AdminCommand::Media(command) => media::process(command, body).await?,
|
||||||
AdminCommand::Users(command) => user::process(command, body).await?,
|
AdminCommand::Users(command) => user::user::process(command, body).await?,
|
||||||
AdminCommand::Rooms(command) => room::process(command, body).await?,
|
AdminCommand::Rooms(command) => room::process(command, body).await?,
|
||||||
AdminCommand::Federation(command) => federation::process(command, body).await?,
|
AdminCommand::Federation(command) => federation::process(command, body).await?,
|
||||||
AdminCommand::Server(command) => server::process(command, body).await?,
|
AdminCommand::Server(command) => server::process(command, body).await?,
|
||||||
AdminCommand::Debug(command) => debug::process(command, body).await?,
|
AdminCommand::Debug(command) => debug::debug::process(command, body).await?,
|
||||||
AdminCommand::Query(command) => query::query::process(command, body).await?,
|
AdminCommand::Query(command) => query::process(command, body).await?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(reply_message_content)
|
Ok(reply_message_content)
|
||||||
|
|
|
@ -1,8 +1,54 @@
|
||||||
#[allow(clippy::module_inception)]
|
|
||||||
pub(crate) mod query;
|
|
||||||
|
|
||||||
pub(crate) mod account_data;
|
pub(crate) mod account_data;
|
||||||
pub(crate) mod appservice;
|
pub(crate) mod appservice;
|
||||||
pub(crate) mod globals;
|
pub(crate) mod globals;
|
||||||
pub(crate) mod presence;
|
pub(crate) mod presence;
|
||||||
pub(crate) mod room_alias;
|
pub(crate) mod room_alias;
|
||||||
|
|
||||||
|
use clap::Subcommand;
|
||||||
|
use ruma::events::room::message::RoomMessageEventContent;
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
account_data::{account_data, AccountData},
|
||||||
|
appservice::{appservice, Appservice},
|
||||||
|
globals::{globals, Globals},
|
||||||
|
presence::{presence, Presence},
|
||||||
|
room_alias::{room_alias, RoomAlias},
|
||||||
|
};
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
/// Query tables from database
|
||||||
|
pub(crate) enum QueryCommand {
|
||||||
|
/// - account_data.rs iterators and getters
|
||||||
|
#[command(subcommand)]
|
||||||
|
AccountData(AccountData),
|
||||||
|
|
||||||
|
/// - appservice.rs iterators and getters
|
||||||
|
#[command(subcommand)]
|
||||||
|
Appservice(Appservice),
|
||||||
|
|
||||||
|
/// - presence.rs iterators and getters
|
||||||
|
#[command(subcommand)]
|
||||||
|
Presence(Presence),
|
||||||
|
|
||||||
|
/// - rooms/alias.rs iterators and getters
|
||||||
|
#[command(subcommand)]
|
||||||
|
RoomAlias(RoomAlias),
|
||||||
|
|
||||||
|
/// - globals.rs iterators and getters
|
||||||
|
#[command(subcommand)]
|
||||||
|
Globals(Globals),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes admin query commands
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub(crate) async fn process(command: QueryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||||
|
match command {
|
||||||
|
QueryCommand::AccountData(AccountData) => account_data(AccountData).await,
|
||||||
|
QueryCommand::Appservice(Appservice) => appservice(Appservice).await,
|
||||||
|
QueryCommand::Presence(Presence) => presence(Presence).await,
|
||||||
|
QueryCommand::RoomAlias(RoomAlias) => room_alias(RoomAlias).await,
|
||||||
|
QueryCommand::Globals(Globals) => globals(Globals).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
use clap::Subcommand;
|
|
||||||
use ruma::events::room::message::RoomMessageEventContent;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
account_data::{account_data, AccountData},
|
|
||||||
appservice::{appservice, Appservice},
|
|
||||||
globals::{globals, Globals},
|
|
||||||
presence::{presence, Presence},
|
|
||||||
room_alias::{room_alias, RoomAlias},
|
|
||||||
};
|
|
||||||
use crate::Result;
|
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
/// Query tables from database
|
|
||||||
pub(crate) enum QueryCommand {
|
|
||||||
/// - account_data.rs iterators and getters
|
|
||||||
#[command(subcommand)]
|
|
||||||
AccountData(AccountData),
|
|
||||||
|
|
||||||
/// - appservice.rs iterators and getters
|
|
||||||
#[command(subcommand)]
|
|
||||||
Appservice(Appservice),
|
|
||||||
|
|
||||||
/// - presence.rs iterators and getters
|
|
||||||
#[command(subcommand)]
|
|
||||||
Presence(Presence),
|
|
||||||
|
|
||||||
/// - rooms/alias.rs iterators and getters
|
|
||||||
#[command(subcommand)]
|
|
||||||
RoomAlias(RoomAlias),
|
|
||||||
|
|
||||||
/// - globals.rs iterators and getters
|
|
||||||
#[command(subcommand)]
|
|
||||||
Globals(Globals),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Processes admin query commands
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub(crate) async fn process(command: QueryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
|
||||||
match command {
|
|
||||||
QueryCommand::AccountData(AccountData) => account_data(AccountData).await,
|
|
||||||
QueryCommand::Appservice(Appservice) => appservice(Appservice).await,
|
|
||||||
QueryCommand::Presence(Presence) => presence(Presence).await,
|
|
||||||
QueryCommand::RoomAlias(RoomAlias) => room_alias(RoomAlias).await,
|
|
||||||
QueryCommand::Globals(Globals) => globals(Globals).await,
|
|
||||||
}
|
|
||||||
}
|
|
63
src/service/admin/user/mod.rs
Normal file
63
src/service/admin/user/mod.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
|
pub(crate) mod user;
|
||||||
|
|
||||||
|
use clap::Subcommand;
|
||||||
|
use ruma::UserId;
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub(crate) enum UserCommand {
|
||||||
|
/// - Create a new user
|
||||||
|
Create {
|
||||||
|
/// Username of the new user
|
||||||
|
username: String,
|
||||||
|
/// Password of the new user, if unspecified one is generated
|
||||||
|
password: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Reset user password
|
||||||
|
ResetPassword {
|
||||||
|
/// Username of the user for whom the password should be reset
|
||||||
|
username: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Deactivate a user
|
||||||
|
///
|
||||||
|
/// User will not be removed from all rooms by default.
|
||||||
|
/// Use --leave-rooms to force the user to leave all rooms
|
||||||
|
Deactivate {
|
||||||
|
#[arg(short, long)]
|
||||||
|
leave_rooms: bool,
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - Deactivate a list of users
|
||||||
|
///
|
||||||
|
/// Recommended to use in conjunction with list-local-users.
|
||||||
|
///
|
||||||
|
/// Users will not be removed from joined rooms by default.
|
||||||
|
/// Can be overridden with --leave-rooms flag.
|
||||||
|
/// Removing a mass amount of users from a room may cause a significant
|
||||||
|
/// amount of leave events. The time to leave rooms may depend significantly
|
||||||
|
/// on joined rooms and servers.
|
||||||
|
///
|
||||||
|
/// This command needs a newline separated list of users provided in a
|
||||||
|
/// Markdown code block below the command.
|
||||||
|
DeactivateAll {
|
||||||
|
#[arg(short, long)]
|
||||||
|
/// Remove users from their joined rooms
|
||||||
|
leave_rooms: bool,
|
||||||
|
#[arg(short, long)]
|
||||||
|
/// Also deactivate admin accounts
|
||||||
|
force: bool,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// - List local users in the database
|
||||||
|
List,
|
||||||
|
|
||||||
|
/// - Lists all the rooms (local and remote) that the specified user is
|
||||||
|
/// joined in
|
||||||
|
ListJoinedRooms {
|
||||||
|
user_id: Box<UserId>,
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,74 +1,17 @@
|
||||||
use std::{fmt::Write as _, sync::Arc};
|
use std::{fmt::Write as _, sync::Arc};
|
||||||
|
|
||||||
use clap::Subcommand;
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, UserId};
|
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, UserId};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
|
|
||||||
|
use super::UserCommand;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
api::client_server::{join_room_by_id_helper, leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH},
|
api::client_server::{join_room_by_id_helper, leave_all_rooms, AUTO_GEN_PASSWORD_LENGTH},
|
||||||
service::admin::{escape_html, get_room_info},
|
service::admin::{escape_html, get_room_info},
|
||||||
services, utils, Result,
|
services, utils, Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub(crate) enum UserCommand {
|
|
||||||
/// - Create a new user
|
|
||||||
Create {
|
|
||||||
/// Username of the new user
|
|
||||||
username: String,
|
|
||||||
/// Password of the new user, if unspecified one is generated
|
|
||||||
password: Option<String>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Reset user password
|
|
||||||
ResetPassword {
|
|
||||||
/// Username of the user for whom the password should be reset
|
|
||||||
username: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Deactivate a user
|
|
||||||
///
|
|
||||||
/// User will not be removed from all rooms by default.
|
|
||||||
/// Use --leave-rooms to force the user to leave all rooms
|
|
||||||
Deactivate {
|
|
||||||
#[arg(short, long)]
|
|
||||||
leave_rooms: bool,
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - Deactivate a list of users
|
|
||||||
///
|
|
||||||
/// Recommended to use in conjunction with list-local-users.
|
|
||||||
///
|
|
||||||
/// Users will not be removed from joined rooms by default.
|
|
||||||
/// Can be overridden with --leave-rooms flag.
|
|
||||||
/// Removing a mass amount of users from a room may cause a significant
|
|
||||||
/// amount of leave events. The time to leave rooms may depend significantly
|
|
||||||
/// on joined rooms and servers.
|
|
||||||
///
|
|
||||||
/// This command needs a newline separated list of users provided in a
|
|
||||||
/// Markdown code block below the command.
|
|
||||||
DeactivateAll {
|
|
||||||
#[arg(short, long)]
|
|
||||||
/// Remove users from their joined rooms
|
|
||||||
leave_rooms: bool,
|
|
||||||
#[arg(short, long)]
|
|
||||||
/// Also deactivate admin accounts
|
|
||||||
force: bool,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// - List local users in the database
|
|
||||||
List,
|
|
||||||
|
|
||||||
/// - Lists all the rooms (local and remote) that the specified user is
|
|
||||||
/// joined in
|
|
||||||
ListJoinedRooms {
|
|
||||||
user_id: Box<UserId>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn process(command: UserCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
pub(crate) async fn process(command: UserCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
|
||||||
match command {
|
match command {
|
||||||
UserCommand::List => match services().users.list_local_users() {
|
UserCommand::List => match services().users.list_local_users() {
|
Loading…
Add table
Add a link
Reference in a new issue