From 06c4deeaa91fdc05c12510ad3c583feec9348815 Mon Sep 17 00:00:00 2001 From: jude Date: Sat, 19 Feb 2022 22:11:21 +0000 Subject: [PATCH] component models --- postman/src/lib.rs | 10 +-- postman/src/sender.rs | 3 +- src/commands/info_cmds.rs | 87 +++++++++++++-------- src/commands/reminder_cmds.rs | 8 +- src/commands/todo_cmds.rs | 5 +- src/component_models/mod.rs | 121 ++++++++++++++++++------------ src/event_handlers.rs | 28 ++----- src/hooks.rs | 46 +++++------- src/main.rs | 1 + src/models/mod.rs | 13 +++- src/models/reminder/mod.rs | 22 +++--- src/utils.rs | 48 +++++++++++- web/src/lib.rs | 12 ++- web/src/routes/dashboard/guild.rs | 13 ++-- web/src/routes/dashboard/user.rs | 36 ++++----- 15 files changed, 269 insertions(+), 184 deletions(-) diff --git a/postman/src/lib.rs b/postman/src/lib.rs index ed1b852..1de374b 100644 --- a/postman/src/lib.rs +++ b/postman/src/lib.rs @@ -1,23 +1,23 @@ mod sender; +use std::env; + use log::info; use serenity::client::Context; use sqlx::{Executor, MySql}; -use std::env; -use tokio::time::sleep_until; -use tokio::time::{Duration, Instant}; +use tokio::time::{sleep_until, Duration, Instant}; type Database = MySql; 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::().ok()) .ok() .flatten() .unwrap_or(10); 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; if reminders.len() > 0 { diff --git a/postman/src/sender.rs b/postman/src/sender.rs index 140a14d..fd17451 100644 --- a/postman/src/sender.rs +++ b/postman/src/sender.rs @@ -1,4 +1,3 @@ -use crate::Database; use chrono::Duration; use chrono_tz::Tz; use lazy_static::lazy_static; @@ -20,6 +19,8 @@ use sqlx::{ Executor, }; +use crate::Database; + lazy_static! { pub static ref TIMEFROM_REGEX: Regex = Regex::new(r#"<\d+):(?P.+)?>>"#).unwrap(); diff --git a/src/commands/info_cmds.rs b/src/commands/info_cmds.rs index 3376f64..fb8184c 100644 --- a/src/commands/info_cmds.rs +++ b/src/commands/info_cmds.rs @@ -1,9 +1,11 @@ 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}; -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 = 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> { let footer = footer(ctx); - let _ = ctx - .send(|m| { - m.embed(|e| { - e.title("Help") - .color(*THEME_COLOR) - .description( - "__Info Commands__ + ctx.send(|m| { + m.ephemeral(true).embed(|e| { + e.title("Help") + .color(*THEME_COLOR) + .description( + "__Info Commands__ `/help` `/info` `/donate` `/dashboard` `/clock` *run these commands with no options* @@ -52,11 +53,11 @@ __Setup Commands__ __Advanced Commands__ `/macro` - Record and replay command sequences ", - ) - .footer(footer) - }) + ) + .footer(footer) }) - .await; + }) + .await?; Ok(()) } @@ -68,7 +69,7 @@ pub async fn info(ctx: Context<'_>) -> Result<(), Error> { let _ = ctx .send(|m| { - m.embed(|e| { + m.ephemeral(true).embed(|e| { e.title("Info") .description(format!( "Help: `/help` @@ -95,9 +96,10 @@ Use our dashboard: https://reminder-bot.com/", pub async fn donate(ctx: Context<'_>) -> Result<(), Error> { let footer = footer(ctx); - let _ = ctx.send(|m| m.embed(|e| { - e.title("Donate") - .description("Thinking of adding a monthly contribution? Click below for my Patreon and official bot server :) + ctx.send(|m| m.embed(|e| { + e.title("Donate") + .description("Thinking of adding a monthly contribution? +Click below for my Patreon and official bot server :) **https://www.patreon.com/jellywx/** **https://discord.jellywx.com/** @@ -112,11 +114,11 @@ With your new rank, you'll be able to: Just $2 USD/month! *Please note, you must be in the JellyWX Discord server to receive Patreon features*") - .footer(footer) - .color(*THEME_COLOR) - }), - ) - .await; + .footer(footer) + .color(*THEME_COLOR) + }), + ) + .await?; Ok(()) } @@ -126,21 +128,20 @@ Just $2 USD/month! pub async fn dashboard(ctx: Context<'_>) -> Result<(), Error> { let footer = footer(ctx); - let _ = ctx - .send(|m| { - m.embed(|e| { - e.title("Dashboard") - .description("**https://reminder-bot.com/dashboard**") - .footer(footer) - .color(*THEME_COLOR) - }) + ctx.send(|m| { + m.ephemeral(true).embed(|e| { + e.title("Dashboard") + .description("**https://reminder-bot.com/dashboard**") + .footer(footer) + .color(*THEME_COLOR) }) - .await; + }) + .await?; Ok(()) } -/// View the current time in a user's selected timezone +/// View the current time in your selected timezone #[poise::command(slash_command)] pub async fn clock(ctx: Context<'_>) -> Result<(), Error> { ctx.defer_ephemeral().await?; @@ -155,3 +156,25 @@ pub async fn clock(ctx: Context<'_>) -> Result<(), Error> { 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(()) +} diff --git a/src/commands/reminder_cmds.rs b/src/commands/reminder_cmds.rs index aca114d..007dcab 100644 --- a/src/commands/reminder_cmds.rs +++ b/src/commands/reminder_cmds.rs @@ -9,7 +9,6 @@ use chrono_tz::Tz; use num_integer::Integer; use poise::{ serenity::{builder::CreateEmbed, model::channel::Channel}, - serenity_prelude::ActionRole::Create, CreateReply, }; @@ -32,7 +31,6 @@ use crate::{ Reminder, }, timer::Timer, - user_data::UserData, CtxData, }, time_parser::natural_parser, @@ -212,7 +210,7 @@ pub async fn look( 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() { 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> { 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); diff --git a/src/commands/todo_cmds.rs b/src/commands/todo_cmds.rs index e79113c..39bf319 100644 --- a/src/commands/todo_cmds.rs +++ b/src/commands/todo_cmds.rs @@ -6,6 +6,7 @@ use crate::{ ComponentDataModel, TodoSelector, }, consts::{EMBED_DESCRIPTION_MAX_LENGTH, SELECT_MAX_ENTRIES, THEME_COLOR}, + hooks::guild_only, Context, Error, }; @@ -16,7 +17,7 @@ pub async fn todo_base(_ctx: Context<'_>) -> Result<(), Error> { } /// 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> { Ok(()) } @@ -70,7 +71,7 @@ WHERE guilds.guild = ?", } /// 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> { Ok(()) } diff --git a/src/component_models/mod.rs b/src/component_models/mod.rs index 82fc879..a84cab8 100644 --- a/src/component_models/mod.rs +++ b/src/component_models/mod.rs @@ -5,6 +5,7 @@ use std::io::Cursor; use chrono_tz::Tz; use poise::serenity::{ builder::CreateEmbed, + client::Context, model::{ channel::Channel, interactions::{message_component::MessageComponentInteraction, InteractionResponseType}, @@ -22,8 +23,9 @@ use crate::{ }, component_models::pager::{DelPager, LookPager, MacroPager, Pager, TodoPager}, consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR}, - models::{reminder::Reminder, CtxData}, - Context, Data, + models::reminder::Reminder, + utils::send_as_initial_response, + Data, }; #[derive(Deserialize, Serialize)] @@ -53,12 +55,12 @@ impl ComponentDataModel { 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 { ComponentDataModel::LookPager(pager) => { 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 { if Some(channel.guild_id) == component.guild_id { @@ -70,7 +72,7 @@ impl ComponentDataModel { 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 .iter() @@ -78,13 +80,12 @@ impl ComponentDataModel { .fold(0, |t, r| t + r.len()) .div_ceil(EMBED_DESCRIPTION_MAX_LENGTH); - let channel_name = if let Some(Channel::Guild(channel)) = - channel_id.to_channel_cached(&ctx.discord()) - { - Some(channel.name) - } else { - None - }; + let channel_name = + if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) { + Some(channel.name) + } else { + None + }; let next_page = pager.next_page(pages); @@ -118,7 +119,7 @@ impl ComponentDataModel { .color(*THEME_COLOR); let _ = component - .create_interaction_response(&ctx.discord(), |r| { + .create_interaction_response(&ctx, |r| { r.kind(InteractionResponseType::UpdateMessage).interaction_response_data( |response| { response.embeds(vec![embed]).components(|comp| { @@ -132,17 +133,26 @@ impl ComponentDataModel { .await; } ComponentDataModel::DelPager(pager) => { - let reminders = - Reminder::from_guild(&ctx, component.guild_id, component.user.id).await; + let reminders = Reminder::from_guild( + &ctx, + &data.database, + component.guild_id, + component.user.id, + ) + .await; let max_pages = max_delete_page(&reminders, &pager.timezone); let resp = show_delete_page(&reminders, pager.next_page(max_pages), pager.timezone); - let _ = ctx - .send(|r| { - *r = resp; - r + let _ = component + .create_interaction_response(&ctx, |f| { + f.kind(InteractionResponseType::UpdateMessage).interaction_response_data( + |d| { + send_as_initial_response(resp, d); + d + }, + ) }) .await; } @@ -150,19 +160,28 @@ impl ComponentDataModel { let selected_id = component.data.values.join(","); sqlx::query!("DELETE FROM reminders WHERE FIND_IN_SET(id, ?)", selected_id) - .execute(&ctx.data().database) + .execute(&data.database) .await .unwrap(); - let reminders = - Reminder::from_guild(&ctx, component.guild_id, component.user.id).await; + let reminders = Reminder::from_guild( + &ctx, + &data.database, + component.guild_id, + component.user.id, + ) + .await; let resp = show_delete_page(&reminders, selector.page, selector.timezone); - let _ = ctx - .send(|r| { - *r = resp; - r + let _ = component + .create_interaction_response(&ctx, |f| { + f.kind(InteractionResponseType::UpdateMessage).interaction_response_data( + |d| { + send_as_initial_response(resp, d); + d + }, + ) }) .await; } @@ -175,7 +194,7 @@ INNER JOIN users ON todos.user_id = users.id WHERE users.user = ?", uid, ) - .fetch_all(&ctx.data().database) + .fetch_all(&data.database) .await .unwrap() .iter() @@ -188,7 +207,7 @@ INNER JOIN channels ON todos.channel_id = channels.id WHERE channels.channel = ?", cid, ) - .fetch_all(&ctx.data().database) + .fetch_all(&data.database) .await .unwrap() .iter() @@ -201,7 +220,7 @@ INNER JOIN guilds ON todos.guild_id = guilds.id WHERE guilds.guild = ?", pager.guild_id, ) - .fetch_all(&ctx.data().database) + .fetch_all(&data.database) .await .unwrap() .iter() @@ -219,15 +238,18 @@ WHERE guilds.guild = ?", pager.guild_id, ); - let _ = ctx - .send(|r| { - *r = resp; - r + let _ = component + .create_interaction_response(&ctx, |f| { + f.kind(InteractionResponseType::UpdateMessage) + .interaction_response_data(|d| { + send_as_initial_response(resp, d); + d + }) }) .await; } else { let _ = component - .create_interaction_response(&ctx.discord(), |r| { + .create_interaction_response(&ctx, |r| { r.kind(InteractionResponseType::ChannelMessageWithSource) .interaction_response_data(|d| { d.flags( @@ -244,7 +266,7 @@ WHERE guilds.guild = ?", let selected_id = component.data.values.join(","); sqlx::query!("DELETE FROM todos WHERE FIND_IN_SET(id, ?)", selected_id) - .execute(&ctx.data().database) + .execute(&data.database) .await .unwrap(); @@ -255,7 +277,7 @@ WHERE guilds.guild = ?", selector.channel_id, selector.guild_id, ) - .fetch_all(&ctx.data().database) + .fetch_all(&data.database) .await .unwrap() .iter() @@ -270,15 +292,18 @@ WHERE guilds.guild = ?", selector.guild_id, ); - let _ = ctx - .send(|r| { - *r = resp; - r + let _ = component + .create_interaction_response(&ctx, |f| { + f.kind(InteractionResponseType::UpdateMessage) + .interaction_response_data(|d| { + send_as_initial_response(resp, d); + d + }) }) .await; } else { let _ = component - .create_interaction_response(&ctx.discord(), |r| { + .create_interaction_response(&ctx, |r| { r.kind(InteractionResponseType::ChannelMessageWithSource) .interaction_response_data(|d| { d.flags( @@ -291,17 +316,21 @@ WHERE guilds.guild = ?", } } 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 page = pager.next_page(max_page); let resp = show_macro_page(¯os, page); - let _ = ctx - .send(|r| { - *r = resp; - r + let _ = component + .create_interaction_response(&ctx, |f| { + f.kind(InteractionResponseType::UpdateMessage).interaction_response_data( + |d| { + send_as_initial_response(resp, d); + d + }, + ) }) .await; } diff --git a/src/event_handlers.rs b/src/event_handlers.rs index bc7e014..b5c6ba1 100644 --- a/src/event_handlers.rs +++ b/src/event_handlers.rs @@ -1,21 +1,12 @@ -use std::{ - collections::HashMap, - env, - sync::atomic::{AtomicBool, Ordering}, -}; +use std::{collections::HashMap, env, sync::atomic::Ordering}; use log::{info, warn}; use poise::{ serenity::{model::interactions::Interaction, utils::shard_id}, 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( ctx: &serenity::Context, @@ -56,15 +47,10 @@ pub async fn listener( } } poise::Event::ChannelDelete { channel } => { - sqlx::query!( - " -DELETE FROM channels WHERE channel = ? - ", - channel.id.as_u64() - ) - .execute(&data.database) - .await - .unwrap(); + sqlx::query!("DELETE FROM channels WHERE channel = ?", channel.id.as_u64()) + .execute(&data.database) + .await + .unwrap(); } poise::Event::GuildCreate { guild, is_new } => { if *is_new { @@ -122,7 +108,7 @@ DELETE FROM channels WHERE channel = ? Interaction::MessageComponent(component) => { let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id); - // component_model.act(ctx, component).await; + component_model.act(ctx, data, component).await; } _ => {} }, diff --git a/src/hooks.rs b/src/hooks.rs index 35cd4df..612c3ec 100644 --- a/src/hooks.rs +++ b/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}; @@ -14,39 +14,33 @@ pub async fn guild_only(ctx: Context<'_>) -> Result { async fn macro_check(ctx: Context<'_>) -> bool { if let Context::Application(app_ctx) = ctx { - if let ApplicationCommandOrAutocompleteInteraction::ApplicationCommand(interaction) = - app_ctx.interaction - { - 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(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 command_macro.commands.len() >= MACRO_MAX_COMMANDS { - let _ = ctx.send(|m| { + if let Some(command_macro) = lock.get_mut(&(guild_id, ctx.author().id)) { + if command_macro.commands.len() >= MACRO_MAX_COMMANDS { + let _ = ctx.send(|m| { m.ephemeral(true).content( format!("{} commands already recorded. Please use `/macro finish` to end recording.", MACRO_MAX_COMMANDS), ) }) .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 { - 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 { true } diff --git a/src/main.rs b/src/main.rs index 44f3fe4..33299ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,6 +59,7 @@ async fn main() -> Result<(), Box> { info_cmds::info(), info_cmds::donate(), info_cmds::clock(), + info_cmds::clock_context_menu(), info_cmds::dashboard(), moderation_cmds::timezone(), poise::Command { diff --git a/src/models/mod.rs b/src/models/mod.rs index 63c19a1..14acbdd 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -9,7 +9,7 @@ use poise::serenity::{async_trait, model::id::UserId}; use crate::{ models::{channel_data::ChannelData, user_data::UserData}, - CommandMacro, Context, Data, Error, + CommandMacro, Context, Data, Error, GuildId, }; #[async_trait] @@ -49,13 +49,20 @@ impl CtxData for Context<'_> { } async fn command_macros(&self) -> Result>, 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>, Error> { let rows = sqlx::query!( "SELECT name, description FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)", guild_id.0 ) - .fetch_all(&self.data().database) + .fetch_all(&self.database) .await?.iter().map(|row| CommandMacro { guild_id, name: row.name.clone(), diff --git a/src/models/reminder/mod.rs b/src/models/reminder/mod.rs index 6b46a74..f50203d 100644 --- a/src/models/reminder/mod.rs +++ b/src/models/reminder/mod.rs @@ -6,12 +6,15 @@ pub mod look_flags; use chrono::{NaiveDateTime, TimeZone}; 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 crate::{ models::reminder::look_flags::{LookFlags, TimeDisplayType}, - Context, Data, Database, + Database, }; #[derive(Debug, Clone)] @@ -70,7 +73,7 @@ WHERE } pub async fn from_channel>( - ctx: &Context<'_>, + pool: impl Executor<'_, Database = Database>, channel_id: C, flags: &LookFlags, ) -> Vec { @@ -111,18 +114,19 @@ ORDER BY channel_id.as_u64(), enabled, ) - .fetch_all(&ctx.data().database) + .fetch_all(pool) .await .unwrap() } pub async fn from_guild( - ctx: &Context<'_>, + cache: impl AsRef, + pool: impl Executor<'_, Database = Database>, guild_id: Option, user: UserId, ) -> Vec { 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 { let channels = guild @@ -163,7 +167,7 @@ WHERE ", channels ) - .fetch_all(&ctx.data().database) + .fetch_all(pool) .await } else { sqlx::query_as_unchecked!( @@ -196,7 +200,7 @@ WHERE ", guild_id.as_u64() ) - .fetch_all(&ctx.data().database) + .fetch_all(pool) .await } } else { @@ -230,7 +234,7 @@ WHERE ", user.as_u64() ) - .fetch_all(&ctx.data().database) + .fetch_all(pool) .await } .unwrap() diff --git a/src/utils.rs b/src/utils.rs index e11f47c..ca2c11f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,10 @@ -use poise::serenity::{ - builder::CreateApplicationCommands, - http::CacheHttp, - model::id::{GuildId, UserId}, +use poise::{ + serenity::{ + builder::CreateApplicationCommands, + http::CacheHttp, + model::id::{GuildId, UserId}, + }, + serenity_prelude as serenity, }; use crate::{ @@ -65,3 +68,40 @@ pub async fn check_guild_subscription( 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); + } +} diff --git a/web/src/lib.rs b/web/src/lib.rs index 21c6530..ed3ce7f 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -4,17 +4,15 @@ extern crate rocket; mod consts; mod routes; -use rocket::fs::{relative, FileServer}; -use std::collections::HashMap; +use std::{collections::HashMap, env}; -use oauth2::basic::BasicClient; -use oauth2::{AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl}; - -use crate::consts::{DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN}; +use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl}; +use rocket::fs::FileServer; use rocket_dyn_templates::Template; use serenity::client::Context; use sqlx::{MySql, Pool}; -use std::env; + +use crate::consts::{DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN}; type Database = MySql; diff --git a/web/src/routes/dashboard/guild.rs b/web/src/routes/dashboard/guild.rs index 81fc9ca..55f40e9 100644 --- a/web/src/routes/dashboard/guild.rs +++ b/web/src/routes/dashboard/guild.rs @@ -1,14 +1,13 @@ -use rocket::State; - -use crate::consts::DISCORD_CDN; +use rocket::{ + serde::json::{json, Json, Value as JsonValue}, + State, +}; use serde::Serialize; +use serenity::{client::Context, model::id::GuildId}; use sqlx::{MySql, Pool}; use super::Reminder; -use rocket::serde::json::{json, Json, Value as JsonValue}; -use serenity::client::Context; -use serenity::http::CacheHttp; -use serenity::model::id::GuildId; +use crate::consts::DISCORD_CDN; #[derive(Serialize)] struct ChannelInfo { diff --git a/web/src/routes/dashboard/user.rs b/web/src/routes/dashboard/user.rs index a6521a4..1d64efc 100644 --- a/web/src/routes/dashboard/user.rs +++ b/web/src/routes/dashboard/user.rs @@ -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 super::Reminder; -use crate::consts::DISCORD_API; -use crate::routes::dashboard::DeleteReminder; use chrono_tz::Tz; -use serenity::client::Context; -use serenity::model::id::UserId; +use reqwest::Client; +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)] struct UserInfo { @@ -166,7 +168,7 @@ pub async fn get_user_guilds(cookies: &CookieJar<'_>, reqwest_client: &State, - ctx: &State, + _ctx: &State, pool: &State>, ) -> JsonValue { match sqlx::query!(