241 lines
7.4 KiB
Rust
241 lines
7.4 KiB
Rust
use chrono::{naive::NaiveDateTime, Utc};
|
|
use futures::TryFutureExt;
|
|
use log::warn;
|
|
use rocket::serde::json::json;
|
|
use serde::{Deserialize, Serialize};
|
|
use serenity::{client::Context, model::id::UserId};
|
|
use sqlx::types::Json;
|
|
|
|
use crate::utils::check_subscription;
|
|
use crate::web::{
|
|
consts::{
|
|
DAY, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, MAX_EMBED_DESCRIPTION_LENGTH,
|
|
MAX_EMBED_FIELDS, MAX_EMBED_FIELD_TITLE_LENGTH, MAX_EMBED_FIELD_VALUE_LENGTH,
|
|
MAX_EMBED_FOOTER_LENGTH, MAX_EMBED_TITLE_LENGTH, MAX_NAME_LENGTH, MAX_URL_LENGTH,
|
|
MIN_INTERVAL,
|
|
},
|
|
guards::transaction::Transaction,
|
|
routes::{
|
|
dashboard::{create_database_channel, generate_uid, name_default, Attachment, EmbedField},
|
|
JsonResult,
|
|
},
|
|
Error,
|
|
};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct Reminder {
|
|
pub attachment: Option<Attachment>,
|
|
pub attachment_name: Option<String>,
|
|
pub content: String,
|
|
pub embed_author: String,
|
|
pub embed_author_url: Option<String>,
|
|
pub embed_color: u32,
|
|
pub embed_description: String,
|
|
pub embed_footer: String,
|
|
pub embed_footer_url: Option<String>,
|
|
pub embed_image_url: Option<String>,
|
|
pub embed_thumbnail_url: Option<String>,
|
|
pub embed_title: String,
|
|
pub embed_fields: Option<Json<Vec<EmbedField>>>,
|
|
pub enabled: bool,
|
|
pub expires: Option<NaiveDateTime>,
|
|
pub interval_seconds: Option<u32>,
|
|
pub interval_days: Option<u32>,
|
|
pub interval_months: Option<u32>,
|
|
#[serde(default = "name_default")]
|
|
pub name: String,
|
|
pub tts: bool,
|
|
#[serde(default)]
|
|
pub uid: String,
|
|
pub utc_time: NaiveDateTime,
|
|
}
|
|
|
|
pub async fn create_reminder(
|
|
ctx: &Context,
|
|
transaction: &mut Transaction<'_>,
|
|
user_id: UserId,
|
|
reminder: Reminder,
|
|
) -> JsonResult {
|
|
let channel = user_id
|
|
.create_dm_channel(&ctx)
|
|
.map_err(|e| Error::Serenity(e))
|
|
.and_then(|dm_channel| create_database_channel(&ctx, dm_channel.id, transaction))
|
|
.await;
|
|
|
|
if let Err(e) = channel {
|
|
warn!("`create_database_channel` returned an error code: {:?}", e);
|
|
|
|
// Provide more specific error messages based on the error type
|
|
let error_msg = match e {
|
|
Error::MissingDiscordPermission(permission) => format!(
|
|
"Please ensure the bot has the \"{}\" permission in the channel",
|
|
permission
|
|
),
|
|
_ => "Failed to configure channel for reminders.".to_string(),
|
|
};
|
|
|
|
return Err(json!({"error": error_msg}));
|
|
}
|
|
|
|
let channel = channel.unwrap();
|
|
|
|
// validate lengths
|
|
check_length!(MAX_NAME_LENGTH, reminder.name);
|
|
check_length!(MAX_CONTENT_LENGTH, reminder.content);
|
|
check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder.embed_description);
|
|
check_length!(MAX_EMBED_TITLE_LENGTH, reminder.embed_title);
|
|
check_length!(MAX_EMBED_AUTHOR_LENGTH, reminder.embed_author);
|
|
check_length!(MAX_EMBED_FOOTER_LENGTH, reminder.embed_footer);
|
|
check_length_opt!(MAX_EMBED_FIELDS, reminder.embed_fields);
|
|
if let Some(fields) = &reminder.embed_fields {
|
|
for field in &fields.0 {
|
|
check_length!(MAX_EMBED_FIELD_VALUE_LENGTH, field.value);
|
|
check_length!(MAX_EMBED_FIELD_TITLE_LENGTH, field.title);
|
|
}
|
|
}
|
|
check_length_opt!(
|
|
MAX_URL_LENGTH,
|
|
reminder.embed_footer_url,
|
|
reminder.embed_thumbnail_url,
|
|
reminder.embed_author_url,
|
|
reminder.embed_image_url
|
|
);
|
|
|
|
// validate urls
|
|
check_url_opt!(
|
|
reminder.embed_footer_url,
|
|
reminder.embed_thumbnail_url,
|
|
reminder.embed_author_url,
|
|
reminder.embed_image_url
|
|
);
|
|
|
|
// validate time and interval
|
|
if reminder.utc_time < Utc::now().naive_utc() {
|
|
return Err(json!({"error": "Time must be in the future"}));
|
|
}
|
|
if reminder.interval_seconds.is_some()
|
|
|| reminder.interval_days.is_some()
|
|
|| reminder.interval_months.is_some()
|
|
{
|
|
if reminder.interval_months.unwrap_or(0) * 30 * DAY as u32
|
|
+ reminder.interval_days.unwrap_or(0) * DAY as u32
|
|
+ reminder.interval_seconds.unwrap_or(0)
|
|
< *MIN_INTERVAL
|
|
{
|
|
return Err(json!({"error": "Interval too short"}));
|
|
}
|
|
}
|
|
|
|
// check patreon if necessary
|
|
if reminder.interval_seconds.is_some()
|
|
|| reminder.interval_days.is_some()
|
|
|| reminder.interval_months.is_some()
|
|
{
|
|
if !check_subscription(&ctx, transaction.executor(), user_id, None).await {
|
|
return Err(json!({"error": "Patreon is required to set intervals"}));
|
|
}
|
|
}
|
|
|
|
let name = if reminder.name.is_empty() { name_default() } else { reminder.name.clone() };
|
|
let new_uid = generate_uid();
|
|
|
|
// write to db
|
|
match sqlx::query!(
|
|
"INSERT INTO reminders (
|
|
uid,
|
|
attachment,
|
|
attachment_name,
|
|
channel_id,
|
|
content,
|
|
embed_author,
|
|
embed_author_url,
|
|
embed_color,
|
|
embed_description,
|
|
embed_footer,
|
|
embed_footer_url,
|
|
embed_image_url,
|
|
embed_thumbnail_url,
|
|
embed_title,
|
|
embed_fields,
|
|
enabled,
|
|
expires,
|
|
interval_seconds,
|
|
interval_days,
|
|
interval_months,
|
|
name,
|
|
tts,
|
|
`utc_time`
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
new_uid,
|
|
reminder.attachment,
|
|
reminder.attachment_name,
|
|
channel,
|
|
reminder.content,
|
|
reminder.embed_author,
|
|
reminder.embed_author_url,
|
|
reminder.embed_color,
|
|
reminder.embed_description,
|
|
reminder.embed_footer,
|
|
reminder.embed_footer_url,
|
|
reminder.embed_image_url,
|
|
reminder.embed_thumbnail_url,
|
|
reminder.embed_title,
|
|
reminder.embed_fields,
|
|
reminder.enabled,
|
|
reminder.expires,
|
|
reminder.interval_seconds,
|
|
reminder.interval_days,
|
|
reminder.interval_months,
|
|
name,
|
|
reminder.tts,
|
|
reminder.utc_time,
|
|
)
|
|
.execute(transaction.executor())
|
|
.await
|
|
{
|
|
Ok(_) => sqlx::query_as_unchecked!(
|
|
Reminder,
|
|
"SELECT
|
|
reminders.attachment,
|
|
reminders.attachment_name,
|
|
reminders.content,
|
|
reminders.embed_author,
|
|
reminders.embed_author_url,
|
|
reminders.embed_color,
|
|
reminders.embed_description,
|
|
reminders.embed_footer,
|
|
reminders.embed_footer_url,
|
|
reminders.embed_image_url,
|
|
reminders.embed_thumbnail_url,
|
|
reminders.embed_title,
|
|
reminders.embed_fields,
|
|
reminders.enabled,
|
|
reminders.expires,
|
|
reminders.interval_seconds,
|
|
reminders.interval_days,
|
|
reminders.interval_months,
|
|
reminders.name,
|
|
reminders.tts,
|
|
reminders.uid,
|
|
reminders.utc_time
|
|
FROM reminders
|
|
WHERE uid = ?",
|
|
new_uid
|
|
)
|
|
.fetch_one(transaction.executor())
|
|
.await
|
|
.map(|r| Ok(json!(r)))
|
|
.unwrap_or_else(|e| {
|
|
warn!("Failed to complete SQL query: {:?}", e);
|
|
|
|
Err(json!({"error": "Could not load reminder"}))
|
|
}),
|
|
|
|
Err(e) => {
|
|
warn!("Error in `create_reminder`: Could not execute query: {:?}", e);
|
|
|
|
Err(json!({"error": "Unknown error"}))
|
|
}
|
|
}
|
|
}
|