components
This commit is contained in:
parent
9b5333dc87
commit
3e547861ea
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -1230,7 +1230,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"regex_command_attr",
|
"regex_command_attr",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"ring",
|
"rmp-serde",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serenity",
|
"serenity",
|
||||||
@ -1302,6 +1302,27 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp-serde"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"rmp",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rsa"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -19,11 +19,11 @@ lazy_static = "1.4"
|
|||||||
num-integer = "0.1"
|
num-integer = "0.1"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
rmp-serde = "0.15"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
levenshtein = "1.0"
|
levenshtein = "1.0"
|
||||||
serenity = { git = "https://github.com/serenity-rs/serenity", branch = "next", features = ["collector", "unstable_discord_api"] }
|
serenity = { git = "https://github.com/serenity-rs/serenity", branch = "next", features = ["collector", "unstable_discord_api"] }
|
||||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]}
|
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono"]}
|
||||||
ring = "0.16"
|
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
|
|
||||||
[dependencies.regex_command_attr]
|
[dependencies.regex_command_attr]
|
||||||
|
@ -17,6 +17,7 @@ use serenity::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
component_models::{ComponentDataModel, Restrict},
|
||||||
consts::{REGEX_ALIAS, REGEX_COMMANDS, THEME_COLOR},
|
consts::{REGEX_ALIAS, REGEX_COMMANDS, THEME_COLOR},
|
||||||
framework::{CommandInvoke, CreateGenericResponse, PermissionLevel},
|
framework::{CommandInvoke, CreateGenericResponse, PermissionLevel},
|
||||||
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData, CtxData},
|
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData, CtxData},
|
||||||
@ -264,6 +265,8 @@ async fn restrict(
|
|||||||
|
|
||||||
let len = restrictable_commands.len();
|
let len = restrictable_commands.len();
|
||||||
|
|
||||||
|
let restrict_pl = ComponentDataModel::Restrict(Restrict { role_id: role });
|
||||||
|
|
||||||
invoke
|
invoke
|
||||||
.respond(
|
.respond(
|
||||||
ctx.http.clone(),
|
ctx.http.clone(),
|
||||||
@ -273,7 +276,7 @@ async fn restrict(
|
|||||||
c.create_action_row(|row| {
|
c.create_action_row(|row| {
|
||||||
row.create_select_menu(|select| {
|
row.create_select_menu(|select| {
|
||||||
select
|
select
|
||||||
.custom_id("test_id")
|
.custom_id(restrict_pl.to_custom_id())
|
||||||
.options(|options| {
|
.options(|options| {
|
||||||
for command in restrictable_commands {
|
for command in restrictable_commands {
|
||||||
options.create_option(|opt| {
|
options.create_option(|opt| {
|
||||||
|
@ -10,14 +10,19 @@ use num_integer::Integer;
|
|||||||
use regex_command_attr::command;
|
use regex_command_attr::command;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
client::Context,
|
client::Context,
|
||||||
model::channel::{Channel, Message},
|
futures::StreamExt,
|
||||||
|
model::{
|
||||||
|
channel::{Channel, Message},
|
||||||
|
id::ChannelId,
|
||||||
|
misc::Mentionable,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
check_subscription_on_message,
|
check_subscription_on_message,
|
||||||
consts::{
|
consts::{
|
||||||
REGEX_CHANNEL_USER, REGEX_NATURAL_COMMAND_1, REGEX_NATURAL_COMMAND_2, REGEX_REMIND_COMMAND,
|
EMBED_DESCRIPTION_MAX_LENGTH, REGEX_CHANNEL_USER, REGEX_NATURAL_COMMAND_1,
|
||||||
THEME_COLOR,
|
REGEX_NATURAL_COMMAND_2, REGEX_REMIND_COMMAND, THEME_COLOR,
|
||||||
},
|
},
|
||||||
framework::{CommandInvoke, CreateGenericResponse},
|
framework::{CommandInvoke, CreateGenericResponse},
|
||||||
models::{
|
models::{
|
||||||
@ -26,7 +31,7 @@ use crate::{
|
|||||||
reminder::{
|
reminder::{
|
||||||
builder::{MultiReminderBuilder, ReminderScope},
|
builder::{MultiReminderBuilder, ReminderScope},
|
||||||
content::Content,
|
content::Content,
|
||||||
look_flags::LookFlags,
|
look_flags::{LookFlags, TimeDisplayType},
|
||||||
Reminder,
|
Reminder,
|
||||||
},
|
},
|
||||||
timer::Timer,
|
timer::Timer,
|
||||||
@ -116,146 +121,249 @@ async fn pause(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
#[command("offset")]
|
||||||
#[command]
|
#[description("Move all reminders in the current server by a certain amount of time. Times get added together")]
|
||||||
#[permission_level(Restricted)]
|
#[arg(
|
||||||
async fn offset(ctx: &Context, msg: &Message, args: String) {
|
name = "hours",
|
||||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
description = "Number of hours to offset by",
|
||||||
|
kind = "Integer",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[arg(
|
||||||
|
name = "minutes",
|
||||||
|
description = "Number of minutes to offset by",
|
||||||
|
kind = "Integer",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[arg(
|
||||||
|
name = "seconds",
|
||||||
|
description = "Number of seconds to offset by",
|
||||||
|
kind = "Integer",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[required_permissions(Restricted)]
|
||||||
|
async fn offset(
|
||||||
|
ctx: &Context,
|
||||||
|
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||||
|
args: HashMap<String, String>,
|
||||||
|
) {
|
||||||
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
|
||||||
let user_data = UserData::from_user(&msg.author, &ctx, &pool).await.unwrap();
|
let combined_time = args.get("hours").map_or(0, |h| h.parse::<i64>().unwrap() * 3600)
|
||||||
|
+ args.get("minutes").map_or(0, |m| m.parse::<i64>().unwrap() * 60)
|
||||||
|
+ args.get("seconds").map_or(0, |s| s.parse::<i64>().unwrap());
|
||||||
|
|
||||||
if args.is_empty() {
|
if combined_time == 0 {
|
||||||
let prefix = ctx.prefix(msg.guild_id).await;
|
let _ = invoke
|
||||||
|
.respond(
|
||||||
command_help(ctx, msg, lm, &prefix, &user_data.language, "offset").await;
|
ctx.http.clone(),
|
||||||
|
CreateGenericResponse::new()
|
||||||
|
.content("Please specify one of `hours`, `minutes` or `seconds`"),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
} else {
|
} else {
|
||||||
let parser = TimeParser::new(&args, user_data.timezone());
|
if let Some(guild) = invoke.guild(ctx.cache.clone()) {
|
||||||
|
let channels = guild
|
||||||
|
.channels
|
||||||
|
.iter()
|
||||||
|
.filter(|(channel_id, channel)| match channel {
|
||||||
|
Channel::Guild(guild_channel) => guild_channel.is_text_based(),
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.map(|(id, _)| id.0.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(",");
|
||||||
|
|
||||||
if let Ok(displacement) = parser.displacement() {
|
sqlx::query!(
|
||||||
if let Some(guild) = msg.guild(&ctx) {
|
"
|
||||||
let guild_data = GuildData::from_guild(guild, &pool).await.unwrap();
|
|
||||||
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
UPDATE reminders
|
UPDATE reminders
|
||||||
INNER JOIN `channels`
|
INNER JOIN
|
||||||
ON `channels`.id = reminders.channel_id
|
`channels` ON `channels`.id = reminders.channel_id
|
||||||
SET
|
SET reminders.`utc_time` = reminders.`utc_time` + ?
|
||||||
reminders.`utc_time` = reminders.`utc_time` + ?
|
WHERE FIND_IN_SET(channels.`channel`, ?)",
|
||||||
WHERE channels.guild_id = ?
|
combined_time,
|
||||||
",
|
channels
|
||||||
displacement,
|
)
|
||||||
guild_data.id
|
.execute(&pool)
|
||||||
)
|
.await
|
||||||
.execute(&pool)
|
.unwrap();
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
sqlx::query!(
|
|
||||||
"
|
|
||||||
UPDATE reminders SET `utc_time` = `utc_time` + ? WHERE reminders.channel_id = ?
|
|
||||||
",
|
|
||||||
displacement,
|
|
||||||
user_data.dm_channel
|
|
||||||
)
|
|
||||||
.execute(&pool)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = lm.get(&user_data.language, "offset/success").replacen(
|
|
||||||
"{}",
|
|
||||||
&displacement.to_string(),
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let _ = msg.channel_id.say(&ctx, response).await;
|
|
||||||
} else {
|
} else {
|
||||||
let _ =
|
sqlx::query!(
|
||||||
msg.channel_id.say(&ctx, lm.get(&user_data.language, "offset/invalid_time")).await;
|
"UPDATE reminders INNER JOIN `channels` ON `channels`.id = reminders.channel_id SET reminders.`utc_time` = reminders.`utc_time` + ? WHERE channels.`channel` = ?",
|
||||||
|
combined_time,
|
||||||
|
invoke.channel_id().0
|
||||||
|
)
|
||||||
|
.execute(&pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = invoke
|
||||||
|
.respond(
|
||||||
|
ctx.http.clone(),
|
||||||
|
CreateGenericResponse::new()
|
||||||
|
.content(format!("All reminders offset by {} seconds", combined_time)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command("nudge")]
|
||||||
#[permission_level(Restricted)]
|
#[description("Nudge all future reminders on this channel by a certain amount (don't use for DST! See `/offset`)")]
|
||||||
async fn nudge(ctx: &Context, msg: &Message, args: String) {
|
#[arg(
|
||||||
let (pool, lm) = get_ctx_data(&ctx).await;
|
name = "minutes",
|
||||||
|
description = "Number of minutes to nudge new reminders by",
|
||||||
|
kind = "Integer",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[arg(
|
||||||
|
name = "seconds",
|
||||||
|
description = "Number of seconds to nudge new reminders by",
|
||||||
|
kind = "Integer",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[required_permissions(Restricted)]
|
||||||
|
async fn nudge(
|
||||||
|
ctx: &Context,
|
||||||
|
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||||
|
args: HashMap<String, String>,
|
||||||
|
) {
|
||||||
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
|
||||||
let language = UserData::language_of(&msg.author, &pool).await;
|
let combined_time = args.get("minutes").map_or(0, |m| m.parse::<i64>().unwrap() * 60)
|
||||||
let timezone = UserData::timezone_of(&msg.author, &pool).await;
|
+ args.get("seconds").map_or(0, |s| s.parse::<i64>().unwrap());
|
||||||
|
|
||||||
let mut channel =
|
if combined_time < i16::MIN as i64 || combined_time > i16::MAX as i64 {
|
||||||
ChannelData::from_channel(msg.channel(&ctx).await.unwrap(), &pool).await.unwrap();
|
let _ = invoke
|
||||||
|
.respond(
|
||||||
if args.is_empty() {
|
ctx.http.clone(),
|
||||||
let content = lm
|
CreateGenericResponse::new().content("Nudge times must be less than 500 minutes"),
|
||||||
.get(&language, "nudge/no_argument")
|
)
|
||||||
.replace("{nudge}", &format!("{}s", &channel.nudge.to_string()));
|
.await;
|
||||||
|
|
||||||
let _ = msg.channel_id.say(&ctx, content).await;
|
|
||||||
} else {
|
} else {
|
||||||
let parser = TimeParser::new(&args, timezone);
|
let mut channel_data = ctx.channel_data(invoke.channel_id()).await.unwrap();
|
||||||
let nudge_time = parser.displacement();
|
|
||||||
|
|
||||||
match nudge_time {
|
channel_data.nudge = combined_time as i16;
|
||||||
Ok(displacement) => {
|
channel_data.commit_changes(&pool).await;
|
||||||
if displacement < i16::MIN as i64 || displacement > i16::MAX as i64 {
|
|
||||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "nudge/invalid_time")).await;
|
|
||||||
} else {
|
|
||||||
channel.nudge = displacement as i16;
|
|
||||||
|
|
||||||
channel.commit_changes(&pool).await;
|
let _ = invoke
|
||||||
|
.respond(
|
||||||
let response = lm.get(&language, "nudge/success").replacen(
|
ctx.http.clone(),
|
||||||
"{}",
|
CreateGenericResponse::new().content(format!(
|
||||||
&displacement.to_string(),
|
"Future reminders will be nudged by {} seconds",
|
||||||
1,
|
combined_time
|
||||||
);
|
)),
|
||||||
|
)
|
||||||
let _ = msg.channel_id.say(&ctx, response).await;
|
.await;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(_) => {
|
|
||||||
let _ = msg.channel_id.say(&ctx, lm.get(&language, "nudge/invalid_time")).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command("look")]
|
#[command("look")]
|
||||||
#[permission_level(Managed)]
|
#[description("View reminders on a specific channel")]
|
||||||
async fn look(ctx: &Context, msg: &Message, args: String) {
|
#[arg(
|
||||||
let (pool, _lm) = get_ctx_data(&ctx).await;
|
name = "channel",
|
||||||
|
description = "The channel to view reminders on",
|
||||||
|
kind = "Channel",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[arg(
|
||||||
|
name = "disabled",
|
||||||
|
description = "Whether to show disabled reminders or not",
|
||||||
|
kind = "Boolean",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[arg(
|
||||||
|
name = "relative",
|
||||||
|
description = "Whether to display times as relative or exact times",
|
||||||
|
kind = "Boolean",
|
||||||
|
required = false
|
||||||
|
)]
|
||||||
|
#[required_permissions(Managed)]
|
||||||
|
async fn look(
|
||||||
|
ctx: &Context,
|
||||||
|
invoke: &(dyn CommandInvoke + Send + Sync),
|
||||||
|
args: HashMap<String, String>,
|
||||||
|
) {
|
||||||
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
|
||||||
let timezone = UserData::timezone_of(&msg.author, &pool).await;
|
let timezone = UserData::timezone_of(&invoke.author_id(), &pool).await;
|
||||||
|
|
||||||
let flags = LookFlags::from_string(&args);
|
let flags = LookFlags {
|
||||||
|
show_disabled: args.get("disabled").map(|b| b == "true").unwrap_or(true),
|
||||||
|
channel_id: args.get("channel").map(|c| ChannelId(c.parse::<u64>().unwrap())),
|
||||||
|
time_display: args.get("relative").map_or(TimeDisplayType::Relative, |b| {
|
||||||
|
if b == "true" {
|
||||||
|
TimeDisplayType::Relative
|
||||||
|
} else {
|
||||||
|
TimeDisplayType::Absolute
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
let channel_opt = msg.channel_id.to_channel_cached(&ctx);
|
let channel_opt = invoke.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) == msg.guild_id {
|
if Some(channel.guild_id) == invoke.guild_id() {
|
||||||
flags.channel_id.unwrap_or(msg.channel_id)
|
flags.channel_id.unwrap_or(invoke.channel_id())
|
||||||
} else {
|
} else {
|
||||||
msg.channel_id
|
invoke.channel_id()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msg.channel_id
|
invoke.channel_id()
|
||||||
};
|
};
|
||||||
|
|
||||||
let reminders = Reminder::from_channel(ctx, channel_id, &flags).await;
|
let reminders = Reminder::from_channel(ctx, channel_id, &flags).await;
|
||||||
|
|
||||||
if reminders.is_empty() {
|
if reminders.is_empty() {
|
||||||
let _ = msg.channel_id.say(&ctx, "No reminders on specified channel").await;
|
let _ = invoke
|
||||||
|
.respond(
|
||||||
|
ctx.http.clone(),
|
||||||
|
CreateGenericResponse::new().content("No reminders on specified channel"),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
} else {
|
} else {
|
||||||
let display = reminders.iter().map(|reminder| reminder.display(&flags, &timezone));
|
let mut char_count = 0;
|
||||||
|
|
||||||
let _ = msg.channel_id.say_lines(&ctx, display).await;
|
let display = reminders
|
||||||
|
.iter()
|
||||||
|
.map(|reminder| reminder.display(&flags, &timezone))
|
||||||
|
.take_while(|p| {
|
||||||
|
char_count += p.len();
|
||||||
|
|
||||||
|
char_count < EMBED_DESCRIPTION_MAX_LENGTH
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
let pages = reminders
|
||||||
|
.iter()
|
||||||
|
.map(|reminder| reminder.display(&flags, &timezone))
|
||||||
|
.fold(0, |t, r| t + r.len())
|
||||||
|
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||||
|
|
||||||
|
let _ = invoke
|
||||||
|
.respond(
|
||||||
|
ctx.http.clone(),
|
||||||
|
CreateGenericResponse::new()
|
||||||
|
.embed(|e| {
|
||||||
|
e.title(format!("Reminders on {}", channel_id.mention()))
|
||||||
|
.description(display)
|
||||||
|
.footer(|f| f.text(format!("Page {} of {}", 1, pages)))
|
||||||
|
})
|
||||||
|
.components(|comp| {
|
||||||
|
comp.create_action_row(|row| {
|
||||||
|
row.create_button(|b| b.label("⏮️").custom_id(".1"))
|
||||||
|
.create_button(|b| b.label("◀️").custom_id(".2"))
|
||||||
|
.create_button(|b| b.label("▶️").custom_id(".3"))
|
||||||
|
.create_button(|b| b.label("⏭️").custom_id(".4"))
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[command("del")]
|
#[command("del")]
|
||||||
#[permission_level(Managed)]
|
#[permission_level(Managed)]
|
||||||
async fn delete(ctx: &Context, msg: &Message, _args: String) {
|
async fn delete(ctx: &Context, msg: &Message, _args: String) {
|
||||||
|
51
src/component_models/mod.rs
Normal file
51
src/component_models/mod.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use rmp_serde::Serializer;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serenity::model::{
|
||||||
|
id::{ChannelId, RoleId},
|
||||||
|
interactions::message_component::MessageComponentInteraction,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::models::reminder::look_flags::LookFlags;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub enum ComponentDataModel {
|
||||||
|
Restrict(Restrict),
|
||||||
|
LookPager(LookPager),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentDataModel {
|
||||||
|
pub fn to_custom_id(&self) -> String {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
self.serialize(&mut Serializer::new(&mut buf)).unwrap();
|
||||||
|
base64::encode(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_custom_id(data: &String) -> Self {
|
||||||
|
let buf = base64::decode(data).unwrap();
|
||||||
|
let cur = Cursor::new(buf);
|
||||||
|
rmp_serde::from_read(cur).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn act(&self, component: MessageComponentInteraction) {
|
||||||
|
match self {
|
||||||
|
ComponentDataModel::Restrict(restrict) => {
|
||||||
|
println!("{:?}", component.data.values);
|
||||||
|
}
|
||||||
|
ComponentDataModel::LookPager(pager) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Restrict {
|
||||||
|
pub role_id: RoleId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct LookPager {
|
||||||
|
pub flags: LookFlags,
|
||||||
|
pub page_request: u16,
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
pub const DAY: u64 = 86_400;
|
pub const DAY: u64 = 86_400;
|
||||||
pub const HOUR: u64 = 3_600;
|
pub const HOUR: u64 = 3_600;
|
||||||
pub const MINUTE: u64 = 60;
|
pub const MINUTE: u64 = 60;
|
||||||
|
pub const EMBED_DESCRIPTION_MAX_LENGTH: usize = 4000;
|
||||||
|
|
||||||
pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||||
|
|
||||||
|
13
src/main.rs
13
src/main.rs
@ -1,7 +1,9 @@
|
|||||||
|
#![feature(int_roundings)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
|
mod component_models;
|
||||||
mod consts;
|
mod consts;
|
||||||
mod framework;
|
mod framework;
|
||||||
mod models;
|
mod models;
|
||||||
@ -34,6 +36,7 @@ use tokio::sync::RwLock;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
commands::{info_cmds, moderation_cmds, reminder_cmds},
|
commands::{info_cmds, moderation_cmds, reminder_cmds},
|
||||||
|
component_models::ComponentDataModel,
|
||||||
consts::{CNC_GUILD, DEFAULT_PREFIX, SUBSCRIPTION_ROLES, THEME_COLOR},
|
consts::{CNC_GUILD, DEFAULT_PREFIX, SUBSCRIPTION_ROLES, THEME_COLOR},
|
||||||
framework::RegexFramework,
|
framework::RegexFramework,
|
||||||
models::guild_data::GuildData,
|
models::guild_data::GuildData,
|
||||||
@ -253,6 +256,10 @@ DELETE FROM guilds WHERE guild = ?
|
|||||||
|
|
||||||
framework.execute(ctx, application_command).await;
|
framework.execute(ctx, application_command).await;
|
||||||
}
|
}
|
||||||
|
Interaction::MessageComponent(component) => {
|
||||||
|
let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id);
|
||||||
|
component_model.act(component).await;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,13 +305,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
.add_command("n", &reminder_cmds::NATURAL_COMMAND)
|
.add_command("n", &reminder_cmds::NATURAL_COMMAND)
|
||||||
.add_command("", &reminder_cmds::NATURAL_COMMAND)
|
.add_command("", &reminder_cmds::NATURAL_COMMAND)
|
||||||
// management commands
|
// management commands
|
||||||
.add_command("look", &reminder_cmds::LOOK_COMMAND)
|
|
||||||
.add_command("del", &reminder_cmds::DELETE_COMMAND)
|
.add_command("del", &reminder_cmds::DELETE_COMMAND)
|
||||||
*/
|
*/
|
||||||
|
.add_command(&reminder_cmds::LOOK_COMMAND)
|
||||||
.add_command(&reminder_cmds::PAUSE_COMMAND)
|
.add_command(&reminder_cmds::PAUSE_COMMAND)
|
||||||
|
.add_command(&reminder_cmds::OFFSET_COMMAND)
|
||||||
|
.add_command(&reminder_cmds::NUDGE_COMMAND)
|
||||||
/*
|
/*
|
||||||
.add_command("offset", &reminder_cmds::OFFSET_COMMAND)
|
|
||||||
.add_command("nudge", &reminder_cmds::NUDGE_COMMAND)
|
|
||||||
// to-do commands
|
// to-do commands
|
||||||
.add_command("todo", &todo_cmds::TODO_USER_COMMAND)
|
.add_command("todo", &todo_cmds::TODO_USER_COMMAND)
|
||||||
.add_command("todo user", &todo_cmds::TODO_USER_COMMAND)
|
.add_command("todo user", &todo_cmds::TODO_USER_COMMAND)
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use serenity::model::id::ChannelId;
|
use serenity::model::id::ChannelId;
|
||||||
|
|
||||||
use crate::consts::REGEX_CHANNEL;
|
use crate::consts::REGEX_CHANNEL;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub enum TimeDisplayType {
|
pub enum TimeDisplayType {
|
||||||
Absolute,
|
Absolute = 0,
|
||||||
Relative,
|
Relative = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct LookFlags {
|
pub struct LookFlags {
|
||||||
pub limit: u16,
|
|
||||||
pub show_disabled: bool,
|
pub show_disabled: bool,
|
||||||
pub channel_id: Option<ChannelId>,
|
pub channel_id: Option<ChannelId>,
|
||||||
pub time_display: TimeDisplayType,
|
pub time_display: TimeDisplayType,
|
||||||
@ -16,44 +18,6 @@ pub struct LookFlags {
|
|||||||
|
|
||||||
impl Default for LookFlags {
|
impl Default for LookFlags {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self { show_disabled: true, channel_id: None, time_display: TimeDisplayType::Relative }
|
||||||
limit: u16::MAX,
|
|
||||||
show_disabled: true,
|
|
||||||
channel_id: None,
|
|
||||||
time_display: TimeDisplayType::Relative,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LookFlags {
|
|
||||||
pub fn from_string(args: &str) -> Self {
|
|
||||||
let mut new_flags: Self = Default::default();
|
|
||||||
|
|
||||||
for arg in args.split(' ') {
|
|
||||||
match arg {
|
|
||||||
"enabled" => {
|
|
||||||
new_flags.show_disabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
"time" => {
|
|
||||||
new_flags.time_display = TimeDisplayType::Absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
param => {
|
|
||||||
if let Ok(val) = param.parse::<u16>() {
|
|
||||||
new_flags.limit = val;
|
|
||||||
} else if let Some(channel) = REGEX_CHANNEL
|
|
||||||
.captures(arg)
|
|
||||||
.map(|cap| cap.get(1))
|
|
||||||
.flatten()
|
|
||||||
.map(|c| c.as_str().parse::<u64>().unwrap())
|
|
||||||
{
|
|
||||||
new_flags.channel_id = Some(ChannelId(channel));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_flags
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ use std::{
|
|||||||
|
|
||||||
use chrono::{NaiveDateTime, TimeZone};
|
use chrono::{NaiveDateTime, TimeZone};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use ring::hmac;
|
|
||||||
use serenity::{
|
use serenity::{
|
||||||
client::Context,
|
client::Context,
|
||||||
model::id::{ChannelId, GuildId, UserId},
|
model::id::{ChannelId, GuildId, UserId},
|
||||||
@ -27,31 +26,6 @@ use crate::{
|
|||||||
SQLPool,
|
SQLPool,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum ReminderAction {
|
|
||||||
Delete,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToString for ReminderAction {
|
|
||||||
fn to_string(&self) -> String {
|
|
||||||
match self {
|
|
||||||
Self::Delete => String::from("del"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for ReminderAction {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
|
||||||
"del" => Ok(Self::Delete),
|
|
||||||
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Reminder {
|
pub struct Reminder {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
@ -178,12 +152,9 @@ WHERE
|
|||||||
FIND_IN_SET(reminders.enabled, ?)
|
FIND_IN_SET(reminders.enabled, ?)
|
||||||
ORDER BY
|
ORDER BY
|
||||||
reminders.utc_time
|
reminders.utc_time
|
||||||
LIMIT
|
|
||||||
?
|
|
||||||
",
|
",
|
||||||
channel_id.as_u64(),
|
channel_id.as_u64(),
|
||||||
enabled,
|
enabled,
|
||||||
flags.limit
|
|
||||||
)
|
)
|
||||||
.fetch_all(&pool)
|
.fetch_all(&pool)
|
||||||
.await
|
.await
|
||||||
@ -341,59 +312,6 @@ WHERE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_interaction<U: Into<u64>>(
|
|
||||||
ctx: &Context,
|
|
||||||
member_id: U,
|
|
||||||
payload: String,
|
|
||||||
) -> Result<(Self, ReminderAction), InteractionError> {
|
|
||||||
let sections = payload.split('.').collect::<Vec<&str>>();
|
|
||||||
|
|
||||||
if sections.len() != 3 {
|
|
||||||
Err(InteractionError::InvalidFormat)
|
|
||||||
} else {
|
|
||||||
let action = ReminderAction::try_from(sections[0])
|
|
||||||
.map_err(|_| InteractionError::InvalidAction)?;
|
|
||||||
|
|
||||||
let reminder_id = u32::from_le_bytes(
|
|
||||||
base64::decode(sections[1])
|
|
||||||
.map_err(|_| InteractionError::InvalidBase64)?
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| InteractionError::InvalidSize)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(reminder) = Self::from_id(ctx, reminder_id).await {
|
|
||||||
if reminder.signed_action(member_id, action) == payload {
|
|
||||||
Ok((reminder, action))
|
|
||||||
} else {
|
|
||||||
Err(InteractionError::SignatureMismatch)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(InteractionError::NoReminder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn signed_action<U: Into<u64>>(&self, member_id: U, action: ReminderAction) -> String {
|
|
||||||
let s_key = hmac::Key::new(
|
|
||||||
hmac::HMAC_SHA256,
|
|
||||||
env::var("SECRET_KEY").expect("No SECRET_KEY provided").as_bytes(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut context = hmac::Context::with_key(&s_key);
|
|
||||||
|
|
||||||
context.update(&self.id.to_le_bytes());
|
|
||||||
context.update(&member_id.into().to_le_bytes());
|
|
||||||
|
|
||||||
let signature = context.sign();
|
|
||||||
|
|
||||||
format!(
|
|
||||||
"{}.{}.{}",
|
|
||||||
action.to_string(),
|
|
||||||
base64::encode(self.id.to_le_bytes()),
|
|
||||||
base64::encode(&signature)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete(&self, ctx: &Context) {
|
pub async fn delete(&self, ctx: &Context) {
|
||||||
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
let pool = ctx.data.read().await.get::<SQLPool>().cloned().unwrap();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user