component models
This commit is contained in:
		@@ -1,23 +1,23 @@
 | 
				
			|||||||
mod sender;
 | 
					mod sender;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::env;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use log::info;
 | 
					use log::info;
 | 
				
			||||||
use serenity::client::Context;
 | 
					use serenity::client::Context;
 | 
				
			||||||
use sqlx::{Executor, MySql};
 | 
					use sqlx::{Executor, MySql};
 | 
				
			||||||
use std::env;
 | 
					use tokio::time::{sleep_until, Duration, Instant};
 | 
				
			||||||
use tokio::time::sleep_until;
 | 
					 | 
				
			||||||
use tokio::time::{Duration, Instant};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Database = MySql;
 | 
					type Database = MySql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub async fn initialize(ctx: Context, pool: impl Executor<'_, Database = Database> + Copy) {
 | 
					pub async fn initialize(ctx: Context, pool: impl Executor<'_, Database = Database> + Copy) {
 | 
				
			||||||
    let REMIND_INTERVAL = env::var("REMIND_INTERVAL")
 | 
					    let remind_interval = env::var("REMIND_INTERVAL")
 | 
				
			||||||
        .map(|inner| inner.parse::<u64>().ok())
 | 
					        .map(|inner| inner.parse::<u64>().ok())
 | 
				
			||||||
        .ok()
 | 
					        .ok()
 | 
				
			||||||
        .flatten()
 | 
					        .flatten()
 | 
				
			||||||
        .unwrap_or(10);
 | 
					        .unwrap_or(10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        let sleep_to = Instant::now() + Duration::from_secs(REMIND_INTERVAL);
 | 
					        let sleep_to = Instant::now() + Duration::from_secs(remind_interval);
 | 
				
			||||||
        let reminders = sender::Reminder::fetch_reminders(pool).await;
 | 
					        let reminders = sender::Reminder::fetch_reminders(pool).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if reminders.len() > 0 {
 | 
					        if reminders.len() > 0 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,3 @@
 | 
				
			|||||||
use crate::Database;
 | 
					 | 
				
			||||||
use chrono::Duration;
 | 
					use chrono::Duration;
 | 
				
			||||||
use chrono_tz::Tz;
 | 
					use chrono_tz::Tz;
 | 
				
			||||||
use lazy_static::lazy_static;
 | 
					use lazy_static::lazy_static;
 | 
				
			||||||
@@ -20,6 +19,8 @@ use sqlx::{
 | 
				
			|||||||
    Executor,
 | 
					    Executor,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
lazy_static! {
 | 
					lazy_static! {
 | 
				
			||||||
    pub static ref TIMEFROM_REGEX: Regex =
 | 
					    pub static ref TIMEFROM_REGEX: Regex =
 | 
				
			||||||
        Regex::new(r#"<<timefrom:(?P<time>\d+):(?P<format>.+)?>>"#).unwrap();
 | 
					        Regex::new(r#"<<timefrom:(?P<time>\d+):(?P<format>.+)?>>"#).unwrap();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,11 @@
 | 
				
			|||||||
use chrono::offset::Utc;
 | 
					use chrono::offset::Utc;
 | 
				
			||||||
use poise::serenity::builder::CreateEmbedFooter;
 | 
					use poise::{serenity_prelude as serenity, serenity_prelude::Mentionable};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{models::CtxData, Context, Error, THEME_COLOR};
 | 
					use crate::{models::CtxData, Context, Error, THEME_COLOR};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn footer(ctx: Context<'_>) -> impl FnOnce(&mut CreateEmbedFooter) -> &mut CreateEmbedFooter {
 | 
					fn footer(
 | 
				
			||||||
 | 
					    ctx: Context<'_>,
 | 
				
			||||||
 | 
					) -> impl FnOnce(&mut serenity::CreateEmbedFooter) -> &mut serenity::CreateEmbedFooter {
 | 
				
			||||||
    let shard_count = ctx.discord().cache.shard_count();
 | 
					    let shard_count = ctx.discord().cache.shard_count();
 | 
				
			||||||
    let shard = ctx.discord().shard_id;
 | 
					    let shard = ctx.discord().shard_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -22,13 +24,12 @@ fn footer(ctx: Context<'_>) -> impl FnOnce(&mut CreateEmbedFooter) -> &mut Creat
 | 
				
			|||||||
pub async fn help(ctx: Context<'_>) -> Result<(), Error> {
 | 
					pub async fn help(ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			||||||
    let footer = footer(ctx);
 | 
					    let footer = footer(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let _ = ctx
 | 
					    ctx.send(|m| {
 | 
				
			||||||
        .send(|m| {
 | 
					        m.ephemeral(true).embed(|e| {
 | 
				
			||||||
            m.embed(|e| {
 | 
					            e.title("Help")
 | 
				
			||||||
                e.title("Help")
 | 
					                .color(*THEME_COLOR)
 | 
				
			||||||
                    .color(*THEME_COLOR)
 | 
					                .description(
 | 
				
			||||||
                    .description(
 | 
					                    "__Info Commands__
 | 
				
			||||||
                        "__Info Commands__
 | 
					 | 
				
			||||||
`/help` `/info` `/donate` `/dashboard` `/clock`
 | 
					`/help` `/info` `/donate` `/dashboard` `/clock`
 | 
				
			||||||
*run these commands with no options*
 | 
					*run these commands with no options*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,11 +53,11 @@ __Setup Commands__
 | 
				
			|||||||
__Advanced Commands__
 | 
					__Advanced Commands__
 | 
				
			||||||
`/macro` - Record and replay command sequences
 | 
					`/macro` - Record and replay command sequences
 | 
				
			||||||
                    ",
 | 
					                    ",
 | 
				
			||||||
                    )
 | 
					                )
 | 
				
			||||||
                    .footer(footer)
 | 
					                .footer(footer)
 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .await;
 | 
					    })
 | 
				
			||||||
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -68,7 +69,7 @@ pub async fn info(ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let _ = ctx
 | 
					    let _ = ctx
 | 
				
			||||||
        .send(|m| {
 | 
					        .send(|m| {
 | 
				
			||||||
            m.embed(|e| {
 | 
					            m.ephemeral(true).embed(|e| {
 | 
				
			||||||
                e.title("Info")
 | 
					                e.title("Info")
 | 
				
			||||||
                    .description(format!(
 | 
					                    .description(format!(
 | 
				
			||||||
                        "Help: `/help`
 | 
					                        "Help: `/help`
 | 
				
			||||||
@@ -95,9 +96,10 @@ Use our dashboard: https://reminder-bot.com/",
 | 
				
			|||||||
pub async fn donate(ctx: Context<'_>) -> Result<(), Error> {
 | 
					pub async fn donate(ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			||||||
    let footer = footer(ctx);
 | 
					    let footer = footer(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let _ = ctx.send(|m| m.embed(|e| {
 | 
					    ctx.send(|m| m.embed(|e| {
 | 
				
			||||||
                e.title("Donate")
 | 
					        e.title("Donate")
 | 
				
			||||||
                    .description("Thinking of adding a monthly contribution? Click below for my Patreon and official bot server :)
 | 
					            .description("Thinking of adding a monthly contribution?
 | 
				
			||||||
 | 
					Click below for my Patreon and official bot server :)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**https://www.patreon.com/jellywx/**
 | 
					**https://www.patreon.com/jellywx/**
 | 
				
			||||||
**https://discord.jellywx.com/**
 | 
					**https://discord.jellywx.com/**
 | 
				
			||||||
@@ -112,11 +114,11 @@ With your new rank, you'll be able to:
 | 
				
			|||||||
Just $2 USD/month!
 | 
					Just $2 USD/month!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*Please note, you must be in the JellyWX Discord server to receive Patreon features*")
 | 
					*Please note, you must be in the JellyWX Discord server to receive Patreon features*")
 | 
				
			||||||
                    .footer(footer)
 | 
					                .footer(footer)
 | 
				
			||||||
                    .color(*THEME_COLOR)
 | 
					                .color(*THEME_COLOR)
 | 
				
			||||||
            }),
 | 
					        }),
 | 
				
			||||||
        )
 | 
					    )
 | 
				
			||||||
        .await;
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -126,21 +128,20 @@ Just $2 USD/month!
 | 
				
			|||||||
pub async fn dashboard(ctx: Context<'_>) -> Result<(), Error> {
 | 
					pub async fn dashboard(ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			||||||
    let footer = footer(ctx);
 | 
					    let footer = footer(ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let _ = ctx
 | 
					    ctx.send(|m| {
 | 
				
			||||||
        .send(|m| {
 | 
					        m.ephemeral(true).embed(|e| {
 | 
				
			||||||
            m.embed(|e| {
 | 
					            e.title("Dashboard")
 | 
				
			||||||
                e.title("Dashboard")
 | 
					                .description("**https://reminder-bot.com/dashboard**")
 | 
				
			||||||
                    .description("**https://reminder-bot.com/dashboard**")
 | 
					                .footer(footer)
 | 
				
			||||||
                    .footer(footer)
 | 
					                .color(*THEME_COLOR)
 | 
				
			||||||
                    .color(*THEME_COLOR)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .await;
 | 
					    })
 | 
				
			||||||
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// View the current time in a user's selected timezone
 | 
					/// View the current time in your selected timezone
 | 
				
			||||||
#[poise::command(slash_command)]
 | 
					#[poise::command(slash_command)]
 | 
				
			||||||
pub async fn clock(ctx: Context<'_>) -> Result<(), Error> {
 | 
					pub async fn clock(ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			||||||
    ctx.defer_ephemeral().await?;
 | 
					    ctx.defer_ephemeral().await?;
 | 
				
			||||||
@@ -155,3 +156,25 @@ pub async fn clock(ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// View the current time in a user's selected timezone
 | 
				
			||||||
 | 
					#[poise::command(context_menu_command = "View Local Time")]
 | 
				
			||||||
 | 
					pub async fn clock_context_menu(ctx: Context<'_>, user: serenity::User) -> Result<(), Error> {
 | 
				
			||||||
 | 
					    ctx.defer_ephemeral().await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let user_data = ctx.user_data(user.id).await?;
 | 
				
			||||||
 | 
					    let tz = user_data.timezone();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let now = Utc::now().with_timezone(&tz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ctx.send(|m| {
 | 
				
			||||||
 | 
					        m.ephemeral(true).content(format!(
 | 
				
			||||||
 | 
					            "Time in {}'s timezone: `{}`",
 | 
				
			||||||
 | 
					            user.mention(),
 | 
				
			||||||
 | 
					            now.format("%H:%M")
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .await?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ok(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ use chrono_tz::Tz;
 | 
				
			|||||||
use num_integer::Integer;
 | 
					use num_integer::Integer;
 | 
				
			||||||
use poise::{
 | 
					use poise::{
 | 
				
			||||||
    serenity::{builder::CreateEmbed, model::channel::Channel},
 | 
					    serenity::{builder::CreateEmbed, model::channel::Channel},
 | 
				
			||||||
    serenity_prelude::ActionRole::Create,
 | 
					 | 
				
			||||||
    CreateReply,
 | 
					    CreateReply,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -32,7 +31,6 @@ use crate::{
 | 
				
			|||||||
            Reminder,
 | 
					            Reminder,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        timer::Timer,
 | 
					        timer::Timer,
 | 
				
			||||||
        user_data::UserData,
 | 
					 | 
				
			||||||
        CtxData,
 | 
					        CtxData,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    time_parser::natural_parser,
 | 
					    time_parser::natural_parser,
 | 
				
			||||||
@@ -212,7 +210,7 @@ pub async fn look(
 | 
				
			|||||||
            None
 | 
					            None
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let reminders = Reminder::from_channel(&ctx, channel_id, &flags).await;
 | 
					    let reminders = Reminder::from_channel(&ctx.data().database, channel_id, &flags).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if reminders.is_empty() {
 | 
					    if reminders.is_empty() {
 | 
				
			||||||
        let _ = ctx.say("No reminders on specified channel").await;
 | 
					        let _ = ctx.say("No reminders on specified channel").await;
 | 
				
			||||||
@@ -266,7 +264,9 @@ pub async fn look(
 | 
				
			|||||||
pub async fn delete(ctx: Context<'_>) -> Result<(), Error> {
 | 
					pub async fn delete(ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			||||||
    let timezone = ctx.timezone().await;
 | 
					    let timezone = ctx.timezone().await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let reminders = Reminder::from_guild(&ctx, ctx.guild_id(), ctx.author().id).await;
 | 
					    let reminders =
 | 
				
			||||||
 | 
					        Reminder::from_guild(&ctx.discord(), &ctx.data().database, ctx.guild_id(), ctx.author().id)
 | 
				
			||||||
 | 
					            .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let resp = show_delete_page(&reminders, 0, timezone);
 | 
					    let resp = show_delete_page(&reminders, 0, timezone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ use crate::{
 | 
				
			|||||||
        ComponentDataModel, TodoSelector,
 | 
					        ComponentDataModel, TodoSelector,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    consts::{EMBED_DESCRIPTION_MAX_LENGTH, SELECT_MAX_ENTRIES, THEME_COLOR},
 | 
					    consts::{EMBED_DESCRIPTION_MAX_LENGTH, SELECT_MAX_ENTRIES, THEME_COLOR},
 | 
				
			||||||
 | 
					    hooks::guild_only,
 | 
				
			||||||
    Context, Error,
 | 
					    Context, Error,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -16,7 +17,7 @@ pub async fn todo_base(_ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Manage the server todo list
 | 
					/// Manage the server todo list
 | 
				
			||||||
#[poise::command(slash_command, rename = "server")]
 | 
					#[poise::command(slash_command, rename = "server", check = "guild_only")]
 | 
				
			||||||
pub async fn todo_guild_base(_ctx: Context<'_>) -> Result<(), Error> {
 | 
					pub async fn todo_guild_base(_ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -70,7 +71,7 @@ WHERE guilds.guild = ?",
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Manage the channel todo list
 | 
					/// Manage the channel todo list
 | 
				
			||||||
#[poise::command(slash_command, rename = "channel")]
 | 
					#[poise::command(slash_command, rename = "channel", check = "guild_only")]
 | 
				
			||||||
pub async fn todo_channel_base(_ctx: Context<'_>) -> Result<(), Error> {
 | 
					pub async fn todo_channel_base(_ctx: Context<'_>) -> Result<(), Error> {
 | 
				
			||||||
    Ok(())
 | 
					    Ok(())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ use std::io::Cursor;
 | 
				
			|||||||
use chrono_tz::Tz;
 | 
					use chrono_tz::Tz;
 | 
				
			||||||
use poise::serenity::{
 | 
					use poise::serenity::{
 | 
				
			||||||
    builder::CreateEmbed,
 | 
					    builder::CreateEmbed,
 | 
				
			||||||
 | 
					    client::Context,
 | 
				
			||||||
    model::{
 | 
					    model::{
 | 
				
			||||||
        channel::Channel,
 | 
					        channel::Channel,
 | 
				
			||||||
        interactions::{message_component::MessageComponentInteraction, InteractionResponseType},
 | 
					        interactions::{message_component::MessageComponentInteraction, InteractionResponseType},
 | 
				
			||||||
@@ -22,8 +23,9 @@ use crate::{
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    component_models::pager::{DelPager, LookPager, MacroPager, Pager, TodoPager},
 | 
					    component_models::pager::{DelPager, LookPager, MacroPager, Pager, TodoPager},
 | 
				
			||||||
    consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
 | 
					    consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
 | 
				
			||||||
    models::{reminder::Reminder, CtxData},
 | 
					    models::reminder::Reminder,
 | 
				
			||||||
    Context, Data,
 | 
					    utils::send_as_initial_response,
 | 
				
			||||||
 | 
					    Data,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Deserialize, Serialize)]
 | 
					#[derive(Deserialize, Serialize)]
 | 
				
			||||||
@@ -53,12 +55,12 @@ impl ComponentDataModel {
 | 
				
			|||||||
        rmp_serde::from_read(cur).unwrap()
 | 
					        rmp_serde::from_read(cur).unwrap()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn act(&self, ctx: Context<'_>, component: &MessageComponentInteraction) {
 | 
					    pub async fn act(&self, ctx: &Context, data: &Data, component: &MessageComponentInteraction) {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            ComponentDataModel::LookPager(pager) => {
 | 
					            ComponentDataModel::LookPager(pager) => {
 | 
				
			||||||
                let flags = pager.flags;
 | 
					                let flags = pager.flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let channel_opt = component.channel_id.to_channel_cached(&ctx.discord());
 | 
					                let channel_opt = component.channel_id.to_channel_cached(&ctx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let channel_id = if let Some(Channel::Guild(channel)) = channel_opt {
 | 
					                let channel_id = if let Some(Channel::Guild(channel)) = channel_opt {
 | 
				
			||||||
                    if Some(channel.guild_id) == component.guild_id {
 | 
					                    if Some(channel.guild_id) == component.guild_id {
 | 
				
			||||||
@@ -70,7 +72,7 @@ impl ComponentDataModel {
 | 
				
			|||||||
                    component.channel_id
 | 
					                    component.channel_id
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let reminders = Reminder::from_channel(&ctx, channel_id, &flags).await;
 | 
					                let reminders = Reminder::from_channel(&data.database, channel_id, &flags).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let pages = reminders
 | 
					                let pages = reminders
 | 
				
			||||||
                    .iter()
 | 
					                    .iter()
 | 
				
			||||||
@@ -78,13 +80,12 @@ impl ComponentDataModel {
 | 
				
			|||||||
                    .fold(0, |t, r| t + r.len())
 | 
					                    .fold(0, |t, r| t + r.len())
 | 
				
			||||||
                    .div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
 | 
					                    .div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let channel_name = if let Some(Channel::Guild(channel)) =
 | 
					                let channel_name =
 | 
				
			||||||
                    channel_id.to_channel_cached(&ctx.discord())
 | 
					                    if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {
 | 
				
			||||||
                {
 | 
					                        Some(channel.name)
 | 
				
			||||||
                    Some(channel.name)
 | 
					                    } else {
 | 
				
			||||||
                } else {
 | 
					                        None
 | 
				
			||||||
                    None
 | 
					                    };
 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let next_page = pager.next_page(pages);
 | 
					                let next_page = pager.next_page(pages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -118,7 +119,7 @@ impl ComponentDataModel {
 | 
				
			|||||||
                    .color(*THEME_COLOR);
 | 
					                    .color(*THEME_COLOR);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let _ = component
 | 
					                let _ = component
 | 
				
			||||||
                    .create_interaction_response(&ctx.discord(), |r| {
 | 
					                    .create_interaction_response(&ctx, |r| {
 | 
				
			||||||
                        r.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
 | 
					                        r.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
 | 
				
			||||||
                            |response| {
 | 
					                            |response| {
 | 
				
			||||||
                                response.embeds(vec![embed]).components(|comp| {
 | 
					                                response.embeds(vec![embed]).components(|comp| {
 | 
				
			||||||
@@ -132,17 +133,26 @@ impl ComponentDataModel {
 | 
				
			|||||||
                    .await;
 | 
					                    .await;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ComponentDataModel::DelPager(pager) => {
 | 
					            ComponentDataModel::DelPager(pager) => {
 | 
				
			||||||
                let reminders =
 | 
					                let reminders = Reminder::from_guild(
 | 
				
			||||||
                    Reminder::from_guild(&ctx, component.guild_id, component.user.id).await;
 | 
					                    &ctx,
 | 
				
			||||||
 | 
					                    &data.database,
 | 
				
			||||||
 | 
					                    component.guild_id,
 | 
				
			||||||
 | 
					                    component.user.id,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let max_pages = max_delete_page(&reminders, &pager.timezone);
 | 
					                let max_pages = max_delete_page(&reminders, &pager.timezone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let resp = show_delete_page(&reminders, pager.next_page(max_pages), pager.timezone);
 | 
					                let resp = show_delete_page(&reminders, pager.next_page(max_pages), pager.timezone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let _ = ctx
 | 
					                let _ = component
 | 
				
			||||||
                    .send(|r| {
 | 
					                    .create_interaction_response(&ctx, |f| {
 | 
				
			||||||
                        *r = resp;
 | 
					                        f.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
 | 
				
			||||||
                        r
 | 
					                            |d| {
 | 
				
			||||||
 | 
					                                send_as_initial_response(resp, d);
 | 
				
			||||||
 | 
					                                d
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                    .await;
 | 
					                    .await;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -150,19 +160,28 @@ impl ComponentDataModel {
 | 
				
			|||||||
                let selected_id = component.data.values.join(",");
 | 
					                let selected_id = component.data.values.join(",");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                sqlx::query!("DELETE FROM reminders WHERE FIND_IN_SET(id, ?)", selected_id)
 | 
					                sqlx::query!("DELETE FROM reminders WHERE FIND_IN_SET(id, ?)", selected_id)
 | 
				
			||||||
                    .execute(&ctx.data().database)
 | 
					                    .execute(&data.database)
 | 
				
			||||||
                    .await
 | 
					                    .await
 | 
				
			||||||
                    .unwrap();
 | 
					                    .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let reminders =
 | 
					                let reminders = Reminder::from_guild(
 | 
				
			||||||
                    Reminder::from_guild(&ctx, component.guild_id, component.user.id).await;
 | 
					                    &ctx,
 | 
				
			||||||
 | 
					                    &data.database,
 | 
				
			||||||
 | 
					                    component.guild_id,
 | 
				
			||||||
 | 
					                    component.user.id,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                .await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let resp = show_delete_page(&reminders, selector.page, selector.timezone);
 | 
					                let resp = show_delete_page(&reminders, selector.page, selector.timezone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let _ = ctx
 | 
					                let _ = component
 | 
				
			||||||
                    .send(|r| {
 | 
					                    .create_interaction_response(&ctx, |f| {
 | 
				
			||||||
                        *r = resp;
 | 
					                        f.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
 | 
				
			||||||
                        r
 | 
					                            |d| {
 | 
				
			||||||
 | 
					                                send_as_initial_response(resp, d);
 | 
				
			||||||
 | 
					                                d
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                    .await;
 | 
					                    .await;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -175,7 +194,7 @@ INNER JOIN users ON todos.user_id = users.id
 | 
				
			|||||||
WHERE users.user = ?",
 | 
					WHERE users.user = ?",
 | 
				
			||||||
                            uid,
 | 
					                            uid,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                        .fetch_all(&ctx.data().database)
 | 
					                        .fetch_all(&data.database)
 | 
				
			||||||
                        .await
 | 
					                        .await
 | 
				
			||||||
                        .unwrap()
 | 
					                        .unwrap()
 | 
				
			||||||
                        .iter()
 | 
					                        .iter()
 | 
				
			||||||
@@ -188,7 +207,7 @@ INNER JOIN channels ON todos.channel_id = channels.id
 | 
				
			|||||||
WHERE channels.channel = ?",
 | 
					WHERE channels.channel = ?",
 | 
				
			||||||
                            cid,
 | 
					                            cid,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                        .fetch_all(&ctx.data().database)
 | 
					                        .fetch_all(&data.database)
 | 
				
			||||||
                        .await
 | 
					                        .await
 | 
				
			||||||
                        .unwrap()
 | 
					                        .unwrap()
 | 
				
			||||||
                        .iter()
 | 
					                        .iter()
 | 
				
			||||||
@@ -201,7 +220,7 @@ INNER JOIN guilds ON todos.guild_id = guilds.id
 | 
				
			|||||||
WHERE guilds.guild = ?",
 | 
					WHERE guilds.guild = ?",
 | 
				
			||||||
                            pager.guild_id,
 | 
					                            pager.guild_id,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                        .fetch_all(&ctx.data().database)
 | 
					                        .fetch_all(&data.database)
 | 
				
			||||||
                        .await
 | 
					                        .await
 | 
				
			||||||
                        .unwrap()
 | 
					                        .unwrap()
 | 
				
			||||||
                        .iter()
 | 
					                        .iter()
 | 
				
			||||||
@@ -219,15 +238,18 @@ WHERE guilds.guild = ?",
 | 
				
			|||||||
                        pager.guild_id,
 | 
					                        pager.guild_id,
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let _ = ctx
 | 
					                    let _ = component
 | 
				
			||||||
                        .send(|r| {
 | 
					                        .create_interaction_response(&ctx, |f| {
 | 
				
			||||||
                            *r = resp;
 | 
					                            f.kind(InteractionResponseType::UpdateMessage)
 | 
				
			||||||
                            r
 | 
					                                .interaction_response_data(|d| {
 | 
				
			||||||
 | 
					                                    send_as_initial_response(resp, d);
 | 
				
			||||||
 | 
					                                    d
 | 
				
			||||||
 | 
					                                })
 | 
				
			||||||
                        })
 | 
					                        })
 | 
				
			||||||
                        .await;
 | 
					                        .await;
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    let _ = component
 | 
					                    let _ = component
 | 
				
			||||||
                        .create_interaction_response(&ctx.discord(), |r| {
 | 
					                        .create_interaction_response(&ctx, |r| {
 | 
				
			||||||
                            r.kind(InteractionResponseType::ChannelMessageWithSource)
 | 
					                            r.kind(InteractionResponseType::ChannelMessageWithSource)
 | 
				
			||||||
                                .interaction_response_data(|d| {
 | 
					                                .interaction_response_data(|d| {
 | 
				
			||||||
                                    d.flags(
 | 
					                                    d.flags(
 | 
				
			||||||
@@ -244,7 +266,7 @@ WHERE guilds.guild = ?",
 | 
				
			|||||||
                    let selected_id = component.data.values.join(",");
 | 
					                    let selected_id = component.data.values.join(",");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    sqlx::query!("DELETE FROM todos WHERE FIND_IN_SET(id, ?)", selected_id)
 | 
					                    sqlx::query!("DELETE FROM todos WHERE FIND_IN_SET(id, ?)", selected_id)
 | 
				
			||||||
                        .execute(&ctx.data().database)
 | 
					                        .execute(&data.database)
 | 
				
			||||||
                        .await
 | 
					                        .await
 | 
				
			||||||
                        .unwrap();
 | 
					                        .unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -255,7 +277,7 @@ WHERE guilds.guild = ?",
 | 
				
			|||||||
                    selector.channel_id,
 | 
					                    selector.channel_id,
 | 
				
			||||||
                    selector.guild_id,
 | 
					                    selector.guild_id,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                .fetch_all(&ctx.data().database)
 | 
					                .fetch_all(&data.database)
 | 
				
			||||||
                .await
 | 
					                .await
 | 
				
			||||||
                .unwrap()
 | 
					                .unwrap()
 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
@@ -270,15 +292,18 @@ WHERE guilds.guild = ?",
 | 
				
			|||||||
                        selector.guild_id,
 | 
					                        selector.guild_id,
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let _ = ctx
 | 
					                    let _ = component
 | 
				
			||||||
                        .send(|r| {
 | 
					                        .create_interaction_response(&ctx, |f| {
 | 
				
			||||||
                            *r = resp;
 | 
					                            f.kind(InteractionResponseType::UpdateMessage)
 | 
				
			||||||
                            r
 | 
					                                .interaction_response_data(|d| {
 | 
				
			||||||
 | 
					                                    send_as_initial_response(resp, d);
 | 
				
			||||||
 | 
					                                    d
 | 
				
			||||||
 | 
					                                })
 | 
				
			||||||
                        })
 | 
					                        })
 | 
				
			||||||
                        .await;
 | 
					                        .await;
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    let _ = component
 | 
					                    let _ = component
 | 
				
			||||||
                        .create_interaction_response(&ctx.discord(), |r| {
 | 
					                        .create_interaction_response(&ctx, |r| {
 | 
				
			||||||
                            r.kind(InteractionResponseType::ChannelMessageWithSource)
 | 
					                            r.kind(InteractionResponseType::ChannelMessageWithSource)
 | 
				
			||||||
                                .interaction_response_data(|d| {
 | 
					                                .interaction_response_data(|d| {
 | 
				
			||||||
                                    d.flags(
 | 
					                                    d.flags(
 | 
				
			||||||
@@ -291,17 +316,21 @@ WHERE guilds.guild = ?",
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ComponentDataModel::MacroPager(pager) => {
 | 
					            ComponentDataModel::MacroPager(pager) => {
 | 
				
			||||||
                let macros = ctx.command_macros().await.unwrap();
 | 
					                let macros = data.command_macros(component.guild_id.unwrap()).await.unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let max_page = max_macro_page(¯os);
 | 
					                let max_page = max_macro_page(¯os);
 | 
				
			||||||
                let page = pager.next_page(max_page);
 | 
					                let page = pager.next_page(max_page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let resp = show_macro_page(¯os, page);
 | 
					                let resp = show_macro_page(¯os, page);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let _ = ctx
 | 
					                let _ = component
 | 
				
			||||||
                    .send(|r| {
 | 
					                    .create_interaction_response(&ctx, |f| {
 | 
				
			||||||
                        *r = resp;
 | 
					                        f.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
 | 
				
			||||||
                        r
 | 
					                            |d| {
 | 
				
			||||||
 | 
					                                send_as_initial_response(resp, d);
 | 
				
			||||||
 | 
					                                d
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                    .await;
 | 
					                    .await;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,21 +1,12 @@
 | 
				
			|||||||
use std::{
 | 
					use std::{collections::HashMap, env, sync::atomic::Ordering};
 | 
				
			||||||
    collections::HashMap,
 | 
					 | 
				
			||||||
    env,
 | 
					 | 
				
			||||||
    sync::atomic::{AtomicBool, Ordering},
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use log::{info, warn};
 | 
					use log::{info, warn};
 | 
				
			||||||
use poise::{
 | 
					use poise::{
 | 
				
			||||||
    serenity::{model::interactions::Interaction, utils::shard_id},
 | 
					    serenity::{model::interactions::Interaction, utils::shard_id},
 | 
				
			||||||
    serenity_prelude as serenity,
 | 
					    serenity_prelude as serenity,
 | 
				
			||||||
    serenity_prelude::{
 | 
					 | 
				
			||||||
        ApplicationCommandInteraction, ApplicationCommandInteractionData, ApplicationCommandType,
 | 
					 | 
				
			||||||
        InteractionType,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    ApplicationCommandOrAutocompleteInteraction, ApplicationContext, Command,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{component_models::ComponentDataModel, Context, Data, Error};
 | 
					use crate::{component_models::ComponentDataModel, Data, Error};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub async fn listener(
 | 
					pub async fn listener(
 | 
				
			||||||
    ctx: &serenity::Context,
 | 
					    ctx: &serenity::Context,
 | 
				
			||||||
@@ -56,15 +47,10 @@ pub async fn listener(
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        poise::Event::ChannelDelete { channel } => {
 | 
					        poise::Event::ChannelDelete { channel } => {
 | 
				
			||||||
            sqlx::query!(
 | 
					            sqlx::query!("DELETE FROM channels WHERE channel = ?", channel.id.as_u64())
 | 
				
			||||||
                "
 | 
					                .execute(&data.database)
 | 
				
			||||||
DELETE FROM channels WHERE channel = ?
 | 
					                .await
 | 
				
			||||||
                ",
 | 
					                .unwrap();
 | 
				
			||||||
                channel.id.as_u64()
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            .execute(&data.database)
 | 
					 | 
				
			||||||
            .await
 | 
					 | 
				
			||||||
            .unwrap();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        poise::Event::GuildCreate { guild, is_new } => {
 | 
					        poise::Event::GuildCreate { guild, is_new } => {
 | 
				
			||||||
            if *is_new {
 | 
					            if *is_new {
 | 
				
			||||||
@@ -122,7 +108,7 @@ DELETE FROM channels WHERE channel = ?
 | 
				
			|||||||
            Interaction::MessageComponent(component) => {
 | 
					            Interaction::MessageComponent(component) => {
 | 
				
			||||||
                let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id);
 | 
					                let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // component_model.act(ctx, component).await;
 | 
					                component_model.act(ctx, data, component).await;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            _ => {}
 | 
					            _ => {}
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										46
									
								
								src/hooks.rs
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								src/hooks.rs
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
				
			|||||||
use poise::{serenity::model::channel::Channel, ApplicationCommandOrAutocompleteInteraction};
 | 
					use poise::serenity::model::channel::Channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{consts::MACRO_MAX_COMMANDS, models::command_macro::RecordedCommand, Context, Error};
 | 
					use crate::{consts::MACRO_MAX_COMMANDS, models::command_macro::RecordedCommand, Context, Error};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,39 +14,33 @@ pub async fn guild_only(ctx: Context<'_>) -> Result<bool, 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 ApplicationCommandOrAutocompleteInteraction::ApplicationCommand(interaction) =
 | 
					        if let Some(guild_id) = ctx.guild_id() {
 | 
				
			||||||
            app_ctx.interaction
 | 
					            if ctx.command().identifying_name != "macro_finish" {
 | 
				
			||||||
        {
 | 
					                let mut lock = ctx.data().recording_macros.write().await;
 | 
				
			||||||
            if let Some(guild_id) = ctx.guild_id() {
 | 
					 | 
				
			||||||
                if ctx.command().identifying_name != "macro_finish" {
 | 
					 | 
				
			||||||
                    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 {
 | 
					 | 
				
			||||||
                            let recorded = RecordedCommand {
 | 
					 | 
				
			||||||
                                action: None,
 | 
					 | 
				
			||||||
                                command_name: ctx.command().identifying_name.clone(),
 | 
					 | 
				
			||||||
                                options: Vec::from(app_ctx.args),
 | 
					 | 
				
			||||||
                            };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            command_macro.commands.push(recorded);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                            let _ = ctx
 | 
					 | 
				
			||||||
                                .send(|m| m.ephemeral(true).content("Command recorded to macro"))
 | 
					 | 
				
			||||||
                                .await;
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        false
 | 
					 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        true
 | 
					                        let recorded = RecordedCommand {
 | 
				
			||||||
 | 
					                            action: None,
 | 
				
			||||||
 | 
					                            command_name: ctx.command().identifying_name.clone(),
 | 
				
			||||||
 | 
					                            options: Vec::from(app_ctx.args),
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        command_macro.commands.push(recorded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        let _ = ctx
 | 
				
			||||||
 | 
					                            .send(|m| m.ephemeral(true).content("Command recorded to macro"))
 | 
				
			||||||
 | 
					                            .await;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    false
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    true
 | 
					                    true
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
 | 
				
			|||||||
            info_cmds::info(),
 | 
					            info_cmds::info(),
 | 
				
			||||||
            info_cmds::donate(),
 | 
					            info_cmds::donate(),
 | 
				
			||||||
            info_cmds::clock(),
 | 
					            info_cmds::clock(),
 | 
				
			||||||
 | 
					            info_cmds::clock_context_menu(),
 | 
				
			||||||
            info_cmds::dashboard(),
 | 
					            info_cmds::dashboard(),
 | 
				
			||||||
            moderation_cmds::timezone(),
 | 
					            moderation_cmds::timezone(),
 | 
				
			||||||
            poise::Command {
 | 
					            poise::Command {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ use poise::serenity::{async_trait, model::id::UserId};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    models::{channel_data::ChannelData, user_data::UserData},
 | 
					    models::{channel_data::ChannelData, user_data::UserData},
 | 
				
			||||||
    CommandMacro, Context, Data, Error,
 | 
					    CommandMacro, Context, Data, Error, GuildId,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[async_trait]
 | 
					#[async_trait]
 | 
				
			||||||
@@ -49,13 +49,20 @@ impl CtxData for Context<'_> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn command_macros(&self) -> Result<Vec<CommandMacro<Data, Error>>, Error> {
 | 
					    async fn command_macros(&self) -> Result<Vec<CommandMacro<Data, Error>>, Error> {
 | 
				
			||||||
        let guild_id = self.guild_id().unwrap();
 | 
					        self.data().command_macros(self.guild_id().unwrap()).await
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Data {
 | 
				
			||||||
 | 
					    pub(crate) async fn command_macros(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        guild_id: GuildId,
 | 
				
			||||||
 | 
					    ) -> Result<Vec<CommandMacro<Data, Error>>, Error> {
 | 
				
			||||||
        let rows = sqlx::query!(
 | 
					        let rows = sqlx::query!(
 | 
				
			||||||
            "SELECT name, description FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
 | 
					            "SELECT name, description FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
 | 
				
			||||||
            guild_id.0
 | 
					            guild_id.0
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .fetch_all(&self.data().database)
 | 
					        .fetch_all(&self.database)
 | 
				
			||||||
        .await?.iter().map(|row| CommandMacro {
 | 
					        .await?.iter().map(|row| CommandMacro {
 | 
				
			||||||
            guild_id,
 | 
					            guild_id,
 | 
				
			||||||
            name: row.name.clone(),
 | 
					            name: row.name.clone(),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,12 +6,15 @@ pub mod look_flags;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use chrono::{NaiveDateTime, TimeZone};
 | 
					use chrono::{NaiveDateTime, TimeZone};
 | 
				
			||||||
use chrono_tz::Tz;
 | 
					use chrono_tz::Tz;
 | 
				
			||||||
use poise::serenity::model::id::{ChannelId, GuildId, UserId};
 | 
					use poise::{
 | 
				
			||||||
 | 
					    serenity::model::id::{ChannelId, GuildId, UserId},
 | 
				
			||||||
 | 
					    serenity_prelude::Cache,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
use sqlx::Executor;
 | 
					use sqlx::Executor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    models::reminder::look_flags::{LookFlags, TimeDisplayType},
 | 
					    models::reminder::look_flags::{LookFlags, TimeDisplayType},
 | 
				
			||||||
    Context, Data, Database,
 | 
					    Database,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
@@ -70,7 +73,7 @@ WHERE
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn from_channel<C: Into<ChannelId>>(
 | 
					    pub async fn from_channel<C: Into<ChannelId>>(
 | 
				
			||||||
        ctx: &Context<'_>,
 | 
					        pool: impl Executor<'_, Database = Database>,
 | 
				
			||||||
        channel_id: C,
 | 
					        channel_id: C,
 | 
				
			||||||
        flags: &LookFlags,
 | 
					        flags: &LookFlags,
 | 
				
			||||||
    ) -> Vec<Self> {
 | 
					    ) -> Vec<Self> {
 | 
				
			||||||
@@ -111,18 +114,19 @@ ORDER BY
 | 
				
			|||||||
            channel_id.as_u64(),
 | 
					            channel_id.as_u64(),
 | 
				
			||||||
            enabled,
 | 
					            enabled,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        .fetch_all(&ctx.data().database)
 | 
					        .fetch_all(pool)
 | 
				
			||||||
        .await
 | 
					        .await
 | 
				
			||||||
        .unwrap()
 | 
					        .unwrap()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub async fn from_guild(
 | 
					    pub async fn from_guild(
 | 
				
			||||||
        ctx: &Context<'_>,
 | 
					        cache: impl AsRef<Cache>,
 | 
				
			||||||
 | 
					        pool: impl Executor<'_, Database = Database>,
 | 
				
			||||||
        guild_id: Option<GuildId>,
 | 
					        guild_id: Option<GuildId>,
 | 
				
			||||||
        user: UserId,
 | 
					        user: UserId,
 | 
				
			||||||
    ) -> Vec<Self> {
 | 
					    ) -> Vec<Self> {
 | 
				
			||||||
        if let Some(guild_id) = guild_id {
 | 
					        if let Some(guild_id) = guild_id {
 | 
				
			||||||
            let guild_opt = guild_id.to_guild_cached(&ctx.discord());
 | 
					            let guild_opt = guild_id.to_guild_cached(cache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if let Some(guild) = guild_opt {
 | 
					            if let Some(guild) = guild_opt {
 | 
				
			||||||
                let channels = guild
 | 
					                let channels = guild
 | 
				
			||||||
@@ -163,7 +167,7 @@ WHERE
 | 
				
			|||||||
                ",
 | 
					                ",
 | 
				
			||||||
                    channels
 | 
					                    channels
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                .fetch_all(&ctx.data().database)
 | 
					                .fetch_all(pool)
 | 
				
			||||||
                .await
 | 
					                .await
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                sqlx::query_as_unchecked!(
 | 
					                sqlx::query_as_unchecked!(
 | 
				
			||||||
@@ -196,7 +200,7 @@ WHERE
 | 
				
			|||||||
                ",
 | 
					                ",
 | 
				
			||||||
                    guild_id.as_u64()
 | 
					                    guild_id.as_u64()
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                .fetch_all(&ctx.data().database)
 | 
					                .fetch_all(pool)
 | 
				
			||||||
                .await
 | 
					                .await
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -230,7 +234,7 @@ WHERE
 | 
				
			|||||||
            ",
 | 
					            ",
 | 
				
			||||||
                user.as_u64()
 | 
					                user.as_u64()
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .fetch_all(&ctx.data().database)
 | 
					            .fetch_all(pool)
 | 
				
			||||||
            .await
 | 
					            .await
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        .unwrap()
 | 
					        .unwrap()
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										48
									
								
								src/utils.rs
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								src/utils.rs
									
									
									
									
									
								
							@@ -1,7 +1,10 @@
 | 
				
			|||||||
use poise::serenity::{
 | 
					use poise::{
 | 
				
			||||||
    builder::CreateApplicationCommands,
 | 
					    serenity::{
 | 
				
			||||||
    http::CacheHttp,
 | 
					        builder::CreateApplicationCommands,
 | 
				
			||||||
    model::id::{GuildId, UserId},
 | 
					        http::CacheHttp,
 | 
				
			||||||
 | 
					        model::id::{GuildId, UserId},
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    serenity_prelude as serenity,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
@@ -65,3 +68,40 @@ pub async fn check_guild_subscription(
 | 
				
			|||||||
        false
 | 
					        false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Sends the message, specified via [`crate::CreateReply`], to the interaction initial response
 | 
				
			||||||
 | 
					/// endpoint
 | 
				
			||||||
 | 
					pub fn send_as_initial_response(
 | 
				
			||||||
 | 
					    data: poise::CreateReply<'_>,
 | 
				
			||||||
 | 
					    f: &mut serenity::CreateInteractionResponseData,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    let poise::CreateReply {
 | 
				
			||||||
 | 
					        content,
 | 
				
			||||||
 | 
					        embeds,
 | 
				
			||||||
 | 
					        attachments: _, // serenity doesn't support attachments in initial response yet
 | 
				
			||||||
 | 
					        components,
 | 
				
			||||||
 | 
					        ephemeral,
 | 
				
			||||||
 | 
					        allowed_mentions,
 | 
				
			||||||
 | 
					        reference_message: _, // can't reply to a message in interactions
 | 
				
			||||||
 | 
					    } = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if let Some(content) = content {
 | 
				
			||||||
 | 
					        f.content(content);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    f.embeds(embeds);
 | 
				
			||||||
 | 
					    if let Some(allowed_mentions) = allowed_mentions {
 | 
				
			||||||
 | 
					        f.allowed_mentions(|f| {
 | 
				
			||||||
 | 
					            *f = allowed_mentions.clone();
 | 
				
			||||||
 | 
					            f
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if let Some(components) = components {
 | 
				
			||||||
 | 
					        f.components(|f| {
 | 
				
			||||||
 | 
					            f.0 = components.0;
 | 
				
			||||||
 | 
					            f
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if ephemeral {
 | 
				
			||||||
 | 
					        f.flags(serenity::InteractionApplicationCommandCallbackDataFlags::EPHEMERAL);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,17 +4,15 @@ extern crate rocket;
 | 
				
			|||||||
mod consts;
 | 
					mod consts;
 | 
				
			||||||
mod routes;
 | 
					mod routes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use rocket::fs::{relative, FileServer};
 | 
					use std::{collections::HashMap, env};
 | 
				
			||||||
use std::collections::HashMap;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use oauth2::basic::BasicClient;
 | 
					use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
 | 
				
			||||||
use oauth2::{AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
 | 
					use rocket::fs::FileServer;
 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::consts::{DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN};
 | 
					 | 
				
			||||||
use rocket_dyn_templates::Template;
 | 
					use rocket_dyn_templates::Template;
 | 
				
			||||||
use serenity::client::Context;
 | 
					use serenity::client::Context;
 | 
				
			||||||
use sqlx::{MySql, Pool};
 | 
					use sqlx::{MySql, Pool};
 | 
				
			||||||
use std::env;
 | 
					
 | 
				
			||||||
 | 
					use crate::consts::{DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Database = MySql;
 | 
					type Database = MySql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +1,13 @@
 | 
				
			|||||||
use rocket::State;
 | 
					use rocket::{
 | 
				
			||||||
 | 
					    serde::json::{json, Json, Value as JsonValue},
 | 
				
			||||||
use crate::consts::DISCORD_CDN;
 | 
					    State,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
use serde::Serialize;
 | 
					use serde::Serialize;
 | 
				
			||||||
 | 
					use serenity::{client::Context, model::id::GuildId};
 | 
				
			||||||
use sqlx::{MySql, Pool};
 | 
					use sqlx::{MySql, Pool};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::Reminder;
 | 
					use super::Reminder;
 | 
				
			||||||
use rocket::serde::json::{json, Json, Value as JsonValue};
 | 
					use crate::consts::DISCORD_CDN;
 | 
				
			||||||
use serenity::client::Context;
 | 
					 | 
				
			||||||
use serenity::http::CacheHttp;
 | 
					 | 
				
			||||||
use serenity::model::id::GuildId;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Serialize)]
 | 
					#[derive(Serialize)]
 | 
				
			||||||
struct ChannelInfo {
 | 
					struct ChannelInfo {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,24 @@
 | 
				
			|||||||
use rocket::serde::json::{json, Json, Value as JsonValue};
 | 
					 | 
				
			||||||
use rocket::{http::CookieJar, State};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use reqwest::Client;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use serde::{Deserialize, Serialize};
 | 
					 | 
				
			||||||
use serenity::model::{
 | 
					 | 
				
			||||||
    id::{GuildId, RoleId},
 | 
					 | 
				
			||||||
    permissions::Permissions,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
use sqlx::{MySql, Pool};
 | 
					 | 
				
			||||||
use std::env;
 | 
					use std::env;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::Reminder;
 | 
					 | 
				
			||||||
use crate::consts::DISCORD_API;
 | 
					 | 
				
			||||||
use crate::routes::dashboard::DeleteReminder;
 | 
					 | 
				
			||||||
use chrono_tz::Tz;
 | 
					use chrono_tz::Tz;
 | 
				
			||||||
use serenity::client::Context;
 | 
					use reqwest::Client;
 | 
				
			||||||
use serenity::model::id::UserId;
 | 
					use rocket::{
 | 
				
			||||||
 | 
					    http::CookieJar,
 | 
				
			||||||
 | 
					    serde::json::{json, Json, Value as JsonValue},
 | 
				
			||||||
 | 
					    State,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					use serenity::{
 | 
				
			||||||
 | 
					    client::Context,
 | 
				
			||||||
 | 
					    model::{
 | 
				
			||||||
 | 
					        id::{GuildId, RoleId, UserId},
 | 
				
			||||||
 | 
					        permissions::Permissions,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use sqlx::{MySql, Pool};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::Reminder;
 | 
				
			||||||
 | 
					use crate::{consts::DISCORD_API, routes::dashboard::DeleteReminder};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Serialize)]
 | 
					#[derive(Serialize)]
 | 
				
			||||||
struct UserInfo {
 | 
					struct UserInfo {
 | 
				
			||||||
@@ -166,7 +168,7 @@ pub async fn get_user_guilds(cookies: &CookieJar<'_>, reqwest_client: &State<Cli
 | 
				
			|||||||
#[post("/api/user/reminders", data = "<reminder>")]
 | 
					#[post("/api/user/reminders", data = "<reminder>")]
 | 
				
			||||||
pub async fn create_reminder(
 | 
					pub async fn create_reminder(
 | 
				
			||||||
    reminder: Json<Reminder>,
 | 
					    reminder: Json<Reminder>,
 | 
				
			||||||
    ctx: &State<Context>,
 | 
					    _ctx: &State<Context>,
 | 
				
			||||||
    pool: &State<Pool<MySql>>,
 | 
					    pool: &State<Pool<MySql>>,
 | 
				
			||||||
) -> JsonValue {
 | 
					) -> JsonValue {
 | 
				
			||||||
    match sqlx::query!(
 | 
					    match sqlx::query!(
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user