Add an autocomplete for time hints

Shows the approximate time until a reminder will send in the autocomplete area.
This commit is contained in:
jude 2022-09-12 17:49:10 +01:00
parent 8f8235a86e
commit b62d24c024
3 changed files with 131 additions and 39 deletions

View File

@ -1,6 +1,9 @@
use chrono_tz::TZ_VARIANTS; use std::time::{SystemTime, UNIX_EPOCH};
use crate::Context; use chrono_tz::TZ_VARIANTS;
use poise::AutocompleteChoice;
use crate::{models::CtxData, time_parser::natural_parser, Context};
pub async fn timezone_autocomplete(ctx: Context<'_>, partial: &str) -> Vec<String> { pub async fn timezone_autocomplete(ctx: Context<'_>, partial: &str) -> Vec<String> {
if partial.is_empty() { if partial.is_empty() {
@ -33,3 +36,96 @@ WHERE
.map(|s| s.name.clone()) .map(|s| s.name.clone())
.collect() .collect()
} }
pub async fn multiline_autocomplete(
_ctx: Context<'_>,
partial: &str,
) -> Vec<AutocompleteChoice<String>> {
if partial.is_empty() {
vec![AutocompleteChoice { name: "Multiline content...".to_string(), value: "".to_string() }]
} else {
vec![
AutocompleteChoice { name: partial.to_string(), value: partial.to_string() },
AutocompleteChoice { name: "Multiline content...".to_string(), value: "".to_string() },
]
}
}
pub async fn time_hint_autocomplete(
ctx: Context<'_>,
partial: &str,
) -> Vec<AutocompleteChoice<String>> {
if partial.is_empty() {
vec![AutocompleteChoice {
name: "Start typing a time...".to_string(),
value: "now".to_string(),
}]
} else {
match natural_parser(partial, &ctx.timezone().await.to_string()).await {
Some(timestamp) => match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(now) => {
let diff = timestamp - now.as_secs() as i64;
if diff < 0 {
vec![AutocompleteChoice {
name: "Time is in the past".to_string(),
value: "now".to_string(),
}]
} else {
if diff > 86400 {
vec![
AutocompleteChoice {
name: partial.to_string(),
value: partial.to_string(),
},
AutocompleteChoice {
name: format!(
"In approximately {} days, {} hours",
diff / 86400,
(diff % 86400) / 3600
),
value: partial.to_string(),
},
]
} else if diff > 3600 {
vec![
AutocompleteChoice {
name: partial.to_string(),
value: partial.to_string(),
},
AutocompleteChoice {
name: format!("In approximately {} hours", diff / 3600),
value: partial.to_string(),
},
]
} else {
vec![
AutocompleteChoice {
name: partial.to_string(),
value: partial.to_string(),
},
AutocompleteChoice {
name: format!("In approximately {} minutes", diff / 60),
value: partial.to_string(),
},
]
}
}
}
Err(_) => {
vec![AutocompleteChoice {
name: partial.to_string(),
value: partial.to_string(),
}]
}
},
None => {
vec![AutocompleteChoice {
name: "Time not recognised".to_string(),
value: "now".to_string(),
}]
}
}
}
}

View File

@ -11,11 +11,13 @@ use poise::{
serenity_prelude::{ serenity_prelude::{
builder::CreateEmbed, component::ButtonStyle, model::channel::Channel, ReactionType, builder::CreateEmbed, component::ButtonStyle, model::channel::Channel, ReactionType,
}, },
AutocompleteChoice, CreateReply, Modal, CreateReply, Modal,
}; };
use super::autocomplete::timezone_autocomplete;
use crate::{ use crate::{
commands::autocomplete::{
multiline_autocomplete, time_hint_autocomplete, timezone_autocomplete,
},
component_models::{ component_models::{
pager::{DelPager, LookPager, Pager}, pager::{DelPager, LookPager, Pager},
ComponentDataModel, DelSelector, UndoReminder, ComponentDataModel, DelSelector, UndoReminder,
@ -550,20 +552,6 @@ pub async fn delete_timer(
Ok(()) Ok(())
} }
async fn multiline_autocomplete(
_ctx: Context<'_>,
partial: &str,
) -> Vec<AutocompleteChoice<String>> {
if partial.is_empty() {
vec![AutocompleteChoice { name: "Multiline content...".to_string(), value: "".to_string() }]
} else {
vec![
AutocompleteChoice { name: partial.to_string(), value: partial.to_string() },
AutocompleteChoice { name: "Multiline content...".to_string(), value: "".to_string() },
]
}
}
#[derive(poise::Modal)] #[derive(poise::Modal)]
#[name = "Reminder"] #[name = "Reminder"]
struct ContentModal { struct ContentModal {
@ -574,7 +562,7 @@ struct ContentModal {
content: String, content: String,
} }
/// Create a reminder. Press "+5 more" for other options. A modal will open if "content" is not provided /// Create a reminder. Press "+4 more" for other options.
#[poise::command( #[poise::command(
slash_command, slash_command,
identifying_name = "remind", identifying_name = "remind",
@ -582,7 +570,9 @@ struct ContentModal {
)] )]
pub async fn remind( pub async fn remind(
ctx: ApplicationContext<'_>, ctx: ApplicationContext<'_>,
#[description = "A description of the time to set the reminder for"] time: String, #[description = "A description of the time to set the reminder for"]
#[autocomplete = "time_hint_autocomplete"]
time: String,
#[description = "The message content to send"] #[description = "The message content to send"]
#[autocomplete = "multiline_autocomplete"] #[autocomplete = "multiline_autocomplete"]
content: String, content: String,

View File

@ -1,36 +1,42 @@
use poise::serenity_prelude::model::channel::Channel; use poise::{
serenity_prelude::model::channel::Channel, ApplicationCommandOrAutocompleteInteraction,
};
use crate::{consts::MACRO_MAX_COMMANDS, models::command_macro::RecordedCommand, Context, Error}; use crate::{consts::MACRO_MAX_COMMANDS, models::command_macro::RecordedCommand, Context, Error};
async fn macro_check(ctx: Context<'_>) -> bool { async fn macro_check(ctx: Context<'_>) -> bool {
if let Context::Application(app_ctx) = ctx { if let Context::Application(app_ctx) = ctx {
if let Some(guild_id) = ctx.guild_id() { if let ApplicationCommandOrAutocompleteInteraction::ApplicationCommand(_) =
if ctx.command().identifying_name != "finish_macro" { app_ctx.interaction
let mut lock = ctx.data().recording_macros.write().await; {
if let Some(guild_id) = ctx.guild_id() {
if ctx.command().identifying_name != "finish_macro" {
let mut lock = ctx.data().recording_macros.write().await;
if let Some(command_macro) = lock.get_mut(&(guild_id, ctx.author().id)) { if let Some(command_macro) = lock.get_mut(&(guild_id, ctx.author().id)) {
if command_macro.commands.len() >= MACRO_MAX_COMMANDS { if command_macro.commands.len() >= MACRO_MAX_COMMANDS {
let _ = ctx.send(|m| { let _ = ctx.send(|m| {
m.ephemeral(true).content( m.ephemeral(true).content(
format!("{} commands already recorded. Please use `/macro finish` to end recording.", MACRO_MAX_COMMANDS), format!("{} commands already recorded. Please use `/macro finish` to end recording.", MACRO_MAX_COMMANDS),
) )
}) })
.await; .await;
} else { } else {
let recorded = RecordedCommand { let recorded = RecordedCommand {
action: None, action: None,
command_name: ctx.command().identifying_name.clone(), command_name: ctx.command().identifying_name.clone(),
options: Vec::from(app_ctx.args), options: Vec::from(app_ctx.args),
}; };
command_macro.commands.push(recorded); command_macro.commands.push(recorded);
let _ = ctx let _ = ctx
.send(|m| m.ephemeral(true).content("Command recorded to macro")) .send(|m| m.ephemeral(true).content("Command recorded to macro"))
.await; .await;
}
return false;
} }
return false;
} }
} }
} }