Extract trait
This commit is contained in:
parent
4823754955
commit
c1305cfb36
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
/target
|
target
|
||||||
.env
|
.env
|
||||||
/venv
|
/venv
|
||||||
.cargo
|
.cargo
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
use crate::{consts::MINUTE, models::CtxData, Context, Error};
|
use crate::{consts::MINUTE, models::CtxData, Context, Error};
|
||||||
|
|
||||||
/// Nudge all future reminders on this channel by a certain amount (don't use for DST! See `/offset`)
|
pub struct Options {
|
||||||
#[poise::command(
|
minutes: Option<isize>,
|
||||||
slash_command,
|
seconds: Option<isize>,
|
||||||
identifying_name = "nudge",
|
}
|
||||||
default_member_permissions = "MANAGE_GUILD"
|
|
||||||
)]
|
pub async fn nudge(ctx: Context<'_>, options: Options) -> Result<(), Error> {
|
||||||
pub async fn nudge(
|
let combined_time =
|
||||||
ctx: Context<'_>,
|
options.minutes.map_or(0, |m| m * MINUTE as isize) + options.seconds.map_or(0, |s| s);
|
||||||
#[description = "Number of minutes to nudge new reminders by"] minutes: Option<isize>,
|
|
||||||
#[description = "Number of seconds to nudge new reminders by"] seconds: Option<isize>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let combined_time = minutes.map_or(0, |m| m * MINUTE as isize) + seconds.map_or(0, |s| s);
|
|
||||||
|
|
||||||
if combined_time < i16::MIN as isize || combined_time > i16::MAX as isize {
|
if combined_time < i16::MIN as isize || combined_time > i16::MAX as isize {
|
||||||
ctx.say("Nudge times must be less than 500 minutes").await?;
|
ctx.say("Nudge times must be less than 500 minutes").await?;
|
||||||
@ -26,3 +22,17 @@ pub async fn nudge(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Nudge all future reminders on this channel by a certain amount (don't use for DST! See `/offset`)
|
||||||
|
#[poise::command(
|
||||||
|
slash_command,
|
||||||
|
identifying_name = "nudge",
|
||||||
|
default_member_permissions = "MANAGE_GUILD"
|
||||||
|
)]
|
||||||
|
pub async fn command(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "Number of minutes to nudge new reminders by"] minutes: Option<isize>,
|
||||||
|
#[description = "Number of seconds to nudge new reminders by"] seconds: Option<isize>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
nudge(ctx, Options { minutes, seconds }).await
|
||||||
|
}
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{HOUR, MINUTE},
|
consts::{HOUR, MINUTE},
|
||||||
Context, Error,
|
Context, Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Move all reminders in the current server by a certain amount of time. Times get added together
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
#[poise::command(
|
pub struct Options {
|
||||||
slash_command,
|
hours: Option<isize>,
|
||||||
identifying_name = "offset",
|
minutes: Option<isize>,
|
||||||
default_member_permissions = "MANAGE_GUILD"
|
seconds: Option<isize>,
|
||||||
)]
|
}
|
||||||
pub async fn offset(
|
|
||||||
ctx: Context<'_>,
|
async fn offset(ctx: Context<'_>, options: Options) -> Result<(), Error> {
|
||||||
#[description = "Number of hours to offset by"] hours: Option<isize>,
|
|
||||||
#[description = "Number of minutes to offset by"] minutes: Option<isize>,
|
|
||||||
#[description = "Number of seconds to offset by"] seconds: Option<isize>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
ctx.defer().await?;
|
ctx.defer().await?;
|
||||||
|
|
||||||
let combined_time = hours.map_or(0, |h| h * HOUR as isize)
|
let combined_time = options.hours.map_or(0, |h| h * HOUR as isize)
|
||||||
+ minutes.map_or(0, |m| m * MINUTE as isize)
|
+ options.minutes.map_or(0, |m| m * MINUTE as isize)
|
||||||
+ seconds.map_or(0, |s| s);
|
+ options.seconds.map_or(0, |s| s);
|
||||||
|
|
||||||
if combined_time == 0 {
|
if combined_time == 0 {
|
||||||
ctx.say("Please specify one of `hours`, `minutes` or `seconds`").await?;
|
ctx.say("Please specify one of `hours`, `minutes` or `seconds`").await?;
|
||||||
@ -69,3 +67,18 @@ pub async fn offset(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move all reminders in the current server by a certain amount of time. Times get added together
|
||||||
|
#[poise::command(
|
||||||
|
slash_command,
|
||||||
|
identifying_name = "offset",
|
||||||
|
default_member_permissions = "MANAGE_GUILD"
|
||||||
|
)]
|
||||||
|
pub async fn command(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "Number of hours to offset by"] hours: Option<isize>,
|
||||||
|
#[description = "Number of minutes to offset by"] minutes: Option<isize>,
|
||||||
|
#[description = "Number of seconds to offset by"] seconds: Option<isize>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
offset(ctx, Options { hours, minutes, seconds }).await
|
||||||
|
}
|
||||||
|
@ -1,22 +1,28 @@
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{models::CtxData, time_parser::natural_parser, Context, Error};
|
use crate::{
|
||||||
|
models::CtxData, time_parser::natural_parser, utils::Extract, ApplicationContext, Context,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
/// Pause all reminders on the current channel until a certain time or indefinitely
|
#[derive(Serialize, Deserialize, Extract)]
|
||||||
#[poise::command(
|
pub struct Options {
|
||||||
slash_command,
|
until: Option<String>,
|
||||||
identifying_name = "pause",
|
}
|
||||||
default_member_permissions = "MANAGE_GUILD"
|
|
||||||
)]
|
impl Extract for Options {
|
||||||
pub async fn pause(
|
fn extract(ctx: ApplicationContext) -> Self {
|
||||||
ctx: Context<'_>,
|
Self { until: extract_arg!(ctx, "until", Option<String>) }
|
||||||
#[description = "When to pause until"] until: Option<String>,
|
}
|
||||||
) -> Result<(), Error> {
|
}
|
||||||
|
|
||||||
|
pub async fn pause(ctx: Context<'_>, options: Options) -> Result<(), Error> {
|
||||||
let timezone = ctx.timezone().await;
|
let timezone = ctx.timezone().await;
|
||||||
|
|
||||||
let mut channel = ctx.channel_data().await.unwrap();
|
let mut channel = ctx.channel_data().await.unwrap();
|
||||||
|
|
||||||
match until {
|
match options.until {
|
||||||
Some(until) => {
|
Some(until) => {
|
||||||
let parsed = natural_parser(&until, &timezone.to_string()).await;
|
let parsed = natural_parser(&until, &timezone.to_string()).await;
|
||||||
|
|
||||||
@ -65,3 +71,16 @@ pub async fn pause(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pause all reminders on the current channel until a certain time or indefinitely
|
||||||
|
#[poise::command(
|
||||||
|
slash_command,
|
||||||
|
identifying_name = "pause",
|
||||||
|
default_member_permissions = "MANAGE_GUILD"
|
||||||
|
)]
|
||||||
|
pub async fn command(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "When to pause until"] until: Option<String>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
pause(ctx, Options { until }).await
|
||||||
|
}
|
||||||
|
@ -4,18 +4,49 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::{
|
use crate::{
|
||||||
commands::autocomplete::{time_hint_autocomplete, timezone_autocomplete},
|
commands::autocomplete::{time_hint_autocomplete, timezone_autocomplete},
|
||||||
models::reminder::create_reminder,
|
models::reminder::create_reminder,
|
||||||
|
utils::{extract_arg, Extract},
|
||||||
ApplicationContext, Context, Error,
|
ApplicationContext, Context, Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct RemindOptions {
|
pub struct Options {
|
||||||
pub time: String,
|
time: String,
|
||||||
pub content: String,
|
content: String,
|
||||||
pub channels: Option<String>,
|
channels: Option<String>,
|
||||||
pub interval: Option<String>,
|
interval: Option<String>,
|
||||||
pub expires: Option<String>,
|
expires: Option<String>,
|
||||||
pub tts: Option<bool>,
|
tts: Option<bool>,
|
||||||
pub timezone: Option<Tz>,
|
timezone: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extract for Options {
|
||||||
|
fn extract(ctx: ApplicationContext) -> Self {
|
||||||
|
Self {
|
||||||
|
time: extract_arg!(ctx, "time", String),
|
||||||
|
content: extract_arg!(ctx, "content", String),
|
||||||
|
channels: extract_arg!(ctx, "channels", Option<String>),
|
||||||
|
interval: extract_arg!(ctx, "interval", Option<String>),
|
||||||
|
expires: extract_arg!(ctx, "expires", Option<String>),
|
||||||
|
tts: extract_arg!(ctx, "tts", Option<bool>),
|
||||||
|
timezone: extract_arg!(ctx, "timezone", Option<String>),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remind(ctx: Context<'_>, options: Options) -> Result<(), Error> {
|
||||||
|
let tz = options.timezone.map(|t| t.parse::<Tz>().ok()).flatten();
|
||||||
|
|
||||||
|
create_reminder(
|
||||||
|
ctx,
|
||||||
|
options.time,
|
||||||
|
options.content,
|
||||||
|
options.channels,
|
||||||
|
options.interval,
|
||||||
|
options.expires,
|
||||||
|
options.tts,
|
||||||
|
tz,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a reminder. Press "+4 more" for other options. Use "/multiline" for multiline content.
|
/// Create a reminder. Press "+4 more" for other options. Use "/multiline" for multiline content.
|
||||||
@ -24,8 +55,8 @@ pub struct RemindOptions {
|
|||||||
identifying_name = "remind",
|
identifying_name = "remind",
|
||||||
default_member_permissions = "MANAGE_GUILD"
|
default_member_permissions = "MANAGE_GUILD"
|
||||||
)]
|
)]
|
||||||
pub async fn remind(
|
pub async fn command(
|
||||||
ctx: ApplicationContext<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "The time (and optionally date) to set the reminder for"]
|
#[description = "The time (and optionally date) to set the reminder for"]
|
||||||
#[autocomplete = "time_hint_autocomplete"]
|
#[autocomplete = "time_hint_autocomplete"]
|
||||||
time: String,
|
time: String,
|
||||||
@ -41,8 +72,5 @@ pub async fn remind(
|
|||||||
#[autocomplete = "timezone_autocomplete"]
|
#[autocomplete = "timezone_autocomplete"]
|
||||||
timezone: Option<String>,
|
timezone: Option<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let tz = timezone.map(|t| t.parse::<Tz>().ok()).flatten();
|
remind(ctx, Options { time, content, channels, interval, expires, tts, timezone }).await
|
||||||
|
|
||||||
create_reminder(Context::Application(ctx), time, content, channels, interval, expires, tts, tz)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
@ -5,25 +5,24 @@ use poise::{
|
|||||||
serenity_prelude::{CreateEmbed, CreateEmbedFooter},
|
serenity_prelude::{CreateEmbed, CreateEmbedFooter},
|
||||||
CreateReply,
|
CreateReply,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::autocomplete::timezone_autocomplete, consts::THEME_COLOR, models::CtxData, Context,
|
commands::autocomplete::timezone_autocomplete, consts::THEME_COLOR, models::CtxData, Context,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Select your timezone
|
#[derive(Serialize, Deserialize)]
|
||||||
#[poise::command(slash_command, identifying_name = "timezone")]
|
pub struct Options {
|
||||||
pub async fn timezone(
|
pub timezone: Option<String>,
|
||||||
ctx: Context<'_>,
|
}
|
||||||
#[description = "Timezone to use from this list: https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee"]
|
|
||||||
#[autocomplete = "timezone_autocomplete"]
|
pub async fn timezone_fn(ctx: Context<'_>, options: Options) -> Result<(), Error> {
|
||||||
timezone: Option<String>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let mut user_data = ctx.author_data().await.unwrap();
|
let mut user_data = ctx.author_data().await.unwrap();
|
||||||
|
|
||||||
let footer_text = format!("Current timezone: {}", user_data.timezone);
|
let footer_text = format!("Current timezone: {}", user_data.timezone);
|
||||||
|
|
||||||
if let Some(timezone) = timezone {
|
if let Some(timezone) = options.timezone {
|
||||||
match timezone.parse::<Tz>() {
|
match timezone.parse::<Tz>() {
|
||||||
Ok(tz) => {
|
Ok(tz) => {
|
||||||
user_data.timezone = timezone.clone();
|
user_data.timezone = timezone.clone();
|
||||||
@ -115,3 +114,14 @@ You may want to use one of the popular timezones below, otherwise click [here](h
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select your timezone
|
||||||
|
#[poise::command(slash_command, identifying_name = "timezone")]
|
||||||
|
pub async fn command(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "Timezone to use from this list: https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee"]
|
||||||
|
#[autocomplete = "timezone_autocomplete"]
|
||||||
|
timezone: Option<String>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
timezone_fn(ctx, Options { timezone }).await
|
||||||
|
}
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
use poise::CreateReply;
|
use poise::CreateReply;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{models::CtxData, Context, Error};
|
use crate::{models::CtxData, utils::Extract, ApplicationContext, Context, Error};
|
||||||
|
|
||||||
/// View the webhook being used to send reminders to this channel
|
#[derive(Serialize, Deserialize)]
|
||||||
#[poise::command(
|
pub struct Options;
|
||||||
slash_command,
|
|
||||||
identifying_name = "webhook_url",
|
impl Extract for Options {
|
||||||
required_permissions = "ADMINISTRATOR"
|
fn extract(_ctx: ApplicationContext) -> Self {
|
||||||
)]
|
Self {}
|
||||||
pub async fn webhook(ctx: Context<'_>) -> Result<(), Error> {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn webhook(ctx: Context<'_>, _options: Options) -> Result<(), Error> {
|
||||||
match ctx.channel_data().await {
|
match ctx.channel_data().await {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
if let (Some(id), Some(token)) = (data.webhook_id, data.webhook_token) {
|
if let (Some(id), Some(token)) = (data.webhook_id, data.webhook_token) {
|
||||||
@ -34,3 +38,13 @@ Do not share it!
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// View the webhook being used to send reminders to this channel
|
||||||
|
#[poise::command(
|
||||||
|
slash_command,
|
||||||
|
identifying_name = "webhook_url",
|
||||||
|
required_permissions = "ADMINISTRATOR"
|
||||||
|
)]
|
||||||
|
pub async fn command(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
|
webhook(ctx, Options {}).await
|
||||||
|
}
|
||||||
|
46
src/extract_macro/Cargo.lock
generated
Normal file
46
src/extract_macro/Cargo.lock
generated
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "extract_macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
11
src/extract_macro/Cargo.toml
Normal file
11
src/extract_macro/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "extract_macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "1.0.35"
|
||||||
|
syn = { version = "2.0.49", features = ["full"] }
|
70
src/extract_macro/src/lib.rs
Normal file
70
src/extract_macro/src/lib.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
macro_rules! extract_arg {
|
||||||
|
($ctx:ident, $name:literal, String) => {
|
||||||
|
$ctx.args.iter().find(|opt| opt.name == $name).map(|opt| &opt.value).map_or_else(
|
||||||
|
|| String::new(),
|
||||||
|
|v| match v {
|
||||||
|
poise::serenity_prelude::ResolvedValue::String(s) => s.to_string(),
|
||||||
|
_ => String::new(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
($ctx:ident, $name:literal, Option<String>) => {
|
||||||
|
$ctx.args
|
||||||
|
.iter()
|
||||||
|
.find(|opt| opt.name == $name)
|
||||||
|
.map(|opt| &opt.value)
|
||||||
|
.map(|v| match v {
|
||||||
|
poise::serenity_prelude::ResolvedValue::String(s) => Some(s.to_string()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
};
|
||||||
|
($ctx:ident, $name:literal, bool) => {
|
||||||
|
$ctx.args.iter().find(|opt| opt.name == $name).map(|opt| &opt.value).map_or(false, |v| {
|
||||||
|
match v {
|
||||||
|
poise::serenity_prelude::ResolvedValue::Boolean(b) => b.to_owned(),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($ctx:ident, $name:literal, Option<bool>) => {
|
||||||
|
$ctx.args
|
||||||
|
.iter()
|
||||||
|
.find(|opt| opt.name == $name)
|
||||||
|
.map(|opt| &opt.value)
|
||||||
|
.map(|v| match v {
|
||||||
|
poise::serenity_prelude::ResolvedValue::Boolean(b) => Some(b.to_owned()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use syn::parse::Parser;
|
||||||
|
|
||||||
|
#[proc_macro_derive(Extract)]
|
||||||
|
pub fn extract(input: TokenStream) -> TokenStream {
|
||||||
|
// Construct a string representation of the type definition
|
||||||
|
let s = input.to_string();
|
||||||
|
|
||||||
|
// Parse the string representation
|
||||||
|
let ast = syn::parse_derive_input(&s).unwrap();
|
||||||
|
|
||||||
|
// Build the impl
|
||||||
|
let gen = impl_extract(&ast);
|
||||||
|
|
||||||
|
// Return the generated impl
|
||||||
|
gen.parse().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_extract(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
|
let name = &ast.ident;
|
||||||
|
TokenStream::from(quote::quote! {
|
||||||
|
impl Extract for #name {
|
||||||
|
fn extract(ctx: ) -> Self {
|
||||||
|
println!("Hello, World! My name is {}", stringify!(#name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
20
src/main.rs
20
src/main.rs
@ -37,7 +37,7 @@ use crate::{
|
|||||||
commands::{
|
commands::{
|
||||||
allowed_dm, clock::clock, clock_context_menu::clock_context_menu, command_macro,
|
allowed_dm, clock::clock, clock_context_menu::clock_context_menu, command_macro,
|
||||||
dashboard::dashboard, delete, donate::donate, help::help, info::info, look, multiline,
|
dashboard::dashboard, delete, donate::donate, help::help, info::info, look, multiline,
|
||||||
nudge, offset, pause, remind, settings, timer, timezone::timezone, todo, webhook::webhook,
|
nudge, offset, pause, remind, settings, timer, timezone, todo, webhook,
|
||||||
},
|
},
|
||||||
consts::THEME_COLOR,
|
consts::THEME_COLOR,
|
||||||
event_handlers::listener,
|
event_handlers::listener,
|
||||||
@ -109,7 +109,7 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
|||||||
clock(),
|
clock(),
|
||||||
clock_context_menu(),
|
clock_context_menu(),
|
||||||
dashboard(),
|
dashboard(),
|
||||||
timezone(),
|
timezone::command(),
|
||||||
poise::Command {
|
poise::Command {
|
||||||
subcommands: vec![
|
subcommands: vec![
|
||||||
allowed_dm::set_allowed_dm::set_allowed_dm(),
|
allowed_dm::set_allowed_dm::set_allowed_dm(),
|
||||||
@ -127,7 +127,7 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
|||||||
}],
|
}],
|
||||||
..settings::settings()
|
..settings::settings()
|
||||||
},
|
},
|
||||||
webhook(),
|
webhook::command(),
|
||||||
poise::Command {
|
poise::Command {
|
||||||
subcommands: vec![
|
subcommands: vec![
|
||||||
command_macro::delete_macro::delete_macro(),
|
command_macro::delete_macro::delete_macro(),
|
||||||
@ -138,9 +138,9 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
|||||||
],
|
],
|
||||||
..command_macro::command_macro()
|
..command_macro::command_macro()
|
||||||
},
|
},
|
||||||
pause::pause(),
|
pause::command(),
|
||||||
offset::offset(),
|
offset::command(),
|
||||||
nudge::nudge(),
|
nudge::command(),
|
||||||
look::look(),
|
look::look(),
|
||||||
delete::delete(),
|
delete::delete(),
|
||||||
poise::Command {
|
poise::Command {
|
||||||
@ -152,7 +152,7 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
|||||||
..timer::timer()
|
..timer::timer()
|
||||||
},
|
},
|
||||||
multiline::multiline(),
|
multiline::multiline(),
|
||||||
remind::remind(),
|
remind::command(),
|
||||||
poise::Command {
|
poise::Command {
|
||||||
subcommands: vec![
|
subcommands: vec![
|
||||||
poise::Command {
|
poise::Command {
|
||||||
@ -197,12 +197,14 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
|||||||
sqlx::migrate!().run(&database).await?;
|
sqlx::migrate!().run(&database).await?;
|
||||||
|
|
||||||
let popular_timezones = sqlx::query!(
|
let popular_timezones = sqlx::query!(
|
||||||
"SELECT IFNULL(timezone, 'UTC') AS timezone
|
"
|
||||||
|
SELECT IFNULL(timezone, 'UTC') AS timezone
|
||||||
FROM users
|
FROM users
|
||||||
WHERE timezone IS NOT NULL
|
WHERE timezone IS NOT NULL
|
||||||
GROUP BY timezone
|
GROUP BY timezone
|
||||||
ORDER BY COUNT(timezone) DESC
|
ORDER BY COUNT(timezone) DESC
|
||||||
LIMIT 21"
|
LIMIT 21
|
||||||
|
"
|
||||||
)
|
)
|
||||||
.fetch_all(&database)
|
.fetch_all(&database)
|
||||||
.await
|
.await
|
||||||
|
@ -1,84 +1,27 @@
|
|||||||
use chrono_tz::Tz;
|
use poise::serenity_prelude::model::id::GuildId;
|
||||||
use poise::serenity_prelude::{model::id::GuildId, ResolvedValue};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{commands::remind, utils::Extract, ApplicationContext, Context, Error};
|
||||||
commands::remind::RemindOptions, models::reminder::create_reminder, ApplicationContext,
|
|
||||||
Context, Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(tag = "command_name")]
|
#[serde(tag = "command_name")]
|
||||||
pub enum RecordedCommand {
|
pub enum RecordedCommand {
|
||||||
Remind(RemindOptions),
|
Remind(remind::Options),
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! extract_arg {
|
|
||||||
($ctx:ident, $name:literal, String) => {
|
|
||||||
$ctx.args.iter().find(|opt| opt.name == $name).map(|opt| &opt.value).map_or_else(
|
|
||||||
|| String::new(),
|
|
||||||
|v| match v {
|
|
||||||
ResolvedValue::String(s) => s.to_string(),
|
|
||||||
_ => String::new(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
};
|
|
||||||
($ctx:ident, $name:literal, Option<String>) => {
|
|
||||||
$ctx.args.iter().find(|opt| opt.name == $name).map(|opt| &opt.value).map(|v| match v {
|
|
||||||
ResolvedValue::String(s) => s.to_string(),
|
|
||||||
_ => String::new(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
($ctx:ident, $name:literal, bool) => {
|
|
||||||
$ctx.args.iter().find(|opt| opt.name == $name).map(|opt| &opt.value).map(|v| match v {
|
|
||||||
ResolvedValue::Boolean(b) => b.to_owned(),
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
($ctx:ident, $name:literal, Option<Tz>) => {
|
|
||||||
$ctx.args
|
|
||||||
.iter()
|
|
||||||
.find(|opt| opt.name == $name)
|
|
||||||
.map(|opt| &opt.value)
|
|
||||||
.map(|v| match v {
|
|
||||||
ResolvedValue::String(s) => s.parse::<Tz>().ok(),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecordedCommand {
|
impl RecordedCommand {
|
||||||
pub fn from_context(ctx: ApplicationContext) -> Option<Self> {
|
pub fn from_context(ctx: ApplicationContext) -> Option<Self> {
|
||||||
match ctx.command().identifying_name.as_str() {
|
match ctx.command().identifying_name.as_str() {
|
||||||
"remind" => Some(Self::Remind(RemindOptions {
|
"remind" => Some(Self::Remind(remind::Options::extract(ctx))),
|
||||||
time: extract_arg!(ctx, "time", String),
|
|
||||||
content: extract_arg!(ctx, "content", String),
|
|
||||||
channels: extract_arg!(ctx, "channels", Option<String>),
|
|
||||||
interval: extract_arg!(ctx, "interval", Option<String>),
|
|
||||||
expires: extract_arg!(ctx, "expires", Option<String>),
|
|
||||||
tts: extract_arg!(ctx, "tts", bool),
|
|
||||||
timezone: extract_arg!(ctx, "timezone", Option<Tz>),
|
|
||||||
})),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute(self, ctx: ApplicationContext<'_>) -> Result<(), Error> {
|
pub async fn execute(self, ctx: ApplicationContext<'_>) -> Result<(), Error> {
|
||||||
match self {
|
match self {
|
||||||
RecordedCommand::Remind(command_options) => {
|
RecordedCommand::Remind(options) => {
|
||||||
create_reminder(
|
remind::remind(Context::Application(ctx), options).await
|
||||||
Context::Application(ctx),
|
|
||||||
command_options.time,
|
|
||||||
command_options.content,
|
|
||||||
command_options.channels,
|
|
||||||
command_options.interval,
|
|
||||||
command_options.expires,
|
|
||||||
command_options.tts,
|
|
||||||
command_options.timezone,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,9 @@ pub struct ReminderBuilder {
|
|||||||
impl ReminderBuilder {
|
impl ReminderBuilder {
|
||||||
pub async fn build(self) -> Result<Reminder, ReminderError> {
|
pub async fn build(self) -> Result<Reminder, ReminderError> {
|
||||||
let queried_time = sqlx::query!(
|
let queried_time = sqlx::query!(
|
||||||
"SELECT DATE_ADD(?, INTERVAL (SELECT nudge FROM channels WHERE id = ?) SECOND) AS `utc_time`",
|
"
|
||||||
|
SELECT DATE_ADD(?, INTERVAL (SELECT nudge FROM channels WHERE id = ?) SECOND) AS `utc_time`
|
||||||
|
",
|
||||||
self.utc_time,
|
self.utc_time,
|
||||||
self.channel,
|
self.channel,
|
||||||
)
|
)
|
||||||
@ -84,36 +86,24 @@ impl ReminderBuilder {
|
|||||||
} else {
|
} else {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
INSERT INTO reminders (
|
INSERT INTO reminders (
|
||||||
`uid`,
|
`uid`,
|
||||||
`channel_id`,
|
`channel_id`,
|
||||||
`utc_time`,
|
`utc_time`,
|
||||||
`timezone`,
|
`timezone`,
|
||||||
`interval_seconds`,
|
`interval_seconds`,
|
||||||
`interval_days`,
|
`interval_days`,
|
||||||
`interval_months`,
|
`interval_months`,
|
||||||
`expires`,
|
`expires`,
|
||||||
`content`,
|
`content`,
|
||||||
`tts`,
|
`tts`,
|
||||||
`attachment_name`,
|
`attachment_name`,
|
||||||
`attachment`,
|
`attachment`,
|
||||||
`set_by`
|
`set_by`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
?,
|
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||||
?,
|
)
|
||||||
?,
|
",
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?
|
|
||||||
)
|
|
||||||
",
|
|
||||||
self.uid,
|
self.uid,
|
||||||
self.channel,
|
self.channel,
|
||||||
utc_time,
|
utc_time,
|
||||||
|
@ -9,7 +9,7 @@ use poise::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{CNC_GUILD, SUBSCRIPTION_ROLES},
|
consts::{CNC_GUILD, SUBSCRIPTION_ROLES},
|
||||||
Context,
|
ApplicationContext, Context,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn check_subscription(cache_http: impl CacheHttp, user_id: impl Into<UserId>) -> bool {
|
pub async fn check_subscription(cache_http: impl CacheHttp, user_id: impl Into<UserId>) -> bool {
|
||||||
@ -64,3 +64,7 @@ pub fn footer(ctx: Context<'_>) -> CreateEmbedFooter {
|
|||||||
shard_count,
|
shard_count,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Extract {
|
||||||
|
fn extract(ctx: ApplicationContext) -> Self;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user