From 4e08e255751d5456f47eceabf2c144018f04830b Mon Sep 17 00:00:00 2001 From: minneelyyyy Date: Fri, 11 Oct 2024 20:33:12 -0400 Subject: [PATCH] large refactor: reorganize code --- src/commands/dox.rs | 73 ++++++++++++ src/commands/gambling/balance.rs | 20 ++++ src/commands/gambling/give.rs | 29 +++++ src/commands/gambling/mod.rs | 14 +++ src/commands/gambling/wager.rs | 26 ++++ src/commands/mod.rs | 19 +++ src/commands/ping.rs | 17 +++ src/commands/yeehaw.rs | 18 +++ src/common.rs | 11 ++ src/main.rs | 196 ++----------------------------- 10 files changed, 240 insertions(+), 183 deletions(-) create mode 100644 src/commands/dox.rs create mode 100644 src/commands/gambling/balance.rs create mode 100644 src/commands/gambling/give.rs create mode 100644 src/commands/gambling/mod.rs create mode 100644 src/commands/gambling/wager.rs create mode 100644 src/commands/mod.rs create mode 100644 src/commands/ping.rs create mode 100644 src/commands/yeehaw.rs create mode 100644 src/common.rs diff --git a/src/commands/dox.rs b/src/commands/dox.rs new file mode 100644 index 0000000..3a27672 --- /dev/null +++ b/src/commands/dox.rs @@ -0,0 +1,73 @@ +use crate::common::{Context, Error}; + +use poise::serenity_prelude as serenity; +use serenity::Colour; + +fn get_dox_output(ctx: &mut Context<'_>, + user: &serenity::User, + member: Option<&serenity::Member>, + show_permissions: bool) -> String { + let mut output = String::new(); + + if user.bot { + output.push_str("This user is a bot.\n"); + } + + output.push_str(&format!("**User ID**: {}\n", user.id)); + + if let Some(locale) = &user.locale { + output.push_str(&format!("**Locale**: {locale}\n")); + } + + if let Some(verified) = &user.verified { + output.push_str(&format!("**Verified**: {verified}\n")); + } + + output.push_str(&format!("**Account Created**: {}\n", user.created_at())); + + if let Some(Some(join_date)) = member.as_ref().map(|m| m.joined_at) { + output.push_str(&format!("**Joined this Server at**: {join_date}\n")); + } + + if let Some(Some(premium_since)) = member.as_ref().map(|m| m.premium_since) { + output.push_str( + &format!("**Boosting this Server**: Yes\n**Boosting since**: {premium_since}\n")); + } + + if let Some(Ok(permissions)) = member.map(|m| m.permissions(ctx)).filter(|_| show_permissions) { + output.push_str(&format!("**Permissions**: {}\n", + permissions.get_permission_names().join(", "))) + } + + output +} + +/// Display information about a given user +#[poise::command(slash_command, prefix_command)] +pub async fn dox(mut ctx: Context<'_>, + #[description = "The user to display information of"] + user: serenity::User, + #[rename = "permissions"] + #[description = "Rather or not to show the user's permissions"] + show_permissions: Option) -> Result<(), Error> +{ + let user = ctx.http().get_user(user.id).await?; + let member = if let Some(guild) = ctx.guild_id() { + guild.member(ctx.http(), user.id).await.ok() + } else { + None + }; + + let embed = serenity::CreateEmbed::default() + .title(format!("Information about {}", user.name)) + .description(get_dox_output( + &mut ctx, &user, member.as_ref(), show_permissions.unwrap_or(false))) + .colour(member.map(|m| m.colour(ctx.cache())) + .unwrap_or(None) + .unwrap_or(user.accent_colour.unwrap_or(Colour::from_rgb(255, 255, 255)))) + .image(user.face()); + + ctx.send(poise::CreateReply::default().embed(embed)).await?; + + Ok(()) +} diff --git a/src/commands/gambling/balance.rs b/src/commands/gambling/balance.rs new file mode 100644 index 0000000..370b9d7 --- /dev/null +++ b/src/commands/gambling/balance.rs @@ -0,0 +1,20 @@ +use super::get_user_wealth_mut; +use crate::common::{Context, Error}; +use poise::serenity_prelude as serenity; + +#[poise::command(slash_command, prefix_command)] +pub async fn balance(ctx: Context<'_>, user: Option) -> Result<(), Error> { + let user = user.as_ref().unwrap_or(ctx.author()); + let mut users = ctx.data().users.lock().await; + + let wealth = get_user_wealth_mut(&mut users, user.id); + + ctx.reply(format!("{} **{}** token(s).", + if user.id == ctx.author().id { + "You have".to_string() + } else { + format!("{} has", user.name) + }, *wealth)).await?; + + Ok(()) +} diff --git a/src/commands/gambling/give.rs b/src/commands/gambling/give.rs new file mode 100644 index 0000000..8b3283a --- /dev/null +++ b/src/commands/gambling/give.rs @@ -0,0 +1,29 @@ +use crate::{Context, Error}; +use super::get_user_wealth_mut; +use poise::serenity_prelude as serenity; + +#[poise::command(slash_command, prefix_command)] +pub async fn give(ctx: Context<'_>, user: serenity::User, amount: usize) -> Result<(), Error> { + if user.bot { + ctx.reply("Don't waste your token(s) by giving them to a bot!").await?; + return Ok(()); + } + + let mut users = ctx.data().users.lock().await; + let author_wealth = get_user_wealth_mut(&mut users, ctx.author().id); + + if *author_wealth < amount { + ctx.reply(format!("You only have **{}** token(s) and cannot give away **{}**.", + *author_wealth, amount)).await?; + return Ok(()); + } + + *author_wealth -= amount; + + let receiver_wealth = get_user_wealth_mut(&mut users, user.id); + *receiver_wealth += amount; + + ctx.reply(format!("You've given **{}** **{}** token(s).", user.name, amount)).await?; + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/gambling/mod.rs b/src/commands/gambling/mod.rs new file mode 100644 index 0000000..5d024e0 --- /dev/null +++ b/src/commands/gambling/mod.rs @@ -0,0 +1,14 @@ +use std::collections::HashMap; +use poise::serenity_prelude::UserId; + +pub mod balance; +pub mod give; +pub mod wager; + +pub(self) fn get_user_wealth_mut(users: &mut HashMap, id: UserId) -> &mut usize { + if users.get(&id).is_none() { + users.insert(id, 100); + } + + users.get_mut(&id).unwrap() +} diff --git a/src/commands/gambling/wager.rs b/src/commands/gambling/wager.rs new file mode 100644 index 0000000..4454571 --- /dev/null +++ b/src/commands/gambling/wager.rs @@ -0,0 +1,26 @@ +use crate::common::{Context, Error}; +use super::get_user_wealth_mut; + +#[poise::command(slash_command, prefix_command)] +pub async fn wager(ctx: Context<'_>, amount: usize) -> Result<(), Error> { + let mut users = ctx.data().users.lock().await; + + let wealth = get_user_wealth_mut(&mut users, ctx.author().id); + + if *wealth < amount { + ctx.reply("You do not have enough tokens to wager this amount.").await?; + return Ok(()); + } + + if rand::random() { + *wealth += amount; + ctx.reply(format!("You just gained {} token(s)! You now have **{}**.", + amount, *wealth)).await?; + } else { + *wealth -= amount; + ctx.reply(format!("You've lost **{}** token(s), you now have **{}**.", + amount, *wealth)).await?; + } + + Ok(()) +} \ No newline at end of file diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..c3c1bc8 --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,19 @@ +use crate::{Data, Error}; +use poise::Command; + +mod ping; +mod dox; +mod yeehaw; +mod gambling; + +pub fn commands() -> Vec> { + vec![ + ping::ping(), + dox::dox(), + yeehaw::yeehaw(), + gambling::balance::balance(), + gambling::give::give(), + gambling::wager::wager(), + ] +} + diff --git a/src/commands/ping.rs b/src/commands/ping.rs new file mode 100644 index 0000000..e380943 --- /dev/null +++ b/src/commands/ping.rs @@ -0,0 +1,17 @@ +use crate::common::{Context, Error}; + +/// Display the bot's latency to Discord's REST and Gateway APIs +#[poise::command(slash_command, prefix_command)] +pub async fn ping(ctx: Context<'_>) -> Result<(), Error> { + use std::time::Instant; + + let start = Instant::now(); + let msg = ctx.say("Pong! \u{1F3D3}").await?; + let time = start.elapsed(); + + msg.edit(ctx, poise::reply::CreateReply::default() + .content(format!("Pong! \u{1F3D3}\nREST: {:.2?}\nGateway: {:.2?}", + time, ctx.ping().await))).await?; + + Ok(()) +} diff --git a/src/commands/yeehaw.rs b/src/commands/yeehaw.rs new file mode 100644 index 0000000..30e3d84 --- /dev/null +++ b/src/commands/yeehaw.rs @@ -0,0 +1,18 @@ +use crate::common::{Context, Error}; +use std::iter; + +/// Pardner +#[poise::command(slash_command, prefix_command)] +pub async fn yeehaw(ctx: Context<'_>, + #[min = 1] + width: Option, + #[min = 1] + height: Option) -> Result<(), Error> +{ + ctx.reply(iter::repeat("\u{1F920}".to_string().repeat(width.unwrap_or(1))) + .take(height.unwrap_or(1)) + .collect::>() + .join("\n")).await?; + + Ok(()) +} diff --git a/src/common.rs b/src/common.rs new file mode 100644 index 0000000..70bf39c --- /dev/null +++ b/src/common.rs @@ -0,0 +1,11 @@ +use std::sync::Arc; +use tokio::sync::Mutex; +use std::collections::HashMap; +use poise::serenity_prelude::UserId; + +pub struct Data { + pub users: Arc>> +} + +pub type Error = Box; +pub type Context<'a> = poise::Context<'a, Data, Error>; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f0ff733..5f62b6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,187 +1,15 @@ + +mod commands; + +pub mod common; +use crate::common::{Context, Error, Data}; + use std::collections::HashMap; use std::env; -use std::iter; use std::sync::Arc; + +use poise::serenity_prelude as serenity; use tokio::sync::Mutex; -use poise::{serenity_prelude::{self as serenity, Colour, UserId}, CreateReply}; - -struct Data { - users: Arc>> -} - -type Error = Box; -type Context<'a> = poise::Context<'a, Data, Error>; - -/// Display the bot's latency to Discord's REST and Gateway APIs -#[poise::command(slash_command, prefix_command)] -async fn ping(ctx: Context<'_>) -> Result<(), Error> { - use std::time::Instant; - - let start = Instant::now(); - let msg = ctx.say("Pong! \u{1F3D3}").await?; - let time = start.elapsed(); - - msg.edit(ctx, poise::reply::CreateReply::default() - .content(format!("Pong! \u{1F3D3}\nREST: {:.2?}\nGateway: {:.2?}", - time, ctx.ping().await))).await?; - - Ok(()) -} - -fn get_dox_output(ctx: &mut Context<'_>, - user: &serenity::User, - member: Option<&serenity::Member>, - show_permissions: bool) -> String { - let mut output = String::new(); - - if user.bot { - output.push_str("This user is a bot.\n"); - } - - output.push_str(&format!("**User ID**: {}\n", user.id)); - - if let Some(locale) = &user.locale { - output.push_str(&format!("**Locale**: {locale}\n")); - } - - if let Some(verified) = &user.verified { - output.push_str(&format!("**Verified**: {verified}\n")); - } - - output.push_str(&format!("**Account Created**: {}\n", user.created_at())); - - if let Some(Some(join_date)) = member.as_ref().map(|m| m.joined_at) { - output.push_str(&format!("**Joined this Server at**: {join_date}\n")); - } - - if let Some(Some(premium_since)) = member.as_ref().map(|m| m.premium_since) { - output.push_str( - &format!("**Boosting this Server**: Yes\n**Boosting since**: {premium_since}\n")); - } - - if let Some(Ok(permissions)) = member.map(|m| m.permissions(ctx)).filter(|_| show_permissions) { - output.push_str(&format!("**Permissions**: {}\n", permissions.get_permission_names().join(", "))) - } - - output -} - -/// Display information about a given user -#[poise::command(slash_command, prefix_command)] -async fn dox( - mut ctx: Context<'_>, - #[description = "The user to display information of"] - user: serenity::User, - #[rename = "permissions"] - #[description = "Rather or not to show the user's permissions"] - show_permissions: Option) -> Result<(), Error> { - let user = ctx.http().get_user(user.id).await?; - let member = if let Some(guild) = ctx.guild_id() { - guild.member(ctx.http(), user.id).await.ok() - } else { - None - }; - - let embed = serenity::CreateEmbed::default() - .title(format!("Information about {}", user.name)) - .description(get_dox_output(&mut ctx, &user, member.as_ref(), show_permissions.unwrap_or(false))) - .colour(member.map(|m| m.colour(ctx.cache())) - .unwrap_or(None) - .unwrap_or(user.accent_colour.unwrap_or(Colour::from_rgb(255, 255, 255)))) - .image(user.face()); - - ctx.send(CreateReply::default().embed(embed)).await?; - - Ok(()) -} - -/// Pardner -#[poise::command(slash_command, prefix_command)] -async fn yeehaw(ctx: Context<'_>, - #[min = 1] - width: Option, - #[min = 1] - height: Option) -> Result<(), Error> { - ctx.reply(iter::repeat("\u{1F920}".to_string().repeat(width.unwrap_or(1))) - .take(height.unwrap_or(1)) - .collect::>() - .join("\n")).await?; - Ok(()) -} - -fn get_user_wealth_mut(users: &mut HashMap, id: UserId) -> &mut usize { - if users.get(&id).is_none() { - users.insert(id, 100); - } - - users.get_mut(&id).unwrap() -} - -#[poise::command(slash_command, prefix_command)] -async fn wager(ctx: Context<'_>, amount: usize) -> Result<(), Error> { - let mut users = ctx.data().users.lock().await; - - let wealth = get_user_wealth_mut(&mut users, ctx.author().id); - - if *wealth < amount { - ctx.reply("You do not have enough tokens to wager this amount.").await?; - return Ok(()); - } - - if rand::random() { - *wealth += amount; - ctx.reply(format!("You just gained {} token(s)! You now have **{}**.", amount, *wealth)).await?; - } else { - *wealth -= amount; - ctx.reply(format!("You've lost **{}** token(s), you now have **{}**.", amount, *wealth)).await?; - } - - Ok(()) -} - -#[poise::command(slash_command, prefix_command)] -async fn give(ctx: Context<'_>, user: serenity::User, amount: usize) -> Result<(), Error> { - if user.bot { - ctx.reply("Don't waste your token(s) by giving them to a bot!").await?; - return Ok(()); - } - - let mut users = ctx.data().users.lock().await; - - let author_wealth = get_user_wealth_mut(&mut users, ctx.author().id); - - if *author_wealth < amount { - ctx.reply(format!("You only have **{}** token(s) and cannot give away **{}**.", *author_wealth, amount)).await?; - return Ok(()); - } - - *author_wealth -= amount; - - let reciever_wealth = get_user_wealth_mut(&mut users, user.id); - - *reciever_wealth += amount; - - ctx.reply(format!("You've given **{}** **{}** token(s).", user.name, amount)).await?; - - Ok(()) -} - -#[poise::command(slash_command, prefix_command)] -async fn balance(ctx: Context<'_>, user: Option) -> Result<(), Error> { - let user = user.as_ref().unwrap_or(ctx.author()); - let mut users = ctx.data().users.lock().await; - - let wealth = get_user_wealth_mut(&mut users, user.id); - - ctx.reply(format!("{} **{}** token(s).", - if user.id == ctx.author().id { - "You have".to_string() - } else { - format!("{} has", user.name) - }, *wealth)).await?; - - Ok(()) -} #[tokio::main] async fn main() -> Result<(), Error> { @@ -192,10 +20,11 @@ async fn main() -> Result<(), Error> { let framework = poise::Framework::builder() .options(poise::FrameworkOptions { - commands: vec![ping(), dox(), yeehaw(), wager(), balance(), give()], + commands: commands::commands(), prefix_options: poise::PrefixFrameworkOptions { prefix: Some("%".into()), - edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan(std::time::Duration::from_secs(3600)))), + edit_tracker: Some(Arc::new( + poise::EditTracker::for_timespan(std::time::Duration::from_secs(3600)))), case_insensitive_commands: true, ..Default::default() }, @@ -209,7 +38,8 @@ async fn main() -> Result<(), Error> { }) .build(); - let client = serenity::ClientBuilder::new(token, intents).framework(framework).await; + let client = serenity::ClientBuilder::new(token, intents) + .framework(framework).await; client.unwrap().start().await?;