diff --git a/src/commands/challenge.rs b/src/commands/challenge.rs index 74ae5fa..8e1010b 100644 --- a/src/commands/challenge.rs +++ b/src/commands/challenge.rs @@ -12,19 +12,25 @@ use std::fs::File; use std::io::Write; use std::path::Path; -#[command(prefix_command, slash_command, description_localized("en-US", "View the latest handwriting challenge info."))] +#[command( + prefix_command, + slash_command, + description_localized("en-US", "View the latest handwriting challenge info.") +)] pub async fn challenge(ctx: Context<'_>) -> Result<(), Error> { - ctx.say( - format!( - "Tegaki Tuesday #{n}: ", - n = get_challenge_number() - ), - ) + ctx.say(format!( + "Tegaki Tuesday #{n}: ", + n = get_challenge_number() + )) .await?; Ok(()) } -#[command(prefix_command, broadcast_typing, description_localized("en-US", "Submit to the latest handwriting challenge."))] +#[command( + prefix_command, + broadcast_typing, + description_localized("en-US", "Submit to the latest handwriting challenge.") +)] pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> { // TODO: The code for this command needs to be refactored, // there are large duplicated sections that need to be merged somehow. @@ -36,24 +42,32 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> { .unwrap() .contains_key("submissionChannel") { - ctx.msg.reply(&ctx.discord.http, "Submissions aren't enabled for this server yet.").await?; + ctx.msg + .reply( + &ctx.discord.http, + "Submissions aren't enabled for this server yet.", + ) + .await?; return Ok(()); } let current_guild_data = &guild_data[&guild].as_object().unwrap(); let submission_channel = current_guild_data["submissionChannel"].as_str().unwrap(); if submission_channel != ctx.msg.channel_id.as_u64().to_string() { - ctx.msg.reply( - &ctx.discord.http, - format!( - "Sorry, submissions aren't permitted here. Please go to <#{}>. Thanks!", - guild - ), - ) - .await?; + ctx.msg + .reply( + &ctx.discord.http, + format!( + "Sorry, submissions aren't permitted here. Please go to <#{}>. Thanks!", + guild + ), + ) + .await?; return Ok(()); } if ctx.msg.attachments.len() == 0 { - ctx.msg.reply(&ctx.discord.http, "Please attach at least one image.").await?; + ctx.msg + .reply(&ctx.discord.http, "Please attach at least one image.") + .await?; return Ok(()); } let challenge_number = get_challenge_number(); @@ -170,7 +184,11 @@ pub async fn submit(ctx: PrefixContext<'_>) -> Result<(), Error> { Ok(()) } -#[command(prefix_command, slash_command, description_localized("en-US", "List images in your current submission, if available."))] +#[command( + prefix_command, + slash_command, + description_localized("en-US", "List images in your current submission, if available.") +)] pub async fn images(ctx: Context<'_>) -> Result<(), Error> { let submission_data = get_current_submission_data(); let images: Vec = { @@ -187,12 +205,10 @@ pub async fn images(ctx: Context<'_>) -> Result<(), Error> { }; let challenge_number = get_challenge_number(); if images.len() == 0 { - ctx.say( - format!( - "You haven't submitted anything for Tegaki Tuesday #{}.", - challenge_number - ), - ) + ctx.say(format!( + "You haven't submitted anything for Tegaki Tuesday #{}.", + challenge_number + )) .await?; return Ok(()); } @@ -212,13 +228,18 @@ pub async fn images(ctx: Context<'_>) -> Result<(), Error> { Ok(()) } -#[command(prefix_command, slash_command, description_localized("en-US", "Delete images from your current submission using image numbers from the images command."))] -pub async fn imagedelete( - ctx: Context<'_>, - number: i32, -) -> Result<(), Error> { +#[command( + prefix_command, + slash_command, + description_localized( + "en-US", + "Delete images from your current submission using image numbers from the images command." + ) +)] +pub async fn imagedelete(ctx: Context<'_>, number: i32) -> Result<(), Error> { if number < 1 { - ctx.say("That isn't a valid image number. Image numbers start at 1.").await?; + ctx.say("That isn't a valid image number. Image numbers start at 1.") + .await?; return Ok(()); } let challenge_number = get_challenge_number(); @@ -230,23 +251,21 @@ pub async fn imagedelete( let mut images = submission["images"].as_array().unwrap().clone(); let image_count = images.len(); if image_count < number.try_into().unwrap() { - ctx.say( - if image_count == 0 { - // This is an edge case that should never happen. - // In this scenario, there is submission data with an empty image list. - // Submission data should be deleted uppon imageDelete if there are no images remaining. - format!( - "You haven't submitted anything for Tegaki Tuesday #{}.", - challenge_number - ) - } else { - format!( - "That image number doesn't exist, you only have {} image{}.", - image_count, - if image_count == 1 { "" } else { "s" } - ) - }, - ) + ctx.say(if image_count == 0 { + // This is an edge case that should never happen. + // In this scenario, there is submission data with an empty image list. + // Submission data should be deleted uppon imageDelete if there are no images remaining. + format!( + "You haven't submitted anything for Tegaki Tuesday #{}.", + challenge_number + ) + } else { + format!( + "That image number doesn't exist, you only have {} image{}.", + image_count, + if image_count == 1 { "" } else { "s" } + ) + }) .await?; return Ok(()); } @@ -304,22 +323,22 @@ pub async fn imagedelete( ctx.say(message).await?; return Ok(()); } - ctx.say( - format!( - "You haven't submitted anything for Tegaki Tuesday #{}.", - challenge_number - ), - ) + ctx.say(format!( + "You haven't submitted anything for Tegaki Tuesday #{}.", + challenge_number + )) .await?; Ok(()) } // TODO: make also slash command -#[command(prefix_command, description_localized("en-US", "Make a suggestion for future challenge prompts!"))] +#[command( + prefix_command, + description_localized("en-US", "Make a suggestion for future challenge prompts!") +)] pub async fn suggest( ctx: PrefixContext<'_>, - #[description = "Suggestion text. Please include passage and source."] - suggestion: String, + #[description = "Suggestion text. Please include passage and source."] suggestion: String, ) -> Result<(), Error> { let guild_data = get_guild_data(); let channel = serenity::ChannelId( @@ -332,7 +351,9 @@ pub async fn suggest( // User::accent_colour is only available via the REST API // If we just do msg.author.accent_colour here, we will get None let author = &ctx.msg.author; - let accent_color = ctx.discord.http + let accent_color = ctx + .discord + .http .get_user(*author.id.as_u64()) .await .unwrap() diff --git a/src/commands/kanji.rs b/src/commands/kanji.rs index 01961f1..af31f3e 100644 --- a/src/commands/kanji.rs +++ b/src/commands/kanji.rs @@ -1,15 +1,14 @@ use crate::utils::*; use crate::serenity; -use crate::Error; use crate::Context; +use crate::Error; use poise::command; #[command(slash_command, prefix_command, description_localized("en-US", "Get category info and links to Jisho for character(s), with stroke order for single characters."))] pub async fn i( ctx: Context<'_>, - #[description = "Input kanji to get info for"] - input: String, + #[description = "Input kanji to get info for"] input: String, ) -> Result<(), Error> { let chars = input.chars(); let mut message = String::from(""); @@ -46,47 +45,55 @@ pub async fn i( )); } ctx.send(|m| { - m.allowed_mentions(|am| { - am.empty_parse(); - am - }); - m.embed(|e| { - e.title(input); - e.description(message); - let mut author = serenity::builder::CreateEmbedAuthor::default(); - author - .icon_url("https://raw.githubusercontent.com/ElnuDev/ji-chan/main/ji-chan.png") - .name("字ちゃん") - .url("https://github.com/ElnuDev/ji-chan"); - e.set_author(author); - e.color(serenity::utils::Colour::from_rgb(251, 73, 52)); - if found_chars.len() == 1 { - e.thumbnail(get_so_diagram(found_chars[0])); - } - e - }); - m - }) - .await - .unwrap(); + m.allowed_mentions(|am| { + am.empty_parse(); + am + }); + m.embed(|e| { + e.title(input); + e.description(message); + let mut author = serenity::builder::CreateEmbedAuthor::default(); + author + .icon_url("https://raw.githubusercontent.com/ElnuDev/ji-chan/main/ji-chan.png") + .name("字ちゃん") + .url("https://github.com/ElnuDev/ji-chan"); + e.set_author(author); + e.color(serenity::utils::Colour::from_rgb(251, 73, 52)); + if found_chars.len() == 1 { + e.thumbnail(get_so_diagram(found_chars[0])); + } + e + }); + m + }) + .await + .unwrap(); Ok(()) } -#[command(slash_command, prefix_command, description_localized("en-US", "Random Jōyō kanji"))] -pub async fn joyo( - ctx: Context<'_>, -) -> Result<(), Error> { +#[command( + slash_command, + prefix_command, + description_localized("en-US", "Random Jōyō kanji") +)] +pub async fn joyo(ctx: Context<'_>) -> Result<(), Error> { random_kanji("JOYO", ctx, None).await } -#[command(slash_command, prefix_command, description_localized("en-US", "Random Jinmeiyō kanji"))] -pub async fn jinmeiyo( - ctx: Context<'_>, -)-> Result<(), Error> { +#[command( + slash_command, + prefix_command, + description_localized("en-US", "Random Jinmeiyō kanji") +)] +pub async fn jinmeiyo(ctx: Context<'_>) -> Result<(), Error> { random_kanji("JINMEIYO", ctx, None).await } -#[command(slash_command, prefix_command, description_localized("en-US", "Random Kyōiku kanji"))] +#[command( + slash_command, + prefix_command, + description_localized("en-US", "Random Kyōiku kanji") +)] pub async fn kyoiku( ctx: Context<'_>, #[description = "Kyōiku subcategory. GRADE1, GRADE2, GRADE3, GRADE4, GRADE5, GRADE6, or ALL."] @@ -95,29 +102,38 @@ pub async fn kyoiku( random_kanji("KYOIKU", ctx, subcategory).await } -#[command(slash_command, prefix_command, description_localized("en-US", "Random JLPT kanji"))] +#[command( + slash_command, + prefix_command, + description_localized("en-US", "Random JLPT kanji") +)] pub async fn jlpt( ctx: Context<'_>, - #[description = "JLPT subcategory. N1, N2, N3, N4, N5, or ALL"] - subcategory: Option + #[description = "JLPT subcategory. N1, N2, N3, N4, N5, or ALL"] subcategory: Option, ) -> Result<(), Error> { random_kanji("JLPT", ctx, subcategory).await } -#[command(slash_command, prefix_command, description_localized("en-US", "Random Hyōgai kanji"))] +#[command( + slash_command, + prefix_command, + description_localized("en-US", "Random Hyōgai kanji") +)] pub async fn hyogai( ctx: Context<'_>, - #[description = "Hyōgai subcategory. ELEMENTS, MAIN, or ALL."] - subcategory: Option, + #[description = "Hyōgai subcategory. ELEMENTS, MAIN, or ALL."] subcategory: Option, ) -> Result<(), Error> { random_kanji("HYOGAI", ctx, subcategory).await } -#[command(slash_command, prefix_command, description_localized("en-US", "Get stroke order diagrams for character(s), maximum 4"))] +#[command( + slash_command, + prefix_command, + description_localized("en-US", "Get stroke order diagrams for character(s), maximum 4") +)] pub async fn so( ctx: Context<'_>, - #[description = "Input characters to get stroke order for"] - text: String, + #[description = "Input characters to get stroke order for"] text: String, ) -> Result<(), Error> { const MAX_CHARS: i32 = 4; let mut displayed_characters: Vec = Vec::new(); diff --git a/src/commands/meta.rs b/src/commands/meta.rs index c2fb2cb..f0905b3 100644 --- a/src/commands/meta.rs +++ b/src/commands/meta.rs @@ -1,12 +1,16 @@ -use crate::Error; use crate::Context; +use crate::Error; use poise::command; use std::env; // TODO: Implement proper help text for command-specific help, // see https://github.com/kangalioo/poise/blob/90ac24a8ef621ec6dc3fc452762dc9cfa144f693/examples/framework_usage/main.rs#L18-L38 -#[command(prefix_command, slash_command, description_localized("en-US", "Get help for the 字ちゃん Tegaki Tuesday bot"))] +#[command( + prefix_command, + slash_command, + description_localized("en-US", "Get help for the 字ちゃん Tegaki Tuesday bot") +)] pub async fn help(ctx: Context<'_>) -> Result<(), Error> { let p = env::var("PREFIX").unwrap(); let message = format!( diff --git a/src/commands/owner.rs b/src/commands/owner.rs index 508541e..0e4cf70 100644 --- a/src/commands/owner.rs +++ b/src/commands/owner.rs @@ -12,7 +12,12 @@ use crate::utils::*; #[command(prefix_command, hide_in_help, owners_only)] pub async fn sleep(ctx: Context<'_>) -> Result<(), crate::Error> { ctx.say("Good night!").await?; - ctx.framework().shard_manager.lock().await.shutdown_all().await; + ctx.framework() + .shard_manager + .lock() + .await + .shutdown_all() + .await; Ok(()) } @@ -33,13 +38,11 @@ pub async fn setsubmissionchannel(ctx: Context<'_>) -> Result<(), Error> { guild_data.insert(guild, json!({ "submissionChannel": channel })); } set_guild_data(guild_data); - ctx.say( - format!( - "Submission channel for **{}** set to <#{}>.", - ctx.guild().unwrap().name, - ctx.channel_id() - ), - ) + ctx.say(format!( + "Submission channel for **{}** set to <#{}>.", + ctx.guild().unwrap().name, + ctx.channel_id() + )) .await?; Ok(()) } @@ -63,8 +66,7 @@ pub async fn setsuggestionchannel(ctx: Context<'_>) -> Result<(), Error> { #[allow(non_snake_case)] pub async fn setannouncementrole( ctx: Context<'_>, - #[description = "Announcement role ID"] - role: u64, + #[description = "Announcement role ID"] role: u64, ) -> Result<(), Error> { let mut guild_data = get_guild_data(); let guild = ctx.guild_id().unwrap().as_u64().to_string(); @@ -87,8 +89,7 @@ pub async fn setannouncementrole( #[command(prefix_command, hide_in_help, owners_only)] pub async fn announce( ctx: Context<'_>, - #[description = "Announcement text"] - announcement: String, + #[description = "Announcement text"] announcement: String, ) -> Result<(), Error> { send(ctx, &announcement, true, false).await } diff --git a/src/main.rs b/src/main.rs index 3e5d73a..057bae7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,10 +10,8 @@ type PrefixContext<'a> = poise::PrefixContext<'a, Data, Error>; pub struct Data {} use commands::{challenge::*, kanji::*, meta::*, owner::*}; -use poise::serenity_prelude::{ - model::gateway::GatewayIntents -}; use poise::serenity_prelude as serenity; +use poise::serenity_prelude::model::gateway::GatewayIntents; #[poise::command(prefix_command)] async fn register(ctx: Context<'_>) -> Result<(), Error> { @@ -36,7 +34,6 @@ async fn main() { .options(poise::FrameworkOptions { commands: vec![ register(), - // owner sleep(), setsubmissionchannel(), @@ -45,17 +42,14 @@ async fn main() { announce(), announcechallenge(), rebuildsite(), - // challenge challenge(), submit(), images(), imagedelete(), suggest(), - // meta help(), - // kanji i(), joyo(), @@ -63,7 +57,7 @@ async fn main() { kyoiku(), jlpt(), hyogai(), - so() + so(), ], prefix_options: poise::PrefixFrameworkOptions { prefix: Some(env::var("PREFIX").expect("Expected a prefix in the environment")), @@ -72,10 +66,12 @@ async fn main() { ..Default::default() }) .token(std::env::var("DISCORD_TOKEN").expect("Expected a token in the environment")) - .intents(GatewayIntents::GUILD_MESSAGES - | GatewayIntents::DIRECT_MESSAGES - | GatewayIntents::MESSAGE_CONTENT - | GatewayIntents::GUILDS) + .intents( + GatewayIntents::GUILD_MESSAGES + | GatewayIntents::DIRECT_MESSAGES + | GatewayIntents::MESSAGE_CONTENT + | GatewayIntents::GUILDS, + ) .user_data_setup(move |_ctx, _ready, _framework| Box::pin(async move { Ok(Data {}) })); framework.run().await.unwrap(); diff --git a/src/utils.rs b/src/utils.rs index 069df58..541dca4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,6 @@ +use crate::serenity; +use crate::Error; +use crate::{Context, PrefixContext}; use rand::seq::IteratorRandom; use serde_json::Map; use serde_json::Value; @@ -9,9 +12,6 @@ use std::fs::OpenOptions; use std::io::Read; use std::io::Write; use std::process::Command; -use crate::serenity; -use crate::Error; -use crate::{Context, PrefixContext}; pub fn get_challenge_number() -> i32 { let challenge_dir = format!("{}/content/challenges", env::var("HUGO").unwrap()); @@ -152,12 +152,7 @@ pub fn set_guild_data(guild_data: Map) { .unwrap(); } -pub async fn send( - ctx: Context<'_>, - message: &str, - ping: bool, - pin: bool, -) -> Result<(), Error> { +pub async fn send(ctx: Context<'_>, message: &str, ping: bool, pin: bool) -> Result<(), Error> { let guild_data = get_guild_data(); let mut announcements_count = 0; for (_guild, data) in guild_data.iter() { @@ -198,13 +193,11 @@ pub async fn send( } announcements_count += 1; } - ctx.say( - format!( - "Announced to {} server{}!", - announcements_count, - if announcements_count == 1 { "" } else { "s" } - ), - ) + ctx.say(format!( + "Announced to {} server{}!", + announcements_count, + if announcements_count == 1 { "" } else { "s" } + )) .await?; Ok(()) } @@ -220,13 +213,8 @@ pub fn get_so_diagram(kanji: char) -> String { ) } -pub async fn display_kanji( - ctx: Context<'_>, - kanji: char, - comment: &str, -) -> Result<(), Error> { - ctx.say(format!("{}{}", kanji, comment)) - .await?; +pub async fn display_kanji(ctx: Context<'_>, kanji: char, comment: &str) -> Result<(), Error> { + ctx.say(format!("{}{}", kanji, comment)).await?; let url = get_so_diagram(kanji); let client = reqwest::Client::new(); let request = client.head(&url).build().unwrap(); @@ -339,8 +327,7 @@ pub async fn random_kanji( .unwrap(); let list = subcategories[subcategory_key].as_str().unwrap(); let kanji = random_from_string(&list); - display_kanji(ctx, kanji, &format!(", **{}**", subcategory_key)) - .await?; + display_kanji(ctx, kanji, &format!(", **{}**", subcategory_key)).await?; } else if subcategories.contains_key(&subcategory) { let list = list[&subcategory].as_str().unwrap(); let kanji = random_from_string(&list);