use std::sync::Arc; use async_trait::async_trait; use futures::StreamExt; use matrix_sdk::Client; use matrix_sdk::config::SyncSettings; use matrix_sdk::event_handler::Ctx; use matrix_sdk::room::Room; use matrix_sdk::ruma::events::room::message::{SyncRoomMessageEvent, RoomMessageEventContent}; use matrix_sdk::ruma::{RoomId, OwnedUserId}; use tokio::select; use crate::linkmap::Linkmap; use crate::message::{Id, Message}; use crate::{message::{Sender, Receiver}, supervisor::{Task, TaskResult}}; pub struct MatrixTask { rooms: Arc>, user: OwnedUserId, passwd: String, } impl MatrixTask { pub fn new(user: OwnedUserId, passwd: String, rooms: Linkmap) -> Self { Self { user, passwd, rooms: Arc::new(rooms) } } } struct Context { id: Id, tx: Sender, rooms: Arc>, } #[async_trait] impl Task for MatrixTask { async fn start(&self, id: Id, tx: Sender, mut rx: Receiver) -> TaskResult { let client = Client::builder() .server_name(&self.user.server_name()) .build().await?; client.login_username(&self.user, &self.passwd) .send() .await?; client.add_event_handler_context(Arc::new(Context { id, tx, rooms: self.rooms.clone() })); client.add_event_handler(handle_matrix_msg); let mut sync_stream = Box::pin(client.sync_stream(SyncSettings::default()).await); loop { select! { response = sync_stream.next() => match response { Some(Ok(_)) => (), Some(Err(e)) => return Err(e.into()), None => return Err("Lost connection to Matrix server".into()) }, message = rx.recv() => match message { Ok(msg) => handle_bridge_msg(id, &client, &self.rooms, msg).await?, Err(e) => return Err(e.into()) } } } } } async fn handle_matrix_msg(ev: SyncRoomMessageEvent, client: Client, room: Room, ctx: Ctx>) -> TaskResult { if Some(ev.sender()) == client.user_id() { return Ok(()) } let SyncRoomMessageEvent::Original(ev) = ev else { return Ok(()) }; let Ok(Some(sender)) = room.get_member(&ev.sender).await else { return Ok(()) }; let Some(link) = ctx.rooms.get_link(room.room_id().as_str()) else { return Ok(()) }; let body = ev.content.body(); ctx.tx.send(Message { origin: (ctx.id, room.room_id().to_string()), link: link.clone(), author: sender.name().to_owned(), content: body.to_owned(), })?; Ok(()) } async fn handle_bridge_msg(id: Id, client: &Client, links: &Linkmap, msg: Message) -> TaskResult { let content = RoomMessageEventContent::text_plain( format!("<{}> {}", msg.author, msg.content) ); for room in links.get_channels(&msg.link) { if msg.origin.0 == id && &msg.origin.1 == room { continue; } let Ok(room) = <&RoomId>::try_from(room.as_str()) else { continue }; let Some(Room::Joined(room)) = client.get_room(room) else { continue }; room.send(content.clone(), None).await?; } Ok(()) }