Add ban/unban/kick room commands (#327)

This commit is contained in:
Ulyssa 2024-08-17 18:50:48 -07:00 committed by GitHub
parent 2a66496913
commit df3896df9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 145 additions and 0 deletions

View file

@ -161,6 +161,12 @@ Set the room's canonical alias to the one provided, and make the previous one an
Delete the room's canonical alias. Delete the room's canonical alias.
.It Sy ":room canon show" .It Sy ":room canon show"
Show the room's canonical alias, if any is set. Show the room's canonical alias, if any is set.
.It Sy ":room ban [user] [reason]"
Ban a user from this room with an optional reason.
.It Sy ":room unban [user] [reason]"
Unban a user from this room with an optional reason.
.It Sy ":room kick [user] [reason]"
Kick a user from this room with an optional reason.
.El .El
.Sh "WINDOW COMMANDS" .Sh "WINDOW COMMANDS"

View file

@ -391,6 +391,24 @@ pub enum RoomField {
CanonicalAlias, CanonicalAlias,
} }
/// An action that operates on a room member.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum MemberUpdateAction {
Ban,
Kick,
Unban,
}
impl Display for MemberUpdateAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemberUpdateAction::Ban => write!(f, "ban"),
MemberUpdateAction::Kick => write!(f, "kick"),
MemberUpdateAction::Unban => write!(f, "unban"),
}
}
}
/// An action that operates on a focused room. /// An action that operates on a focused room.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub enum RoomAction { pub enum RoomAction {
@ -406,6 +424,9 @@ pub enum RoomAction {
/// Leave this room. /// Leave this room.
Leave(bool), Leave(bool),
/// Update a user's membership in this room.
MemberUpdate(MemberUpdateAction, String, Option<String>, bool),
/// Open the members window. /// Open the members window.
Members(Box<CommandContext>), Members(Box<CommandContext>),

View file

@ -20,6 +20,7 @@ use crate::base::{
IambAction, IambAction,
IambId, IambId,
KeysAction, KeysAction,
MemberUpdateAction,
MessageAction, MessageAction,
ProgramCommand, ProgramCommand,
ProgramCommands, ProgramCommands,
@ -411,6 +412,17 @@ fn iamb_room(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
("dm", "unset", None) => RoomAction::SetDirect(false).into(), ("dm", "unset", None) => RoomAction::SetDirect(false).into(),
("dm", "unset", Some(_)) => return Result::Err(CommandError::InvalidArgument), ("dm", "unset", Some(_)) => return Result::Err(CommandError::InvalidArgument),
// :room [kick|ban|unban] <user>
("kick", u, r) => {
RoomAction::MemberUpdate(MemberUpdateAction::Kick, u.into(), r, desc.bang).into()
},
("ban", u, r) => {
RoomAction::MemberUpdate(MemberUpdateAction::Ban, u.into(), r, desc.bang).into()
},
("unban", u, r) => {
RoomAction::MemberUpdate(MemberUpdateAction::Unban, u.into(), r, desc.bang).into()
},
// :room name set <room-name> // :room name set <room-name>
("name", "set", Some(s)) => RoomAction::Set(RoomField::Name, s).into(), ("name", "set", Some(s)) => RoomAction::Set(RoomField::Name, s).into(),
("name", "set", None) => return Result::Err(CommandError::InvalidArgument), ("name", "set", None) => return Result::Err(CommandError::InvalidArgument),
@ -1047,6 +1059,69 @@ mod tests {
assert_eq!(res, Err(CommandError::InvalidArgument)); assert_eq!(res, Err(CommandError::InvalidArgument));
} }
#[test]
fn test_cmd_room_kick() {
let mut cmds = setup_commands();
let ctx = EditContext::default();
let res = cmds.input_cmd("room kick @user:example.com", ctx.clone()).unwrap();
let act = IambAction::Room(RoomAction::MemberUpdate(
MemberUpdateAction::Kick,
"@user:example.com".into(),
None,
false,
));
assert_eq!(res, vec![(act.into(), ctx.clone())]);
let res = cmds.input_cmd("room! kick @user:example.com", ctx.clone()).unwrap();
let act = IambAction::Room(RoomAction::MemberUpdate(
MemberUpdateAction::Kick,
"@user:example.com".into(),
None,
true,
));
assert_eq!(res, vec![(act.into(), ctx.clone())]);
let res = cmds
.input_cmd("room! kick @user:example.com \"reason here\"", ctx.clone())
.unwrap();
let act = IambAction::Room(RoomAction::MemberUpdate(
MemberUpdateAction::Kick,
"@user:example.com".into(),
Some("reason here".into()),
true,
));
assert_eq!(res, vec![(act.into(), ctx.clone())]);
}
#[test]
fn test_cmd_room_ban_unban() {
let mut cmds = setup_commands();
let ctx = EditContext::default();
let res = cmds
.input_cmd("room! ban @user:example.com \"spam\"", ctx.clone())
.unwrap();
let act = IambAction::Room(RoomAction::MemberUpdate(
MemberUpdateAction::Ban,
"@user:example.com".into(),
Some("spam".into()),
true,
));
assert_eq!(res, vec![(act.into(), ctx.clone())]);
let res = cmds
.input_cmd("room unban @user:example.com \"reconciled\"", ctx.clone())
.unwrap();
let act = IambAction::Room(RoomAction::MemberUpdate(
MemberUpdateAction::Unban,
"@user:example.com".into(),
Some("reconciled".into()),
false,
));
assert_eq!(res, vec![(act.into(), ctx.clone())]);
}
#[test] #[test]
fn test_cmd_redact() { fn test_cmd_redact() {
let mut cmds = setup_commands(); let mut cmds = setup_commands();

View file

@ -22,6 +22,7 @@ use matrix_sdk::{
}, },
OwnedEventId, OwnedEventId,
OwnedRoomAliasId, OwnedRoomAliasId,
OwnedUserId,
RoomId, RoomId,
}, },
DisplayName, DisplayName,
@ -56,6 +57,7 @@ use crate::base::{
IambId, IambId,
IambInfo, IambInfo,
IambResult, IambResult,
MemberUpdateAction,
MessageAction, MessageAction,
ProgramAction, ProgramAction,
ProgramContext, ProgramContext,
@ -269,6 +271,47 @@ impl RoomState {
Err(IambError::NotJoined.into()) Err(IambError::NotJoined.into())
} }
}, },
RoomAction::MemberUpdate(mua, user, reason, skip_confirm) => {
let Some(room) = store.application.worker.client.get_room(self.id()) else {
return Err(IambError::NotJoined.into());
};
let Ok(user_id) = OwnedUserId::try_from(user.as_str()) else {
let err = IambError::InvalidUserId(user);
return Err(err.into());
};
if !skip_confirm {
let msg = format!("Do you really want to {mua} {user} from this room?");
let act = RoomAction::MemberUpdate(mua, user, reason, true);
let act = IambAction::from(act);
let prompt = PromptYesNo::new(msg, vec![Action::from(act)]);
let prompt = Box::new(prompt);
return Err(UIError::NeedConfirm(prompt));
}
match mua {
MemberUpdateAction::Ban => {
room.ban_user(&user_id, reason.as_deref())
.await
.map_err(IambError::from)?;
},
MemberUpdateAction::Unban => {
room.unban_user(&user_id, reason.as_deref())
.await
.map_err(IambError::from)?;
},
MemberUpdateAction::Kick => {
room.kick_user(&user_id, reason.as_deref())
.await
.map_err(IambError::from)?;
},
}
Ok(vec![])
},
RoomAction::Members(mut cmd) => { RoomAction::Members(mut cmd) => {
let width = Count::Exact(30); let width = Count::Exact(30);
let act = let act =