diff --git a/src/commands/fun.rs b/src/commands/fun.rs index 1c6f4d8..49382d1 100644 --- a/src/commands/fun.rs +++ b/src/commands/fun.rs @@ -69,5 +69,136 @@ pub async fn say( .await?; } +Ok(()) +} + +#[derive(serde::Deserialize)] +struct UrbanResponse { + list: Vec, +} + +#[derive(serde::Deserialize, Clone)] +struct UrbanEntry { + definition: String, + permalink: String, + thumbs_up: u64, + thumbs_down: u64, + author: String, + word: String, + example: String, + #[serde(rename = "written_on")] + written_on: String, +} + +#[poise::command(slash_command, prefix_command)] +pub async fn urban( + ctx: Context<'_>, + #[description = "Word to search for"] term: String, +) -> Result<(), Error> { + ctx.defer().await?; + + let url = format!("https://api.urbandictionary.com/v0/define?term={}", term); + let response = reqwest::get(&url).await?.json::().await?; + + if response.list.is_empty() { + ctx.say(format!("No definition found for `{}`.", term)).await?; + return Ok(()); + } + + let entries = response.list; + let mut current_page = 0; + let ctx_id = ctx.id(); + let prev_custom_id = format!("{}prev", ctx_id); + let next_custom_id = format!("{}next", ctx_id); + + let create_page = |page: usize, entries: &[UrbanEntry]| { + let entry = &entries[page]; + let description = format!( + "{}\n\n**Example:**\n{}\n\n**Author:** {}\n**Date:** {}", + entry.definition.replace("[", "").replace("]", ""), + entry.example.replace("[", "").replace("]", ""), + entry.author, + entry.written_on + ); + + let description = if description.len() > 4096 { + format!("{}...", &description[..4093]) + } else { + description + }; + + let embed = serenity::CreateEmbed::new() + .title(&entry.word) + .url(&entry.permalink) + .description(description) + .field("Thumbs Up", entry.thumbs_up.to_string(), true) + .field("Thumbs Down", entry.thumbs_down.to_string(), true) + .footer(serenity::CreateEmbedFooter::new(format!( + "Page {}/{}", + page + 1, + entries.len() + ))) + .color(0xEFFF00); + + let prev_button = serenity::CreateButton::new(&prev_custom_id) + .label("Previous") + .style(serenity::ButtonStyle::Primary) + .disabled(page == 0); + + let next_button = serenity::CreateButton::new(&next_custom_id) + .label("Next") + .style(serenity::ButtonStyle::Primary) + .disabled(page == entries.len() - 1); + + let components = vec![serenity::CreateActionRow::Buttons(vec![ + prev_button, + next_button, + ])]; + + (embed, components) + }; + + let (embed, components) = create_page(current_page, &entries); + ctx.send( + poise::CreateReply::default() + .embed(embed) + .components(components), + ) + .await?; + + while let Some(mci) = serenity::ComponentInteractionCollector::new(ctx) + .author_id(ctx.author().id) + .channel_id(ctx.channel_id()) + .timeout(std::time::Duration::from_secs(60 * 5)) + .filter(move |mci| mci.data.custom_id == prev_custom_id || mci.data.custom_id == next_custom_id) + .await + { + if mci.data.custom_id == prev_custom_id { + if current_page > 0 { + current_page -= 1; + } + } else if mci.data.custom_id == next_custom_id { + if current_page < entries.len() - 1 { + current_page += 1; + } + } + + let (embed, components) = create_page(current_page, &entries); + + if let Err(e) = mci + .create_response( + ctx.http(), + serenity::CreateInteractionResponse::UpdateMessage( + serenity::CreateInteractionResponseMessage::new() + .embed(embed) + .components(components), + ), + ) + .await + { + tracing::error!("Failed to update urban message: {}", e); + } + } + Ok(()) } diff --git a/void-sentinel b/void-sentinel deleted file mode 100755 index 9f57b65..0000000 Binary files a/void-sentinel and /dev/null differ