use std::{cell::RefCell, collections::HashSet, str::FromStr, sync::Mutex}; use sled::Db; use crate::{ channels::ChannelReference, discord::{self, delete_on_discord, edit_on_discord, forward_to_discord}, matrix::{self, delete_on_matrix, edit_on_matrix, forward_to_matrix}, messages::{DeletedMessage, EditedMessage, MessageReference, SentMessage}, }; pub struct Bridgers { pub db: Db, pub discord: Mutex>>, pub matrix: Mutex>>, } impl Bridgers { pub fn new() -> Self { let db = sled::open("data/phoebe.sled").expect("Failed to open database"); Self { db, discord: Mutex::new(RefCell::new(None)), matrix: Mutex::new(RefCell::new(None)), } } fn get_linked_discord_channel(&self, source: &MessageReference) -> Option { let discord_channels = self .db .open_tree("discord_channels") .expect("Failed to open discord channels tree"); match source { MessageReference::Matrix(room_id, _) => { let channel_ref = ChannelReference::Matrix(room_id.to_string()); let channel_ref = bincode::serialize(&channel_ref).unwrap(); discord_channels .get(channel_ref) .unwrap() .map(|bytes| bincode::deserialize::(&bytes).unwrap()) .map(discord::ChannelId) } _ => None, } } fn get_linked_matrix_room(&self, source: &MessageReference) -> Option { let matrix_channels = self .db .open_tree("matrix_channels") .expect("Failed to open matrix channels tree"); match source { MessageReference::Discord(channel_id, _) => { let channel_ref = ChannelReference::Discord(*channel_id); let channel_ref = bincode::serialize(&channel_ref).unwrap(); matrix_channels .get(channel_ref) .unwrap() .map(|bytes| bincode::deserialize::(&bytes).unwrap()) .as_deref() .map(matrix::RoomId::from_str) .map(Result::unwrap) } _ => None, } } fn get_related_messages(&self, source: &MessageReference) -> Option> { let message_relations = self .db .open_tree("message_relations") .expect("Failed to open relations tree"); let key = bincode::serialize(source).expect("Failed to serialize message reference"); message_relations .get(key) .expect("Failed to retrieve message references") .map(|r| { bincode::deserialize::>(&r) .expect("Failed to deserialize message references") }) } fn get_related_discord_message(&self, source: &MessageReference) -> Option<(u64, u64)> { if let MessageReference::Discord(channel_id, message_id) = source { return Some((*channel_id, *message_id)); } if let Some(relations) = self.get_related_messages(source) { for relation in relations { if let MessageReference::Discord(channel_id, message_id) = relation { return Some((channel_id, message_id)); } } } None } fn get_related_matrix_message(&self, source: &MessageReference) -> Option { if let MessageReference::Matrix(_, event_id) = source { return Some(matrix::EventId::from_str(event_id).unwrap()); } if let Some(relations) = self.get_related_messages(source) { for relation in relations { if let MessageReference::Matrix(_, event_id) = relation { return Some(matrix::EventId::from_str(&event_id).unwrap()); } } } None } pub fn link_channels(&self, channels: &[ChannelReference]) { let discord_channels = self .db .open_tree("discord_channels") .expect("Failed to open discord channels tree"); let matrix_channels = self .db .open_tree("matrix_channels") .expect("Failed to open matrix channels tree"); for channel in channels.iter() { let other_channels = channels .iter() .filter(|r| r != &channel) .collect::>(); match channel { ChannelReference::Discord(channel_id) => { for other_channel in other_channels { let key = bincode::serialize(other_channel).unwrap(); let value = bincode::serialize(channel_id).unwrap(); discord_channels.insert(key, value).unwrap(); } } ChannelReference::Matrix(room_id) => { for other_channel in other_channels { let key = bincode::serialize(other_channel).unwrap(); let value = bincode::serialize(room_id.as_str()).unwrap(); matrix_channels.insert(key, value).unwrap(); } } } } } fn store_related_messages(&self, related_messages: &[MessageReference]) { let tree = self .db .open_tree("message_relations") .expect("Failed to open relations tree"); for source in related_messages { let relations = related_messages .iter() .filter(|r| r != &source) .collect::>(); let key = bincode::serialize(source).expect("Failed to serialize message reference"); let value = bincode::serialize(&relations).expect("Failed to serialize message relations"); tree.insert(key, value) .expect("Failed to store message relations"); } } pub async fn send_message(&self, message: SentMessage) { let mut related_messages = vec![message.source.clone()]; if let Some(discord) = self.discord.lock().unwrap().borrow().as_ref() { if let Some(channel) = self.get_linked_discord_channel(&message.source) { let reply = message .replies_to .as_ref() .and_then(|r| self.get_related_discord_message(r)); if let Some(m) = forward_to_discord(discord, channel, &message, reply).await { related_messages.push(m); } } } if let Some(matrix) = self.matrix.lock().unwrap().borrow().as_ref() { if let Some(room_id) = self.get_linked_matrix_room(&message.source) { let reply = message .replies_to .as_ref() .and_then(|r| self.get_related_matrix_message(r)); if let Some(m) = forward_to_matrix(matrix, room_id, &message, reply).await { related_messages.push(m); } } } self.store_related_messages(&related_messages); } pub async fn edit_message(&self, message: EditedMessage) { if let Some(related_messages) = self.get_related_messages(&message.replacing) { let mut new_related_messages = HashSet::new(); related_messages.iter().for_each(|r| { new_related_messages.insert(r.clone()); }); for related_message in related_messages.iter() { match related_message { MessageReference::Discord(channel_id, message_id) => { if let Some(discord) = self.discord.lock().unwrap().borrow().as_ref() { let channel_id = discord::ChannelId(*channel_id); let message_id = discord::MessageId(*message_id); if let Some(m) = edit_on_discord(discord, channel_id, message_id, &message).await { new_related_messages.insert(m); } } } MessageReference::Matrix(room_id, event_id) => { if let Some(matrix) = self.matrix.lock().unwrap().borrow().as_ref() { let room_id = matrix::RoomId::from_str(room_id).unwrap(); let event_id = matrix::EventId::from_str(event_id).unwrap(); if let Some(m) = edit_on_matrix(matrix, room_id, event_id, &message).await { new_related_messages.insert(m); } } } } } self.store_related_messages(&new_related_messages.into_iter().collect::>()) } } pub async fn delete_message(&self, message: DeletedMessage) { if let Some(related_messages) = self.get_related_messages(&message.reference) { for related_message in related_messages.iter() { // TODO: What do we want to do with the success / failure ??? match related_message { MessageReference::Discord(channel_id, message_id) => { if let Some(discord) = self.discord.lock().unwrap().borrow().as_ref() { let channel_id = discord::ChannelId(*channel_id); let message_id = discord::MessageId(*message_id); let _success = delete_on_discord(discord, channel_id, message_id, &message).await; } } MessageReference::Matrix(room_id, event_id) => { if let Some(matrix) = self.matrix.lock().unwrap().borrow().as_ref() { let room_id = matrix::RoomId::from_str(room_id).unwrap(); let event_id = matrix::EventId::from_str(event_id).unwrap(); let _success = delete_on_matrix(matrix, room_id, event_id, &message).await; } } } } } } }