diff --git a/Cargo.lock b/Cargo.lock index 6233a0e..7e55464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,7 +241,10 @@ dependencies = [ "postgres-native-tls", "serde", "sqlx", + "thiserror 1.0.69", "tokio", + "tracing", + "tracing-subscriber", ] [[package]] @@ -1244,6 +1247,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1356,6 +1369,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking" version = "2.2.1" @@ -1990,6 +2009,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -2424,6 +2452,16 @@ dependencies = [ "syn 2.0.95", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.37" @@ -2642,6 +2680,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", ] [[package]] @@ -2787,6 +2851,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2946,6 +3016,22 @@ dependencies = [ "web-sys", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -2955,6 +3041,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 1866681..a7f05c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,6 @@ chrono = { version = "0.4", features = ["serde"] } native-tls = "0.2" postgres-native-tls = "0.5" anyhow = "1.0.95" +tracing = "0.1" +tracing-subscriber = "0.3" +thiserror = "1.0" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..5af034e --- /dev/null +++ b/src/config.rs @@ -0,0 +1,26 @@ +use std::env; + +#[derive(Debug)] +pub struct Config { + pub bot_token: String, + pub database_url: String, + pub command_prefix: String, +} + +impl Config { + pub fn load() -> Result { + Ok(Self { + bot_token: env::var("BOT_TOKEN").map_err(|_| ConfigError::MissingBotToken)?, + database_url: env::var("DATABASE_URL").map_err(|_| ConfigError::MissingDatabaseUrl)?, + command_prefix: env::var("COMMAND_PREFIX").unwrap_or_else(|_| "c:".to_string()), + }) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum ConfigError { + #[error("Missing BOT_TOKEN environment variable")] + MissingBotToken, + #[error("Missing DATABASE_URL environment variable")] + MissingDatabaseUrl, +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 552af5a..588d5f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ mod commands; +mod config; mod database; use std::sync::Arc; -use anyhow::Result; +use config::{Config, ConfigError}; use commands::create_cigarette_ui; use database::Database; use poise::{ @@ -11,6 +12,7 @@ use poise::{ PrefixFrameworkOptions, }; use sqlx::PgPool; +use tracing::{error, info}; pub struct Data { pub database: Arc>, @@ -19,20 +21,22 @@ pub struct Data { pub type Error = Box; pub type Context<'a> = poise::Context<'a, Data, Error>; -#[tokio::main] -async fn main() -> Result<()> { - let token = std::env::var("BOT_TOKEN").expect("missing BOT_TOKEN"); - let intents = serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::all(); +#[derive(Debug, thiserror::Error)] +pub enum BotError { + #[error("Configuration error: {0}")] + Config(#[from] ConfigError), + #[error("Database connection error: {0}")] + Database(#[from] sqlx::Error), + #[error("Client error: {0}")] + Client(#[from] serenity::Error), +} - let pool = - PgPool::connect(&std::env::var("DATABASE_URL").expect("missing DATABASE_URL")).await?; - let db = Database::new(pool); - - let framework = poise::Framework::builder() +async fn setup_framework(config: &Config, db: Database) -> poise::Framework { + poise::Framework::builder() .options(poise::FrameworkOptions { commands: vec![create_cigarette_ui()], prefix_options: PrefixFrameworkOptions { - prefix: Some(String::from("c:")), + prefix: Some(config.command_prefix.clone()), ..Default::default() }, ..Default::default() @@ -44,13 +48,38 @@ async fn main() -> Result<()> { }) }) }) - .build(); + .build() +} - let client = serenity::ClientBuilder::new(token, intents) +async fn create_client(config: &Config, framework: poise::Framework) -> Result { + let intents = serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::all(); + + serenity::ClientBuilder::new(&config.bot_token, intents) .framework(framework) - .await; + .await + .map_err(BotError::from) +} - client.unwrap().start().await?; +async fn connect_database(config: &Config) -> Result { + PgPool::connect(&config.database_url) + .await + .map_err(BotError::from) +} + +#[tokio::main] +async fn main() -> Result<(), BotError> { + tracing_subscriber::fmt::init(); + info!("Starting cigarette counter bot..."); + + let config = Config::load()?; + let pool = connect_database(&config).await?; + let db = Database::new(pool); + + let framework = setup_framework(&config, db).await; + let mut client = create_client(&config, framework).await?; + + info!("Bot is running!"); + client.start().await?; Ok(()) }