macro stuff
This commit is contained in:
parent
4490f19c04
commit
6cf660c7ee
10
README.md
10
README.md
@ -38,3 +38,13 @@ __Other Variables__
|
||||
* `SHARD_COUNT` - default `None`, accepts the number of shards that are being ran
|
||||
* `SHARD_RANGE` - default `None`, if `SHARD_COUNT` is specified, specifies what range of shards to start on this process
|
||||
* `DM_ENABLED` - default `1`, if `1`, Reminder Bot will respond to direct messages
|
||||
|
||||
### Todo List
|
||||
|
||||
* Implement remainder of the `macro` command
|
||||
* Convert aliases to macros
|
||||
* Block users from interacting with another users' components
|
||||
* Split out framework
|
||||
* Help command
|
||||
* Change all db keys to be discord IDs
|
||||
* Test everything
|
||||
|
@ -2,11 +2,14 @@ use chrono::offset::Utc;
|
||||
use chrono_tz::{Tz, TZ_VARIANTS};
|
||||
use levenshtein::levenshtein;
|
||||
use regex_command_attr::command;
|
||||
use serenity::{client::Context, model::misc::Mentionable};
|
||||
use serenity::{
|
||||
client::Context,
|
||||
model::{id::GuildId, misc::Mentionable},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
component_models::{ComponentDataModel, Restrict},
|
||||
consts::THEME_COLOR,
|
||||
component_models::{pager::Pager, ComponentDataModel, Restrict},
|
||||
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
|
||||
framework::{CommandInvoke, CommandOptions, CreateGenericResponse, OptionValue},
|
||||
hooks::{CHECK_GUILD_PERMISSIONS_HOOK, CHECK_MANAGED_PERMISSIONS_HOOK},
|
||||
models::{channel_data::ChannelData, command_macro::CommandMacro, CtxData},
|
||||
@ -335,11 +338,11 @@ async fn macro_cmd(ctx: &Context, invoke: &mut CommandInvoke, args: CommandOptio
|
||||
&ctx,
|
||||
CreateGenericResponse::new().ephemeral().embed(|e| {
|
||||
e
|
||||
.title("Macro Recording Started")
|
||||
.description(
|
||||
.title("Macro Recording Started")
|
||||
.description(
|
||||
"Run up to 5 commands, or type `/macro finish` to stop at any point.
|
||||
Any commands ran as part of recording will be inconsequential")
|
||||
.color(*THEME_COLOR)
|
||||
.color(*THEME_COLOR)
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
@ -396,7 +399,13 @@ Any commands ran as part of recording will be inconsequential")
|
||||
lock.remove(&key);
|
||||
}
|
||||
}
|
||||
"list" => {}
|
||||
"list" => {
|
||||
let macros = CommandMacro::from_guild(ctx, invoke.guild_id().unwrap()).await;
|
||||
|
||||
let resp = show_macro_page(¯os, 0, invoke.guild_id().unwrap());
|
||||
|
||||
invoke.respond(&ctx, resp).await;
|
||||
}
|
||||
"run" => {
|
||||
let macro_name = args.get("name").unwrap().to_string();
|
||||
|
||||
@ -435,7 +444,137 @@ Any commands ran as part of recording will be inconsequential")
|
||||
}
|
||||
}
|
||||
}
|
||||
"delete" => {}
|
||||
"delete" => {
|
||||
let macro_name = args.get("name").unwrap().to_string();
|
||||
|
||||
match sqlx::query!(
|
||||
"SELECT id FROM macro WHERE guild_id = ? AND name = ?",
|
||||
invoke.guild_id().unwrap().0,
|
||||
macro_name
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
{
|
||||
Ok(row) => {
|
||||
sqlx::query!("DELETE FROM macro WHERE id = ?", row.id).execute(&pool).await;
|
||||
|
||||
let _ = invoke
|
||||
.respond(
|
||||
&ctx,
|
||||
CreateGenericResponse::new()
|
||||
.content(format!("Macro \"{}\" deleted", macro_name)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Err(sqlx::Error::RowNotFound) => {
|
||||
let _ = invoke
|
||||
.respond(
|
||||
&ctx,
|
||||
CreateGenericResponse::new()
|
||||
.content(format!("Macro \"{}\" not found", macro_name)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
panic!("{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_macro_page(macros: &[CommandMacro]) -> usize {
|
||||
let mut skipped_char_count = 0;
|
||||
|
||||
macros
|
||||
.iter()
|
||||
.map(|m| {
|
||||
if let Some(description) = &m.description {
|
||||
format!("**{}**\n- *{}*\n- Has {} commands", m.name, description, m.commands.len())
|
||||
} else {
|
||||
format!("**{}**\n- Has {} commands", m.name, m.commands.len())
|
||||
}
|
||||
})
|
||||
.fold(1, |mut pages, p| {
|
||||
skipped_char_count += p.len();
|
||||
|
||||
if skipped_char_count > EMBED_DESCRIPTION_MAX_LENGTH {
|
||||
skipped_char_count = p.len();
|
||||
pages += 1;
|
||||
}
|
||||
|
||||
pages
|
||||
})
|
||||
}
|
||||
|
||||
fn show_macro_page(
|
||||
macros: &[CommandMacro],
|
||||
page: usize,
|
||||
guild_id: GuildId,
|
||||
) -> CreateGenericResponse {
|
||||
let pager = Pager::new(page, guild_id);
|
||||
|
||||
if macros.is_empty() {
|
||||
return CreateGenericResponse::new().embed(|e| {
|
||||
e.title("Macros")
|
||||
.description("No Macros Set Up. Use `/macro record` to get started.")
|
||||
.color(*THEME_COLOR)
|
||||
});
|
||||
}
|
||||
|
||||
let pages = max_macro_page(macros);
|
||||
|
||||
let mut page = page;
|
||||
if page >= pages {
|
||||
page = pages - 1;
|
||||
}
|
||||
|
||||
let mut char_count = 0;
|
||||
let mut skipped_char_count = 0;
|
||||
|
||||
let mut skipped_pages = 0;
|
||||
|
||||
let display_vec: Vec<String> = macros
|
||||
.iter()
|
||||
.map(|m| {
|
||||
if let Some(description) = &m.description {
|
||||
format!("**{}**\n- *{}*\n- Has {} commands", m.name, description, m.commands.len())
|
||||
} else {
|
||||
format!("**{}**\n- Has {} commands", m.name, m.commands.len())
|
||||
}
|
||||
})
|
||||
.skip_while(|p| {
|
||||
skipped_char_count += p.len();
|
||||
|
||||
if skipped_char_count > EMBED_DESCRIPTION_MAX_LENGTH {
|
||||
skipped_char_count = p.len();
|
||||
skipped_pages += 1;
|
||||
}
|
||||
|
||||
skipped_pages < page
|
||||
})
|
||||
.take_while(|p| {
|
||||
char_count += p.len();
|
||||
|
||||
char_count < EMBED_DESCRIPTION_MAX_LENGTH
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let display = display_vec.join("\n");
|
||||
|
||||
CreateGenericResponse::new()
|
||||
.embed(|e| {
|
||||
e.title("Macros")
|
||||
.description(display)
|
||||
.footer(|f| f.text(format!("Page {} of {}", page + 1, pages)))
|
||||
.color(*THEME_COLOR)
|
||||
})
|
||||
.components(|comp| {
|
||||
pager.create_button_row(pages, comp);
|
||||
|
||||
comp
|
||||
})
|
||||
}
|
||||
|
@ -8,7 +8,11 @@ use chrono::NaiveDateTime;
|
||||
use chrono_tz::Tz;
|
||||
use num_integer::Integer;
|
||||
use regex_command_attr::command;
|
||||
use serenity::{builder::CreateEmbed, client::Context, model::channel::Channel};
|
||||
use serenity::{
|
||||
builder::CreateEmbed,
|
||||
client::Context,
|
||||
model::{channel::Channel, id::UserId},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
check_subscription_on_message,
|
||||
@ -363,7 +367,7 @@ async fn delete(ctx: &Context, invoke: &mut CommandInvoke, _args: CommandOptions
|
||||
|
||||
let reminders = Reminder::from_guild(ctx, invoke.guild_id(), invoke.author_id()).await;
|
||||
|
||||
let resp = show_delete_page(&reminders, 0, timezone);
|
||||
let resp = show_delete_page(&reminders, 0, timezone, invoke.author_id());
|
||||
|
||||
let _ = invoke.respond(&ctx, resp).await;
|
||||
}
|
||||
@ -394,8 +398,9 @@ pub fn show_delete_page(
|
||||
reminders: &[Reminder],
|
||||
page: usize,
|
||||
timezone: Tz,
|
||||
author_id: UserId,
|
||||
) -> CreateGenericResponse {
|
||||
let pager = Pager::new(page, DelData { timezone });
|
||||
let pager = Pager::new(page, DelData { author_id, timezone });
|
||||
|
||||
if reminders.is_empty() {
|
||||
return CreateGenericResponse::new()
|
||||
@ -450,7 +455,7 @@ pub fn show_delete_page(
|
||||
|
||||
let display = display_vec.join("\n");
|
||||
|
||||
let del_selector = ComponentDataModel::DelSelector(DelSelector { page, timezone });
|
||||
let del_selector = ComponentDataModel::DelSelector(DelSelector { page, timezone, author_id });
|
||||
|
||||
CreateGenericResponse::new()
|
||||
.embed(|e| {
|
||||
|
@ -37,6 +37,7 @@ pub enum ComponentDataModel {
|
||||
LookPager(Pager<LookData>),
|
||||
DelPager(Pager<DelData>),
|
||||
TodoPager(Pager<TodoData>),
|
||||
MacroPager(Pager<GuildId>),
|
||||
}
|
||||
|
||||
impl ComponentDataModel {
|
||||
@ -89,7 +90,16 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
.await
|
||||
.unwrap();
|
||||
} else {
|
||||
// tell them they cant do this
|
||||
component
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|response| response
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
.content("Only the original command user can interact with this message")
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
ComponentDataModel::LookPager(pager) => {
|
||||
@ -168,16 +178,33 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
.await;
|
||||
}
|
||||
ComponentDataModel::DelPager(pager) => {
|
||||
let reminders =
|
||||
Reminder::from_guild(ctx, component.guild_id, component.user.id).await;
|
||||
if component.user.id == pager.data.author_id {
|
||||
let reminders =
|
||||
Reminder::from_guild(ctx, component.guild_id, component.user.id).await;
|
||||
|
||||
let max_pages = max_delete_page(&reminders, &pager.data.timezone);
|
||||
let max_pages = max_delete_page(&reminders, &pager.data.timezone);
|
||||
|
||||
let resp =
|
||||
show_delete_page(&reminders, pager.next_page(max_pages), pager.data.timezone);
|
||||
let resp = show_delete_page(
|
||||
&reminders,
|
||||
pager.next_page(max_pages),
|
||||
pager.data.timezone,
|
||||
pager.data.author_id,
|
||||
);
|
||||
|
||||
let mut invoke = CommandInvoke::component(component);
|
||||
let _ = invoke.respond(&ctx, resp).await;
|
||||
let mut invoke = CommandInvoke::component(component);
|
||||
let _ = invoke.respond(&ctx, resp).await;
|
||||
} else {
|
||||
component
|
||||
.create_interaction_response(&ctx, |r| {
|
||||
r.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|response| response
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
.content("Only the original command user can interact with this message")
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
ComponentDataModel::DelSelector(selector) => {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
@ -191,7 +218,12 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
let reminders =
|
||||
Reminder::from_guild(ctx, 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,
|
||||
selector.author_id,
|
||||
);
|
||||
|
||||
let mut invoke = CommandInvoke::component(component);
|
||||
let _ = invoke.respond(&ctx, resp).await;
|
||||
@ -260,6 +292,7 @@ INSERT IGNORE INTO roles (role, name, guild_id) VALUES (?, \"Role\", (SELECT id
|
||||
let mut invoke = CommandInvoke::component(component);
|
||||
let _ = invoke.respond(&ctx, resp).await;
|
||||
}
|
||||
ComponentDataModel::MacroPager(pager) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -275,6 +308,7 @@ pub struct Restrict {
|
||||
pub struct DelSelector {
|
||||
pub page: usize,
|
||||
pub timezone: Tz,
|
||||
pub author_id: UserId,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
@ -2,7 +2,10 @@ use chrono_tz::Tz;
|
||||
use rmp_serde::Serializer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::*;
|
||||
use serenity::{builder::CreateComponents, model::interactions::message_component::ButtonStyle};
|
||||
use serenity::{
|
||||
builder::CreateComponents,
|
||||
model::{id::UserId, interactions::message_component::ButtonStyle},
|
||||
};
|
||||
|
||||
use crate::{component_models::ComponentDataModel, models::reminder::look_flags::LookFlags};
|
||||
|
||||
@ -102,6 +105,7 @@ pub struct LookData {
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct DelData {
|
||||
pub author_id: UserId,
|
||||
pub timezone: Tz,
|
||||
}
|
||||
|
||||
|
@ -305,9 +305,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
.add_command(&reminder_cmds::TIMER_COMMAND)
|
||||
.add_command(&reminder_cmds::REMIND_COMMAND)
|
||||
/*
|
||||
.add_command("r", &reminder_cmds::REMIND_COMMAND)
|
||||
.add_command("interval", &reminder_cmds::INTERVAL_COMMAND)
|
||||
.add_command("i", &reminder_cmds::INTERVAL_COMMAND)
|
||||
.add_command("natural", &reminder_cmds::NATURAL_COMMAND)
|
||||
.add_command("n", &reminder_cmds::NATURAL_COMMAND)
|
||||
.add_command("", &reminder_cmds::NATURAL_COMMAND)
|
||||
@ -326,10 +323,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
.add_command(&moderation_cmds::TIMEZONE_COMMAND)
|
||||
.add_command(&moderation_cmds::PREFIX_COMMAND)
|
||||
.add_command(&moderation_cmds::MACRO_CMD_COMMAND)
|
||||
/*
|
||||
.add_command("alias", &moderation_cmds::ALIAS_COMMAND)
|
||||
.add_command("a", &moderation_cmds::ALIAS_COMMAND)
|
||||
*/
|
||||
.add_hook(&hooks::CHECK_SELF_PERMISSIONS_HOOK)
|
||||
.add_hook(&hooks::MACRO_CHECK_HOOK)
|
||||
.build();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serenity::model::id::GuildId;
|
||||
use serenity::{client::Context, model::id::GuildId};
|
||||
|
||||
use crate::framework::CommandOptions;
|
||||
use crate::{framework::CommandOptions, SQLPool};
|
||||
|
||||
pub struct CommandMacro {
|
||||
pub guild_id: GuildId,
|
||||
@ -8,3 +8,23 @@ pub struct CommandMacro {
|
||||
pub description: Option<String>,
|
||||
pub commands: Vec<CommandOptions>,
|
||||
}
|
||||
|
||||
impl CommandMacro {
|
||||
pub async fn from_guild(ctx: &Context, guild_id: impl Into<GuildId>) -> Vec<Self> {
|
||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||
let guild_id = guild_id.into();
|
||||
|
||||
sqlx::query!("SELECT * FROM macro WHERE guild_id = ?", guild_id.0)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|row| Self {
|
||||
guild_id: GuildId(row.guild_id),
|
||||
name: row.name.clone(),
|
||||
description: row.description.clone(),
|
||||
commands: serde_json::from_str(&row.commands).unwrap(),
|
||||
})
|
||||
.collect::<Vec<Self>>()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user