From 7fa879fa4798a300f4b959c3bfe0096f04787813 Mon Sep 17 00:00:00 2001 From: Valentine Briese Date: Thu, 15 Aug 2024 17:02:13 -0700 Subject: [PATCH] Add `/timestamp` command --- README.md | 6 +- src/commands.rs | 2 + src/commands/timestamp.rs | 118 ++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 src/commands/timestamp.rs diff --git a/README.md b/README.md index 9c6167d..a169ff7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Join the [Goober Bot Dev](https://discord.gg/7v2aY2YzJU) Discord server to try t ## Commands -*Last updated Aug 11, 2024* +*Last updated Aug 15, 2024* ### Silly @@ -53,8 +53,10 @@ Join the [Goober Bot Dev](https://discord.gg/7v2aY2YzJU) Discord server to try t ### Other - `/anon ` -- `/debug ` +- `/debug delete_config` +- `/debug error ` - `/rock_paper_scissors [user]` - `/sponsors` 💖 +- `/timestamp [year] [month] [day] [hour] [minute] [second] [timezone] [style]` - `/updates` - `/vote` ❤️ diff --git a/src/commands.rs b/src/commands.rs index 393254e..0ccdad9 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -20,6 +20,7 @@ mod rock_paper_scissors; mod silly; mod sponsors; mod strike; +mod timestamp; mod updates; #[cfg(not(debug_assertions))] mod vote; @@ -30,6 +31,7 @@ pub(super) use rock_paper_scissors::*; pub(super) use silly::*; pub(super) use sponsors::*; pub(super) use strike::*; +pub(super) use timestamp::*; pub(super) use updates::*; #[cfg(not(debug_assertions))] pub(super) use vote::*; diff --git a/src/commands/timestamp.rs b/src/commands/timestamp.rs new file mode 100644 index 0000000..63224f6 --- /dev/null +++ b/src/commands/timestamp.rs @@ -0,0 +1,118 @@ +// Goober Bot, Discord bot +// Copyright (C) 2024 Valentine Briese +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use anyhow::{anyhow, Context as _}; +use chrono::{Datelike, FixedOffset, TimeZone, Timelike, Utc}; +use poise::{ + command, + serenity_prelude::{FormattedTimestamp, FormattedTimestampStyle}, + ChoiceParameter, +}; + +use crate::{emoji::*, error::UserError, Context, Error}; + +const SECONDS_PER_HOUR: i32 = 3600; + +#[derive(ChoiceParameter)] +enum FormattedTimestampStyleChoice { + #[name = "Short time"] + ShortTime, + #[name = "Long time"] + LongTime, + #[name = "Short date"] + ShortDate, + #[name = "Long date"] + LongDate, + #[name = "Short date time"] + ShortDateTime, + #[name = "Long date time"] + LongDateTime, + #[name = "Relative time"] + RelativeTime, +} + +impl From for FormattedTimestampStyle { + fn from(value: FormattedTimestampStyleChoice) -> Self { + match value { + FormattedTimestampStyleChoice::ShortTime => FormattedTimestampStyle::ShortTime, + FormattedTimestampStyleChoice::LongTime => FormattedTimestampStyle::LongTime, + FormattedTimestampStyleChoice::ShortDate => FormattedTimestampStyle::ShortDate, + FormattedTimestampStyleChoice::LongDate => FormattedTimestampStyle::LongDate, + FormattedTimestampStyleChoice::ShortDateTime => FormattedTimestampStyle::ShortDateTime, + FormattedTimestampStyleChoice::LongDateTime => FormattedTimestampStyle::LongDateTime, + FormattedTimestampStyleChoice::RelativeTime => FormattedTimestampStyle::RelativeTime, + } + } +} + +/// Generates a Unix timestamp for your use in Discord styled messages +#[command( + slash_command, + install_context = "Guild|User", + interaction_context = "Guild|BotDm|PrivateChannel", + ephemeral +)] +pub(crate) async fn timestamp( + ctx: Context<'_>, + #[description = "Default is current year"] + // Where the heck did these big ol' numbers come from? + #[min = -262142] + #[max = 262141] + year: Option, + #[description = "Default is current month"] + #[min = 1] + #[max = 12] + month: Option, + #[description = "Default is current day"] + #[min = 1] + #[max = 31] + day: Option, + #[description = "Default is current hour"] + #[max = 23] + hour: Option, + #[description = "Default is current minute"] + #[max = 59] + minute: Option, + #[description = "Default is current second"] + #[max = 59] + second: Option, + #[description = "Offset from UTC (default is 0)"] + #[min = -12] + #[max = 14] + timezone: Option, + #[description = "Default is short date time"] style: Option, +) -> Result<(), Error> { + let timezone = timezone.unwrap_or(0); + let fixed_offset = FixedOffset::east_opt(timezone * SECONDS_PER_HOUR) + .context(UserError(anyhow!("Entered timezone difference is invalid")))?; + let now = Utc::now().with_timezone(&fixed_offset); + let year = year.unwrap_or_else(|| now.year()); + let month = month.unwrap_or_else(|| now.month()); + let day = day.unwrap_or_else(|| now.day()); + let hour = hour.unwrap_or_else(|| now.hour()); + let minute = minute.unwrap_or_else(|| now.minute()); + let second = second.unwrap_or_else(|| now.second()); + let datetime = fixed_offset + .with_ymd_and_hms(year, month, day, hour, minute, second) + .earliest() + .context(UserError(anyhow!("Entered date/time is invalid")))?; + let formatted_timestamp = + FormattedTimestamp::new(datetime.into(), style.and_then(|s| Some(s.into()))); + + ctx.say(format!("Copy this and use it anywhere that supports Discord formatting {FLOOF_HAPPY}\n```\n{formatted_timestamp}\n```\nLooks like this btw: {formatted_timestamp}\n*If this isn't the timestamp you expected, make sure you set `timezone` to your timezone!*")).await?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 7628468..1062341 100644 --- a/src/main.rs +++ b/src/main.rs @@ -102,6 +102,7 @@ async fn main( commands::slap(), commands::sponsors(), commands::strike(), + commands::timestamp(), commands::updates(), #[cfg(not(debug_assertions))] commands::vote(),