From 05c9bed1f8189efd6708dace4d81f26f07e97bea Mon Sep 17 00:00:00 2001 From: toast Date: Thu, 12 Dec 2024 03:01:44 +1100 Subject: [PATCH] Update template with changes --- Cargo.lock | 18 +- Cargo.toml | 23 +- cmds/Cargo.toml | 13 + cmds/src/dispatch.rs | 26 ++ {src/commands => cmds/src/dispatch}/dev.rs | 41 ++- cmds/src/dispatch/eightball.rs | 276 ++++++++++++++++++ {src/commands => cmds/src/dispatch}/ping.rs | 24 +- {src/commands => cmds/src/dispatch}/uptime.rs | 76 ++--- cmds/src/lib.rs | 2 + events/Cargo.toml | 2 +- events/src/events.rs | 10 +- events/src/events/ready.rs | 68 +++-- events/src/events/shards.rs | 14 +- jobs/src/tasks.rs | 24 +- library/Cargo.toml | 2 +- library/build.rs | 11 - library/src/config.rs | 30 +- library/src/utils.rs | 27 +- run.sh | 4 +- rust-toolchain | 2 + rustfmt.toml | 20 ++ src/commands.rs | 26 -- src/commands/eightball.rs | 180 ------------ src/main.rs | 195 +++++++------ src/shutdown.rs | 9 +- tsclient/Cargo.toml | 2 +- tsclient/src/lib.rs | 33 +-- 27 files changed, 659 insertions(+), 499 deletions(-) create mode 100644 cmds/Cargo.toml create mode 100644 cmds/src/dispatch.rs rename {src/commands => cmds/src/dispatch}/dev.rs (81%) create mode 100755 cmds/src/dispatch/eightball.rs rename {src/commands => cmds/src/dispatch}/ping.rs (74%) rename {src/commands => cmds/src/dispatch}/uptime.rs (66%) create mode 100644 cmds/src/lib.rs create mode 100644 rust-toolchain create mode 100644 rustfmt.toml delete mode 100755 src/commands.rs delete mode 100755 src/commands/eightball.rs diff --git a/Cargo.lock b/Cargo.lock index 6527c44..b69fc24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1463,15 +1463,23 @@ name = "rustbot" version = "0.1.0" dependencies = [ "poise", - "rand", - "reqwest", + "rustbot_cmds", "rustbot_events", "rustbot_lib", "rustbot_tokens", + "tokio", +] + +[[package]] +name = "rustbot_cmds" +version = "0.1.0" +dependencies = [ + "poise", + "rand", + "reqwest", + "rustbot_lib", "serde", "sysinfo", - "time", - "tokio", "uptime_lib", ] @@ -1492,7 +1500,7 @@ dependencies = [ [[package]] name = "rustbot_lib" -version = "0.1.19" +version = "0.1.0" dependencies = [ "cargo_toml", "poise", diff --git a/Cargo.toml b/Cargo.toml index 641701d..82e84b7 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [workspace] members = [ + "cmds", "events", "jobs", "library", @@ -16,21 +17,21 @@ cargo_toml = "0.21.0" poise = "0.6.1" regex = "1.11.0" serde = "1.0.210" -tokio = { version = "1.40.0", features = ["macros", "signal", "rt-multi-thread"] } -reqwest = { version = "0.12.8", features = ["native-tls-vendored"] } - -[dependencies] -rustbot_events = { path = "events" } -rustbot_lib = { path = "library" } -rustbot_tokens = { path = "tsclient" } -poise = { workspace = true } rand = "0.8.5" -reqwest = { workspace = true } -serde = { workspace = true } sysinfo = "0.33.0" time = "0.3.36" -tokio = { workspace = true } uptime_lib = "0.3.1" +tokio = { version = "1.40.0", features = ["macros", "signal", "rt-multi-thread"] } +reqwest = { version = "0.12.8", features = ["native-tls-vendored"] } +rustbot_lib = { path = "library" } + +[dependencies] +poise = { workspace = true } +rustbot_cmds = { path = "cmds" } +rustbot_events = { path = "events" } +rustbot_lib = { workspace = true } +rustbot_tokens = { path = "tsclient" } +tokio = { workspace = true } [patch.crates-io] poise = { git = "https://github.com/serenity-rs/poise", branch = "serenity-next" } diff --git a/cmds/Cargo.toml b/cmds/Cargo.toml new file mode 100644 index 0000000..30f94e0 --- /dev/null +++ b/cmds/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rustbot_cmds" +version = "0.1.0" +edition = "2024" + +[dependencies] +poise = { workspace = true } +rand = { workspace = true } +reqwest = { workspace = true } +rustbot_lib = { workspace = true } +serde = { workspace = true } +sysinfo = { workspace = true } +uptime_lib = { workspace = true } diff --git a/cmds/src/dispatch.rs b/cmds/src/dispatch.rs new file mode 100644 index 0000000..4d6fde7 --- /dev/null +++ b/cmds/src/dispatch.rs @@ -0,0 +1,26 @@ +mod dev; +mod eightball; +mod ping; +mod uptime; + +pub use { + dev::dev, + eightball::eightball, + ping::ping, + uptime::uptime +}; + +#[macro_export] +macro_rules! collect { + () => { + vec![ + // Developer command(s) + $crate::dev(), + // Utility commands + $crate::ping(), + $crate::uptime(), + // Unsorted mess + $crate::eightball(), + ] + }; +} diff --git a/src/commands/dev.rs b/cmds/src/dispatch/dev.rs similarity index 81% rename from src/commands/dev.rs rename to cmds/src/dispatch/dev.rs index 99d35f6..ad95497 100755 --- a/src/commands/dev.rs +++ b/cmds/src/dispatch/dev.rs @@ -1,13 +1,15 @@ -use rustbot_lib::{ - RustbotContext, - RustbotResult -}; -use poise::{ - CreateReply, - serenity_prelude::{ - ChannelId, - ShardId, - ShardRunnerInfo +use { + poise::{ + CreateReply, + serenity_prelude::{ + ChannelId, + ShardId, + ShardRunnerInfo + } + }, + rustbot_lib::{ + RustbotContext, + RustbotResult } }; @@ -44,9 +46,7 @@ async fn format_shard_info( interaction_context = "Guild|BotDm|PrivateChannel", subcommands("deploy", "servers", "shards", "echo") )] -pub async fn dev(_: RustbotContext<'_>) -> RustbotResult<()> { - Ok(()) -} +pub async fn dev(_: RustbotContext<'_>) -> RustbotResult<()> { Ok(()) } /// Deploy commands to this guild or globally #[poise::command(prefix_command)] @@ -90,7 +90,8 @@ async fn echo( ctx: RustbotContext<'_>, #[description = "Message to be echoed as a bot"] message: String, #[description = "Channel to send this to"] - #[channel_types("Text", "PublicThread", "PrivateThread")] channel: Option + #[channel_types("Text", "PublicThread", "PrivateThread")] + channel: Option ) -> RustbotResult<()> { ctx.defer_ephemeral().await?; @@ -101,18 +102,10 @@ async fn echo( match ChannelId::new(channel.get()).say(ctx.http(), message).await { Ok(_) => { - ctx.send( - CreateReply::new() - .content("Sent!") - .ephemeral(true) - ).await?; + ctx.send(CreateReply::new().content("Sent!").ephemeral(true)).await?; }, Err(y) => { - ctx.send( - CreateReply::new() - .content(format!("Failed... `{y}`")) - .ephemeral(true) - ).await?; + ctx.send(CreateReply::new().content(format!("Failed... `{y}`")).ephemeral(true)).await?; return Ok(()); } } diff --git a/cmds/src/dispatch/eightball.rs b/cmds/src/dispatch/eightball.rs new file mode 100755 index 0000000..80dc3ae --- /dev/null +++ b/cmds/src/dispatch/eightball.rs @@ -0,0 +1,276 @@ +use { + poise::{ + builtins::paginate, + serenity_prelude::UserId + }, + rand::random, + rustbot_lib::{ + RustbotContext, + RustbotResult, + config::BINARY_PROPERTIES + } +}; + +#[derive(poise::ChoiceParameter, Clone)] +enum ResponseMode { + Normal, + Chicken, + #[name = "Chaotic & Unhinged"] + Chaotic +} + +/// Ask the Magic 8-Ball a yes/no question and get an unpredictable answer +#[poise::command( + slash_command, + install_context = "Guild|User", + interaction_context = "Guild|BotDm|PrivateChannel", + rename = "8ball" +)] +pub async fn eightball( + ctx: RustbotContext<'_>, + #[description = "Your yes/no question"] question: String, + #[description = "Response modes"] mode: Option +) -> RustbotResult<()> { + if question.to_ascii_lowercase().contains("niko, show list") { + show_list(ctx, mode.clone().unwrap_or(ResponseMode::Normal)).await?; + return Ok(()) + } + + let rand_resp = match mode { + Some(ResponseMode::Chicken) => get_random_chicken_response(), + Some(ResponseMode::Chaotic) => get_random_chaotic_response(), + _ => get_random_response() + }; + + ctx.reply(format!("> {question}\n{rand_resp}")).await?; + + Ok(()) +} + +async fn show_list( + ctx: RustbotContext<'_>, + list_type: ResponseMode +) -> RustbotResult<()> { + if ctx.author().id != UserId::new(BINARY_PROPERTIES.developers[0]) { + ctx + .reply("The list knows you're looking, but it's playing a game of hide and seek. For now, it wins.") + .await?; + return Ok(()); + } + + let chunks: Vec = match list_type { + ResponseMode::Normal => RESPONSES.chunks(10).map(|chunk| chunk.join("\n\n")).collect(), + ResponseMode::Chicken => CHICKEN_RESPONSES.chunks(10).map(|chunk| chunk.join("\n\n")).collect(), + ResponseMode::Chaotic => CHAOTIC_RESPONSES.chunks(10).map(|chunk| chunk.join("\n\n")).collect() + }; + + let pages: Vec<&str> = chunks.iter().map(|s| s.as_str()).collect(); + paginate(ctx, &pages).await?; + + Ok(()) +} + +const RESPONSES: [&str; 45] = [ + "Reply hazy. Look it up on Google.", // no + "Meh — Figure it out yourself.", // no + "I don't know, what do you think?", // no + "Yes.", // yes + "No.", // no + "It is decidedly so", // yes + "Signs point to... maybe... depends on... hold on, let me get my glasses, this is getting pretty tiny... depends on whether you'd be up to \ + getting to know your Magic 8-Ball a little better.", // no + "Signs point to... ~~yes~~ no.", // no + "Why do you want to know the answer? It's obviously a yes.", // yes + "Outlook not so good.", // no + "Outlook hazy.", // no + "What are you, stupid?", // no + "How the hell do you not know that?", // no + "Really? Making a decision based on what the plastic 8-Ball says? Jesus...", // no + "Try asking later...", // no + "I don't know, whip out the ouija board and try again?", // no + "The answer is yes.", // yes + "Yes, actually no. Wait, nevermind.", // no + "Maybeee...", // yes + "Definitely!", // yes + "It is decidedly so.", // yes + "My reply is no.", // no + "My sources confirms that the answer is no.\nSource: :sparkles: *i made it up* :sparkles:", // no + "As I see it, yes.", // yes + "Don't count on it.", // no + "Whoa! Why do I have to answer this?", // no + "Highly unlikely.", // no + "Sure, but with extreme cautions.", // yes + "What kind of stupid question is that?? No! I'm not answering that!", // no + "Try asking this to a chicken. Probably knows it better than I do!", // no + "Not in a million years!", // no + "As a matter of fact, yes.", // yes + "It's a no, better go ask someone else.", // no + "In the end, it's not a bad choice.", // yes + "Nope, not today.", // no + "Cross your fingers, the answer is yes!", // yes + "Nope. *shakes head*", // no + "The fortune cookie said yes.", // yes + "Sorry, the fortune cookie over there said no.", // no + "Sorry, not happening.", // no + "I'll have to consult my sources... *flips coin*... no.", // no + "I'll have to consult the magic 8-ball... *shakes*... no.", // no + "I'm not sure to be honest, let's ask your friend. Oh wait...", // no + "This question flew over my head, I'll pass.", // no + "Oops, the Magic 8-Ball shattered itself when you asked that! I'll take that as a no." // no +]; + +const CHICKEN_RESPONSES: [&str; 54] = [ + "Cluck cluck... Reply hazy, try pecking Google.", // no + "Meh... Figure it out yourself, or scratch around a bit.", // no + "I don't know... what do you think? *pecks at ground*", // no + "BAWK! YES!", // yes + "Cluck... no.", // no + "It is decidedly so! *flaps wings*", // yes + "Signs point to... maybe... hold on, let me fluff my feathers... depends on whether you'd get to know your Magic Chicken a bit better.", // no + "Signs point to... ~~yes~~ cluck no.", // no + "Why do you want to know? It's a big cluckin' yes!", // yes + "Outlook not so clucking good.", // no + "Outlook cluckin' hazy.", // no + "What are you, a lost chick? Cluck!", // no + "How the cluck do you not know that?", // no + "Really? Asking a chicken to decide your fate? *clucks judgmentally*", // no + "Peck back later, I'm nesting...", // no + "I don't know, try flapping your wings and ask again?", // no + "The answer is a big ol' yes! *flaps happily*", // yes + "Yes... wait, actually... no. Cluck, I'm confused.", // no + "Maaaaybe... *chicken waddle*?", // yes + "Definitely! *struts confidently*", // yes + "It is decidedly so. *struts with pride*", // yes + "My reply is a solid *cluck* no.", // no + "My sources confirm it's a cluckin' no.\nSource: 🐔 *I made it up* 🐔", // no + "As I see it, yes! *pecks approvingly*", // yes + "Don't count on it. *cluck cluck*", // no + "Whoa, why do I have to answer this? *fluffs feathers*", // no + "Highly unlikely. *chicken stare*", // no + "Sure, but with extreme cluckin' caution.", // yes + "What kind of stupid question is that?? No! *angry clucks*", // no + "Try asking this to a fellow chicken. They probably know better than I do!", // no + "Cluck yes! *does a happy chicken dance*", // yes + "No way, not even for a big bag of feed.", // no + "Yes! *lays egg of approval*", // yes + "It's a no, better go scratch somewhere else.", // no + "Cluck-tastic! That's a definite yes.", // yes + "Cluck yeah! *struts proudly*", // yes + "Nope, not today. *shakes head*", // no + "Feathers crossed, the answer is yes!", // yes + "Chicken says nope. *tilts head*", // no + "Absolutely! *clucks happily*", // yes + "Not a chance. *fluffs feathers*", // no + "Eggcellent choice! Yes!", // yes + "Not in a million clucks!", // no + "As a matter of cluck, yes! *clucks approvingly*", // yes + "It's a nopity nope, better go ask another chicken.", // no + "In the end, it's not a bad cluck", // yes + "Nope, not today. *clucks sadly*", // no + "Cross your feathers, the answer is yes!", // yes + "The fortune cookie said yes. *clucks in agreement*", // yes + "Sorry, the fortune cookie over there said no. *clucks in disagreement*", // no + "I'll have to consult my sources... *flips corn*... no.", // no + "I'll have to consult the magic 8-cluck... *shakes*... no.", // no + "I'm not sure to be honest, let's ask your chicken friend. Oh wait...", // no + "This question floated over my head, I'll pass. *clucks dismissively*" // no +]; + +const CHAOTIC_RESPONSES: [&str; 90] = [ + "Oops! The Magic 8-Ball shattered upon hearing your question. Coincidence?", // no + "Reply hazy. Ask Google’s evil twin, Froogle.", // no + "Meh — Consult the ancient texts of Netflix subtitles.", // no + "I don't know, but your cat probably does.", // no + "Yes, but only if you wear a clown wig.", // yes + "No. Unless the moon winks at you first.", // no + "It is decidedly a resounding honk-honk!", // yes + "Signs point to... maybe... or not... or wait... oh look, a squirrel!", // no + "Signs point to... ~~yes~~ pancakes. Definitely pancakes.", // no + "Why do you want to know? It’s obviously a yes — trust the donut prophecy.", // yes + "Outlook not so good. Blame Mercury retrograde or your Wi-Fi.", // no + "Outlook hazy. Consult the nearest fortune-telling hamster.", // no + "What are you, a toaster in disguise?", // no + "How the heck do you not know this? Ask a sock puppet!", // no + "Really? Making life choices based on a magic ball? Bold move, friend.", // no + "Try asking later... when I’m less busy binge-watching.", // no + "I don't know, summon a raven and whisper your question into the void.", // no + "The answer is yes, as foretold by the mystical spaghetti.", // yes + "Yes, actually no. Wait, yes? Let’s go with potato.", // no + "Maybeee... if the stars align and your pizza has extra cheese.", // yes + "Definitely! Unless gravity stops working.", // yes + "It is decidedly so. So what? Buy a llama and see what happens.", // yes + "My reply is no, and also banana pudding.", // no + "My sources confirm that the answer is no.\nSource: A suspicious pigeon.", // no + "As I see it, yes. As the chicken sees it, no. Trust who you like.", // yes + "Don't count on it. Count on marshmallows instead.", // no + "Whoa! Why do I have to answer this? Ask a rubber duck.", // no + "Highly unlikely. Unless it’s Tuesday on Mars.", // no + "Sure, but with extreme caution and a tinfoil hat.", // yes + "What kind of silly question is that?? No! Also, here’s a kazoo.", // no + "Try asking this to a chicken. They’re the true oracles.", // no + "Not in a million years! Unless the earth is made of cheese.", // no + "As a matter of fact, yes. And it’s raining tacos.", // yes + "It's a no, but the raccoons might know better.", // no + "In the end, it’s not a bad choice. Or is it? Mwahaha.", // yes + "Nope, not today. Try tomorrow after coffee.", // no + "Cross your fingers! Or better yet, cross the streams.", // yes + "Nope. *shakes head like a very judgmental parrot*", // no + "The fortune cookie said yes, but it was written in crayon.", // yes + "Sorry, the fortune cookie over there said no. Blame it.", // no + "Sorry, not happening. But you get a virtual sticker for trying!", // no + "I'll have to consult my sources... *flips a pancake*... no.", // no + "I'll have to consult the magic 8-ball... *shakes it violently*... still no.", // no + "I'm not sure, but your imaginary friend says yes.", // yes + "This question flew over my head, so I’ll just say 'llama'.", // no + "The answer is yes, but only if you do it while wearing socks on your hands.", // yes + "No, and I think you broke the space-time continuum by asking.", // no + "Why not? What’s the worst that could happen? Oh wait...", // no + "The stars say yes, but the planets are still debating.", // yes + "The universe just facepalmed at your question.", // no + "Ask again while juggling flaming pineapples for a clearer answer.", // no + "Nope, not unless you bribe me with tacos.", // no + "I consulted the oracle... she’s out to lunch. Try later.", // no + "Yes, but only if you can lick your elbow right now.", // yes + "No, because I said so and I’m very wise. Also, I’m a plastic ball.", // no + "Yes. No. Wait, I’ve lost track. Did you hear that noise?", // no + "Absolutely, as long as you bring me a rubber chicken as tribute.", // yes + "I asked a wizard, and they just laughed hysterically.", // no + "The spirits say no, but the ghosts are nodding yes.", // no + "Yes, if you believe in unicorns and the power of friendship.", // yes + "No, and also you might want to move. Something’s behind you.", // no + "Ask again, but this time with interpretive dance.", // no + "Definitely! Unless the moon turns into cheese. Then no.", // yes + "I see... wait, no, I don’t see. My crystal ball is buffering.", // no + "Sure! But only after a karaoke duet with a raccoon.", // yes + "Yes, but only if you promise not to tell the ducks.", // yes + "No way, unless you can recite the alphabet backwards in one breath.", // no + "Ask the magic mushroom. It’s way more in touch with reality than I am.", // no + "No, because gravity disagrees with your premise.", // no + "Yes, but first you must complete the sacred quest for nachos.", // yes + "The answer is hidden in the folds of your laundry. Go check.", // no + "I would answer, but I’m legally obligated to stay mysterious.", // no + "Absolutely! If you can solve this riddle: What walks on four legs in the morning, two legs at noon, and... oh wait, wrong universe.", // yes + "The council of frogs says yes, but only if you croak like one.", // yes + "No, but only because the Magic 8-Ball union forbids it.", // no + "Yes, if the dog wags its tail twice before the clock strikes midnight.", // yes + "Try again after doing three cartwheels and making a wish.", // no + "The ducks in my dreams say no. They’re rarely wrong.", // no + "Not today, Satan. Not today.", // no + "Yes, but only on Wednesdays during a full moon.", // yes + "No, because bananas don’t grow in winter.", // no + "The answer is locked in a time capsule. Check back in 50 years.", // no + "I don’t know, but it smells like trouble.", // no + "Why not? The penguins approve, and that’s good enough for me.", // yes + "Sure, but only if you say 'bubblegum' ten times fast.", // yes + "No, unless you can outsmart a sentient toaster.", // no + "The answer is yes, but it comes with a plot twist.", // yes + "Flip a coin, spin three times, and consult your nearest cactus. Good luck!", // no + "Only on the condition that you buy me a donut.", // yes + "Yes, but proceed at your own risk. The llamas are watching." // yes +]; + +fn get_random_response() -> &'static str { RESPONSES[random::() % RESPONSES.len()] } + +fn get_random_chicken_response() -> &'static str { CHICKEN_RESPONSES[random::() % CHICKEN_RESPONSES.len()] } + +fn get_random_chaotic_response() -> &'static str { CHAOTIC_RESPONSES[random::() % CHAOTIC_RESPONSES.len()] } diff --git a/src/commands/ping.rs b/cmds/src/dispatch/ping.rs similarity index 74% rename from src/commands/ping.rs rename to cmds/src/dispatch/ping.rs index 2cef423..3de7847 100755 --- a/src/commands/ping.rs +++ b/cmds/src/dispatch/ping.rs @@ -1,7 +1,9 @@ -use serde::Deserialize; -use rustbot_lib::{ - RustbotContext, - RustbotResult +use { + rustbot_lib::{ + RustbotContext, + RustbotResult + }, + serde::Deserialize }; #[derive(Deserialize)] @@ -20,16 +22,14 @@ struct Summary { } /// Check latency between bot and WebSocket as well as Discord's API latency -#[poise::command( - slash_command, - install_context = "Guild|User", - interaction_context = "Guild|BotDm|PrivateChannel" -)] +#[poise::command(slash_command, install_context = "Guild|User", interaction_context = "Guild|BotDm|PrivateChannel")] pub async fn ping(ctx: RustbotContext<'_>) -> RustbotResult<()> { let statuspage: StatusPage = reqwest::get("https://discordstatus.com/metrics-display/5k2rt9f7pmny/day.json") - .await.unwrap() - .json() - .await.unwrap(); + .await + .unwrap() + .json() + .await + .unwrap(); let mut latencies = Vec::new(); latencies.push(format!("Discord: `{:.0?}ms`", statuspage.metrics[0].summary.mean)); diff --git a/src/commands/uptime.rs b/cmds/src/dispatch/uptime.rs similarity index 66% rename from src/commands/uptime.rs rename to cmds/src/dispatch/uptime.rs index ec351a3..7caa364 100755 --- a/src/commands/uptime.rs +++ b/cmds/src/dispatch/uptime.rs @@ -1,28 +1,31 @@ -use sysinfo::System; -use uptime_lib::get; -use std::{ - env::var, - fs::File, - path::Path, - time::{ - Duration, - SystemTime, - UNIX_EPOCH +use { + rustbot_lib::{ + RustbotContext, + RustbotResult, + config::BINARY_PROPERTIES, + utils::{ + BOT_VERSION, + GIT_COMMIT_BRANCH, + GIT_COMMIT_HASH, + format_duration + } }, - io::{ - BufRead, - BufReader - } -}; -use rustbot_lib::{ - RustbotContext, - RustbotResult, - utils::{ - BOT_VERSION, - GIT_COMMIT_HASH, - GIT_COMMIT_BRANCH, - format_duration - } + std::{ + env::var, + fs::File, + io::{ + BufRead, + BufReader + }, + path::Path, + time::{ + Duration, + SystemTime, + UNIX_EPOCH + } + }, + sysinfo::System, + uptime_lib::get }; fn get_os_info() -> String { @@ -33,13 +36,11 @@ fn get_os_info() -> String { if let Ok(file) = File::open(path) { let reader = BufReader::new(file); let set_value = |s: String| s.split('=').nth(1).unwrap_or_default().trim_matches('"').to_string(); - reader.lines().map_while(Result::ok).for_each(|line| { - match line { - l if l.starts_with("NAME=") => name = set_value(l), - l if l.starts_with("VERSION=") => version = set_value(l), - l if l.starts_with("VERSION_ID=") => version = set_value(l), - _ => {} - } + reader.lines().map_while(Result::ok).for_each(|line| match line { + l if l.starts_with("NAME=") => name = set_value(l), + l if l.starts_with("VERSION=") => version = set_value(l), + l if l.starts_with("VERSION_ID=") => version = set_value(l), + _ => {} }); } @@ -95,16 +96,21 @@ pub async fn uptime(ctx: RustbotContext<'_>) -> RustbotResult<()> { } // Fetch the node hostname from envvar - let docker_node = match var("DOCKER_HOSTNAME") { - Ok(h) => h.to_string(), - Err(_) => "DOCKER_HOSTNAME is empty!".to_string() + let node_hostname = if BINARY_PROPERTIES.env.contains("prod") { + match var("DOCKER_HOSTNAME") { + Ok(h) => h.to_string(), + Err(_) => "DOCKER_HOSTNAME is empty!".to_string() + } + } else { + let hostname = std::process::Command::new("hostname").output().unwrap().stdout; + String::from_utf8(hostname).unwrap().trim().to_string() }; let stat_msg = [ format!("**{} v{}** `{GIT_COMMIT_HASH}:{GIT_COMMIT_BRANCH}`", bot.name, *BOT_VERSION), format!(">>> System: `{}`", format_duration(sys_uptime)), format!("Process: `{}`", format_duration(proc_uptime)), - format!("Node: `{docker_node}`"), + format!("Node: `{node_hostname}`"), format!("CPU: `{}`", cpu[0].brand()), format!("RAM: `{pram}` (`{sram}/{sram_total}`)"), format!("OS: `{}`", get_os_info()) diff --git a/cmds/src/lib.rs b/cmds/src/lib.rs new file mode 100644 index 0000000..0b23e04 --- /dev/null +++ b/cmds/src/lib.rs @@ -0,0 +1,2 @@ +mod dispatch; +pub use dispatch::*; diff --git a/events/Cargo.toml b/events/Cargo.toml index 68beb73..d821aad 100755 --- a/events/Cargo.toml +++ b/events/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -rustbot_lib = { path = "../library" } poise = { workspace = true } +rustbot_lib = { workspace = true } [features] production = ["rustbot_lib/production"] diff --git a/events/src/events.rs b/events/src/events.rs index da7234d..6c8624c 100755 --- a/events/src/events.rs +++ b/events/src/events.rs @@ -1,10 +1,12 @@ mod ready; mod shards; -use poise::serenity_prelude::FullEvent; -use rustbot_lib::{ - RustbotFwCtx, - RustbotResult +use { + poise::serenity_prelude::FullEvent, + rustbot_lib::{ + RustbotFwCtx, + RustbotResult + } }; pub const RUSTBOT_EVENT: &str = "RustbotEvent"; diff --git a/events/src/events/ready.rs b/events/src/events/ready.rs index 0acddaf..f074341 100755 --- a/events/src/events/ready.rs +++ b/events/src/events/ready.rs @@ -3,26 +3,28 @@ use super::{ RUSTBOT_EVENT }; -use rustbot_lib::{ - RustbotFwCtx, - RustbotResult, - utils::{ - BOT_VERSION, - GIT_COMMIT_HASH, - GIT_COMMIT_BRANCH +use { + poise::serenity_prelude::{ + ChannelId, + CreateEmbed, + CreateEmbedAuthor, + CreateMessage, + Ready }, - config::BINARY_PROPERTIES -}; -use std::sync::atomic::{ - AtomicBool, - Ordering::Relaxed -}; -use poise::serenity_prelude::{ - Ready, - ChannelId, - CreateMessage, - CreateEmbed, - CreateEmbedAuthor + rustbot_lib::{ + config::BINARY_PROPERTIES, + utils::{ + BOT_VERSION, + GIT_COMMIT_BRANCH, + GIT_COMMIT_HASH + }, + RustbotFwCtx, + RustbotResult + }, + std::sync::atomic::{ + AtomicBool, + Ordering::Relaxed + } }; static READY_ONCE: AtomicBool = AtomicBool::new(false); @@ -33,14 +35,26 @@ async fn ready_once( ) -> RustbotResult<()> { #[cfg(not(feature = "production"))] { - println!("{RUSTBOT_EVENT}[Ready:Notice:S{}]: Detected a non-production environment!", framework.serenity_context.shard_id); + println!( + "{RUSTBOT_EVENT}[Ready:Notice:S{}]: Detected a non-production environment!", + framework.serenity_context.shard_id + ); let gateway = framework.serenity_context.http.get_bot_gateway().await?; let session = gateway.session_start_limit; - println!("{RUSTBOT_EVENT}[Ready:Notice:S{}]: Session limit: {}/{}", framework.serenity_context.shard_id, session.remaining, session.total); + println!( + "{RUSTBOT_EVENT}[Ready:Notice:S{}]: Session limit: {}/{}", + framework.serenity_context.shard_id, session.remaining, session.total + ); } - println!("{RUSTBOT_EVENT}[Ready:S{}]: Build version: {} ({}:{})", framework.serenity_context.shard_id, *BOT_VERSION, GIT_COMMIT_HASH, GIT_COMMIT_BRANCH); - println!("{RUSTBOT_EVENT}[Ready:S{}]: Connected to API as {}", framework.serenity_context.shard_id, ready.user.name); + println!( + "{RUSTBOT_EVENT}[Ready:S{}]: Build version: {} ({GIT_COMMIT_HASH}:{GIT_COMMIT_BRANCH})", + framework.serenity_context.shard_id, *BOT_VERSION + ); + println!( + "{RUSTBOT_EVENT}[Ready:S{}]: Connected to API as {}", + framework.serenity_context.shard_id, ready.user.name + ); let message = CreateMessage::new(); let ready_embed = CreateEmbed::new() @@ -48,7 +62,9 @@ async fn ready_once( .thumbnail(ready.user.avatar_url().unwrap_or_default()) .author(CreateEmbedAuthor::new(format!("{} is ready!", ready.user.name))); - ChannelId::new(BINARY_PROPERTIES.rustbot_logs).send_message(&framework.serenity_context.http, message.add_embed(ready_embed)).await?; + ChannelId::new(BINARY_PROPERTIES.rustbot_logs) + .send_message(&framework.serenity_context.http, message.add_embed(ready_embed)) + .await?; Ok(()) } @@ -59,7 +75,9 @@ impl EventProcessor<'_> { data_about_bot: &Ready ) -> RustbotResult<()> { if !READY_ONCE.swap(true, Relaxed) { - ready_once(data_about_bot, self.framework).await.expect("Failed to call ready_once method"); + ready_once(data_about_bot, self.framework) + .await + .expect("Failed to call ready_once method"); } Ok(()) diff --git a/events/src/events/shards.rs b/events/src/events/shards.rs index 9f96a1e..8722a73 100755 --- a/events/src/events/shards.rs +++ b/events/src/events/shards.rs @@ -3,16 +3,22 @@ use super::{ RUSTBOT_EVENT }; -use std::num::NonZero; -use rustbot_lib::RustbotResult; -use poise::serenity_prelude::ShardStageUpdateEvent; +use { + poise::serenity_prelude::ShardStageUpdateEvent, + rustbot_lib::RustbotResult, + std::num::NonZero +}; impl EventProcessor<'_> { pub async fn on_shards_ready( &self, total_shards: &NonZero ) -> RustbotResult<()> { - let shards = if *total_shards == NonZero::new(1).unwrap() { "shard is" } else { "shards are" }; + let shards = if *total_shards == NonZero::new(1).unwrap() { + "shard is" + } else { + "shards are" + }; println!("{RUSTBOT_EVENT}[ShardsReady]: {total_shards} {shards} ready!"); Ok(()) diff --git a/jobs/src/tasks.rs b/jobs/src/tasks.rs index c032ad6..b858f54 100755 --- a/jobs/src/tasks.rs +++ b/jobs/src/tasks.rs @@ -1,23 +1,23 @@ use crate::RUSTBOT_SCHEDULER; -use tokio::{ - task, - time::{ - interval, - Duration +use { + std::{ + future::Future, + sync::Arc + }, + tokio::{ + task, + time::{ + interval, + Duration + } } }; -use std::{ - sync::Arc, - future::Future -}; pub struct Scheduler; impl Scheduler { - pub fn new() -> Arc { - Arc::new(Self) - } + pub fn new() -> Arc { Arc::new(Self) } pub async fn spawn_job( &self, diff --git a/library/Cargo.toml b/library/Cargo.toml index d3a5e51..15e77d3 100755 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustbot_lib" -version = "0.1.19" +version = "0.1.0" edition = "2021" [dependencies] diff --git a/library/build.rs b/library/build.rs index e261431..ea15b12 100755 --- a/library/build.rs +++ b/library/build.rs @@ -23,15 +23,4 @@ fn main() { println!("cargo:rustc-env=GIT_COMMIT_BRANCH=not_found"); } } - - { - let hostname = std::process::Command::new("hostname") - .output() - .expect("Command execution failed: hostname"); - - if hostname.status.success() { - let hostname = String::from_utf8(hostname.stdout).expect("Invalid UTF-8 sequence").trim().to_string(); - println!("cargo:rustc-env=DOCKER_HOSTNAME={}", &hostname); - } - } } diff --git a/library/src/config.rs b/library/src/config.rs index e032a17..ae0d168 100755 --- a/library/src/config.rs +++ b/library/src/config.rs @@ -1,43 +1,45 @@ use std::sync::LazyLock; pub struct ConfigMeta { - pub env: &'static str, - pub embed_color: u32, + pub env: &'static str, + pub embed_color: u32, pub rustbot_logs: u64, - pub developers: Vec + pub developers: Vec } #[cfg(feature = "production")] pub static BINARY_PROPERTIES: LazyLock = LazyLock::new(ConfigMeta::new); #[cfg(not(feature = "production"))] -pub static BINARY_PROPERTIES: LazyLock = LazyLock::new(|| - ConfigMeta::new() - .env("dev") - .embed_color(0xf1d63c) -); +pub static BINARY_PROPERTIES: LazyLock = LazyLock::new(|| ConfigMeta::new().env("dev").embed_color(0xF1D63C)); impl ConfigMeta { fn new() -> Self { Self { - env: "prod", - embed_color: 0xf1d63c, + env: "prod", + embed_color: 0xF1D63C, rustbot_logs: 1311282815601741844, - developers: vec![ - 190407856527376384 // toast.ts + developers: vec![ + 190407856527376384, // toast.ts ] } } // Scalable functions below; #[cfg(not(feature = "production"))] - fn env(mut self, env: &'static str) -> Self { + fn env( + mut self, + env: &'static str + ) -> Self { self.env = env; self } #[cfg(not(feature = "production"))] - fn embed_color(mut self, color: u32) -> Self { + fn embed_color( + mut self, + color: u32 + ) -> Self { self.embed_color = color; self } diff --git a/library/src/utils.rs b/library/src/utils.rs index ec296e1..5de5e6f 100755 --- a/library/src/utils.rs +++ b/library/src/utils.rs @@ -1,6 +1,8 @@ -use poise::serenity_prelude::UserId; -use cargo_toml::Manifest; -use std::sync::LazyLock; +use { + cargo_toml::Manifest, + poise::serenity_prelude::UserId, + std::sync::LazyLock +}; #[cfg(feature = "production")] pub static GIT_COMMIT_HASH: &str = env!("GIT_COMMIT_HASH"); @@ -17,9 +19,7 @@ pub static BOT_VERSION: LazyLock = LazyLock::new(|| { .unwrap() }); -pub fn format_timestamp(timestamp: i64) -> String { - format!("\n") -} +pub fn format_timestamp(timestamp: i64) -> String { format!("\n") } pub fn mention_dev(ctx: super::RustbotContext<'_>) -> Option { let devs = super::config::BINARY_PROPERTIES.developers.clone(); @@ -53,18 +53,13 @@ pub fn format_duration(secs: u64) -> String { let minutes = (secs % 3600) / 60; let seconds = secs % 60; - let components = [ - (days, "d"), - (hours, "h"), - (minutes, "m"), - (seconds, "s"), - ]; + let components = [(days, "d"), (hours, "h"), (minutes, "m"), (seconds, "s")]; let formatted_string: Vec = components - .iter() - .filter(|&&(value, _)| value > 0) - .map(|&(value, suffix)| format!("{value}{suffix}")) - .collect(); + .iter() + .filter(|&&(value, _)| value > 0) + .map(|&(value, suffix)| format!("{value}{suffix}")) + .collect(); formatted_string.join(", ") } diff --git a/run.sh b/run.sh index a02df79..b870ef7 100755 --- a/run.sh +++ b/run.sh @@ -1,6 +1,4 @@ #!/bin/bash -export DOCKER_HOSTNAME=$(hostname) export $(cat .env.bot | xargs) -clear && cargo run -unset DOCKER_HOSTNAME +clear && cargo fmt && cargo run diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3086d77 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,20 @@ +edition = "2024" +hex_literal_case = "Upper" +binop_separator = "Front" +brace_style = "SameLineWhere" +fn_params_layout = "Vertical" +imports_layout = "Vertical" +imports_granularity = "One" +fn_single_line = true +format_strings = true +max_width = 150 +tab_spaces = 2 +hard_tabs = false +trailing_comma = "Never" +match_block_trailing_comma = true +reorder_imports = true +reorder_modules = true +reorder_impl_items = true +trailing_semicolon = false +struct_field_align_threshold = 20 +condense_wildcard_suffixes = true diff --git a/src/commands.rs b/src/commands.rs deleted file mode 100755 index dce5848..0000000 --- a/src/commands.rs +++ /dev/null @@ -1,26 +0,0 @@ -mod dev; -mod eightball; -mod ping; -mod uptime; - -pub use dev::dev; -pub use eightball::eightball; -pub use ping::ping; -pub use uptime::uptime; - -macro_rules! collect { - () => { - vec![ - // Developer command(s) - commands::dev(), - - // Utility commands - commands::ping(), - commands::uptime(), - - // Unsorted mess - commands::eightball(), - ] - }; -} -pub(crate) use collect; diff --git a/src/commands/eightball.rs b/src/commands/eightball.rs deleted file mode 100755 index e6ead03..0000000 --- a/src/commands/eightball.rs +++ /dev/null @@ -1,180 +0,0 @@ -use rustbot_lib::{ - RustbotContext, - RustbotResult, - config::BINARY_PROPERTIES -}; -use poise::{ - serenity_prelude::UserId, - builtins::paginate -}; - -#[derive(poise::ChoiceParameter)] -enum ResponseMode { - Normal, - Chicken -} - -/// Ask the Magic 8-Ball a yes/no question and get an unpredictable answer -#[poise::command( - slash_command, - install_context = "Guild|User", - interaction_context = "Guild|BotDm|PrivateChannel", - rename = "8ball" -)] -pub async fn eightball( - ctx: RustbotContext<'_>, - #[description = "Your yes/no question"] question: String, - #[description = "Response modes"] mode: Option -) -> RustbotResult<()> { - if question.to_ascii_lowercase().contains("niko, show list") { - if ctx.author().id == UserId::new(BINARY_PROPERTIES.developers[0]) { - let chunks: Vec = RESPONSES.chunks(10).map(|chunk| chunk.join("\n\n")).collect(); - let pages: Vec<&str> = chunks.iter().map(|s| s.as_str()).collect(); - paginate(ctx, &pages).await?; - - return Ok(()); - } else { - ctx.reply("No.").await?; - return Ok(()); - } - } - - if question.to_ascii_lowercase().contains("niko, show chicken list") { - if ctx.author().id == UserId::new(BINARY_PROPERTIES.developers[0]) { - let chunks: Vec = CHICKEN_RESPONSES.chunks(10).map(|chunk| chunk.join("\n\n")).collect(); - let pages: Vec<&str> = chunks.iter().map(|s| s.as_str()).collect(); - paginate(ctx, &pages).await?; - - return Ok(()); - } else { - ctx.reply("No.").await?; - return Ok(()); - } - } - - let rand_resp = match mode { - Some(ResponseMode::Chicken) => get_random_chicken_response(), - _ => get_random_response() - }; - - ctx.reply(format!("> {question}\n{rand_resp}")).await?; - - Ok(()) -} - -const RESPONSES: [&str; 45] = [ - "Reply hazy. Look it up on Google.", // no - "Meh — Figure it out yourself.", // no - "I don't know, what do you think?", // no - "Yes.", // yes - "No.", // no - "It is decidedly so", // yes - "Signs point to... maybe... depends on... \ - hold on, let me get my glasses, this is getting \ - pretty tiny... depends on whether you'd be up \ - to getting to know your Magic 8-Ball a little better.", // no - "Signs point to... ~~yes~~ no.", // no - "Why do you want to know the answer? It's obviously a yes.", // yes - "Outlook not so good.", // no - "Outlook hazy.", // no - "What are you, stupid?", // no - "How the hell do you not know that?", // no - "Really? Making a decision based on what the plastic 8-Ball says? Jesus...", // no - "Try asking later...", // no - "I don't know, whip out the ouija board and try again?", // no - "The answer is yes.", // yes - "Yes, actually no. Wait, nevermind.", // no - "Maybeee...", // yes - "Definitely!", // yes - "It is decidedly so.", // yes - "My reply is no.", // no - "My sources confirms that the answer is no.\n\ - Source: :sparkles: *i made it up* :sparkles:", // no - "As I see it, yes.", // yes - "Don't count on it.", // no - "Whoa! Why do I have to answer this?", // no - "Highly unlikely.", // no - "Sure, but with extreme cautions.", // yes - "What kind of stupid question is that?? No! I'm not answering that!", // no - "Try asking this to a chicken. Probably knows it better than I do!", // no - "Not in a million years!", // no - "As a matter of fact, yes.", // yes - "It's a no, better go ask someone else.", // no - "In the end, it's not a bad choice.", // yes - "Nope, not today.", // no - "Cross your fingers, the answer is yes!", // yes - "Nope. *shakes head*", // no - "The fortune cookie said yes.", // yes - "Sorry, the fortune cookie over there said no.", // no - "Sorry, not happening.", // no - "I'll have to consult my sources... *flips coin*... no.", // no - "I'll have to consult the magic 8-ball... *shakes*... no.", // no - "I'm not sure to be honest, let's ask your friend. Oh wait...", // no - "This question flew over my head, I'll pass.", // no - "Oops, the Magic 8-Ball shattered itself when you asked that! I'll take that as a no.", // no -]; - -const CHICKEN_RESPONSES: [&str; 54] = [ - "Cluck cluck... Reply hazy, try pecking Google.", // no - "Meh... Figure it out yourself, or scratch around a bit.", // no - "I don't know... what do you think? *pecks at ground*", // no - "BAWK! YES!", // yes - "Cluck... no.", // no - "It is decidedly so! *flaps wings*", // yes - "Signs point to... maybe... hold on, let me fluff my feathers... depends on whether you'd get to know your Magic Chicken a bit better.", // no - "Signs point to... ~~yes~~ cluck no.", // no - "Why do you want to know? It's a big cluckin' yes!", // yes - "Outlook not so clucking good.", // no - "Outlook cluckin' hazy.", // no - "What are you, a lost chick? Cluck!", // no - "How the cluck do you not know that?", // no - "Really? Asking a chicken to decide your fate? *clucks judgmentally*", // no - "Peck back later, I'm nesting...", // no - "I don't know, try flapping your wings and ask again?", // no - "The answer is a big ol' yes! *flaps happily*", // yes - "Yes... wait, actually... no. Cluck, I'm confused.", // no - "Maaaaybe... *chicken waddle*?", // yes - "Definitely! *struts confidently*", // yes - "It is decidedly so. *struts with pride*", // yes - "My reply is a solid *cluck* no.", // no - "My sources confirm it's a cluckin' no.\nSource: 🐔 *I made it up* 🐔", // no - "As I see it, yes! *pecks approvingly*", // yes - "Don't count on it. *cluck cluck*", // no - "Whoa, why do I have to answer this? *fluffs feathers*", // no - "Highly unlikely. *chicken stare*", // no - "Sure, but with extreme cluckin' caution.", // yes - "What kind of stupid question is that?? No! *angry clucks*", // no - "Try asking this to a fellow chicken. They probably know better than I do!", // no - "Cluck yes! *does a happy chicken dance*", // yes - "No way, not even for a big bag of feed.", // no - "Yes! *lays egg of approval*", // yes - "It's a no, better go scratch somewhere else.", // no - "Cluck-tastic! That's a definite yes.", // yes - "Cluck yeah! *struts proudly*", // yes - "Nope, not today. *shakes head*", // no - "Feathers crossed, the answer is yes!", // yes - "Chicken says nope. *tilts head*", // no - "Absolutely! *clucks happily*", // yes - "Not a chance. *fluffs feathers*", // no - "Eggcellent choice! Yes!", // yes - "Not in a million clucks!", // no - "As a matter of cluck, yes! *clucks approvingly*", // yes - "It's a nopity nope, better go ask another chicken.", // no - "In the end, it's not a bad cluck", // yes - "Nope, not today. *clucks sadly*", // no - "Cross your feathers, the answer is yes!", // yes - "The fortune cookie said yes. *clucks in agreement*", // yes - "Sorry, the fortune cookie over there said no. *clucks in disagreement*", // no - "I'll have to consult my sources... *flips corn*... no.", // no - "I'll have to consult the magic 8-cluck... *shakes*... no.", // no - "I'm not sure to be honest, let's ask your chicken friend. Oh wait...", // no - "This question floated over my head, I'll pass. *clucks dismissively*", // no -]; - -fn get_random_response() -> &'static str { - RESPONSES[rand::random::() % RESPONSES.len()] -} - -fn get_random_chicken_response() -> &'static str { - CHICKEN_RESPONSES[rand::random::() % CHICKEN_RESPONSES.len()] -} diff --git a/src/main.rs b/src/main.rs index 52e5a9d..ade446f 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,29 @@ -mod commands; mod shutdown; // https://cdn.toast-server.net/RustFSHiearchy.png // Using the new filesystem hierarchy -use rustbot_tokens::discord_token; -use poise::serenity_prelude::{ - builder::CreateAllowedMentions, - ClientBuilder, - ActivityData, - GatewayIntents -}; -use rustbot_lib::{ - utils::{ - mention_dev, - get_guild_name +use { + poise::serenity_prelude::{ + builder::CreateAllowedMentions, + ActivityData, + ClientBuilder, + GatewayIntents }, - RustbotData, - config::BINARY_PROPERTIES -}; -use rustbot_events::events::processor; -use std::{ - sync::Arc, - borrow::Cow + rustbot_cmds::collect, + rustbot_events::events::processor, + rustbot_lib::{ + config::BINARY_PROPERTIES, + utils::{ + get_guild_name, + mention_dev + }, + RustbotData + }, + rustbot_tokens::discord_token, + std::{ + borrow::Cow, + sync::Arc + } }; #[tokio::main] @@ -32,28 +34,29 @@ async fn main() { Some(Cow::Borrowed("pg!")) }; - let commands = commands::collect!(); let framework = poise::Framework::builder() .options(poise::FrameworkOptions { - commands, - pre_command: |ctx| Box::pin(async move { - let get_guild_channel_name = match ctx.guild_channel().await { - Some(channel) => format!("in #{}", channel.name.clone()), - None => String::from("") - }; - let prefix = match ctx.command().prefix_action { - Some(_) => ctx.framework().options.prefix_options.prefix.as_ref().unwrap(), - None => "/" - }; + commands: collect!(), + pre_command: |ctx| { + Box::pin(async move { + let get_guild_channel_name = match ctx.guild_channel().await { + Some(channel) => format!("in #{}", channel.name.clone()), + None => String::from("") + }; + let prefix = match ctx.command().prefix_action { + Some(_) => ctx.framework().options.prefix_options.prefix.as_ref().unwrap(), + None => "/" + }; - println!( - "Discord[{}:S{}]: {} ran {prefix}{} {get_guild_channel_name}", - get_guild_name(ctx), - ctx.serenity_context().shard_id, - ctx.author().name, - ctx.command().qualified_name, - ); - }), + println!( + "Discord[{}:S{}]: {} ran {prefix}{} {get_guild_channel_name}", + get_guild_name(ctx), + ctx.serenity_context().shard_id, + ctx.author().name, + ctx.command().qualified_name, + ); + }) + }, prefix_options: poise::PrefixFrameworkOptions { prefix, ignore_bots: true, @@ -62,52 +65,69 @@ async fn main() { execute_self_messages: false, ..Default::default() }, - on_error: |error| Box::pin(async move { - match error { - poise::FrameworkError::Command { error, ctx, .. } => { - println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error); - ctx.reply(format!( - "Encountered an error during command execution, ask {} to check console for more details!", - mention_dev(ctx).unwrap_or_default() - )).await.expect("Error sending message"); - }, - poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {}", event.snake_case_name(), error), - poise::FrameworkError::NotAnOwner { ctx, .. } => { - println!("PoiseNotAnOwner: {} tried to execute a developer-level command ({})", ctx.author().name, ctx.command().qualified_name); - ctx.reply("Whoa, you discovered a hidden command! Too bad, I can't allow you to execute it as you're not my creator.").await.expect("Error sending message"); - }, - poise::FrameworkError::UnknownInteraction { interaction, .. } => println!( - "PoiseUnknownInteractionError: {} tried to execute an unknown interaction ({})", - interaction.user.name, - interaction.data.name - ), - poise::FrameworkError::UnknownCommand { msg, .. } => println!( - "PoiseUnknownCommandError: {} tried to execute an unknown command ({})", - msg.author.name, - msg.content - ), - poise::FrameworkError::ArgumentParse { ctx, error, .. } => { - println!("PoiseArgumentParseError: {}", error); - ctx.reply(format!("Error parsing argument(s): {error}")).await.expect("Error sending message"); - }, - poise::FrameworkError::CommandPanic { ctx, payload, .. } => { - if let Some(payload) = payload.clone() { - println!("PoiseCommandPanic: {payload}"); - ctx.reply(format!( - "The command panicked, please tell my developer about this!\n**Error:**```\n{payload}\n```" - )).await.expect("Error sending message"); - } else { - println!("PoiseCommandPanic: No payload provided"); - let uh_oh = [ - "Well, this is concerning... Hopefully you notified my developer about this!", - "The command panicked, but didn't leave any trace behind... Suspicious!", - ].join("\n"); - ctx.reply(uh_oh).await.expect("Error sending message"); - } - }, - other => println!("PoiseOtherError: {other}") - } - }), + on_error: |error| { + Box::pin(async move { + match error { + poise::FrameworkError::Command { error, ctx, .. } => { + println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error); + ctx + .reply(format!( + "Encountered an error during command execution, ask {} to check console for more details!", + mention_dev(ctx).unwrap_or_default() + )) + .await + .expect("Error sending message"); + }, + poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {}", event.snake_case_name(), error), + poise::FrameworkError::NotAnOwner { ctx, .. } => { + println!( + "PoiseNotAnOwner: {} tried to execute a developer-level command ({})", + ctx.author().name, + ctx.command().qualified_name + ); + ctx + .reply("Whoa, you discovered a hidden command! Too bad, I can't allow you to execute it as you're not my creator.") + .await + .expect("Error sending message"); + }, + poise::FrameworkError::UnknownInteraction { interaction, .. } => println!( + "PoiseUnknownInteractionError: {} tried to execute an unknown interaction ({})", + interaction.user.name, interaction.data.name + ), + poise::FrameworkError::UnknownCommand { msg, .. } => println!( + "PoiseUnknownCommandError: {} tried to execute an unknown command ({})", + msg.author.name, msg.content + ), + poise::FrameworkError::ArgumentParse { ctx, error, .. } => { + println!("PoiseArgumentParseError: {}", error); + ctx + .reply(format!("Error parsing argument(s): {error}")) + .await + .expect("Error sending message"); + }, + poise::FrameworkError::CommandPanic { ctx, payload, .. } => { + if let Some(payload) = payload.clone() { + println!("PoiseCommandPanic: {payload}"); + ctx + .reply(format!( + "The command panicked, please tell my developer about this!\n**Error:**```\n{payload}\n```" + )) + .await + .expect("Error sending message"); + } else { + println!("PoiseCommandPanic: No payload provided"); + let uh_oh = [ + "Well, this is concerning... Hopefully you notified my developer about this!", + "The command panicked, but didn't leave any trace behind... Suspicious!" + ] + .join("\n"); + ctx.reply(uh_oh).await.expect("Error sending message"); + } + }, + other => println!("PoiseOtherError: {other}") + } + }) + }, allowed_mentions: Some(CreateAllowedMentions::default().empty_users()), initialize_owners: true, skip_checks_for_owners: true, @@ -118,14 +138,13 @@ async fn main() { let mut client = ClientBuilder::new( discord_token().await, - GatewayIntents::GUILDS - | GatewayIntents::GUILD_MESSAGES - | GatewayIntents::MESSAGE_CONTENT + GatewayIntents::GUILDS | GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT ) .framework(framework) .data(Arc::new(RustbotData {})) .activity(ActivityData::custom("nep nep!")) - .await.expect("Error creating client"); + .await + .expect("Error creating client"); let shard_manager = client.shard_manager.clone(); diff --git a/src/shutdown.rs b/src/shutdown.rs index 261a97d..d0e7dce 100644 --- a/src/shutdown.rs +++ b/src/shutdown.rs @@ -7,16 +7,11 @@ use tokio::{ }; pub async fn gracefully_shutdown() { - let [mut s1, mut s2, mut s3] = [ - signal(SignalKind::interrupt()).unwrap(), - signal(SignalKind::terminate()).unwrap(), - signal(SignalKind::hangup()).unwrap() - ]; + let [mut s1, mut s2] = [signal(SignalKind::interrupt()).unwrap(), signal(SignalKind::hangup()).unwrap()]; select!( v = s1.recv() => v.unwrap(), - v = s2.recv() => v.unwrap(), - v = s3.recv() => v.unwrap() + v = s2.recv() => v.unwrap() ); println!("\nRustbot says goodbye! 👋"); diff --git a/tsclient/Cargo.toml b/tsclient/Cargo.toml index e9d052d..f778da0 100755 --- a/tsclient/Cargo.toml +++ b/tsclient/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] poise = { workspace = true } -tokenservice-client = { version = "0.4.1", registry = "gitea" } +tokenservice-client = { version = "0.4.2", registry = "gitea" } tokio = { workspace = true } diff --git a/tsclient/src/lib.rs b/tsclient/src/lib.rs index efc62a5..7f3d5e6 100755 --- a/tsclient/src/lib.rs +++ b/tsclient/src/lib.rs @@ -1,20 +1,20 @@ -use poise::serenity_prelude::Token; -use tokio::sync::Mutex; -use std::{ - str::FromStr, - sync::LazyLock -}; -use tokenservice_client::{ - TokenService, - TokenServiceApi +use { + poise::serenity_prelude::Token, + std::{ + str::FromStr, + sync::LazyLock + }, + tokenservice_client::{ + TokenService, + TokenServiceApi + }, + tokio::sync::Mutex }; pub struct TSClient(TokenService); impl Default for TSClient { - fn default() -> Self { - Self::new() - } + fn default() -> Self { Self::new() } } impl TSClient { @@ -34,11 +34,6 @@ impl TSClient { static TSCLIENT: LazyLock> = LazyLock::new(|| Mutex::new(TSClient::new())); -pub async fn token_path() -> TokenServiceApi { - TSCLIENT.lock().await.get().await.unwrap() -} +pub async fn token_path() -> TokenServiceApi { TSCLIENT.lock().await.get().await.unwrap() } -pub async fn discord_token() -> Token { - Token::from_str(&token_path().await.main) - .expect("Serenity couldn't parse the bot token!") -} +pub async fn discord_token() -> Token { Token::from_str(&token_path().await.main).expect("Serenity couldn't parse the bot token!") }