From 2f60bbc643b44327a806c7abf437f96e85821c5e Mon Sep 17 00:00:00 2001 From: projectmoon Date: Mon, 31 May 2021 22:22:03 +0000 Subject: [PATCH 1/9] Convert project to workspace with Tonic for gRPC. This commit adds an RPC service to the dicebot, allowing external applications to control it. The project was converted to a cargo workspace to house the protobuf definitions in a common crate (tenebrous-rpc), so that clients and servers can make use of these protobuf definitions. --- Cargo.lock | 202 +++++++++++++++++- Cargo.toml | 51 +---- dicebot/Cargo.toml | 53 +++++ Dockerfile => dicebot/Dockerfile | 0 LICENSE => dicebot/LICENSE | 0 README.md => dicebot/README.md | 0 sqlx-data.json => dicebot/sqlx-data.json | 0 {src => dicebot/src}/basic/dice.rs | 0 {src => dicebot/src}/basic/mod.rs | 0 {src => dicebot/src}/basic/parser.rs | 0 {src => dicebot/src}/basic/roll.rs | 0 {src => dicebot/src}/bin/dicebot-cmd.rs | 0 {src => dicebot/src}/bin/dicebot.rs | 39 +++- {src => dicebot/src}/bin/dicebot_migrate.rs | 0 dicebot/src/bin/tonic_client.rs | 18 ++ {src => dicebot/src}/bot/command_execution.rs | 0 {src => dicebot/src}/bot/event_handlers.rs | 0 {src => dicebot/src}/bot/mod.rs | 0 {src => dicebot/src}/cofd/dice.rs | 0 {src => dicebot/src}/cofd/mod.rs | 0 {src => dicebot/src}/cofd/parser.rs | 0 .../src}/commands/basic_rolling.rs | 0 {src => dicebot/src}/commands/cofd.rs | 0 {src => dicebot/src}/commands/cthulhu.rs | 0 {src => dicebot/src}/commands/management.rs | 0 {src => dicebot/src}/commands/misc.rs | 0 {src => dicebot/src}/commands/mod.rs | 0 {src => dicebot/src}/commands/parser.rs | 0 {src => dicebot/src}/commands/rooms.rs | 0 {src => dicebot/src}/commands/variables.rs | 0 {src => dicebot/src}/config.rs | 29 +-- {src => dicebot/src}/context.rs | 0 {src => dicebot/src}/cthulhu/dice.rs | 0 {src => dicebot/src}/cthulhu/mod.rs | 0 {src => dicebot/src}/cthulhu/parser.rs | 0 {src => dicebot/src}/db/errors.rs | 0 {src => dicebot/src}/db/mod.rs | 0 .../migrator/migrations/V1__variables.rs | 0 .../migrator/migrations/V2__room_info.rs | 0 .../sqlite/migrator/migrations/V3__dbstate.rs | 0 .../migrator/migrations/V4__room_events.rs | 0 .../migrator/migrations/V5__room_users.rs | 0 .../migrator/migrations/V6__user_accounts.rs | 0 .../migrations/V7__no_more_room_state.rs | 0 .../migrator/migrations/V8__user_state.rs | 0 ...9__nullable_password_and_account_status.rs | 0 .../src}/db/sqlite/migrator/migrations/mod.rs | 0 .../src}/db/sqlite/migrator/mod.rs | 0 {src => dicebot/src}/db/sqlite/mod.rs | 0 {src => dicebot/src}/db/sqlite/rooms.rs | 0 {src => dicebot/src}/db/sqlite/state.rs | 0 {src => dicebot/src}/db/sqlite/users.rs | 0 {src => dicebot/src}/db/sqlite/variables.rs | 0 {src => dicebot/src}/error.rs | 8 + {src => dicebot/src}/help.rs | 0 {src => dicebot/src}/lib.rs | 1 + {src => dicebot/src}/logic.rs | 0 {src => dicebot/src}/matrix.rs | 0 {src => dicebot/src}/models.rs | 0 {src => dicebot/src}/parser/dice.rs | 0 {src => dicebot/src}/parser/mod.rs | 0 {src => dicebot/src}/parser/variables.rs | 0 dicebot/src/rpc.rs | 77 +++++++ {src => dicebot/src}/state.rs | 0 rpc/Cargo.toml | 14 ++ rpc/build.rs | 4 + rpc/protos/dicebot.proto | 47 ++++ rpc/src/lib.rs | 5 + rpc/src/main.rs | 3 + 69 files changed, 484 insertions(+), 67 deletions(-) create mode 100644 dicebot/Cargo.toml rename Dockerfile => dicebot/Dockerfile (100%) rename LICENSE => dicebot/LICENSE (100%) rename README.md => dicebot/README.md (100%) rename sqlx-data.json => dicebot/sqlx-data.json (100%) rename {src => dicebot/src}/basic/dice.rs (100%) rename {src => dicebot/src}/basic/mod.rs (100%) rename {src => dicebot/src}/basic/parser.rs (100%) rename {src => dicebot/src}/basic/roll.rs (100%) rename {src => dicebot/src}/bin/dicebot-cmd.rs (100%) rename {src => dicebot/src}/bin/dicebot.rs (56%) rename {src => dicebot/src}/bin/dicebot_migrate.rs (100%) create mode 100644 dicebot/src/bin/tonic_client.rs rename {src => dicebot/src}/bot/command_execution.rs (100%) rename {src => dicebot/src}/bot/event_handlers.rs (100%) rename {src => dicebot/src}/bot/mod.rs (100%) rename {src => dicebot/src}/cofd/dice.rs (100%) rename {src => dicebot/src}/cofd/mod.rs (100%) rename {src => dicebot/src}/cofd/parser.rs (100%) rename {src => dicebot/src}/commands/basic_rolling.rs (100%) rename {src => dicebot/src}/commands/cofd.rs (100%) rename {src => dicebot/src}/commands/cthulhu.rs (100%) rename {src => dicebot/src}/commands/management.rs (100%) rename {src => dicebot/src}/commands/misc.rs (100%) rename {src => dicebot/src}/commands/mod.rs (100%) rename {src => dicebot/src}/commands/parser.rs (100%) rename {src => dicebot/src}/commands/rooms.rs (100%) rename {src => dicebot/src}/commands/variables.rs (100%) rename {src => dicebot/src}/config.rs (95%) rename {src => dicebot/src}/context.rs (100%) rename {src => dicebot/src}/cthulhu/dice.rs (100%) rename {src => dicebot/src}/cthulhu/mod.rs (100%) rename {src => dicebot/src}/cthulhu/parser.rs (100%) rename {src => dicebot/src}/db/errors.rs (100%) rename {src => dicebot/src}/db/mod.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V1__variables.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V2__room_info.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V3__dbstate.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V4__room_events.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V5__room_users.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V6__user_accounts.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V7__no_more_room_state.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V8__user_state.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/V9__nullable_password_and_account_status.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/migrations/mod.rs (100%) rename {src => dicebot/src}/db/sqlite/migrator/mod.rs (100%) rename {src => dicebot/src}/db/sqlite/mod.rs (100%) rename {src => dicebot/src}/db/sqlite/rooms.rs (100%) rename {src => dicebot/src}/db/sqlite/state.rs (100%) rename {src => dicebot/src}/db/sqlite/users.rs (100%) rename {src => dicebot/src}/db/sqlite/variables.rs (100%) rename {src => dicebot/src}/error.rs (93%) rename {src => dicebot/src}/help.rs (100%) rename {src => dicebot/src}/lib.rs (94%) rename {src => dicebot/src}/logic.rs (100%) rename {src => dicebot/src}/matrix.rs (100%) rename {src => dicebot/src}/models.rs (100%) rename {src => dicebot/src}/parser/dice.rs (100%) rename {src => dicebot/src}/parser/mod.rs (100%) rename {src => dicebot/src}/parser/variables.rs (100%) create mode 100644 dicebot/src/rpc.rs rename {src => dicebot/src}/state.rs (100%) create mode 100644 rpc/Cargo.toml create mode 100644 rpc/build.rs create mode 100644 rpc/protos/dicebot.proto create mode 100644 rpc/src/lib.rs create mode 100644 rpc/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 28835d9..536a0a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + [[package]] name = "arrayref" version = "0.3.6" @@ -119,6 +125,27 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f093eed78becd229346bf859eec0aa4dd7ddde0757287b2b4107a1f09c80002" +[[package]] +name = "async-stream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.50" @@ -529,6 +556,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + [[package]] name = "fnv" version = "1.0.7" @@ -991,6 +1024,15 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.10.0" @@ -1263,6 +1305,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "native-tls" version = "0.2.7" @@ -1462,6 +1510,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "phf" version = "0.8.0" @@ -1618,6 +1676,57 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "prost" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3" +dependencies = [ + "bytes", + "heck", + "itertools 0.9.0", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4" +dependencies = [ + "anyhow", + "itertools 0.9.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb" +dependencies = [ + "bytes", + "prost", +] + [[package]] name = "quote" version = "1.0.9" @@ -2557,11 +2666,12 @@ dependencies = [ "futures", "html2text", "indoc", - "itertools", + "itertools 0.10.0", "log", "matrix-sdk", "nom 5.1.2", "phf", + "prost", "rand 0.8.3", "refinery", "rust-argon2", @@ -2569,13 +2679,25 @@ dependencies = [ "sqlx", "substring", "tempfile", + "tenebrous-rpc", "thiserror", "tokio", "toml", + "tonic", + "tonic-build", "tracing-subscriber", "url", ] +[[package]] +name = "tenebrous-rpc" +version = "0.1.0" +dependencies = [ + "prost", + "tonic", + "tonic-build", +] + [[package]] name = "thiserror" version = "1.0.24" @@ -2743,6 +2865,73 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac42cd97ac6bd2339af5bcabf105540e21e45636ec6fa6aae5e85d44db31be0" +dependencies = [ + "async-stream", + "async-trait", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c695de27302f4697191dda1c7178131a8cb805463dda02864acb80fe1322fdcf" +dependencies = [ + "proc-macro2", + "prost-build", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60422bc7fefa2f3ec70359b8ff1caff59d785877eb70595904605bcc412470f" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "rand 0.8.3", + "slab", + "tokio", + "tokio-stream", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + [[package]] name = "tower-service" version = "0.3.1" @@ -2756,6 +2945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" dependencies = [ "cfg-if", + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3055,6 +3245,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" +dependencies = [ + "either", + "libc", +] + [[package]] name = "whoami" version = "1.1.2" diff --git a/Cargo.toml b/Cargo.toml index 840c280..3c81cef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,47 +1,6 @@ -[package] -name = "tenebrous-dicebot" -version = "0.10.0" -authors = ["Taylor C. Richberger ", "projectmoon "] -edition = "2018" -license = 'AGPL-3.0-or-later' -description = 'An async Matrix dice bot for role-playing games' -readme = 'README.md' -repository = 'https://git.agnos.is/projectmoon/matrix-dicebot' -keywords = ["games", "dice", "matrix", "bot"] -categories = ["games"] +[workspace] -[dependencies] -log = "0.4" -tracing-subscriber = "0.2" -toml = "0.5" -nom = "5" -rand = "0.8" -rust-argon2 = "0.8" -thiserror = "1.0" -itertools = "0.10" -async-trait = "0.1" -url = "2.1" -dirs = "3.0" -indoc = "1.0" -combine = "4.5" -futures = "0.3" -html2text = "0.2" -phf = { version = "0.8", features = ["macros"] } -matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk", branch = "master" } -refinery = { version = "0.5", features = ["rusqlite"]} -barrel = { version = "0.6", features = ["sqlite3"] } -tempfile = "3" -substring = "1.4" -fuse-rust = "0.2" - -[dependencies.sqlx] -version = "0.5" -features = [ "offline", "sqlite", "runtime-tokio-native-tls" ] - -[dependencies.serde] -version = "1" -features = ['derive'] - -[dependencies.tokio] -version = "1" -features = [ "full" ] +members = [ + "dicebot", + "rpc", +] \ No newline at end of file diff --git a/dicebot/Cargo.toml b/dicebot/Cargo.toml new file mode 100644 index 0000000..994b71c --- /dev/null +++ b/dicebot/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "tenebrous-dicebot" +version = "0.10.0" +authors = ["Taylor C. Richberger ", "projectmoon "] +edition = "2018" +license = 'AGPL-3.0-or-later' +description = 'An async Matrix dice bot for role-playing games' +readme = 'README.md' +repository = 'https://git.agnos.is/projectmoon/matrix-dicebot' +keywords = ["games", "dice", "matrix", "bot"] +categories = ["games"] + +[build-dependencies] +tonic-build = "0.4" + +[dependencies] +log = "0.4" +tracing-subscriber = "0.2" +toml = "0.5" +nom = "5" +rand = "0.8" +rust-argon2 = "0.8" +thiserror = "1.0" +itertools = "0.10" +async-trait = "0.1" +url = "2.1" +dirs = "3.0" +indoc = "1.0" +combine = "4.5" +futures = "0.3" +html2text = "0.2" +phf = { version = "0.8", features = ["macros"] } +matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk", branch = "master" } +refinery = { version = "0.5", features = ["rusqlite"]} +barrel = { version = "0.6", features = ["sqlite3"] } +tempfile = "3" +substring = "1.4" +fuse-rust = "0.2" +tonic = "0.4" +prost = "0.7" +tenebrous-rpc = { path = "../rpc" } + +[dependencies.sqlx] +version = "0.5" +features = [ "offline", "sqlite", "runtime-tokio-native-tls" ] + +[dependencies.serde] +version = "1" +features = ['derive'] + +[dependencies.tokio] +version = "1" +features = [ "full" ] diff --git a/Dockerfile b/dicebot/Dockerfile similarity index 100% rename from Dockerfile rename to dicebot/Dockerfile diff --git a/LICENSE b/dicebot/LICENSE similarity index 100% rename from LICENSE rename to dicebot/LICENSE diff --git a/README.md b/dicebot/README.md similarity index 100% rename from README.md rename to dicebot/README.md diff --git a/sqlx-data.json b/dicebot/sqlx-data.json similarity index 100% rename from sqlx-data.json rename to dicebot/sqlx-data.json diff --git a/src/basic/dice.rs b/dicebot/src/basic/dice.rs similarity index 100% rename from src/basic/dice.rs rename to dicebot/src/basic/dice.rs diff --git a/src/basic/mod.rs b/dicebot/src/basic/mod.rs similarity index 100% rename from src/basic/mod.rs rename to dicebot/src/basic/mod.rs diff --git a/src/basic/parser.rs b/dicebot/src/basic/parser.rs similarity index 100% rename from src/basic/parser.rs rename to dicebot/src/basic/parser.rs diff --git a/src/basic/roll.rs b/dicebot/src/basic/roll.rs similarity index 100% rename from src/basic/roll.rs rename to dicebot/src/basic/roll.rs diff --git a/src/bin/dicebot-cmd.rs b/dicebot/src/bin/dicebot-cmd.rs similarity index 100% rename from src/bin/dicebot-cmd.rs rename to dicebot/src/bin/dicebot-cmd.rs diff --git a/src/bin/dicebot.rs b/dicebot/src/bin/dicebot.rs similarity index 56% rename from src/bin/dicebot.rs rename to dicebot/src/bin/dicebot.rs index a5abbb1..d386ebd 100644 --- a/src/bin/dicebot.rs +++ b/dicebot/src/bin/dicebot.rs @@ -1,5 +1,6 @@ //Needed for nested Result handling from tokio. Probably can go away after 1.47.0. #![type_length_limit = "7605144"] +use futures::try_join; use log::error; use std::env; use std::sync::{Arc, RwLock}; @@ -7,15 +8,27 @@ use tenebrous_dicebot::bot::DiceBot; use tenebrous_dicebot::config::*; use tenebrous_dicebot::db::sqlite::Database; use tenebrous_dicebot::error::BotError; +use tenebrous_dicebot::rpc; use tenebrous_dicebot::state::DiceBotState; use tracing_subscriber::filter::EnvFilter; +/// Attempt to create config object and ddatabase connection pool from +/// the given config path. An error is returned if config creation or +/// database pool creation fails for some reason. +async fn init(config_path: &str) -> Result<(Arc, Database), BotError> { + let cfg = read_config(config_path)?; + let cfg = Arc::new(cfg); + let sqlite_path = format!("{}/dicebot.sqlite", cfg.database_path()); + let db = Database::new(&sqlite_path).await?; + Ok((cfg, db)) +} + #[tokio::main] -async fn main() { +async fn main() -> Result<(), BotError> { let filter = if env::var("RUST_LOG").is_ok() { EnvFilter::from_default_env() } else { - EnvFilter::new("tenebrous_dicebot=info,dicebot=info,refinery=info") + EnvFilter::new("tonic=info,tenebrous_dicebot=info,dicebot=info,refinery=info") }; tracing_subscriber::fmt().with_env_filter(filter).init(); @@ -23,7 +36,9 @@ async fn main() { match run().await { Ok(_) => (), Err(e) => error!("Error: {}", e), - }; + } + + Ok(()) } async fn run() -> Result<(), BotError> { @@ -32,12 +47,22 @@ async fn run() -> Result<(), BotError> { .next() .expect("Need a config as an argument"); - let cfg = Arc::new(read_config(config_path)?); - let sqlite_path = format!("{}/dicebot.sqlite", cfg.database_path()); - let db = Database::new(&sqlite_path).await?; + let (cfg, db) = init(&config_path).await?; + let grpc = rpc::serve_grpc(&cfg, &db); + let bot = run_bot(&cfg, &db); + + match try_join!(bot, grpc) { + Ok(_) => (), + Err(e) => error!("Error: {}", e), + }; + + Ok(()) +} + +async fn run_bot(cfg: &Arc, db: &Database) -> Result<(), BotError> { let state = Arc::new(RwLock::new(DiceBotState::new(&cfg))); - match DiceBot::new(&cfg, &state, &db) { + match DiceBot::new(cfg, &state, db) { Ok(bot) => bot.run().await?, Err(e) => println!("Error connecting: {:?}", e), }; diff --git a/src/bin/dicebot_migrate.rs b/dicebot/src/bin/dicebot_migrate.rs similarity index 100% rename from src/bin/dicebot_migrate.rs rename to dicebot/src/bin/dicebot_migrate.rs diff --git a/dicebot/src/bin/tonic_client.rs b/dicebot/src/bin/tonic_client.rs new file mode 100644 index 0000000..5bec6ab --- /dev/null +++ b/dicebot/src/bin/tonic_client.rs @@ -0,0 +1,18 @@ +use tenebrous_rpc::protos::dicebot::dicebot_client::DicebotClient; +use tenebrous_rpc::protos::dicebot::UserIdRequest; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let mut client = DicebotClient::connect("http://0.0.0.0:9090").await?; + + let request = tonic::Request::new(UserIdRequest { + user_id: "Tonic".into(), + }); + + let response = client.rooms_for_user(request).await?.into_inner(); + + println!("RESPONSE={:?}", response); + println!("User friendly response is: {:?}", response.room_ids); + + Ok(()) +} diff --git a/src/bot/command_execution.rs b/dicebot/src/bot/command_execution.rs similarity index 100% rename from src/bot/command_execution.rs rename to dicebot/src/bot/command_execution.rs diff --git a/src/bot/event_handlers.rs b/dicebot/src/bot/event_handlers.rs similarity index 100% rename from src/bot/event_handlers.rs rename to dicebot/src/bot/event_handlers.rs diff --git a/src/bot/mod.rs b/dicebot/src/bot/mod.rs similarity index 100% rename from src/bot/mod.rs rename to dicebot/src/bot/mod.rs diff --git a/src/cofd/dice.rs b/dicebot/src/cofd/dice.rs similarity index 100% rename from src/cofd/dice.rs rename to dicebot/src/cofd/dice.rs diff --git a/src/cofd/mod.rs b/dicebot/src/cofd/mod.rs similarity index 100% rename from src/cofd/mod.rs rename to dicebot/src/cofd/mod.rs diff --git a/src/cofd/parser.rs b/dicebot/src/cofd/parser.rs similarity index 100% rename from src/cofd/parser.rs rename to dicebot/src/cofd/parser.rs diff --git a/src/commands/basic_rolling.rs b/dicebot/src/commands/basic_rolling.rs similarity index 100% rename from src/commands/basic_rolling.rs rename to dicebot/src/commands/basic_rolling.rs diff --git a/src/commands/cofd.rs b/dicebot/src/commands/cofd.rs similarity index 100% rename from src/commands/cofd.rs rename to dicebot/src/commands/cofd.rs diff --git a/src/commands/cthulhu.rs b/dicebot/src/commands/cthulhu.rs similarity index 100% rename from src/commands/cthulhu.rs rename to dicebot/src/commands/cthulhu.rs diff --git a/src/commands/management.rs b/dicebot/src/commands/management.rs similarity index 100% rename from src/commands/management.rs rename to dicebot/src/commands/management.rs diff --git a/src/commands/misc.rs b/dicebot/src/commands/misc.rs similarity index 100% rename from src/commands/misc.rs rename to dicebot/src/commands/misc.rs diff --git a/src/commands/mod.rs b/dicebot/src/commands/mod.rs similarity index 100% rename from src/commands/mod.rs rename to dicebot/src/commands/mod.rs diff --git a/src/commands/parser.rs b/dicebot/src/commands/parser.rs similarity index 100% rename from src/commands/parser.rs rename to dicebot/src/commands/parser.rs diff --git a/src/commands/rooms.rs b/dicebot/src/commands/rooms.rs similarity index 100% rename from src/commands/rooms.rs rename to dicebot/src/commands/rooms.rs diff --git a/src/commands/variables.rs b/dicebot/src/commands/variables.rs similarity index 100% rename from src/commands/variables.rs rename to dicebot/src/commands/variables.rs diff --git a/src/config.rs b/dicebot/src/config.rs similarity index 95% rename from src/config.rs rename to dicebot/src/config.rs index 22e9b91..5510557 100644 --- a/src/config.rs +++ b/dicebot/src/config.rs @@ -4,10 +4,6 @@ use std::fs; use std::path::PathBuf; use thiserror::Error; -/// Shortcut to defining db migration versions. Will probably -/// eventually be moved to a config file. -const MIGRATION_VERSION: u32 = 5; - #[derive(Error, Debug)] pub enum ConfigError { #[error("i/o error: {0}")] @@ -57,6 +53,10 @@ fn db_path_from_env() -> String { struct BotConfig { /// How far back from current time should we process a message? oldest_message_age: Option, + + /// What address and port to run the RPC service on. If not + /// specified, RPC will not be enabled. + rpc_addr: Option, } /// The "database" section of the config file. @@ -84,6 +84,12 @@ impl BotConfig { self.oldest_message_age .unwrap_or(DEFAULT_OLDEST_MESSAGE_AGE) } + + #[inline] + #[must_use] + fn rpc_addr(&self) -> Option { + self.rpc_addr.clone() + } } /// Represents the toml config file for the dicebot. The sections of @@ -128,15 +134,6 @@ impl Config { .unwrap_or_else(|| db_path_from_env()) } - /// The current migration version we expect of the database. If - /// this number is higher than the one in the database, we will - /// execute migrations to update the data. - #[inline] - #[must_use] - pub fn migration_version(&self) -> u32 { - MIGRATION_VERSION - } - /// Figure out the allowed oldest message age, in seconds. This will /// be the defined oldest message age in the bot config, if the bot /// configuration and associated "oldest_message_age" setting are @@ -150,6 +147,12 @@ impl Config { .map(|bc| bc.oldest_message_age()) .unwrap_or(DEFAULT_OLDEST_MESSAGE_AGE) } + + #[inline] + #[must_use] + pub fn rpc_addr(&self) -> Option { + self.bot.as_ref().and_then(|bc| bc.rpc_addr()) + } } #[cfg(test)] diff --git a/src/context.rs b/dicebot/src/context.rs similarity index 100% rename from src/context.rs rename to dicebot/src/context.rs diff --git a/src/cthulhu/dice.rs b/dicebot/src/cthulhu/dice.rs similarity index 100% rename from src/cthulhu/dice.rs rename to dicebot/src/cthulhu/dice.rs diff --git a/src/cthulhu/mod.rs b/dicebot/src/cthulhu/mod.rs similarity index 100% rename from src/cthulhu/mod.rs rename to dicebot/src/cthulhu/mod.rs diff --git a/src/cthulhu/parser.rs b/dicebot/src/cthulhu/parser.rs similarity index 100% rename from src/cthulhu/parser.rs rename to dicebot/src/cthulhu/parser.rs diff --git a/src/db/errors.rs b/dicebot/src/db/errors.rs similarity index 100% rename from src/db/errors.rs rename to dicebot/src/db/errors.rs diff --git a/src/db/mod.rs b/dicebot/src/db/mod.rs similarity index 100% rename from src/db/mod.rs rename to dicebot/src/db/mod.rs diff --git a/src/db/sqlite/migrator/migrations/V1__variables.rs b/dicebot/src/db/sqlite/migrator/migrations/V1__variables.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V1__variables.rs rename to dicebot/src/db/sqlite/migrator/migrations/V1__variables.rs diff --git a/src/db/sqlite/migrator/migrations/V2__room_info.rs b/dicebot/src/db/sqlite/migrator/migrations/V2__room_info.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V2__room_info.rs rename to dicebot/src/db/sqlite/migrator/migrations/V2__room_info.rs diff --git a/src/db/sqlite/migrator/migrations/V3__dbstate.rs b/dicebot/src/db/sqlite/migrator/migrations/V3__dbstate.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V3__dbstate.rs rename to dicebot/src/db/sqlite/migrator/migrations/V3__dbstate.rs diff --git a/src/db/sqlite/migrator/migrations/V4__room_events.rs b/dicebot/src/db/sqlite/migrator/migrations/V4__room_events.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V4__room_events.rs rename to dicebot/src/db/sqlite/migrator/migrations/V4__room_events.rs diff --git a/src/db/sqlite/migrator/migrations/V5__room_users.rs b/dicebot/src/db/sqlite/migrator/migrations/V5__room_users.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V5__room_users.rs rename to dicebot/src/db/sqlite/migrator/migrations/V5__room_users.rs diff --git a/src/db/sqlite/migrator/migrations/V6__user_accounts.rs b/dicebot/src/db/sqlite/migrator/migrations/V6__user_accounts.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V6__user_accounts.rs rename to dicebot/src/db/sqlite/migrator/migrations/V6__user_accounts.rs diff --git a/src/db/sqlite/migrator/migrations/V7__no_more_room_state.rs b/dicebot/src/db/sqlite/migrator/migrations/V7__no_more_room_state.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V7__no_more_room_state.rs rename to dicebot/src/db/sqlite/migrator/migrations/V7__no_more_room_state.rs diff --git a/src/db/sqlite/migrator/migrations/V8__user_state.rs b/dicebot/src/db/sqlite/migrator/migrations/V8__user_state.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V8__user_state.rs rename to dicebot/src/db/sqlite/migrator/migrations/V8__user_state.rs diff --git a/src/db/sqlite/migrator/migrations/V9__nullable_password_and_account_status.rs b/dicebot/src/db/sqlite/migrator/migrations/V9__nullable_password_and_account_status.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/V9__nullable_password_and_account_status.rs rename to dicebot/src/db/sqlite/migrator/migrations/V9__nullable_password_and_account_status.rs diff --git a/src/db/sqlite/migrator/migrations/mod.rs b/dicebot/src/db/sqlite/migrator/migrations/mod.rs similarity index 100% rename from src/db/sqlite/migrator/migrations/mod.rs rename to dicebot/src/db/sqlite/migrator/migrations/mod.rs diff --git a/src/db/sqlite/migrator/mod.rs b/dicebot/src/db/sqlite/migrator/mod.rs similarity index 100% rename from src/db/sqlite/migrator/mod.rs rename to dicebot/src/db/sqlite/migrator/mod.rs diff --git a/src/db/sqlite/mod.rs b/dicebot/src/db/sqlite/mod.rs similarity index 100% rename from src/db/sqlite/mod.rs rename to dicebot/src/db/sqlite/mod.rs diff --git a/src/db/sqlite/rooms.rs b/dicebot/src/db/sqlite/rooms.rs similarity index 100% rename from src/db/sqlite/rooms.rs rename to dicebot/src/db/sqlite/rooms.rs diff --git a/src/db/sqlite/state.rs b/dicebot/src/db/sqlite/state.rs similarity index 100% rename from src/db/sqlite/state.rs rename to dicebot/src/db/sqlite/state.rs diff --git a/src/db/sqlite/users.rs b/dicebot/src/db/sqlite/users.rs similarity index 100% rename from src/db/sqlite/users.rs rename to dicebot/src/db/sqlite/users.rs diff --git a/src/db/sqlite/variables.rs b/dicebot/src/db/sqlite/variables.rs similarity index 100% rename from src/db/sqlite/variables.rs rename to dicebot/src/db/sqlite/variables.rs diff --git a/src/error.rs b/dicebot/src/error.rs similarity index 93% rename from src/error.rs rename to dicebot/src/error.rs index 8ad1914..aec2272 100644 --- a/src/error.rs +++ b/dicebot/src/error.rs @@ -1,3 +1,5 @@ +use std::net::AddrParseError; + use crate::commands::CommandError; use crate::config::ConfigError; use crate::db::errors::DataError; @@ -93,6 +95,12 @@ pub enum BotError { #[error("room name or id does not exist")] RoomDoesNotExist, + + #[error("tonic transport error: {0}")] + TonicTransportError(#[from] tonic::transport::Error), + + #[error("address parsing error: {0}")] + AddressParseError(#[from] AddrParseError), } #[derive(Error, Debug)] diff --git a/src/help.rs b/dicebot/src/help.rs similarity index 100% rename from src/help.rs rename to dicebot/src/help.rs diff --git a/src/lib.rs b/dicebot/src/lib.rs similarity index 94% rename from src/lib.rs rename to dicebot/src/lib.rs index eb49213..08bb2f6 100644 --- a/src/lib.rs +++ b/dicebot/src/lib.rs @@ -12,4 +12,5 @@ pub mod logic; pub mod matrix; pub mod models; mod parser; +pub mod rpc; pub mod state; diff --git a/src/logic.rs b/dicebot/src/logic.rs similarity index 100% rename from src/logic.rs rename to dicebot/src/logic.rs diff --git a/src/matrix.rs b/dicebot/src/matrix.rs similarity index 100% rename from src/matrix.rs rename to dicebot/src/matrix.rs diff --git a/src/models.rs b/dicebot/src/models.rs similarity index 100% rename from src/models.rs rename to dicebot/src/models.rs diff --git a/src/parser/dice.rs b/dicebot/src/parser/dice.rs similarity index 100% rename from src/parser/dice.rs rename to dicebot/src/parser/dice.rs diff --git a/src/parser/mod.rs b/dicebot/src/parser/mod.rs similarity index 100% rename from src/parser/mod.rs rename to dicebot/src/parser/mod.rs diff --git a/src/parser/variables.rs b/dicebot/src/parser/variables.rs similarity index 100% rename from src/parser/variables.rs rename to dicebot/src/parser/variables.rs diff --git a/dicebot/src/rpc.rs b/dicebot/src/rpc.rs new file mode 100644 index 0000000..bb59f9f --- /dev/null +++ b/dicebot/src/rpc.rs @@ -0,0 +1,77 @@ +use std::sync::Arc; + +use crate::error::BotError; +use crate::{config::Config, db::sqlite::Database}; +use log::info; +use tenebrous_rpc::protos::dicebot::{ + dicebot_server::{Dicebot, DicebotServer}, + GetAllVariablesReply, GetAllVariablesRequest, RoomsListReply, SetVariableReply, + SetVariableRequest, UserIdRequest, +}; +use tenebrous_rpc::protos::dicebot::{GetVariableReply, GetVariableRequest}; +use tonic::{transport::Server, Request, Response, Status}; + +pub struct DicebotRpcService { + config: Arc, + db: Database, +} + +#[tonic::async_trait] +impl Dicebot for DicebotRpcService { + async fn set_variable( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new(SetVariableReply { success: true })) + } + + async fn get_variable( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new(GetVariableReply { value: 1 })) + } + + async fn get_all_variables( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new(GetAllVariablesReply { + variables: std::collections::HashMap::new(), + })) + } + + async fn rooms_for_user( + &self, + request: Request, + ) -> Result, Status> { + Ok(Response::new(RoomsListReply { + room_ids: Vec::new(), + })) + } +} + +pub async fn serve_grpc(config: &Arc, db: &Database) -> Result<(), BotError> { + match config.rpc_addr() { + Some(addr) => { + let addr = addr.parse()?; + let rpc_service = DicebotRpcService { + db: db.clone(), + config: config.clone(), + }; + + info!("Serving Dicebot gRPC service on {}", addr); + Server::builder() + .add_service(DicebotServer::new(rpc_service)) + .serve(addr) + .await + .map_err(|e| e.into()) + } + _ => noop().await, + } +} + +pub async fn noop() -> Result<(), BotError> { + info!("RPC address not specified. Not enabling gRPC."); + Ok(()) +} diff --git a/src/state.rs b/dicebot/src/state.rs similarity index 100% rename from src/state.rs rename to dicebot/src/state.rs diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml new file mode 100644 index 0000000..f08a205 --- /dev/null +++ b/rpc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "tenebrous-rpc" +version = "0.1.0" +authors = ["projectmoon "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +tonic-build = "0.4" + +[dependencies] +tonic = "0.4" +prost = "0.7" \ No newline at end of file diff --git a/rpc/build.rs b/rpc/build.rs new file mode 100644 index 0000000..bc2d5de --- /dev/null +++ b/rpc/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + tonic_build::compile_protos("protos/dicebot.proto")?; + Ok(()) +} diff --git a/rpc/protos/dicebot.proto b/rpc/protos/dicebot.proto new file mode 100644 index 0000000..59f9959 --- /dev/null +++ b/rpc/protos/dicebot.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; +package dicebot; + +service Dicebot { + rpc GetVariable(GetVariableRequest) returns (GetVariableReply); + rpc GetAllVariables(GetAllVariablesRequest) returns (GetAllVariablesReply); + rpc SetVariable(SetVariableRequest) returns (SetVariableReply); + rpc RoomsForUser(UserIdRequest) returns (RoomsListReply); +} + +message GetVariableRequest { + string user_id = 1; + string room_id = 2; + string variable_name = 3; +} + +message GetVariableReply { + int32 value = 1; +} + +message GetAllVariablesRequest { + string user_id = 1; + string room_id = 2; +} + +message GetAllVariablesReply { + map variables = 1; +} + +message SetVariableRequest { + string user_id = 1; + string room_id = 2; + string variable_name = 3; + int32 value = 4; +} + +message SetVariableReply { + bool success = 1; +} + +message UserIdRequest { + string user_id = 1; +} + +message RoomsListReply { + repeated string room_ids = 1; +} \ No newline at end of file diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs new file mode 100644 index 0000000..1a1bff7 --- /dev/null +++ b/rpc/src/lib.rs @@ -0,0 +1,5 @@ +pub mod protos { + pub mod dicebot { + tonic::include_proto!("dicebot"); + } +} diff --git a/rpc/src/main.rs b/rpc/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/rpc/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} -- 2.40.1 From d4a041129b84fdec849d02f425f7e01669c55f92 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Tue, 1 Jun 2021 20:21:45 +0000 Subject: [PATCH 2/9] Implement get variable --- dicebot/src/bin/tonic_client.rs | 12 +++++++----- dicebot/src/rpc.rs | 26 ++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/dicebot/src/bin/tonic_client.rs b/dicebot/src/bin/tonic_client.rs index 5bec6ab..d049977 100644 --- a/dicebot/src/bin/tonic_client.rs +++ b/dicebot/src/bin/tonic_client.rs @@ -1,18 +1,20 @@ -use tenebrous_rpc::protos::dicebot::dicebot_client::DicebotClient; use tenebrous_rpc::protos::dicebot::UserIdRequest; +use tenebrous_rpc::protos::dicebot::{dicebot_client::DicebotClient, GetVariableRequest}; #[tokio::main] async fn main() -> Result<(), Box> { let mut client = DicebotClient::connect("http://0.0.0.0:9090").await?; - let request = tonic::Request::new(UserIdRequest { - user_id: "Tonic".into(), + let request = tonic::Request::new(GetVariableRequest { + user_id: "@projectmoon:agnos.is".into(), + room_id: "!agICWvldGfuCywUVUM:agnos.is".into(), + variable_name: "stuff".into(), }); - let response = client.rooms_for_user(request).await?.into_inner(); + let response = client.get_variable(request).await?.into_inner(); println!("RESPONSE={:?}", response); - println!("User friendly response is: {:?}", response.room_ids); + println!("User friendly response is: {:?}", response.value); Ok(()) } diff --git a/dicebot/src/rpc.rs b/dicebot/src/rpc.rs index bb59f9f..58373ff 100644 --- a/dicebot/src/rpc.rs +++ b/dicebot/src/rpc.rs @@ -1,15 +1,27 @@ -use std::sync::Arc; - +use crate::db::{errors::DataError, Variables}; use crate::error::BotError; use crate::{config::Config, db::sqlite::Database}; use log::info; +use std::sync::Arc; use tenebrous_rpc::protos::dicebot::{ dicebot_server::{Dicebot, DicebotServer}, GetAllVariablesReply, GetAllVariablesRequest, RoomsListReply, SetVariableReply, SetVariableRequest, UserIdRequest, }; use tenebrous_rpc::protos::dicebot::{GetVariableReply, GetVariableRequest}; -use tonic::{transport::Server, Request, Response, Status}; +use tonic::{transport::Server, Code, Request, Response, Status}; + +impl From for Status { + fn from(error: BotError) -> Status { + Status::new(Code::Internal, error.to_string()) + } +} + +impl From for Status { + fn from(error: DataError) -> Status { + Status::new(Code::Internal, error.to_string()) + } +} pub struct DicebotRpcService { config: Arc, @@ -29,7 +41,13 @@ impl Dicebot for DicebotRpcService { &self, request: Request, ) -> Result, Status> { - Ok(Response::new(GetVariableReply { value: 1 })) + let request = request.into_inner(); + let value = self + .db + .get_user_variable(&request.user_id, &request.room_id, &request.variable_name) + .await?; + + Ok(Response::new(GetVariableReply { value })) } async fn get_all_variables( -- 2.40.1 From 74f2ef88981cc0a00db02531ad49f79486d81a6b Mon Sep 17 00:00:00 2001 From: projectmoon Date: Tue, 1 Jun 2021 22:05:13 +0000 Subject: [PATCH 3/9] Implement remaining rpc methods. Give rpc server access to matrix client. --- dicebot/src/bin/dicebot.rs | 16 ++++---- dicebot/src/bin/tonic_client.rs | 21 +++++++---- dicebot/src/bot/mod.rs | 19 +--------- dicebot/src/matrix.rs | 22 ++++++++++- dicebot/src/rpc.rs | 67 +++++++++++++++++++++++++++++---- rpc/protos/dicebot.proto | 7 +++- 6 files changed, 111 insertions(+), 41 deletions(-) diff --git a/dicebot/src/bin/dicebot.rs b/dicebot/src/bin/dicebot.rs index d386ebd..a1d488c 100644 --- a/dicebot/src/bin/dicebot.rs +++ b/dicebot/src/bin/dicebot.rs @@ -2,6 +2,7 @@ #![type_length_limit = "7605144"] use futures::try_join; use log::error; +use matrix_sdk::Client; use std::env; use std::sync::{Arc, RwLock}; use tenebrous_dicebot::bot::DiceBot; @@ -15,12 +16,13 @@ use tracing_subscriber::filter::EnvFilter; /// Attempt to create config object and ddatabase connection pool from /// the given config path. An error is returned if config creation or /// database pool creation fails for some reason. -async fn init(config_path: &str) -> Result<(Arc, Database), BotError> { +async fn init(config_path: &str) -> Result<(Arc, Database, Client), BotError> { let cfg = read_config(config_path)?; let cfg = Arc::new(cfg); let sqlite_path = format!("{}/dicebot.sqlite", cfg.database_path()); let db = Database::new(&sqlite_path).await?; - Ok((cfg, db)) + let client = tenebrous_dicebot::matrix::create_client(&cfg)?; + Ok((cfg, db, client)) } #[tokio::main] @@ -47,9 +49,9 @@ async fn run() -> Result<(), BotError> { .next() .expect("Need a config as an argument"); - let (cfg, db) = init(&config_path).await?; - let grpc = rpc::serve_grpc(&cfg, &db); - let bot = run_bot(&cfg, &db); + let (cfg, db, client) = init(&config_path).await?; + let grpc = rpc::serve_grpc(&cfg, &db, &client); + let bot = run_bot(&cfg, &db, &client); match try_join!(bot, grpc) { Ok(_) => (), @@ -59,10 +61,10 @@ async fn run() -> Result<(), BotError> { Ok(()) } -async fn run_bot(cfg: &Arc, db: &Database) -> Result<(), BotError> { +async fn run_bot(cfg: &Arc, db: &Database, client: &Client) -> Result<(), BotError> { let state = Arc::new(RwLock::new(DiceBotState::new(&cfg))); - match DiceBot::new(cfg, &state, db) { + match DiceBot::new(cfg, &state, db, client) { Ok(bot) => bot.run().await?, Err(e) => println!("Error connecting: {:?}", e), }; diff --git a/dicebot/src/bin/tonic_client.rs b/dicebot/src/bin/tonic_client.rs index d049977..7d61f5b 100644 --- a/dicebot/src/bin/tonic_client.rs +++ b/dicebot/src/bin/tonic_client.rs @@ -5,16 +5,21 @@ use tenebrous_rpc::protos::dicebot::{dicebot_client::DicebotClient, GetVariableR async fn main() -> Result<(), Box> { let mut client = DicebotClient::connect("http://0.0.0.0:9090").await?; - let request = tonic::Request::new(GetVariableRequest { + // let request = tonic::Request::new(GetVariableRequest { + // user_id: "@projectmoon:agnos.is".into(), + // room_id: "!agICWvldGfuCywUVUM:agnos.is".into(), + // variable_name: "stuff".into(), + // }); + + // let response = client.get_variable(request).await?.into_inner(); + + let request = tonic::Request::new(UserIdRequest { user_id: "@projectmoon:agnos.is".into(), - room_id: "!agICWvldGfuCywUVUM:agnos.is".into(), - variable_name: "stuff".into(), }); - let response = client.get_variable(request).await?.into_inner(); - - println!("RESPONSE={:?}", response); - println!("User friendly response is: {:?}", response.value); - + let response = client.rooms_for_user(request).await?.into_inner(); + // println!("RESPONSE={:?}", response); + // println!("User friendly response is: {:?}", response.value); + println!("Rooms: {:?}", response.rooms); Ok(()) } diff --git a/dicebot/src/bot/mod.rs b/dicebot/src/bot/mod.rs index 965b7f3..5928cda 100644 --- a/dicebot/src/bot/mod.rs +++ b/dicebot/src/bot/mod.rs @@ -35,22 +35,6 @@ pub struct DiceBot { db: Database, } -fn cache_dir() -> Result { - let mut dir = dirs::cache_dir().ok_or(BotError::NoCacheDirectoryError)?; - dir.push("matrix-dicebot"); - Ok(dir) -} - -/// Creates the matrix client. -fn create_client(config: &Config) -> Result { - let cache_dir = cache_dir()?; - //let store = JsonStore::open(&cache_dir)?; - let client_config = ClientConfig::new().store_path(cache_dir); - let homeserver_url = Url::parse(&config.matrix_homeserver())?; - - Ok(Client::new_with_config(homeserver_url, client_config)?) -} - impl DiceBot { /// Create a new dicebot with the given configuration and state /// actor. This function returns a Result because it is possible @@ -60,9 +44,10 @@ impl DiceBot { config: &Arc, state: &Arc>, db: &Database, + client: &Client, ) -> Result { Ok(DiceBot { - client: create_client(&config)?, + client: client.clone(), config: config.clone(), state: state.clone(), db: db.clone(), diff --git a/dicebot/src/matrix.rs b/dicebot/src/matrix.rs index 7bec296..d1fa6c0 100644 --- a/dicebot/src/matrix.rs +++ b/dicebot/src/matrix.rs @@ -1,6 +1,8 @@ +use std::path::PathBuf; + use futures::stream::{self, StreamExt, TryStreamExt}; use log::error; -use matrix_sdk::{events::room::message::NoticeMessageEventContent, room::Joined}; +use matrix_sdk::{events::room::message::NoticeMessageEventContent, room::Joined, ClientConfig}; use matrix_sdk::{ events::room::message::{InReplyTo, Relation}, events::room::message::{MessageEventContent, MessageType}, @@ -9,6 +11,15 @@ use matrix_sdk::{ Error as MatrixError, }; use matrix_sdk::{identifiers::RoomId, identifiers::UserId, Client}; +use url::Url; + +use crate::{config::Config, error::BotError}; + +fn cache_dir() -> Result { + let mut dir = dirs::cache_dir().ok_or(BotError::NoCacheDirectoryError)?; + dir.push("matrix-dicebot"); + Ok(dir) +} /// Extracts more detailed error messages out of a matrix SDK error. fn extract_error_message(error: MatrixError) -> String { @@ -20,6 +31,15 @@ fn extract_error_message(error: MatrixError) -> String { } } +/// Creates the matrix client. +pub fn create_client(config: &Config) -> Result { + let cache_dir = cache_dir()?; + let client_config = ClientConfig::new().store_path(cache_dir); + let homeserver_url = Url::parse(&config.matrix_homeserver())?; + + Ok(Client::new_with_config(homeserver_url, client_config)?) +} + /// Retrieve a list of users in a given room. pub async fn get_users_in_room( client: &Client, diff --git a/dicebot/src/rpc.rs b/dicebot/src/rpc.rs index 58373ff..6e7dcfc 100644 --- a/dicebot/src/rpc.rs +++ b/dicebot/src/rpc.rs @@ -1,10 +1,16 @@ use crate::db::{errors::DataError, Variables}; use crate::error::BotError; +use crate::matrix; use crate::{config::Config, db::sqlite::Database}; +use futures::stream; +use futures::{StreamExt, TryFutureExt, TryStreamExt}; use log::info; +use matrix_sdk::{identifiers::UserId, Client}; +use std::convert::TryFrom; use std::sync::Arc; use tenebrous_rpc::protos::dicebot::{ dicebot_server::{Dicebot, DicebotServer}, + rooms_list_reply::Room, GetAllVariablesReply, GetAllVariablesRequest, RoomsListReply, SetVariableReply, SetVariableRequest, UserIdRequest, }; @@ -26,6 +32,7 @@ impl From for Status { pub struct DicebotRpcService { config: Arc, db: Database, + client: Client, } #[tonic::async_trait] @@ -34,6 +41,17 @@ impl Dicebot for DicebotRpcService { &self, request: Request, ) -> Result, Status> { + let SetVariableRequest { + user_id, + room_id, + variable_name, + value, + } = request.into_inner(); + + self.db + .set_user_variable(&user_id, &room_id, &variable_name, value) + .await?; + Ok(Response::new(SetVariableReply { success: true })) } @@ -54,28 +72,63 @@ impl Dicebot for DicebotRpcService { &self, request: Request, ) -> Result, Status> { - Ok(Response::new(GetAllVariablesReply { - variables: std::collections::HashMap::new(), - })) + let request = request.into_inner(); + let variables = self + .db + .get_user_variables(&request.user_id, &request.room_id) + .await?; + + Ok(Response::new(GetAllVariablesReply { variables })) } async fn rooms_for_user( &self, request: Request, ) -> Result, Status> { - Ok(Response::new(RoomsListReply { - room_ids: Vec::new(), - })) + let UserIdRequest { user_id } = request.into_inner(); + let user_id = UserId::try_from(user_id).map_err(BotError::from)?; + + let rooms_for_user = matrix::get_rooms_for_user(&self.client, &user_id) + .err_into::() + .await?; + + let mut rooms: Vec = stream::iter(rooms_for_user) + .filter_map(|room| async move { + let rooms = room.display_name().await.map(|room_name| Room { + room_id: room.room_id().to_string(), + display_name: room_name, + }); + + Some(rooms) + }) + .err_into::() + .try_collect() + .await?; + + let sort = |r1: &Room, r2: &Room| { + r1.display_name + .to_lowercase() + .cmp(&r2.display_name.to_lowercase()) + }; + + rooms.sort_by(sort); + + Ok(Response::new(RoomsListReply { rooms })) } } -pub async fn serve_grpc(config: &Arc, db: &Database) -> Result<(), BotError> { +pub async fn serve_grpc( + config: &Arc, + db: &Database, + client: &Client, +) -> Result<(), BotError> { match config.rpc_addr() { Some(addr) => { let addr = addr.parse()?; let rpc_service = DicebotRpcService { db: db.clone(), config: config.clone(), + client: client.clone(), }; info!("Serving Dicebot gRPC service on {}", addr); diff --git a/rpc/protos/dicebot.proto b/rpc/protos/dicebot.proto index 59f9959..b27f0f0 100644 --- a/rpc/protos/dicebot.proto +++ b/rpc/protos/dicebot.proto @@ -43,5 +43,10 @@ message UserIdRequest { } message RoomsListReply { - repeated string room_ids = 1; + message Room { + string room_id = 1; + string display_name = 2; + } + + repeated Room rooms = 1; } \ No newline at end of file -- 2.40.1 From d8733258e8424ea5c914da716116ef4e73ea52a5 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Wed, 2 Jun 2021 14:57:41 +0000 Subject: [PATCH 4/9] Add shared secret key authorization to rpc. Not TLS yet, but we can at least authenticate clients... in clear text! --- dicebot/src/bin/tonic_client.rs | 20 ++++++++- dicebot/src/config.rs | 17 ++++++++ dicebot/src/error.rs | 4 ++ dicebot/src/rpc/mod.rs | 50 +++++++++++++++++++++++ dicebot/src/{rpc.rs => rpc/service.rs} | 56 ++++++-------------------- 5 files changed, 102 insertions(+), 45 deletions(-) create mode 100644 dicebot/src/rpc/mod.rs rename dicebot/src/{rpc.rs => rpc/service.rs} (68%) diff --git a/dicebot/src/bin/tonic_client.rs b/dicebot/src/bin/tonic_client.rs index 7d61f5b..4ac8fd8 100644 --- a/dicebot/src/bin/tonic_client.rs +++ b/dicebot/src/bin/tonic_client.rs @@ -1,9 +1,27 @@ use tenebrous_rpc::protos::dicebot::UserIdRequest; use tenebrous_rpc::protos::dicebot::{dicebot_client::DicebotClient, GetVariableRequest}; +use tonic::{metadata::MetadataValue, transport::Channel, Request}; + +async fn create_client( + shared_secret: &str, +) -> Result, Box> { + let channel = Channel::from_static("http://0.0.0.0:9090") + .connect() + .await?; + + let bearer = MetadataValue::from_str(&format!("Bearer {}", shared_secret))?; + + let client = DicebotClient::with_interceptor(channel, move |mut req: Request<()>| { + req.metadata_mut().insert("authorization", bearer.clone()); + Ok(req) + }); + + Ok(client) +} #[tokio::main] async fn main() -> Result<(), Box> { - let mut client = DicebotClient::connect("http://0.0.0.0:9090").await?; + let mut client = create_client("example-key").await?; // let request = tonic::Request::new(GetVariableRequest { // user_id: "@projectmoon:agnos.is".into(), diff --git a/dicebot/src/config.rs b/dicebot/src/config.rs index 5510557..55abbce 100644 --- a/dicebot/src/config.rs +++ b/dicebot/src/config.rs @@ -57,6 +57,11 @@ struct BotConfig { /// What address and port to run the RPC service on. If not /// specified, RPC will not be enabled. rpc_addr: Option, + + /// The shared secret key between the bot and any RPC clients that + /// want to connect to it. The RPC server will reject any clients + /// that don't present the shared key. + rpc_key: Option, } /// The "database" section of the config file. @@ -90,6 +95,12 @@ impl BotConfig { fn rpc_addr(&self) -> Option { self.rpc_addr.clone() } + + #[inline] + #[must_use] + fn rpc_key(&self) -> Option { + self.rpc_key.clone() + } } /// Represents the toml config file for the dicebot. The sections of @@ -153,6 +164,12 @@ impl Config { pub fn rpc_addr(&self) -> Option { self.bot.as_ref().and_then(|bc| bc.rpc_addr()) } + + #[inline] + #[must_use] + pub fn rpc_key(&self) -> Option { + self.bot.as_ref().and_then(|bc| bc.rpc_key()) + } } #[cfg(test)] diff --git a/dicebot/src/error.rs b/dicebot/src/error.rs index aec2272..e8c4a1a 100644 --- a/dicebot/src/error.rs +++ b/dicebot/src/error.rs @@ -4,6 +4,7 @@ use crate::commands::CommandError; use crate::config::ConfigError; use crate::db::errors::DataError; use thiserror::Error; +use tonic::metadata::errors::InvalidMetadataValue; #[derive(Error, Debug)] pub enum BotError { @@ -101,6 +102,9 @@ pub enum BotError { #[error("address parsing error: {0}")] AddressParseError(#[from] AddrParseError), + + #[error("invalid metadata value: {0}")] + TonicInvalidMetadata(#[from] InvalidMetadataValue), } #[derive(Error, Debug)] diff --git a/dicebot/src/rpc/mod.rs b/dicebot/src/rpc/mod.rs new file mode 100644 index 0000000..5a11f7e --- /dev/null +++ b/dicebot/src/rpc/mod.rs @@ -0,0 +1,50 @@ +use crate::error::BotError; +use crate::{config::Config, db::sqlite::Database}; +use log::{info, warn}; +use matrix_sdk::Client; +use service::DicebotRpcService; +use std::sync::Arc; +use tenebrous_rpc::protos::dicebot::dicebot_server::DicebotServer; +use tonic::{metadata::MetadataValue, transport::Server, Request, Status}; + +pub(crate) mod service; + +pub async fn serve_grpc( + config: &Arc, + db: &Database, + client: &Client, +) -> Result<(), BotError> { + match config.rpc_addr().zip(config.rpc_key()) { + Some((addr, rpc_key)) => { + let expected_bearer = MetadataValue::from_str(&format!("Bearer {}", rpc_key))?; + let addr = addr.parse()?; + + let rpc_service = DicebotRpcService { + db: db.clone(), + config: config.clone(), + client: client.clone(), + }; + + info!("Serving Dicebot gRPC service on {}", addr); + + let interceptor = move |req: Request<()>| match req.metadata().get("authorization") { + Some(bearer) if bearer == expected_bearer => Ok(req), + _ => Err(Status::unauthenticated("No valid auth token")), + }; + + let server = DicebotServer::with_interceptor(rpc_service, interceptor); + + Server::builder() + .add_service(server) + .serve(addr) + .await + .map_err(|e| e.into()) + } + _ => noop().await, + } +} + +pub async fn noop() -> Result<(), BotError> { + warn!("RPC address or shared secret not specified. Not enabling gRPC."); + Ok(()) +} diff --git a/dicebot/src/rpc.rs b/dicebot/src/rpc/service.rs similarity index 68% rename from dicebot/src/rpc.rs rename to dicebot/src/rpc/service.rs index 6e7dcfc..1d3136b 100644 --- a/dicebot/src/rpc.rs +++ b/dicebot/src/rpc/service.rs @@ -4,18 +4,15 @@ use crate::matrix; use crate::{config::Config, db::sqlite::Database}; use futures::stream; use futures::{StreamExt, TryFutureExt, TryStreamExt}; -use log::info; -use matrix_sdk::{identifiers::UserId, Client}; +use matrix_sdk::{identifiers::UserId, room::Joined, Client}; use std::convert::TryFrom; use std::sync::Arc; use tenebrous_rpc::protos::dicebot::{ - dicebot_server::{Dicebot, DicebotServer}, - rooms_list_reply::Room, - GetAllVariablesReply, GetAllVariablesRequest, RoomsListReply, SetVariableReply, - SetVariableRequest, UserIdRequest, + dicebot_server::Dicebot, rooms_list_reply::Room, GetAllVariablesReply, GetAllVariablesRequest, + RoomsListReply, SetVariableReply, SetVariableRequest, UserIdRequest, }; use tenebrous_rpc::protos::dicebot::{GetVariableReply, GetVariableRequest}; -use tonic::{transport::Server, Code, Request, Response, Status}; +use tonic::{Code, Request, Response, Status}; impl From for Status { fn from(error: BotError) -> Status { @@ -29,10 +26,11 @@ impl From for Status { } } -pub struct DicebotRpcService { - config: Arc, - db: Database, - client: Client, +#[derive(Clone)] +pub(super) struct DicebotRpcService { + pub(super) config: Arc, + pub(super) db: Database, + pub(super) client: Client, } #[tonic::async_trait] @@ -93,13 +91,13 @@ impl Dicebot for DicebotRpcService { .await?; let mut rooms: Vec = stream::iter(rooms_for_user) - .filter_map(|room| async move { - let rooms = room.display_name().await.map(|room_name| Room { + .filter_map(|room: Joined| async move { + let room: Result = room.display_name().await.map(|room_name| Room { room_id: room.room_id().to_string(), display_name: room_name, }); - Some(rooms) + Some(room) }) .err_into::() .try_collect() @@ -116,33 +114,3 @@ impl Dicebot for DicebotRpcService { Ok(Response::new(RoomsListReply { rooms })) } } - -pub async fn serve_grpc( - config: &Arc, - db: &Database, - client: &Client, -) -> Result<(), BotError> { - match config.rpc_addr() { - Some(addr) => { - let addr = addr.parse()?; - let rpc_service = DicebotRpcService { - db: db.clone(), - config: config.clone(), - client: client.clone(), - }; - - info!("Serving Dicebot gRPC service on {}", addr); - Server::builder() - .add_service(DicebotServer::new(rpc_service)) - .serve(addr) - .await - .map_err(|e| e.into()) - } - _ => noop().await, - } -} - -pub async fn noop() -> Result<(), BotError> { - info!("RPC address not specified. Not enabling gRPC."); - Ok(()) -} -- 2.40.1 From 0b47a5d099c7181ea2a9276e20b197ff39cac6a2 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Wed, 2 Jun 2021 15:06:09 +0000 Subject: [PATCH 5/9] Remove warnings --- dicebot/src/bot/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dicebot/src/bot/mod.rs b/dicebot/src/bot/mod.rs index 5928cda..955f9ff 100644 --- a/dicebot/src/bot/mod.rs +++ b/dicebot/src/bot/mod.rs @@ -4,13 +4,10 @@ use crate::db::sqlite::Database; use crate::db::DbState; use crate::error::BotError; use crate::state::DiceBotState; -use dirs; use log::info; -use matrix_sdk::{self, identifiers::EventId, room::Joined, Client, ClientConfig, SyncSettings}; +use matrix_sdk::{self, identifiers::EventId, room::Joined, Client, SyncSettings}; use std::clone::Clone; -use std::path::PathBuf; use std::sync::{Arc, RwLock}; -use url::Url; mod command_execution; pub mod event_handlers; -- 2.40.1 From c28d5a5c215e936502b08896935e5feac1a8441d Mon Sep 17 00:00:00 2001 From: projectmoon Date: Wed, 2 Jun 2021 15:14:43 +0000 Subject: [PATCH 6/9] Fix tests --- dicebot/src/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dicebot/src/config.rs b/dicebot/src/config.rs index 55abbce..fc842db 100644 --- a/dicebot/src/config.rs +++ b/dicebot/src/config.rs @@ -49,7 +49,7 @@ fn db_path_from_env() -> String { } /// The "bot" section of the config file, for bot settings. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] struct BotConfig { /// How far back from current time should we process a message? oldest_message_age: Option, @@ -189,6 +189,7 @@ mod tests { }), bot: Some(BotConfig { oldest_message_age: None, + ..Default::default() }), }; -- 2.40.1 From d2da664c8c88cb5436be16614e13b0819f1a8238 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Wed, 2 Jun 2021 15:19:58 +0000 Subject: [PATCH 7/9] Move license, readme, and dockerfile to new repo root. --- dicebot/Dockerfile => Dockerfile | 0 dicebot/LICENSE => LICENSE | 0 dicebot/README.md => README.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename dicebot/Dockerfile => Dockerfile (100%) rename dicebot/LICENSE => LICENSE (100%) rename dicebot/README.md => README.md (100%) diff --git a/dicebot/Dockerfile b/Dockerfile similarity index 100% rename from dicebot/Dockerfile rename to Dockerfile diff --git a/dicebot/LICENSE b/LICENSE similarity index 100% rename from dicebot/LICENSE rename to LICENSE diff --git a/dicebot/README.md b/README.md similarity index 100% rename from dicebot/README.md rename to README.md -- 2.40.1 From fbd3d8c8ac8c9d9337169b423d935956db3229bf Mon Sep 17 00:00:00 2001 From: projectmoon Date: Wed, 2 Jun 2021 15:35:10 +0000 Subject: [PATCH 8/9] Add rustfmt to builder image for protobufs --- dicebot/Dockerfile | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 dicebot/Dockerfile diff --git a/dicebot/Dockerfile b/dicebot/Dockerfile new file mode 100644 index 0000000..6dc7621 --- /dev/null +++ b/dicebot/Dockerfile @@ -0,0 +1,35 @@ +# Builder image with development dependencies. +FROM bougyman/voidlinux:glibc as builder +RUN xbps-install -Syu +RUN xbps-install -Sy base-devel rustup cargo cmake wget gnupg +RUN xbps-install -Sy openssl-devel libstdc++-devel +RUN rustup-init -qy +RUN rustup component add rustfmt # Needed for protobuf building. + +# Install tini for signal processing and zombie killing +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/local/bin/tini +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini.asc /tini.asc +RUN gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 \ + && gpg --batch --verify /tini.asc /usr/local/bin/tini +RUN chmod +x /usr/local/bin/tini + +# Build dicebot +RUN mkdir -p /root/src +WORKDIR /root/src +ADD . ./ +RUN . /root/.cargo/env && cargo build --release + +# Final image +FROM bougyman/voidlinux:tiny +RUN xbps-install -Sy ca-certificates libstdc++ +COPY --from=builder \ + /root/src/target/release/dicebot \ + /usr/local/bin/ +COPY --from=builder \ + /usr/local/bin/tini \ + /usr/local/bin/ + +ENV XDG_CACHE_HOME "/cache" +ENV DATABASE_PATH "/cache/bot-db" +ENTRYPOINT [ "/usr/local/bin/tini", "-v", "--", "/usr/local/bin/dicebot", "/config/dicebot-config.toml" ] -- 2.40.1 From 5eff2b93c7f8d022cb8b2f72cb43cf62fb0c1da6 Mon Sep 17 00:00:00 2001 From: projectmoon Date: Wed, 2 Jun 2021 15:48:08 +0000 Subject: [PATCH 9/9] Add rustfmt for protobufs to drone test target. --- .drone.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.drone.yml b/.drone.yml index f45000c..9710734 100644 --- a/.drone.yml +++ b/.drone.yml @@ -7,6 +7,7 @@ steps: commands: - apt-get update - apt-get install -y cmake + - rustup component add rustfmt - cargo build --verbose --all - cargo test --verbose --all -- 2.40.1