Extract trait
This commit is contained in:
		
							
								
								
									
										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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user