From c834e86e67204ecbc723505eb63b9ed0dc09f84d Mon Sep 17 00:00:00 2001 From: strawberry Date: Mon, 10 Jun 2024 02:48:04 -0400 Subject: [PATCH] adminroom: user cmds to put/get/delete room account data primarily useful for inserting `m.server_notice` user account data onto the admin room Signed-off-by: strawberry --- src/admin/user/mod.rs | 43 +++++++++++++- src/admin/user/user_commands.rs | 102 +++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/src/admin/user/mod.rs b/src/admin/user/mod.rs index 031dd97a..e715daf0 100644 --- a/src/admin/user/mod.rs +++ b/src/admin/user/mod.rs @@ -1,7 +1,8 @@ pub(crate) mod user_commands; use clap::Subcommand; -use ruma::events::room::message::RoomMessageEventContent; +use ruma::{events::room::message::RoomMessageEventContent, RoomId}; +use user_commands::{delete_room_tag, get_room_tags, put_room_tag}; use self::user_commands::{create, deactivate, deactivate_all, list, list_joined_rooms, reset_password}; use crate::Result; @@ -62,6 +63,32 @@ pub(crate) enum UserCommand { ListJoinedRooms { user_id: String, }, + + /// - Puts a room tag for the specified user and room ID. + /// + /// This is primarily useful if you'd like to set your admin room + /// to the special "System Alerts" section in Element as a way to + /// permanently see your admin room without it being buried away in your + /// favourites or rooms. To do this, you would pass your user, your admin + /// room's internal ID, and the tag name `m.server_notice`. + PutRoomTag { + user_id: String, + room_id: Box, + tag: String, + }, + + /// - Deletes the room tag for the specified user and room ID + DeleteRoomTag { + user_id: String, + room_id: Box, + tag: String, + }, + + /// - Gets all the room tags for the specified user and room ID + GetRoomTags { + user_id: String, + room_id: Box, + }, } pub(crate) async fn process(command: UserCommand, body: Vec<&str>) -> Result { @@ -85,5 +112,19 @@ pub(crate) async fn process(command: UserCommand, body: Vec<&str>) -> Result list_joined_rooms(body, user_id).await?, + UserCommand::PutRoomTag { + user_id, + room_id, + tag, + } => put_room_tag(body, user_id, room_id, tag).await?, + UserCommand::DeleteRoomTag { + user_id, + room_id, + tag, + } => delete_room_tag(body, user_id, room_id, tag).await?, + UserCommand::GetRoomTags { + user_id, + room_id, + } => get_room_tags(body, user_id, room_id).await?, }) } diff --git a/src/admin/user/user_commands.rs b/src/admin/user/user_commands.rs index 4869af9a..9bd963dd 100644 --- a/src/admin/user/user_commands.rs +++ b/src/admin/user/user_commands.rs @@ -1,8 +1,15 @@ -use std::fmt::Write as _; +use std::{collections::BTreeMap, fmt::Write as _}; use api::client::{join_room_by_id_helper, leave_all_rooms}; use conduit::utils; -use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId, OwnedUserId, RoomId, UserId}; +use ruma::{ + events::{ + room::message::RoomMessageEventContent, + tag::{TagEvent, TagEventContent, TagInfo}, + RoomAccountDataEventType, + }, + OwnedRoomId, OwnedUserId, RoomId, UserId, +}; use tracing::{error, info, warn}; use crate::{ @@ -315,3 +322,94 @@ pub(crate) async fn list_joined_rooms(_body: Vec<&str>, user_id: String) -> Resu Ok(RoomMessageEventContent::text_html(output_plain, output_html)) } + +pub(crate) async fn put_room_tag( + _body: Vec<&str>, user_id: String, room_id: Box, tag: String, +) -> Result { + let user_id = parse_active_local_user_id(&user_id)?; + + let event = services() + .account_data + .get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?; + + let mut tags_event = event.map_or_else( + || TagEvent { + content: TagEventContent { + tags: BTreeMap::new(), + }, + }, + |e| serde_json::from_str(e.get()).expect("Bad account data in database for user {user_id}"), + ); + + tags_event + .content + .tags + .insert(tag.clone().into(), TagInfo::new()); + + services().account_data.update( + Some(&room_id), + &user_id, + RoomAccountDataEventType::Tag, + &serde_json::to_value(tags_event).expect("to json value always works"), + )?; + + Ok(RoomMessageEventContent::text_plain(format!( + "Successfully updated room account data for {user_id} and room {room_id} with tag {tag}" + ))) +} + +pub(crate) async fn delete_room_tag( + _body: Vec<&str>, user_id: String, room_id: Box, tag: String, +) -> Result { + let user_id = parse_active_local_user_id(&user_id)?; + + let event = services() + .account_data + .get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?; + + let mut tags_event = event.map_or_else( + || TagEvent { + content: TagEventContent { + tags: BTreeMap::new(), + }, + }, + |e| serde_json::from_str(e.get()).expect("Bad account data in database for user {user_id}"), + ); + + tags_event.content.tags.remove(&tag.clone().into()); + + services().account_data.update( + Some(&room_id), + &user_id, + RoomAccountDataEventType::Tag, + &serde_json::to_value(tags_event).expect("to json value always works"), + )?; + + Ok(RoomMessageEventContent::text_plain(format!( + "Successfully updated room account data for {user_id} and room {room_id}, deleting room tag {tag}" + ))) +} + +pub(crate) async fn get_room_tags( + _body: Vec<&str>, user_id: String, room_id: Box, +) -> Result { + let user_id = parse_active_local_user_id(&user_id)?; + + let event = services() + .account_data + .get(Some(&room_id), &user_id, RoomAccountDataEventType::Tag)?; + + let tags_event = event.map_or_else( + || TagEvent { + content: TagEventContent { + tags: BTreeMap::new(), + }, + }, + |e| serde_json::from_str(e.get()).expect("Bad account data in database for user {user_id}"), + ); + + Ok(RoomMessageEventContent::text_html( + format!("
\n{:?}\n
", tags_event.content.tags), + format!("```\n{:?}\n```", tags_event.content.tags), + )) +}