103 lines
2.8 KiB
Rust
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(())
|
|
}
|