Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(matrix/user): add membership-related room API calls #21

Merged
merged 3 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions crates/core/src/room/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use tracing::instrument;
use validator::Validate;

use matrix::client::resources::room::{
CreateRoomCreationContent, CreateRoomRequestBody, Room as MatrixRoom, RoomPreset,
Room as MatrixRoom, RoomPreset, RoomCreationContent, CreateRoomBody,
};
use matrix::Client as MatrixAdminClient;

Expand Down Expand Up @@ -46,12 +46,13 @@ impl RoomService {
match MatrixRoom::create(
&self.admin,
access_token.to_string(),
CreateRoomRequestBody {
creation_content: CreateRoomCreationContent { m_federate: false },
CreateRoomBody {
creation_content: Some(RoomCreationContent { federate: false }),
preset: Some(RoomPreset::PublicChat),
name: dto.name,
preset: RoomPreset::PublicChat,
room_alias_name: dto.alias,
topic: dto.topic,
..Default::default()
},
)
.await
Expand Down
3 changes: 3 additions & 0 deletions crates/matrix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ hmac = "0.12.1"
serde_path_to_error = "0.1.14"
serde_qs = "0.12.0"
sha1 = "0.10.6"
ruma-events = { version = "0.27.11", features = ["html", "markdown"] }
ruma-common = { version = "0.12.1", features = ["rand"] }
ruma-macros = "0.12.0"

# Workspace Dependencies
anyhow = { workspace = true }
Expand Down
282 changes: 260 additions & 22 deletions crates/matrix/src/client/resources/room.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,106 @@
use anyhow::Result;
use ruma_common::{serde::Raw, OwnedUserId, RoomOrAliasId, RoomId};
use ruma_events::{AnyInitialStateEvent, room::power_levels::RoomPowerLevelsEventContent};
use serde::{Deserialize, Serialize};
use tracing::instrument;

use crate::admin::resources::user_id::UserId;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum RoomPreset {
PrivateChat,
PublicChat,
TrustedPrivateChat,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateRoomCreationContent {
#[derive(Clone, Debug, Default, Serialize)]
pub struct RoomCreationContent {
#[serde(rename = "m.federate")]
pub m_federate: bool,
pub federate: bool,
}

#[derive(Clone, Debug, Default, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum RoomVisibility {
Public,
#[default]
Private,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateRoomRequestBody {
pub creation_content: CreateRoomCreationContent,
#[derive(Clone, Debug, Default, Serialize)]
pub struct CreateRoomBody {
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub initial_state: Vec<Raw<AnyInitialStateEvent>>,

#[serde(skip_serializing_if = "Option::is_none")]
pub creation_content: Option<RoomCreationContent>,

#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub invite: Vec<OwnedUserId>,

pub is_direct: bool,

#[serde(skip_serializing_if = "String::is_empty")]
pub name: String,
pub preset: RoomPreset,

#[serde(skip_serializing_if = "Option::is_none")]
pub power_override: Option<RoomPowerLevelsEventContent>,

#[serde(skip_serializing_if = "Option::is_none")]
pub preset: Option<RoomPreset>,

#[serde(skip_serializing_if = "String::is_empty")]
pub room_alias_name: String,

#[serde(skip_serializing_if = "String::is_empty")]
pub topic: String,

#[serde(skip_serializing_if = "Option::is_none")]
pub visibility: Option<RoomVisibility>,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct JoinRoomBody {
#[serde(skip_serializing_if = "String::is_empty")]
pub reason: String,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct ForgetRoomBody {
#[serde(skip_serializing_if = "String::is_empty")]
pub reason: String,
}

#[derive(Clone, Debug, Serialize)]
pub struct LeaveRoomBody {
#[serde(skip_serializing_if = "String::is_empty")]
pub reason: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CreateRoomResponseBody {
#[derive(Clone, Debug, Serialize)]
pub struct RoomKickOrBanBody {
#[serde(skip_serializing_if = "String::is_empty")]
pub reason: String,

pub user_id: OwnedUserId,
}

#[derive(Clone, Debug, Deserialize)]
pub struct CreateRoomResponse {
pub room_id: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
pub struct JoinRoomResponse {
pub room_id: String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct RoomApiError {
pub errcode: String,
pub error: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Room {
pub device_id: String,
pub is_guest: bool,
pub user_id: UserId,
}
pub struct Room;

impl Room {
/// Create a new room with various configuration options.
Expand All @@ -53,13 +110,194 @@ impl Room {
pub async fn create(
client: &crate::http::Client,
access_token: impl Into<String>,
creation_content: CreateRoomRequestBody,
) -> Result<CreateRoomResponseBody> {
body: CreateRoomBody,
) -> Result<CreateRoomResponse> {
let mut tmp = (*client).clone();
tmp.set_token(access_token)?;

let resp = tmp
.post_json("/_matrix/client/v3/createRoom", &body)
.await?;

if resp.status().is_success() {
return Ok(resp.json().await?);
}

let error = resp.json::<RoomApiError>().await?;

Err(anyhow::anyhow!(error.error))
}

/// Join a particular room, if we are allowed to participate.
///
/// Refer: https://spec.matrix.org/v1.9/client-server-api/#joining-rooms
#[instrument(skip(client, access_token))]
pub async fn join(
client: &crate::http::Client,
access_token: impl Into<String>,
alias_or_id: &RoomOrAliasId,
body: JoinRoomBody,
) -> Result<JoinRoomResponse> {
let mut tmp = (*client).clone();
tmp.set_token(access_token)?;

let resp = tmp
.post_json(
format!("/_matrix/client/v3/join/{alias_or_id}"),
&body,
)
.await?;

if resp.status().is_success() {
return Ok(resp.json().await?);
}

let error = resp.json::<RoomApiError>().await?;

Err(anyhow::anyhow!(error.error))
}

/// Forget a particular room.
/// This will prevent the user from accessing the history of the room.
///
/// Refer: https://spec.matrix.org/v1.9/client-server-api/#leaving-rooms
#[instrument(skip(client, access_token))]
pub async fn forget(
client: &crate::http::Client,
access_token: impl Into<String>,
room_id: &RoomId,
body: ForgetRoomBody,
) -> Result<()> {
let mut tmp = (*client).clone();
tmp.set_token(access_token)?;

let resp = tmp
.post_json(
format!("/_matrix/client/v3/rooms/{room_id}/forget"),
&body,
)
.await?;

if resp.status().is_success() {
return Ok(());
}

let error = resp.json::<RoomApiError>().await?;

Err(anyhow::anyhow!(error.error))
}

/// Leave a particular room.
/// They are still allowed to retrieve the history which they were previously allowed to see.
///
/// Refer: https://spec.matrix.org/v1.9/client-server-api/#leaving-rooms
#[instrument(skip(client, access_token))]
pub async fn leave(
client: &crate::http::Client,
access_token: impl Into<String>,
room_id: &RoomId,
body: LeaveRoomBody,
) -> Result<()> {
let mut tmp = (*client).clone();
tmp.set_token(access_token)?;

let resp = tmp
.post_json(
format!("/_matrix/client/v3/rooms/{room_id}/leave"),
&body,
)
.await?;

if resp.status().is_success() {
return Ok(resp.json().await?);
}

let error = resp.json::<RoomApiError>().await?;

Err(anyhow::anyhow!(error.error))
}

/// Kick a user from a particular room.
/// The caller must have the required power level in order to perform this operation.
///
/// Refer: https://spec.matrix.org/v1.9/client-server-api/#leaving-rooms
#[instrument(skip(client, access_token))]
pub async fn kick(
client: &crate::http::Client,
access_token: impl Into<String>,
room_id: &RoomId,
body: RoomKickOrBanBody,
) -> Result<()> {
let mut tmp = (*client).clone();
tmp.set_token(access_token)?;

let resp = tmp
.post_json(
format!("/_matrix/client/v3/rooms/{room_id}/kick"),
&body,
)
.await?;

if resp.status().is_success() {
return Ok(resp.json().await?);
}

let error = resp.json::<RoomApiError>().await?;

Err(anyhow::anyhow!(error.error))
}

/// Ban a user from a particular room.
/// This will kick them too if they are still a member.
/// The caller must have the required power level in order to perform this operation.
///
/// Refer: https://spec.matrix.org/v1.9/client-server-api/#leaving-rooms
#[instrument(skip(client, access_token))]
pub async fn ban(
client: &crate::http::Client,
access_token: impl Into<String>,
room_id: &RoomId,
body: RoomKickOrBanBody,
) -> Result<()> {
let mut tmp = (*client).clone();
tmp.set_token(access_token)?;

let resp = tmp
.post_json(
format!("/_matrix/client/v3/rooms/{room_id}/ban"),
&body,
)
.await?;

if resp.status().is_success() {
return Ok(resp.json().await?);
}

let error = resp.json::<RoomApiError>().await?;

Err(anyhow::anyhow!(error.error))
}

/// Unban a user from a particular room.
/// This will allow them to re-join or be re-invited.
/// The caller must have the required power level in order to perform this operation.
///
/// Refer: https://spec.matrix.org/v1.9/client-server-api/#banning-users-in-a-room
#[instrument(skip(client, access_token))]
pub async fn unban(
client: &crate::http::Client,
access_token: impl Into<String>,
room_id: &RoomId,
body: RoomKickOrBanBody,
) -> Result<()> {
let mut tmp = (*client).clone();
tmp.set_token(access_token)?;

let resp = tmp
.post_json("/_matrix/client/v3/createRoom", &creation_content)
.post_json(
format!("/_matrix/client/v3/rooms/{room_id}/unban"),
&body,
)
.await?;

if resp.status().is_success() {
Expand Down
1 change: 1 addition & 0 deletions crates/test/src/matrix/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
mod shared_token_registration;
mod room;