Add routes for getting/posting user reminders
This commit is contained in:
parent
dbe8e8e358
commit
5f0aa0f834
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -942,6 +942,7 @@ checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
@ -2366,6 +2367,7 @@ dependencies = [
|
|||||||
"dotenv",
|
"dotenv",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"extract_derive",
|
"extract_derive",
|
||||||
|
"futures",
|
||||||
"lazy-regex",
|
"lazy-regex",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"levenshtein",
|
"levenshtein",
|
||||||
|
@ -28,6 +28,7 @@ levenshtein = "1.0"
|
|||||||
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "migrate"] }
|
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "migrate"] }
|
||||||
base64 = "0.21"
|
base64 = "0.21"
|
||||||
secrecy = "0.8.0"
|
secrecy = "0.8.0"
|
||||||
|
futures = "0.3.30"
|
||||||
|
|
||||||
[dependencies.postman]
|
[dependencies.postman]
|
||||||
path = "postman"
|
path = "postman"
|
||||||
|
@ -182,3 +182,8 @@ export const fetchUserReminders = () => ({
|
|||||||
axios.get(`/dashboard/api/user/reminders`).then((resp) => resp.data) as Promise<Reminder[]>,
|
axios.get(`/dashboard/api/user/reminders`).then((resp) => resp.data) as Promise<Reminder[]>,
|
||||||
staleTime: OTHER_STALE_TIME,
|
staleTime: OTHER_STALE_TIME,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const postUserReminder = () => ({
|
||||||
|
mutationFn: (reminder: Reminder) =>
|
||||||
|
axios.post(`/dashboard/api/user/reminders`, reminder).then((resp) => resp.data),
|
||||||
|
});
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { LoadTemplate } from "../LoadTemplate";
|
import { LoadTemplate } from "../LoadTemplate";
|
||||||
import { useReminder } from "../ReminderContext";
|
import { useReminder } from "../ReminderContext";
|
||||||
import { useMutation, useQueryClient } from "react-query";
|
import { useMutation, useQueryClient } from "react-query";
|
||||||
import { postGuildReminder, postGuildTemplate } from "../../../api";
|
import { postGuildReminder, postGuildTemplate, postUserReminder } from "../../../api";
|
||||||
import { useParams } from "wouter";
|
import { useParams } from "wouter";
|
||||||
import { useState } from "preact/hooks";
|
import { useState } from "preact/hooks";
|
||||||
import { ICON_FLASH_TIME } from "../../../consts";
|
import { ICON_FLASH_TIME } from "../../../consts";
|
||||||
import { useFlash } from "../../App/FlashContext";
|
import { useFlash } from "../../App/FlashContext";
|
||||||
|
import { useGuild } from "../../App/useGuild";
|
||||||
|
|
||||||
export const CreateButtonRow = () => {
|
export const CreateButtonRow = () => {
|
||||||
const { guild } = useParams();
|
const guild = useGuild();
|
||||||
const [reminder] = useReminder();
|
const [reminder] = useReminder();
|
||||||
|
|
||||||
const [recentlyCreated, setRecentlyCreated] = useState(false);
|
const [recentlyCreated, setRecentlyCreated] = useState(false);
|
||||||
@ -17,7 +18,7 @@ export const CreateButtonRow = () => {
|
|||||||
const flash = useFlash();
|
const flash = useFlash();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
...postGuildReminder(guild),
|
...(guild ? postGuildReminder(guild) : postUserReminder()),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
flash({
|
flash({
|
||||||
@ -29,9 +30,15 @@ export const CreateButtonRow = () => {
|
|||||||
message: "Reminder created",
|
message: "Reminder created",
|
||||||
type: "success",
|
type: "success",
|
||||||
});
|
});
|
||||||
|
if (guild) {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["GUILD_REMINDERS", guild],
|
queryKey: ["GUILD_REMINDERS", guild],
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ["USER_REMINDERS"],
|
||||||
|
});
|
||||||
|
}
|
||||||
setRecentlyCreated(true);
|
setRecentlyCreated(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setRecentlyCreated(false);
|
setRecentlyCreated(false);
|
||||||
|
@ -9,6 +9,7 @@ import { ReminderContext } from "./ReminderContext";
|
|||||||
import { useQuery } from "react-query";
|
import { useQuery } from "react-query";
|
||||||
import { useParams } from "wouter";
|
import { useParams } from "wouter";
|
||||||
import "./styles.scss";
|
import "./styles.scss";
|
||||||
|
import { useGuild } from "../App/useGuild";
|
||||||
|
|
||||||
function defaultReminder(): Reminder {
|
function defaultReminder(): Reminder {
|
||||||
return {
|
return {
|
||||||
@ -42,7 +43,7 @@ function defaultReminder(): Reminder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const CreateReminder = () => {
|
export const CreateReminder = () => {
|
||||||
const { guild } = useParams();
|
const guild = useGuild();
|
||||||
|
|
||||||
const [reminder, setReminder] = useState(defaultReminder());
|
const [reminder, setReminder] = useState(defaultReminder());
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
@ -7,8 +7,10 @@ import { useReminder } from "./ReminderContext";
|
|||||||
import { Attachment } from "./Attachment";
|
import { Attachment } from "./Attachment";
|
||||||
import { TTS } from "./TTS";
|
import { TTS } from "./TTS";
|
||||||
import { TimeInput } from "./TimeInput";
|
import { TimeInput } from "./TimeInput";
|
||||||
|
import { useGuild } from "../App/useGuild";
|
||||||
|
|
||||||
export const Settings = () => {
|
export const Settings = () => {
|
||||||
|
const guild = useGuild();
|
||||||
const { isSuccess: userFetched, data: userInfo } = useQuery(fetchUserInfo());
|
const { isSuccess: userFetched, data: userInfo } = useQuery(fetchUserInfo());
|
||||||
|
|
||||||
const [reminder, setReminder] = useReminder();
|
const [reminder, setReminder] = useReminder();
|
||||||
@ -19,6 +21,7 @@ export const Settings = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="column settings">
|
<div class="column settings">
|
||||||
|
{guild && (
|
||||||
<div class="field channel-field">
|
<div class="field channel-field">
|
||||||
<div class="collapses">
|
<div class="collapses">
|
||||||
<label class="label" for="channelOption">
|
<label class="label" for="channelOption">
|
||||||
@ -35,6 +38,7 @@ export const Settings = () => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
@ -35,8 +35,10 @@ type Database = MySql;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
SQLx,
|
#[allow(unused)]
|
||||||
Serenity,
|
SQLx(sqlx::Error),
|
||||||
|
#[allow(unused)]
|
||||||
|
Serenity(serenity::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn initialize(
|
pub async fn initialize(
|
||||||
@ -132,6 +134,8 @@ pub async fn initialize(
|
|||||||
routes::dashboard::api::user::get_user_info,
|
routes::dashboard::api::user::get_user_info,
|
||||||
routes::dashboard::api::user::update_user_info,
|
routes::dashboard::api::user::update_user_info,
|
||||||
routes::dashboard::api::user::get_user_guilds,
|
routes::dashboard::api::user::get_user_guilds,
|
||||||
|
routes::dashboard::api::user::get_reminders,
|
||||||
|
routes::dashboard::api::user::create_user_reminder,
|
||||||
routes::dashboard::api::guild::get_guild_info,
|
routes::dashboard::api::guild::get_guild_info,
|
||||||
routes::dashboard::api::guild::get_guild_channels,
|
routes::dashboard::api::guild::get_guild_channels,
|
||||||
routes::dashboard::api::guild::get_guild_roles,
|
routes::dashboard::api::guild::get_guild_roles,
|
||||||
|
@ -106,7 +106,7 @@ pub async fn get_reminders(
|
|||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time
|
reminders.utc_time
|
||||||
FROM reminders
|
FROM reminders
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
INNER JOIN channels ON channels.id = reminders.channel_id
|
||||||
WHERE `status` = 'pending' AND FIND_IN_SET(channels.channel, ?)",
|
WHERE `status` = 'pending' AND FIND_IN_SET(channels.channel, ?)",
|
||||||
channels
|
channels
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
mod guilds;
|
mod guilds;
|
||||||
|
mod models;
|
||||||
|
mod reminders;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
pub use guilds::*;
|
pub use guilds::*;
|
||||||
|
pub use reminders::*;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::CookieJar,
|
http::CookieJar,
|
||||||
serde::json::{json, Json, Value as JsonValue},
|
serde::json::{json, Json, Value as JsonValue},
|
||||||
|
@ -1,20 +1,230 @@
|
|||||||
use std::env;
|
use chrono::{naive::NaiveDateTime, Utc};
|
||||||
|
use futures::TryFutureExt;
|
||||||
use chrono_tz::Tz;
|
use rocket::serde::json::json;
|
||||||
use reqwest::Client;
|
|
||||||
use rocket::{
|
|
||||||
http::CookieJar,
|
|
||||||
serde::json::{json, Json, Value as JsonValue},
|
|
||||||
State,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serenity::{
|
use serenity::{client::Context, futures, model::id::UserId};
|
||||||
client::Context,
|
use sqlx::types::Json;
|
||||||
model::{
|
|
||||||
id::{GuildId, RoleId},
|
|
||||||
permissions::Permissions,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use sqlx::{MySql, Pool};
|
|
||||||
|
|
||||||
use crate::{consts::DISCORD_API, routes::JsonResult};
|
use crate::{
|
||||||
|
check_subscription,
|
||||||
|
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);
|
||||||
|
|
||||||
|
return Err(json!({"error": "Failed to configure channel for reminders."}));
|
||||||
|
}
|
||||||
|
|
||||||
|
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, user_id).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"}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,23 +1,48 @@
|
|||||||
use std::env;
|
|
||||||
|
|
||||||
use chrono_tz::Tz;
|
|
||||||
use reqwest::Client;
|
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::CookieJar,
|
http::CookieJar,
|
||||||
serde::json::{json, Json, Value as JsonValue},
|
serde::json::{json, Json},
|
||||||
State,
|
State,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serenity::{client::Context, model::id::UserId};
|
||||||
use serenity::{
|
|
||||||
client::Context,
|
|
||||||
model::{
|
|
||||||
id::{GuildId, RoleId},
|
|
||||||
permissions::Permissions,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use sqlx::{MySql, Pool};
|
use sqlx::{MySql, Pool};
|
||||||
|
|
||||||
use crate::{consts::DISCORD_API, routes::JsonResult};
|
use crate::{
|
||||||
|
guards::transaction::Transaction,
|
||||||
|
routes::{
|
||||||
|
dashboard::api::user::models::{create_reminder, Reminder},
|
||||||
|
JsonResult,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[post("/api/user/reminders", data = "<reminder>")]
|
||||||
|
pub async fn create_user_reminder(
|
||||||
|
reminder: Json<Reminder>,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
mut transaction: Transaction<'_>,
|
||||||
|
) -> JsonResult {
|
||||||
|
let user_id =
|
||||||
|
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
|
||||||
|
|
||||||
|
match create_reminder(
|
||||||
|
ctx.inner(),
|
||||||
|
&mut transaction,
|
||||||
|
UserId::new(user_id),
|
||||||
|
reminder.into_inner(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(r) => match transaction.commit().await {
|
||||||
|
Ok(_) => Ok(r),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Couldn't commit transaction: {:?}", e);
|
||||||
|
json_err!("Couldn't commit transaction.")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/api/user/reminders")]
|
#[get("/api/user/reminders")]
|
||||||
pub async fn get_reminders(
|
pub async fn get_reminders(
|
||||||
@ -25,5 +50,56 @@ pub async fn get_reminders(
|
|||||||
ctx: &State<Context>,
|
ctx: &State<Context>,
|
||||||
pool: &State<Pool<MySql>>,
|
pool: &State<Pool<MySql>>,
|
||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
Ok(json! {})
|
let user_id =
|
||||||
|
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
|
||||||
|
let channel = UserId::new(user_id).create_dm_channel(ctx.inner()).await;
|
||||||
|
|
||||||
|
match channel {
|
||||||
|
Ok(channel) => 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,
|
||||||
|
IFNULL(reminders.embed_fields, '[]') AS 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
|
||||||
|
INNER JOIN channels ON channels.id = reminders.channel_id
|
||||||
|
WHERE `status` = 'pending' AND channels.channel = ?
|
||||||
|
",
|
||||||
|
channel.id.get()
|
||||||
|
)
|
||||||
|
.fetch_all(pool.inner())
|
||||||
|
.await
|
||||||
|
.map(|r| Ok(json!(r)))
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
warn!("Failed to complete SQL query: {:?}", e);
|
||||||
|
|
||||||
|
json_err!("Could not load reminders")
|
||||||
|
}),
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Couldn't get DM channel: {:?}", e);
|
||||||
|
|
||||||
|
json_err!("Could not find a DM channel")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ fn interval_default() -> Unset<Option<u32>> {
|
|||||||
|
|
||||||
#[derive(sqlx::Type)]
|
#[derive(sqlx::Type)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
struct Attachment(Vec<u8>);
|
pub struct Attachment(Vec<u8>);
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Attachment {
|
impl<'de> Deserialize<'de> for Attachment {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Attachment, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Attachment, D::Error>
|
||||||
@ -605,11 +605,13 @@ async fn create_database_channel(
|
|||||||
|
|
||||||
match row {
|
match row {
|
||||||
Ok(row) => {
|
Ok(row) => {
|
||||||
if row.webhook_token.is_none() || row.webhook_id.is_none() {
|
let is_dm =
|
||||||
|
channel.to_channel(&ctx).await.map_err(|e| Error::Serenity(e))?.private().is_some();
|
||||||
|
if !is_dm && (row.webhook_token.is_none() || row.webhook_id.is_none()) {
|
||||||
let webhook = channel
|
let webhook = channel
|
||||||
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::Serenity)?;
|
.map_err(|e| Error::Serenity(e))?;
|
||||||
|
|
||||||
let token = webhook.token.unwrap();
|
let token = webhook.token.unwrap();
|
||||||
|
|
||||||
@ -623,7 +625,7 @@ async fn create_database_channel(
|
|||||||
)
|
)
|
||||||
.execute(transaction.executor())
|
.execute(transaction.executor())
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::SQLx)?;
|
.map_err(|e| Error::SQLx(e))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -634,7 +636,7 @@ async fn create_database_channel(
|
|||||||
let webhook = channel
|
let webhook = channel
|
||||||
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::Serenity)?;
|
.map_err(|e| Error::Serenity(e))?;
|
||||||
|
|
||||||
let token = webhook.token.unwrap();
|
let token = webhook.token.unwrap();
|
||||||
|
|
||||||
@ -653,18 +655,18 @@ async fn create_database_channel(
|
|||||||
)
|
)
|
||||||
.execute(transaction.executor())
|
.execute(transaction.executor())
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::SQLx)?;
|
.map_err(|e| Error::SQLx(e))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(_) => Err(Error::SQLx),
|
Err(e) => Err(Error::SQLx(e)),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let row = sqlx::query!("SELECT id FROM channels WHERE channel = ?", channel.get())
|
let row = sqlx::query!("SELECT id FROM channels WHERE channel = ?", channel.get())
|
||||||
.fetch_one(transaction.executor())
|
.fetch_one(transaction.executor())
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::SQLx)?;
|
.map_err(|e| Error::SQLx(e))?;
|
||||||
|
|
||||||
Ok(row.id)
|
Ok(row.id)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user