commit fbc6270a0a4e68acd0872ddddef49d188dd5e317 Author: videogame hacker Date: Fri Apr 8 17:52:59 2022 +0100 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bcb5235 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true + +[*.rs] +indent_size = 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..40e2017 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "phoebe", + "phoebe-main", + "mid-chat", + "services/*" +] diff --git a/README.md b/README.md new file mode 100644 index 0000000..02df694 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# phoebe + +bridgers [primarily, discord ↔ matrix] + +A [Charlotte Som](https://som.codes/) project. + +## Architecture + +- `mid-chat` - An intermediate representation for chat messages. Best-effort common denomination +- `services/*` - Handling for individual chat services & conversion to and from the common-denominator chat message IR +- `phoebe` - Main: Database, message dispatch, service orchestration, etc diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..120f485 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +* +!/.gitignore diff --git a/mid-chat/Cargo.toml b/mid-chat/Cargo.toml new file mode 100644 index 0000000..6c7dba2 --- /dev/null +++ b/mid-chat/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "mid-chat" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/mid-chat/src/content.rs b/mid-chat/src/content.rs new file mode 100644 index 0000000..224696f --- /dev/null +++ b/mid-chat/src/content.rs @@ -0,0 +1,30 @@ +pub type ChatMessageContent = Vec; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ChatContentComponent { + Plain(String), + Link { + target: String, + text: ChatMessageContent, + }, + + Italic(ChatMessageContent), + Bold(ChatMessageContent), + Strikethrough(ChatMessageContent), + Underline(ChatMessageContent), + + Code(String), + CodeBlock { + lang: Option, + source: String, + }, + + Spoiler { + reason: Option, + content: ChatMessageContent, + }, + + HardBreak, + + BlockQuote(ChatMessageContent), +} diff --git a/mid-chat/src/event.rs b/mid-chat/src/event.rs new file mode 100644 index 0000000..84f0f2b --- /dev/null +++ b/mid-chat/src/event.rs @@ -0,0 +1,6 @@ +use crate::ChatMessage; + +#[derive(Debug, Clone)] +pub enum ChatEvent { + NewMessage(ChatMessage), +} diff --git a/mid-chat/src/lib.rs b/mid-chat/src/lib.rs new file mode 100644 index 0000000..1d6f22f --- /dev/null +++ b/mid-chat/src/lib.rs @@ -0,0 +1,22 @@ +pub mod reference; +pub use reference::*; + +#[derive(Debug, Clone)] +pub struct ChatAuthor { + pub reference: ChatReference, + pub display_name: String, + pub display_color: Option<[u8; 3]>, +} + +mod content; +pub use content::*; + +#[derive(Debug, Clone)] +pub struct ChatMessage { + pub origin: ChatReference, + pub author: ChatAuthor, + pub content: ChatMessageContent, + // TODO: Attachments +} + +pub mod event; diff --git a/mid-chat/src/reference.rs b/mid-chat/src/reference.rs new file mode 100644 index 0000000..bdc70c3 --- /dev/null +++ b/mid-chat/src/reference.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ChatReference { + pub service: &'static str, + pub id: String, +} diff --git a/phoebe-main/Cargo.toml b/phoebe-main/Cargo.toml new file mode 100644 index 0000000..2deee91 --- /dev/null +++ b/phoebe-main/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "phoebe-main" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["full"] } +tracing-subscriber = { version = "0.3.10", features = ["env-filter"] } +color-eyre = "0.6.1" +phoebe = { path = "../phoebe" } +phoebe-discord = { path = "../services/phoebe-discord" } diff --git a/phoebe-main/src/main.rs b/phoebe-main/src/main.rs new file mode 100644 index 0000000..8b97599 --- /dev/null +++ b/phoebe-main/src/main.rs @@ -0,0 +1,23 @@ +use color_eyre::Result; +use tracing_subscriber::EnvFilter; + +use phoebe::service::Service; + +#[tokio::main] +async fn main() -> Result<()> { + color_eyre::install()?; + + tracing_subscriber::fmt() + .with_target(true) + .with_env_filter(EnvFilter::from_default_env()) + .init(); + + let (tx, rx) = tokio::sync::broadcast::channel(512); + + let db = phoebe::open_core_db().await?; + let services: Vec> = vec![Box::new( + phoebe_discord::setup(db.clone(), tx.clone()).await?, + )]; + + Ok(()) +} diff --git a/phoebe/.env b/phoebe/.env new file mode 100644 index 0000000..c7b4089 --- /dev/null +++ b/phoebe/.env @@ -0,0 +1 @@ +DATABASE_URL="sqlite://PHOEBE_DB_ROOT/main.db" diff --git a/phoebe/Cargo.toml b/phoebe/Cargo.toml new file mode 100644 index 0000000..760bcdc --- /dev/null +++ b/phoebe/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "phoebe" +version = "0.1.0" +edition = "2021" +authors = ["videogame hacker "] + +[dependencies] +mid-chat = { path = "../mid-chat" } +tokio = { version = "1", features = ["full"] } +sqlx = { version = "0.5", features = ["runtime-tokio-native-tls", "sqlite"] } +tracing = "0.1" +async-trait = "0.1.53" +eyre = "0.6.8" diff --git a/phoebe/src/db.rs b/phoebe/src/db.rs new file mode 100644 index 0000000..4c6f9fe --- /dev/null +++ b/phoebe/src/db.rs @@ -0,0 +1,26 @@ +use sqlx::{sqlite::SqliteConnectOptions, SqlitePool}; +use tokio::sync::OnceCell; +use tracing::debug; + +static PHOEBE_DB_ROOT: OnceCell = OnceCell::const_new(); + +async fn get_db_root() -> &'static str { + PHOEBE_DB_ROOT + .get_or_init(|| async { + std::env::var("PHOEBE_DB_ROOT") + .expect("PHOEBE_DB_ROOT environment variable was not set!") + }) + .await +} + +async fn create_or_open_sqlite(url: &str) -> sqlx::Result { + let options = url.parse::()?.create_if_missing(true); + SqlitePool::connect_with(options).await +} + +pub async fn open(name: &str) -> sqlx::Result { + debug!("Opening {name} database…"); + let db_root = get_db_root().await; + let db_url = format!("sqlite://{}/{}.db", db_root, name); + create_or_open_sqlite(&db_url).await +} diff --git a/phoebe/src/lib.rs b/phoebe/src/lib.rs new file mode 100644 index 0000000..ceb3bbc --- /dev/null +++ b/phoebe/src/lib.rs @@ -0,0 +1,14 @@ +use mid_chat::event::ChatEvent; + +pub mod db; +pub mod prelude; +pub mod service; + +pub type ChatEventSender = tokio::sync::broadcast::Sender; +pub type ChatEventReceiver = tokio::sync::broadcast::Receiver; + +pub async fn open_core_db() -> sqlx::Result { + let db = db::open("main").await?; + sqlx::migrate!().run(&db).await?; + Ok(db) +} diff --git a/phoebe/src/prelude.rs b/phoebe/src/prelude.rs new file mode 100644 index 0000000..5dbfd89 --- /dev/null +++ b/phoebe/src/prelude.rs @@ -0,0 +1,6 @@ +pub use crate::{service::Service, ChatEventReceiver, ChatEventSender}; + +pub use async_trait::async_trait; +pub use eyre::Result; +pub use mid_chat::event::ChatEvent; +pub use sqlx::SqlitePool; diff --git a/phoebe/src/service.rs b/phoebe/src/service.rs new file mode 100644 index 0000000..f06d351 --- /dev/null +++ b/phoebe/src/service.rs @@ -0,0 +1,7 @@ +use mid_chat::event::ChatEvent; + +#[async_trait::async_trait] +pub trait Service { + fn get_service_tag(&self) -> &'static str; + async fn handle_chat_event(&mut self, event: &ChatEvent); +} diff --git a/services/phoebe-discord/.env b/services/phoebe-discord/.env new file mode 100644 index 0000000..9ee280a --- /dev/null +++ b/services/phoebe-discord/.env @@ -0,0 +1 @@ +DATABASE_URL="sqlite://PHOEBE_DB_ROOT/discord_media.db" diff --git a/services/phoebe-discord/Cargo.toml b/services/phoebe-discord/Cargo.toml new file mode 100644 index 0000000..abea4ab --- /dev/null +++ b/services/phoebe-discord/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "phoebe-discord" +version = "0.1.0" +edition = "2021" + +[dependencies] +phoebe = { path = "../../phoebe" } +serenity = "0.10.10" +sqlx = { version = "0.5", features = ["runtime-tokio-native-tls", "sqlite"] } +tracing = "0.1" +tokio = { version = "1", features = ["full"] } diff --git a/services/phoebe-discord/src/lib.rs b/services/phoebe-discord/src/lib.rs new file mode 100644 index 0000000..dafea4d --- /dev/null +++ b/services/phoebe-discord/src/lib.rs @@ -0,0 +1,70 @@ +use phoebe::prelude::*; +use serenity::{ + client::{Context, EventHandler}, + model::prelude::*, + prelude::*, + Client, +}; +use tracing::{debug, info}; + +pub struct DiscordService { + discord_client: Client, + discord_ctx: Context, +} + +struct DiscordHandler { + core_db: SqlitePool, + discord_media_db: SqlitePool, + chat_event_tx: ChatEventSender, + ctx_tx: tokio::sync::mpsc::Sender, +} + +#[async_trait] +impl EventHandler for DiscordHandler { + async fn ready(&self, ctx: Context, _data_about_bot: Ready) { + let _ = self.ctx_tx.send(ctx).await; + } +} + +pub async fn setup(core_db: SqlitePool, tx: ChatEventSender) -> Result { + info!("Setting up Discord service…"); + + let discord_media_db = phoebe::db::open("discord_media").await?; + sqlx::migrate!().run(&discord_media_db).await?; + + let (ctx_tx, mut ctx_rx) = tokio::sync::mpsc::channel::(1); + + let discord_handler = DiscordHandler { + core_db, + discord_media_db, + chat_event_tx: tx, + ctx_tx, + }; + + // TODO: Create a discord client + debug!("Logging in…"); + let discord_token = std::env::var("PHOEBE_DISCORD_TOKEN") + .expect("PHOEBE_DISCORD_TOKEN environment variable was not set!"); + let client = Client::builder(&discord_token) + .event_handler(discord_handler) + .await?; + + let discord_ctx = ctx_rx.recv().await.expect("Couldn't get Discord context"); + debug!("Logged in!"); + + Ok(DiscordService { + discord_client: client, + discord_ctx, + }) +} + +#[async_trait] +impl Service for DiscordService { + async fn handle_chat_event(&mut self, event: &ChatEvent) { + dbg!(event); + } + + fn get_service_tag(&self) -> &'static str { + "discord" + } +} diff --git a/services/phoebe-matrix/Cargo.toml b/services/phoebe-matrix/Cargo.toml new file mode 100644 index 0000000..7b5b677 --- /dev/null +++ b/services/phoebe-matrix/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "phoebe-matrix" +version = "0.1.0" +edition = "2021" + +[dependencies] +phoebe = { path = "../../phoebe" } diff --git a/services/phoebe-matrix/src/lib.rs b/services/phoebe-matrix/src/lib.rs new file mode 100644 index 0000000..e69de29