Add an autocomplete for time hints
Shows the approximate time until a reminder will send in the autocomplete area.
This commit is contained in:
parent
8f8235a86e
commit
b62d24c024
@ -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(),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
44
src/hooks.rs
44
src/hooks.rs
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user