diff --git a/Cargo.toml b/Cargo.toml index 50c9ab3..33863d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] rand = "0.8.5" -serenity = { version = "0.11.6", default-features = false, features = ["client", "gateway", "rustls_backend", "model"] } +serenity = { version = "0.11.6", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "cache"] } tokio = { version = "1.21.1", features = ["full"] } diff --git a/src/main.rs b/src/main.rs index 0e3efd7..ed09a37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,22 +6,28 @@ mod utils; // use std::collections::HashSet; use std::sync::Arc; use std::sync::atomic::AtomicU64; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::time::Duration; use serenity::async_trait; use serenity::prelude::*; // use serenity::model::prelude::*; use serenity::model::channel::Message; use serenity::model::gateway::Ready; +use serenity::model::id::GuildId; use serenity::http::Http; -struct Globals; +struct Owner; -impl TypeMapKey for Globals +impl TypeMapKey for Owner { type Value = Arc; } -struct Handler; +struct Handler +{ + is_loop_running: AtomicBool, +} #[async_trait] impl EventHandler for Handler @@ -43,9 +49,52 @@ impl EventHandler for Handler // private channels, and more. // // In this case, just print what the current user's username is. - async fn ready(&self, _: Context, ready: Ready) { + async fn ready(&self, _: Context, ready: Ready) + { println!("{} is connected!", ready.user.name); } + + // CODE TAKEN FROM EXAMPLE: + // https://github.com/serenity-rs/serenity/blob/current/examples/e13_parallel_loops/src/main.rs + // We use the cache_ready event just in case some cache operation is required in whatever use + // case you have for this. + async fn cache_ready(&self, ctx: Context, _guilds: Vec) + { + println!("Cache built successfully!"); + + // it's safe to clone Context, but Arc is cheaper for this use case. + // Untested claim, just theoretically. :P + let ctx = Arc::new(ctx); + + // We need to check that the loop is not already running when this event triggers, + // as this event triggers every time the bot enters or leaves a guild, along every time the + // ready shard event triggers. + // + // An AtomicBool is used because it doesn't require a mutable reference to be changed, as + // we don't have one due to self being an immutable reference. + if !self.is_loop_running.load(Ordering::Relaxed) { + // We have to clone the Arc, as it gets moved into the new thread. + let ctx1 = Arc::clone(&ctx); + // tokio::spawn creates a new green thread that can run in parallel with the rest of + // the application. + tokio::spawn(async move { + loop { + // We clone Context again here, because Arc is owned, so it moves to the + // new function. + //log_system_load(Arc::clone(&ctx1)).await; + + // TODO: function we want to run called here + // Run racetime.gg and twitch api checks + // Post new stuff to discord channel + + tokio::time::sleep(Duration::from_secs(120)).await; + } + }); + + // Now that the loop is running, we set the bool to true + self.is_loop_running.swap(true, Ordering::Relaxed); + } + } } @@ -78,7 +127,10 @@ async fn main() // Create a new instance of the Client, logging in as a bot. This will // automatically prepend your bot token with "Bot ", which is a requirement // by Discord for bot users. - let mut client = Client::builder(&token, intents).event_handler(Handler).await.expect("Err creating client"); + let mut client = Client::builder(&token, intents).event_handler(Handler + { + is_loop_running: AtomicBool::new(false), + }).await.expect("Err creating client"); // Set the global data @@ -87,9 +139,10 @@ async fn main() // Open the data lock in write mode, so keys can be inserted to it. let mut data = client.data.write().await; - data.insert::(Arc::new(AtomicU64::new(owner.into()))); + data.insert::(Arc::new(AtomicU64::new(owner.into()))); } + // Finally, start a single shard, and start listening to events. // // Shards will automatically attempt to reconnect, and will perform