feat: Pass sender through admin commands

This commit is contained in:
Jade Ellis 2025-06-29 15:17:27 +01:00 committed by Ellis Git
parent ecc6fda98b
commit acb74faa07
5 changed files with 41 additions and 8 deletions

View file

@ -7,13 +7,14 @@ use futures::{
io::{AsyncWriteExt, BufWriter}, io::{AsyncWriteExt, BufWriter},
lock::Mutex, lock::Mutex,
}; };
use ruma::EventId; use ruma::{EventId, UserId};
pub(crate) struct Context<'a> { pub(crate) struct Context<'a> {
pub(crate) services: &'a Services, pub(crate) services: &'a Services,
pub(crate) body: &'a [&'a str], pub(crate) body: &'a [&'a str],
pub(crate) timer: SystemTime, pub(crate) timer: SystemTime,
pub(crate) reply_id: Option<&'a EventId>, pub(crate) reply_id: Option<&'a EventId>,
pub(crate) sender: Option<&'a UserId>,
pub(crate) output: Mutex<BufWriter<Vec<u8>>>, pub(crate) output: Mutex<BufWriter<Vec<u8>>>,
} }
@ -36,4 +37,16 @@ impl Context<'_> {
output.write_all(s.as_bytes()).map_err(Into::into).await output.write_all(s.as_bytes()).map_err(Into::into).await
}) })
} }
/// Get the sender of the admin command, if available
pub(crate) fn sender(&self) -> Option<&UserId> { self.sender }
/// Check if the command has sender information
pub(crate) fn has_sender(&self) -> bool { self.sender.is_some() }
/// Get the sender as a string, or service user ID if not available
pub(crate) fn sender_or_service_user(&self) -> &UserId {
self.sender
.unwrap_or_else(|| self.services.globals.server_user.as_ref())
}
} }

View file

@ -63,6 +63,7 @@ async fn process_command(services: Arc<Services>, input: &CommandInput) -> Proce
body: &body, body: &body,
timer: SystemTime::now(), timer: SystemTime::now(),
reply_id: input.reply_id.as_deref(), reply_id: input.reply_id.as_deref(),
sender: input.sender.as_deref(),
output: BufWriter::new(Vec::new()).into(), output: BufWriter::new(Vec::new()).into(),
}; };

View file

@ -241,7 +241,7 @@ pub(super) async fn suspend(&self, user_id: String) -> Result {
// TODO: Record the actual user that sent the suspension where possible // TODO: Record the actual user that sent the suspension where possible
self.services self.services
.users .users
.suspend_account(&user_id, self.services.globals.server_user.as_ref()) .suspend_account(&user_id, self.sender_or_service_user())
.await; .await;
self.write_str(&format!("User {user_id} has been suspended.")) self.write_str(&format!("User {user_id} has been suspended."))

View file

@ -45,11 +45,13 @@ struct Services {
services: StdRwLock<Option<Weak<crate::Services>>>, services: StdRwLock<Option<Weak<crate::Services>>>,
} }
/// Inputs to a command are a multi-line string and optional reply_id. /// Inputs to a command are a multi-line string, optional reply_id, and optional
/// sender.
#[derive(Debug)] #[derive(Debug)]
pub struct CommandInput { pub struct CommandInput {
pub command: String, pub command: String,
pub reply_id: Option<OwnedEventId>, pub reply_id: Option<OwnedEventId>,
pub sender: Option<Box<UserId>>,
} }
/// Prototype of the tab-completer. The input is buffered text when tab /// Prototype of the tab-completer. The input is buffered text when tab
@ -162,7 +164,22 @@ impl Service {
pub fn command(&self, command: String, reply_id: Option<OwnedEventId>) -> Result<()> { pub fn command(&self, command: String, reply_id: Option<OwnedEventId>) -> Result<()> {
self.channel self.channel
.0 .0
.send(CommandInput { command, reply_id }) .send(CommandInput { command, reply_id, sender: None })
.map_err(|e| err!("Failed to enqueue admin command: {e:?}"))
}
/// Posts a command to the command processor queue with sender information
/// and returns. Processing will take place on the service worker's task
/// asynchronously. Errors if the queue is full.
pub fn command_with_sender(
&self,
command: String,
reply_id: Option<OwnedEventId>,
sender: Box<UserId>,
) -> Result<()> {
self.channel
.0
.send(CommandInput { command, reply_id, sender: Some(sender) })
.map_err(|e| err!("Failed to enqueue admin command: {e:?}")) .map_err(|e| err!("Failed to enqueue admin command: {e:?}"))
} }
@ -173,7 +190,7 @@ impl Service {
command: String, command: String,
reply_id: Option<OwnedEventId>, reply_id: Option<OwnedEventId>,
) -> ProcessorResult { ) -> ProcessorResult {
self.process_command(CommandInput { command, reply_id }) self.process_command(CommandInput { command, reply_id, sender: None })
.await .await
} }

View file

@ -536,9 +536,11 @@ impl Service {
self.services.search.index_pdu(shortroomid, &pdu_id, &body); self.services.search.index_pdu(shortroomid, &pdu_id, &body);
if self.services.admin.is_admin_command(pdu, &body).await { if self.services.admin.is_admin_command(pdu, &body).await {
self.services self.services.admin.command_with_sender(
.admin body,
.command(body, Some((*pdu.event_id).into()))?; Some((*pdu.event_id).into()),
pdu.sender.clone().into(),
)?;
} }
} }
}, },