abridged/src/bridge_matrix/mod.rs

103 lines
2.8 KiB
Rust

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<Linkmap<String>>,
user: OwnedUserId,
passwd: String,
}
impl MatrixTask {
pub fn new(user: OwnedUserId, passwd: String, rooms: Linkmap<String>) -> Self {
Self {
user,
passwd,
rooms: Arc::new(rooms)
}
}
}
struct Context {
id: Id,
tx: Sender,
rooms: Arc<Linkmap<String>>,
}
#[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<Arc<Context>>) -> 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<String>, 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(())
}