Skip to content

Commit

Permalink
Invite the virtual user into the bridged room if the user is not alre…
Browse files Browse the repository at this point in the history
…ady present
  • Loading branch information
exul committed Mar 14, 2017
1 parent 8b75a57 commit fe1958c
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 30 deletions.
2 changes: 2 additions & 0 deletions src/matrix-rocketchat/api/matrix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub trait MatrixApi: Send + Sync + MatrixApiClone {
fn forget_room(&self, matrix_room_id: RoomId) -> Result<()>;
/// Get the list of members for this room.
fn get_room_members(&self, matrix_room_id: RoomId) -> Result<Vec<MemberEvent>>;
/// Invite a user to a room.
fn invite(&self, matrix_room_id: RoomId, matrix_user_id: UserId) -> Result<()>;
/// Join a room with a user.
fn join(&self, matrix_room_id: RoomId, matrix_user_id: UserId) -> Result<()>;
/// Leave a room.
Expand Down
20 changes: 20 additions & 0 deletions src/matrix-rocketchat/api/matrix/r0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use reqwest::StatusCode;
use ruma_client_api::Endpoint;
use ruma_client_api::r0::account::register::{self, Endpoint as RegisterEndpoint};
use ruma_client_api::r0::membership::forget_room::{self, Endpoint as ForgetRoomEndpoint};
use ruma_client_api::r0::membership::invite_user::{self, Endpoint as InviteUserEndpoint};
use ruma_client_api::r0::membership::join_room_by_id::{self, Endpoint as JoinRoomByIdEndpoint};
use ruma_client_api::r0::membership::leave_room::{self, Endpoint as LeaveRoomEndpoint};
use ruma_client_api::r0::room::create_room::{self, Endpoint as CreateRoomEndpoint, RoomPreset};
Expand Down Expand Up @@ -112,6 +113,25 @@ impl super::MatrixApi for MatrixApi {
Ok(room_member_events.chunk)
}

fn invite(&self, matrix_room_id: RoomId, matrix_user_id: UserId) -> Result<()> {
let path_params = invite_user::PathParams { room_id: matrix_room_id.clone() };
let endpoint = self.base_url.clone() + &InviteUserEndpoint::request_path(path_params);
let params = self.params_hash();
let body_params = invite_user::BodyParams { user_id: matrix_user_id.clone() };
let payload = serde_json::to_string(&body_params).chain_err(|| ErrorKind::InvalidJSON("Could not serialize invite user params".to_string()))?;

let (body, status_code) = RestApi::call_matrix(InviteUserEndpoint::method(), &endpoint, &payload, &params)?;
if !status_code.is_success() {
return Err(build_error(&endpoint, &body, &status_code));
}

debug!(self.logger,
"User {} successfully invited into room {}",
matrix_room_id,
matrix_user_id);
Ok(())
}

fn join(&self, matrix_room_id: RoomId, matrix_user_id: UserId) -> Result<()> {
let path_params = join_room_by_id::PathParams { room_id: matrix_room_id.clone() };
let endpoint = self.base_url.clone() + &JoinRoomByIdEndpoint::request_path(path_params);
Expand Down
13 changes: 12 additions & 1 deletion src/matrix-rocketchat/db/user_in_room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,22 @@ impl UserInRoom {
UserInRoom::find(connection, &user_in_room.matrix_user_id, &user_in_room.matrix_room_id)
}

/// Find a `UserInRoom` by its matrix user ID and its matrix room ID
/// Find a `UserInRoom` by its matrix user ID and its matrix room ID, return an error if the user is not found
pub fn find(connection: &SqliteConnection, matrix_user_id: &UserId, matrix_room_id: &RoomId) -> Result<UserInRoom> {
let user_in_room = users_in_rooms::table.find((matrix_user_id, matrix_room_id))
.first(connection)
.chain_err(|| ErrorKind::DBSelectError)?;
Ok(user_in_room)
}

/// Find a `UserInRoom` by its matrix user ID and its matrix room ID.
pub fn find_by_matrix_user_id_and_matrix_room_id(connection: &SqliteConnection,
matrix_user_id: &UserId,
matrix_room_id: &RoomId)
-> Result<Option<UserInRoom>> {
let user_in_room = users_in_rooms::table.find((matrix_user_id, matrix_room_id))
.load(connection)
.chain_err(|| ErrorKind::DBSelectError)?;
Ok(user_in_room.into_iter().next())
}
}
1 change: 1 addition & 0 deletions src/matrix-rocketchat/handlers/events/command_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ impl<'a> CommandHandler<'a> {
let bot_matrix_user_id = self.config.matrix_bot_user_id()?;
let message = t!(["admin_room", "room_successfully_bridged"])
.with_vars(vec![("channel_name", channel.name.clone())]);
info!(self.logger, "Successfully bridged room {}", channel.id.clone());
self.matrix_api
.send_text_message_event(event.room_id.clone(), bot_matrix_user_id, message.l(&user.language))
})
Expand Down
1 change: 1 addition & 0 deletions src/matrix-rocketchat/handlers/events/event_dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl<'a> EventDispatcher<'a> {
msg = msg + " caused by: " + &format!("{}", err);
}

debug!(self.logger, msg);
self.matrix_api.send_text_message_event(room_id, matrix_bot_id, user_message.l(&language))
}
}
72 changes: 50 additions & 22 deletions src/matrix-rocketchat/handlers/rocketchat/forwarder.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::convert::TryFrom;

use diesel::Connection;
use diesel::sqlite::SqliteConnection;
use ruma_identifiers::UserId;
use ruma_identifiers::{RoomId, UserId};
use slog::Logger;

use api::MatrixApi;
use api::rocketchat::Message;
use config::Config;
use db::{NewUser, NewUserOnRocketchatServer, RocketchatServer, Room, User, UserOnRocketchatServer};
use db::{NewUser, NewUserInRoom, NewUserOnRocketchatServer, RocketchatServer, Room, User, UserInRoom,
UserOnRocketchatServer};
use errors::*;
use i18n::DEFAULT_LANGUAGE;

Expand All @@ -26,28 +28,44 @@ pub struct Forwarder<'a> {
impl<'a> Forwarder<'a> {
/// Send a message to the Matrix channel.
pub fn send(&self, rocketchat_server: &RocketchatServer, message: &Message) -> Result<()> {
let user_on_rocketchat_server = match UserOnRocketchatServer::find_by_rocketchat_user_id(self.connection,
rocketchat_server.id,
message.user_id
.clone())? {
Some(user_on_rocketchat_server) => user_on_rocketchat_server,
None => self.create_virtual_user_on_rocketchat_server(rocketchat_server.id, message)?,
};
self.connection
.transaction(|| {
let user_on_rocketchat_server =
match UserOnRocketchatServer::find_by_rocketchat_user_id(self.connection,
rocketchat_server.id,
message.user_id
.clone())? {
Some(user_on_rocketchat_server) => user_on_rocketchat_server,
None => self.create_virtual_user_on_rocketchat_server(rocketchat_server.id, message)?,
};

let room = match Room::find_by_rocketchat_room_id(self.connection,
rocketchat_server.id,
message.channel_id.clone())? {
Some(room) => room,
None => {
debug!(self.logger,
"Ignoring message from Rocket.Chat channel `{}`, because the channel is not bridged.",
message.channel_id);
return Ok(());
}
};

let room =
match Room::find_by_rocketchat_room_id(self.connection, rocketchat_server.id, message.channel_id.clone())? {
Some(room) => room,
None => {
debug!(self.logger,
"Ignoring message from Rocket.Chat channel `{}`, because the channel is not bridged.",
message.channel_id);
return Ok(());

if UserInRoom::find_by_matrix_user_id_and_matrix_room_id(self.connection,
&user_on_rocketchat_server.matrix_user_id,
&room.matrix_room_id)
?
.is_none() {
self.add_virtual_user_to_room(user_on_rocketchat_server.matrix_user_id.clone(),
room.matrix_room_id.clone())?;
}
};

self.matrix_api.send_text_message_event(room.matrix_room_id,
user_on_rocketchat_server.matrix_user_id,
message.text.clone())
self.matrix_api.send_text_message_event(room.matrix_room_id,
user_on_rocketchat_server.matrix_user_id,
message.text.clone())
})
.map_err(Error::from)
}

fn create_virtual_user_on_rocketchat_server(&self,
Expand All @@ -56,7 +74,7 @@ impl<'a> Forwarder<'a> {
-> Result<UserOnRocketchatServer> {
let user_id_local_part = format!("@{}_{}_{}", self.config.sender_localpart, message.user_id, rocketchat_server_id);
self.matrix_api.register(user_id_local_part.clone())?;
let user_id = format!("{}:{}", user_id_local_part, self.config.hs_url);
let user_id = format!("{}:{}", user_id_local_part, self.config.hs_domain);
let matrix_user_id = UserId::try_from(&user_id).chain_err(|| ErrorKind::InvalidUserId(user_id))?;

let new_user = NewUser {
Expand All @@ -77,4 +95,14 @@ impl<'a> Forwarder<'a> {

UserOnRocketchatServer::upsert(self.connection, &new_user_on_rocketchat_server)
}

fn add_virtual_user_to_room(&self, matrix_user_id: UserId, matrix_room_id: RoomId) -> Result<()> {
self.matrix_api.invite(matrix_room_id.clone(), matrix_user_id.clone())?;
let new_user_in_room = NewUserInRoom {
matrix_user_id: matrix_user_id,
matrix_room_id: matrix_room_id,
};
UserInRoom::insert(self.connection, &new_user_in_room)?;
Ok(())
}
}
2 changes: 1 addition & 1 deletion tests/admin_commands_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn sucessfully_list_rocketchat_rooms() {
.with_connected_admin_room()
.with_logged_in_user()
.with_custom_channel_list(channels)
.with_bridged_room(("bridged_room", "@spec_user:localhost"))
.with_bridged_room(("bridged_channel", "spec_user"))
.run();

// discard welcome message
Expand Down
19 changes: 13 additions & 6 deletions tests/rocketchat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,21 @@ use matrix_rocketchat_test::{MessageForwarder, RS_TOKEN, Test, default_timeout,
use router::Router;
use reqwest::{Method, StatusCode};
use ruma_client_api::Endpoint;
use ruma_client_api::r0::membership::invite_user::Endpoint as InviteUserEndpoint;
use ruma_client_api::r0::send::send_message_event::Endpoint as SendMessageEventEndpoint;
use ruma_identifiers::{RoomId, UserId};
use serde_json::to_string;

#[test]
fn successfully_forwards_a_text_message_to_matrix() {
let (message_forwarder, receiver) = MessageForwarder::new();
let (invite_forwarder, invite_receiver) = MessageForwarder::new();
let mut matrix_router = Router::new();
matrix_router.put(SendMessageEventEndpoint::router_path(), message_forwarder, "send_message_event");
matrix_router.post(InviteUserEndpoint::router_path(), invite_forwarder, "invite_user");

// let mut channels = HashMap::new();
// channels.insert("spec_channel", vec!["spec_user"]);
let mut channels = HashMap::new();
channels.insert("spec_channel", vec!["spec_user"]);

let test = Test::new()
.with_matrix_routes(matrix_router)
Expand All @@ -53,6 +56,10 @@ fn successfully_forwards_a_text_message_to_matrix() {

helpers::simulate_message_from_rocketchat(&test.config.as_url, &payload);

// receive the invite message
let invite_message = invite_receiver.recv_timeout(default_timeout()).unwrap();
assert!(invite_message.contains("@rocketchat_new_user_id_1:localhost"));

// discard welcome message
receiver.recv_timeout(default_timeout()).unwrap();
// discard connect message
Expand All @@ -63,7 +70,6 @@ fn successfully_forwards_a_text_message_to_matrix() {
receiver.recv_timeout(default_timeout()).unwrap();

let message_received_by_matrix = receiver.recv_timeout(default_timeout()).unwrap();
println!("MSG: {}", message_received_by_matrix);
assert!(message_received_by_matrix.contains("spec_message"));

let connection = test.connection_pool.get().unwrap();
Expand All @@ -77,17 +83,18 @@ fn successfully_forwards_a_text_message_to_matrix() {
let users = bridged_room.users(&connection).unwrap();
assert_eq!(users.len(), 3);

let bot_user_id = UserId::try_from("@rocketchat:localhost").unwrap();
let spec_user_id = UserId::try_from("@spec_user:localhost").unwrap();
let users_iter = users.iter();
let user_ids = users_iter.filter_map(|u| if u.matrix_user_id != UserId::try_from("@rocketchat:localhost").unwrap() &&
u.matrix_user_id != UserId::try_from("@spec_user@localhost").unwrap() {
let user_ids = users_iter.filter_map(|u| if u.matrix_user_id != bot_user_id && u.matrix_user_id != spec_user_id {
Some(u.matrix_user_id.clone())
} else {
None
})
.collect::<Vec<UserId>>();
let new_user_id = user_ids.iter().next().unwrap();

// the virtual user was create with the Rocket.Chat user id
// the virtual user was create with the Rocket.Chat user ID
let user_on_rocketchat = UserOnRocketchatServer::find(&connection, new_user_id, rocketchat_server_id).unwrap();
assert_eq!(user_on_rocketchat.rocketchat_user_id.unwrap(), "new_user_id".to_string());
}
Expand Down

0 comments on commit fe1958c

Please sign in to comment.