phoebe/src/main.rs

169 lines
4.8 KiB
Rust

use std::{
cell::RefCell,
str::FromStr,
sync::{Arc, Mutex},
};
use discord::create_discord_client;
use matrix::create_matrix_client;
use matrix_sdk::{
ruma::{
events::{room::message::MessageEventContent, AnyMessageEventContent},
RoomId,
},
SyncSettings,
};
use serde::{Deserialize, Serialize};
mod discord;
mod matrix;
mod message_ast;
use message_ast::MessageContent;
use serenity::model::id::ChannelId;
use tokio::sync::mpsc;
#[derive(Serialize, Deserialize)]
pub enum MessageReference {
Discord(u64, u64),
Matrix(String, String),
}
pub struct SentMessage {
pub source: MessageReference,
pub content: MessageContent,
}
struct Bridgers {
discord: Mutex<RefCell<Option<discord::Context>>>,
matrix: Mutex<RefCell<Option<matrix::Client>>>,
}
impl Bridgers {
fn new() -> Self {
Self {
discord: Mutex::new(RefCell::new(None)),
matrix: Mutex::new(RefCell::new(None)),
}
}
async fn send_message(
&self,
source: MessageReference,
content: MessageContent,
) -> Vec<MessageReference> {
let mut created_messages = Vec::new();
if let Some(discord) = self.discord.lock().unwrap().borrow().as_ref() {
// We probably want a function that returns an Option<ChannelId> taking the source
match &source {
MessageReference::Matrix(_room_id, _event_id) => {
let channel_id = ChannelId(885690775193661463); // TODO: Look up linked channel
let discord_message = channel_id
.send_message(&discord.http, |m| {
m.content(message_ast::format_discord(&content))
})
.await
.expect("Failed to send discord message");
created_messages.push(MessageReference::from(&discord_message));
}
_ => {}
};
}
if let Some(matrix) = self.matrix.lock().unwrap().borrow().as_ref() {
match &source {
MessageReference::Discord(_, _) => {
let room_id = RoomId::from_str("asdfghj").unwrap(); // TODO: Get a room id
if let Some(room) = matrix.get_joined_room(&room_id) {
let event = room
.send(
AnyMessageEventContent::RoomMessage(
MessageEventContent::text_plain(message_ast::format_discord(
&content, // TODO: Format as HTML
)),
),
None,
)
.await
.unwrap();
created_messages.push(MessageReference::from((&room_id, &event.event_id)));
}
}
_ => {}
}
}
created_messages
}
}
async fn setup_discord(
token: String,
bridgers: Arc<Bridgers>,
discord_tx: mpsc::UnboundedSender<SentMessage>,
) {
let (discord_ctx_tx, mut discord_ctx_rx) = mpsc::unbounded_channel::<discord::Context>();
tokio::spawn(async move {
let mut discord = create_discord_client(discord_ctx_tx, discord_tx, &token).await;
discord.start().await.unwrap();
});
// Hack to grab the Context object when discord is ready
tokio::spawn(async move {
while let Some(discord) = discord_ctx_rx.recv().await {
bridgers.discord.lock().unwrap().replace(Some(discord));
}
});
}
async fn setup_matrix(
homeserver_url: String,
username: String,
password: String,
bridgers: Arc<Bridgers>,
message_tx: mpsc::UnboundedSender<SentMessage>,
) {
let client = create_matrix_client(homeserver_url, username, password, message_tx).await;
let settings = SyncSettings::default().token(client.sync_token().await.unwrap());
bridgers
.matrix
.lock()
.unwrap()
.replace(Some(client.clone()));
tokio::spawn(async move {
client.sync(settings).await;
});
}
#[tokio::main]
async fn main() {
let bridgers = Arc::new(Bridgers::new());
let (message_tx, mut message_rx) = tokio::sync::mpsc::unbounded_channel::<SentMessage>();
setup_discord(
"token".to_string(),
Arc::clone(&bridgers),
message_tx.clone(),
)
.await;
setup_matrix(
"https://matrix.org".to_string(),
"username".to_string(),
"password".to_string(),
Arc::clone(&bridgers),
message_tx.clone(),
)
.await;
while let Some(message) = message_rx.recv().await {
let _ = bridgers.send_message(message.source, message.content).await;
}
}