Restructure code
This commit is contained in:
parent
55acc8fd16
commit
ca13fd4fa7
@ -121,19 +121,19 @@ pub async fn initialize(
|
|||||||
routes![
|
routes![
|
||||||
routes::dashboard::dashboard,
|
routes::dashboard::dashboard,
|
||||||
routes::dashboard::dashboard_home,
|
routes::dashboard::dashboard_home,
|
||||||
routes::dashboard::user::get_user_info,
|
routes::dashboard::api::user::get_user_info,
|
||||||
routes::dashboard::user::update_user_info,
|
routes::dashboard::api::user::update_user_info,
|
||||||
routes::dashboard::user::get_user_guilds,
|
routes::dashboard::api::user::get_user_guilds,
|
||||||
routes::dashboard::guild::get_guild_info,
|
routes::dashboard::api::guild::get_guild_info,
|
||||||
routes::dashboard::guild::get_guild_channels,
|
routes::dashboard::api::guild::get_guild_channels,
|
||||||
routes::dashboard::guild::get_guild_roles,
|
routes::dashboard::api::guild::get_guild_roles,
|
||||||
routes::dashboard::guild::get_reminder_templates,
|
routes::dashboard::api::guild::get_reminder_templates,
|
||||||
routes::dashboard::guild::create_reminder_template,
|
routes::dashboard::api::guild::create_reminder_template,
|
||||||
routes::dashboard::guild::delete_reminder_template,
|
routes::dashboard::api::guild::delete_reminder_template,
|
||||||
routes::dashboard::guild::create_guild_reminder,
|
routes::dashboard::api::guild::create_guild_reminder,
|
||||||
routes::dashboard::guild::get_reminders,
|
routes::dashboard::api::guild::get_reminders,
|
||||||
routes::dashboard::guild::edit_reminder,
|
routes::dashboard::api::guild::edit_reminder,
|
||||||
routes::dashboard::guild::delete_reminder,
|
routes::dashboard::api::guild::delete_reminder,
|
||||||
routes::dashboard::export::export_reminders,
|
routes::dashboard::export::export_reminders,
|
||||||
routes::dashboard::export::export_reminder_templates,
|
routes::dashboard::export::export_reminder_templates,
|
||||||
routes::dashboard::export::export_todos,
|
routes::dashboard::export::export_todos,
|
||||||
|
61
web/src/routes/dashboard/api/guild/channels.rs
Normal file
61
web/src/routes/dashboard/api/guild/channels.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use rocket::{http::CookieJar, serde::json::json, State};
|
||||||
|
use serde::Serialize;
|
||||||
|
use serenity::{
|
||||||
|
client::Context,
|
||||||
|
model::{
|
||||||
|
channel::GuildChannel,
|
||||||
|
id::{ChannelId, GuildId},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{check_authorization, routes::JsonResult};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ChannelInfo {
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
webhook_avatar: Option<String>,
|
||||||
|
webhook_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/guild/<id>/channels")]
|
||||||
|
pub async fn get_guild_channels(
|
||||||
|
id: u64,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
) -> JsonResult {
|
||||||
|
offline!(Ok(json!(vec![ChannelInfo {
|
||||||
|
name: "general".to_string(),
|
||||||
|
id: "1".to_string(),
|
||||||
|
webhook_avatar: None,
|
||||||
|
webhook_name: None,
|
||||||
|
}])));
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
match GuildId(id).to_guild_cached(ctx.inner()) {
|
||||||
|
Some(guild) => {
|
||||||
|
let mut channels = guild
|
||||||
|
.channels
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id.to_owned(), c)))
|
||||||
|
.filter(|(_, channel)| channel.is_text_based())
|
||||||
|
.collect::<Vec<(ChannelId, GuildChannel)>>();
|
||||||
|
|
||||||
|
channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position));
|
||||||
|
|
||||||
|
let channel_info = channels
|
||||||
|
.iter()
|
||||||
|
.map(|(channel_id, channel)| ChannelInfo {
|
||||||
|
name: channel.name.to_string(),
|
||||||
|
id: channel_id.to_string(),
|
||||||
|
webhook_avatar: None,
|
||||||
|
webhook_name: None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<ChannelInfo>>();
|
||||||
|
|
||||||
|
Ok(json!(channel_info))
|
||||||
|
}
|
||||||
|
|
||||||
|
None => json_err!("Bot not in guild"),
|
||||||
|
}
|
||||||
|
}
|
42
web/src/routes/dashboard/api/guild/mod.rs
Normal file
42
web/src/routes/dashboard/api/guild/mod.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
mod channels;
|
||||||
|
mod reminders;
|
||||||
|
mod roles;
|
||||||
|
mod templates;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
pub use channels::*;
|
||||||
|
pub use reminders::*;
|
||||||
|
use rocket::{http::CookieJar, serde::json::json, State};
|
||||||
|
pub use roles::*;
|
||||||
|
use serenity::{
|
||||||
|
client::Context,
|
||||||
|
model::id::{GuildId, RoleId},
|
||||||
|
};
|
||||||
|
pub use templates::*;
|
||||||
|
|
||||||
|
use crate::{check_authorization, routes::JsonResult};
|
||||||
|
|
||||||
|
#[get("/api/guild/<id>")]
|
||||||
|
pub async fn get_guild_info(id: u64, cookies: &CookieJar<'_>, ctx: &State<Context>) -> JsonResult {
|
||||||
|
offline!(Ok(json!({ "patreon": true, "name": "Guild" })));
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
match GuildId(id).to_guild_cached(ctx.inner()) {
|
||||||
|
Some(guild) => {
|
||||||
|
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||||
|
.member(&ctx.inner(), guild.owner_id)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let patreon = member_res.map_or(false, |member| {
|
||||||
|
member
|
||||||
|
.roles
|
||||||
|
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(json!({ "patreon": patreon, "name": guild.name }))
|
||||||
|
}
|
||||||
|
|
||||||
|
None => json_err!("Bot not in guild"),
|
||||||
|
}
|
||||||
|
}
|
373
web/src/routes/dashboard/api/guild/reminders.rs
Normal file
373
web/src/routes/dashboard/api/guild/reminders.rs
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
use rocket::{
|
||||||
|
http::CookieJar,
|
||||||
|
serde::json::{json, Json},
|
||||||
|
State,
|
||||||
|
};
|
||||||
|
use serenity::{
|
||||||
|
client::Context,
|
||||||
|
model::id::{ChannelId, GuildId, UserId},
|
||||||
|
};
|
||||||
|
use sqlx::{MySql, Pool};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
check_authorization, check_guild_subscription, check_subscription,
|
||||||
|
consts::MIN_INTERVAL,
|
||||||
|
guards::transaction::Transaction,
|
||||||
|
routes::{
|
||||||
|
dashboard::{
|
||||||
|
create_database_channel, create_reminder, DeleteReminder, PatchReminder, Reminder,
|
||||||
|
},
|
||||||
|
JsonResult,
|
||||||
|
},
|
||||||
|
Database,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[post("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||||
|
pub async fn create_guild_reminder(
|
||||||
|
id: u64,
|
||||||
|
reminder: Json<Reminder>,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
mut transaction: Transaction<'_>,
|
||||||
|
) -> JsonResult {
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
let user_id =
|
||||||
|
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
|
||||||
|
|
||||||
|
match create_reminder(
|
||||||
|
ctx.inner(),
|
||||||
|
&mut transaction,
|
||||||
|
GuildId(id),
|
||||||
|
UserId(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/guild/<id>/reminders")]
|
||||||
|
pub async fn get_reminders(
|
||||||
|
id: u64,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonResult {
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
||||||
|
|
||||||
|
match channels_res {
|
||||||
|
Ok(channels) => {
|
||||||
|
let channels = channels
|
||||||
|
.keys()
|
||||||
|
.into_iter()
|
||||||
|
.map(|k| k.as_u64().to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(",");
|
||||||
|
|
||||||
|
sqlx::query_as_unchecked!(
|
||||||
|
Reminder,
|
||||||
|
"SELECT
|
||||||
|
reminders.attachment,
|
||||||
|
reminders.attachment_name,
|
||||||
|
reminders.avatar,
|
||||||
|
channels.channel,
|
||||||
|
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.restartable,
|
||||||
|
reminders.tts,
|
||||||
|
reminders.uid,
|
||||||
|
reminders.username,
|
||||||
|
reminders.utc_time
|
||||||
|
FROM reminders
|
||||||
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
|
WHERE `status` = 'pending' AND FIND_IN_SET(channels.channel, ?)",
|
||||||
|
channels
|
||||||
|
)
|
||||||
|
.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!("Could not fetch channels from {}: {:?}", id, e);
|
||||||
|
|
||||||
|
Ok(json!([]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[patch("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||||
|
pub async fn edit_reminder(
|
||||||
|
id: u64,
|
||||||
|
reminder: Json<PatchReminder>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
mut transaction: Transaction<'_>,
|
||||||
|
pool: &State<Pool<Database>>,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
) -> JsonResult {
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
let mut error = vec![];
|
||||||
|
|
||||||
|
let user_id =
|
||||||
|
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
|
||||||
|
|
||||||
|
if reminder.message_ok() {
|
||||||
|
update_field!(transaction.executor(), error, reminder.[
|
||||||
|
content,
|
||||||
|
embed_author,
|
||||||
|
embed_description,
|
||||||
|
embed_footer,
|
||||||
|
embed_title,
|
||||||
|
embed_fields,
|
||||||
|
username
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
error.push("Message exceeds limits.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
update_field!(transaction.executor(), error, reminder.[
|
||||||
|
attachment,
|
||||||
|
attachment_name,
|
||||||
|
avatar,
|
||||||
|
embed_author_url,
|
||||||
|
embed_color,
|
||||||
|
embed_footer_url,
|
||||||
|
embed_image_url,
|
||||||
|
embed_thumbnail_url,
|
||||||
|
enabled,
|
||||||
|
expires,
|
||||||
|
name,
|
||||||
|
restartable,
|
||||||
|
tts,
|
||||||
|
utc_time
|
||||||
|
]);
|
||||||
|
|
||||||
|
if reminder.interval_days.flatten().is_some()
|
||||||
|
|| reminder.interval_months.flatten().is_some()
|
||||||
|
|| reminder.interval_seconds.flatten().is_some()
|
||||||
|
{
|
||||||
|
if check_guild_subscription(&ctx.inner(), id).await
|
||||||
|
|| check_subscription(&ctx.inner(), user_id).await
|
||||||
|
{
|
||||||
|
let new_interval_length = match reminder.interval_days {
|
||||||
|
Some(interval) => interval.unwrap_or(0),
|
||||||
|
None => sqlx::query!(
|
||||||
|
"SELECT interval_days AS days FROM reminders WHERE uid = ?",
|
||||||
|
reminder.uid
|
||||||
|
)
|
||||||
|
.fetch_one(transaction.executor())
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
warn!("Error updating reminder interval: {:?}", e);
|
||||||
|
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
|
||||||
|
})?
|
||||||
|
.days
|
||||||
|
.unwrap_or(0),
|
||||||
|
} * 86400 + match reminder.interval_months {
|
||||||
|
Some(interval) => interval.unwrap_or(0),
|
||||||
|
None => sqlx::query!(
|
||||||
|
"SELECT interval_months AS months FROM reminders WHERE uid = ?",
|
||||||
|
reminder.uid
|
||||||
|
)
|
||||||
|
.fetch_one(transaction.executor())
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
warn!("Error updating reminder interval: {:?}", e);
|
||||||
|
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
|
||||||
|
})?
|
||||||
|
.months
|
||||||
|
.unwrap_or(0),
|
||||||
|
} * 2592000 + match reminder.interval_seconds {
|
||||||
|
Some(interval) => interval.unwrap_or(0),
|
||||||
|
None => sqlx::query!(
|
||||||
|
"SELECT interval_seconds AS seconds FROM reminders WHERE uid = ?",
|
||||||
|
reminder.uid
|
||||||
|
)
|
||||||
|
.fetch_one(transaction.executor())
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
warn!("Error updating reminder interval: {:?}", e);
|
||||||
|
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
|
||||||
|
})?
|
||||||
|
.seconds
|
||||||
|
.unwrap_or(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
if new_interval_length < *MIN_INTERVAL {
|
||||||
|
error.push(String::from("New interval is too short."));
|
||||||
|
} else {
|
||||||
|
update_field!(transaction.executor(), error, reminder.[
|
||||||
|
interval_days,
|
||||||
|
interval_months,
|
||||||
|
interval_seconds
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reminder.channel > 0 {
|
||||||
|
let channel = ChannelId(reminder.channel).to_channel_cached(&ctx.inner());
|
||||||
|
match channel {
|
||||||
|
Some(channel) => {
|
||||||
|
let channel_matches_guild = channel.guild().map_or(false, |c| c.guild_id.0 == id);
|
||||||
|
|
||||||
|
if !channel_matches_guild {
|
||||||
|
warn!(
|
||||||
|
"Error in `edit_reminder`: channel {:?} not found for guild {}",
|
||||||
|
reminder.channel, id
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(json!({"error": "Channel not found"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = create_database_channel(
|
||||||
|
ctx.inner(),
|
||||||
|
ChannelId(reminder.channel),
|
||||||
|
&mut 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. Please check the bot permissions"}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = channel.unwrap();
|
||||||
|
|
||||||
|
match sqlx::query!(
|
||||||
|
"UPDATE reminders SET channel_id = ? WHERE uid = ?",
|
||||||
|
channel,
|
||||||
|
reminder.uid
|
||||||
|
)
|
||||||
|
.execute(transaction.executor())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error setting channel: {:?}", e);
|
||||||
|
|
||||||
|
error.push("Couldn't set channel".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
warn!(
|
||||||
|
"Error in `edit_reminder`: channel {:?} not found for guild {}",
|
||||||
|
reminder.channel, id
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(json!({"error": "Channel not found"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = transaction.commit().await {
|
||||||
|
warn!("Couldn't commit transaction: {:?}", e);
|
||||||
|
return json_err!("Couldn't commit transaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
match sqlx::query_as_unchecked!(
|
||||||
|
Reminder,
|
||||||
|
"SELECT reminders.attachment,
|
||||||
|
reminders.attachment_name,
|
||||||
|
reminders.avatar,
|
||||||
|
channels.channel,
|
||||||
|
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.restartable,
|
||||||
|
reminders.tts,
|
||||||
|
reminders.uid,
|
||||||
|
reminders.username,
|
||||||
|
reminders.utc_time
|
||||||
|
FROM reminders
|
||||||
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
|
WHERE uid = ?",
|
||||||
|
reminder.uid
|
||||||
|
)
|
||||||
|
.fetch_one(pool.inner())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(reminder) => Ok(json!({"reminder": reminder, "errors": error})),
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error exiting `edit_reminder': {:?}", e);
|
||||||
|
|
||||||
|
Err(json!({"reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"]}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[delete("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||||
|
pub async fn delete_reminder(
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
id: u64,
|
||||||
|
reminder: Json<DeleteReminder>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonResult {
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
match sqlx::query!("UPDATE reminders SET `status` = 'deleted' WHERE uid = ?", reminder.uid)
|
||||||
|
.execute(pool.inner())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => Ok(json!({})),
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error in `delete_reminder`: {:?}", e);
|
||||||
|
|
||||||
|
Err(json!({"error": "Could not delete reminder"}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
web/src/routes/dashboard/api/guild/roles.rs
Normal file
35
web/src/routes/dashboard/api/guild/roles.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use rocket::{http::CookieJar, serde::json::json, State};
|
||||||
|
use serde::Serialize;
|
||||||
|
use serenity::client::Context;
|
||||||
|
|
||||||
|
use crate::{check_authorization, routes::JsonResult};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct RoleInfo {
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/guild/<id>/roles")]
|
||||||
|
pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State<Context>) -> JsonResult {
|
||||||
|
offline!(Ok(json!(vec![RoleInfo { name: "@everyone".to_string(), id: "1".to_string() }])));
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
let roles_res = ctx.cache.guild_roles(id);
|
||||||
|
|
||||||
|
match roles_res {
|
||||||
|
Some(roles) => {
|
||||||
|
let roles = roles
|
||||||
|
.iter()
|
||||||
|
.map(|(_, r)| RoleInfo { id: r.id.to_string(), name: r.name.to_string() })
|
||||||
|
.collect::<Vec<RoleInfo>>();
|
||||||
|
|
||||||
|
Ok(json!(roles))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warn!("Could not fetch roles from {}", id);
|
||||||
|
|
||||||
|
json_err!("Could not get roles")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
181
web/src/routes/dashboard/api/guild/templates.rs
Normal file
181
web/src/routes/dashboard/api/guild/templates.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
use rocket::{
|
||||||
|
http::CookieJar,
|
||||||
|
serde::json::{json, Json},
|
||||||
|
State,
|
||||||
|
};
|
||||||
|
use serenity::client::Context;
|
||||||
|
use sqlx::{MySql, Pool};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
check_authorization,
|
||||||
|
consts::{
|
||||||
|
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_URL_LENGTH, MAX_USERNAME_LENGTH,
|
||||||
|
},
|
||||||
|
routes::{
|
||||||
|
dashboard::{template_name_default, DeleteReminderTemplate, ReminderTemplate},
|
||||||
|
JsonResult,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[get("/api/guild/<id>/templates")]
|
||||||
|
pub async fn get_reminder_templates(
|
||||||
|
id: u64,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonResult {
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
match sqlx::query_as_unchecked!(
|
||||||
|
ReminderTemplate,
|
||||||
|
"SELECT * FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.fetch_all(pool.inner())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(templates) => Ok(json!(templates)),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Could not fetch templates from {}: {:?}", id, e);
|
||||||
|
|
||||||
|
json_err!("Could not get templates")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/api/guild/<id>/templates", data = "<reminder_template>")]
|
||||||
|
pub async fn create_reminder_template(
|
||||||
|
id: u64,
|
||||||
|
reminder_template: Json<ReminderTemplate>,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonResult {
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
// validate lengths
|
||||||
|
check_length!(MAX_CONTENT_LENGTH, reminder_template.content);
|
||||||
|
check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder_template.embed_description);
|
||||||
|
check_length!(MAX_EMBED_TITLE_LENGTH, reminder_template.embed_title);
|
||||||
|
check_length!(MAX_EMBED_AUTHOR_LENGTH, reminder_template.embed_author);
|
||||||
|
check_length!(MAX_EMBED_FOOTER_LENGTH, reminder_template.embed_footer);
|
||||||
|
check_length_opt!(MAX_EMBED_FIELDS, reminder_template.embed_fields);
|
||||||
|
if let Some(fields) = &reminder_template.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_USERNAME_LENGTH, reminder_template.username);
|
||||||
|
check_length_opt!(
|
||||||
|
MAX_URL_LENGTH,
|
||||||
|
reminder_template.embed_footer_url,
|
||||||
|
reminder_template.embed_thumbnail_url,
|
||||||
|
reminder_template.embed_author_url,
|
||||||
|
reminder_template.embed_image_url,
|
||||||
|
reminder_template.avatar
|
||||||
|
);
|
||||||
|
|
||||||
|
// validate urls
|
||||||
|
check_url_opt!(
|
||||||
|
reminder_template.embed_footer_url,
|
||||||
|
reminder_template.embed_thumbnail_url,
|
||||||
|
reminder_template.embed_author_url,
|
||||||
|
reminder_template.embed_image_url,
|
||||||
|
reminder_template.avatar
|
||||||
|
);
|
||||||
|
|
||||||
|
let name = if reminder_template.name.is_empty() {
|
||||||
|
template_name_default()
|
||||||
|
} else {
|
||||||
|
reminder_template.name.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
match sqlx::query!(
|
||||||
|
"INSERT INTO reminder_template
|
||||||
|
(guild_id,
|
||||||
|
name,
|
||||||
|
attachment,
|
||||||
|
attachment_name,
|
||||||
|
avatar,
|
||||||
|
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,
|
||||||
|
interval_seconds,
|
||||||
|
interval_days,
|
||||||
|
interval_months,
|
||||||
|
tts,
|
||||||
|
username
|
||||||
|
) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?, ?, ?)",
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
reminder_template.attachment,
|
||||||
|
reminder_template.attachment_name,
|
||||||
|
reminder_template.avatar,
|
||||||
|
reminder_template.content,
|
||||||
|
reminder_template.embed_author,
|
||||||
|
reminder_template.embed_author_url,
|
||||||
|
reminder_template.embed_color,
|
||||||
|
reminder_template.embed_description,
|
||||||
|
reminder_template.embed_footer,
|
||||||
|
reminder_template.embed_footer_url,
|
||||||
|
reminder_template.embed_image_url,
|
||||||
|
reminder_template.embed_thumbnail_url,
|
||||||
|
reminder_template.embed_title,
|
||||||
|
reminder_template.embed_fields,
|
||||||
|
reminder_template.interval_seconds,
|
||||||
|
reminder_template.interval_days,
|
||||||
|
reminder_template.interval_months,
|
||||||
|
reminder_template.tts,
|
||||||
|
reminder_template.username,
|
||||||
|
)
|
||||||
|
.fetch_all(pool.inner())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => Ok(json!({})),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Could not create template for {}: {:?}", id, e);
|
||||||
|
|
||||||
|
json_err!("Could not create template")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[delete("/api/guild/<id>/templates", data = "<delete_reminder_template>")]
|
||||||
|
pub async fn delete_reminder_template(
|
||||||
|
id: u64,
|
||||||
|
delete_reminder_template: Json<DeleteReminderTemplate>,
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonResult {
|
||||||
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
|
match sqlx::query!(
|
||||||
|
"DELETE FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND id = ?",
|
||||||
|
id, delete_reminder_template.id
|
||||||
|
)
|
||||||
|
.fetch_all(pool.inner())
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
Ok(json!({}))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Could not delete template from {}: {:?}", id, e);
|
||||||
|
|
||||||
|
json_err!("Could not delete template")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
web/src/routes/dashboard/api/mod.rs
Normal file
2
web/src/routes/dashboard/api/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod guild;
|
||||||
|
pub mod user;
|
81
web/src/routes/dashboard/api/user/guilds.rs
Normal file
81
web/src/routes/dashboard/api/user/guilds.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use reqwest::Client;
|
||||||
|
use rocket::{
|
||||||
|
http::CookieJar,
|
||||||
|
serde::json::{json, Value as JsonValue},
|
||||||
|
State,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serenity::model::{id::GuildId, permissions::Permissions};
|
||||||
|
|
||||||
|
use crate::consts::DISCORD_API;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct GuildInfo {
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct PartialGuild {
|
||||||
|
pub id: GuildId,
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub owner: bool,
|
||||||
|
#[serde(rename = "permissions_new")]
|
||||||
|
pub permissions: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/user/guilds")]
|
||||||
|
pub async fn get_user_guilds(cookies: &CookieJar<'_>, reqwest_client: &State<Client>) -> JsonValue {
|
||||||
|
offline!(json!(vec![GuildInfo { id: "1".to_string(), name: "Guild".to_string() }]));
|
||||||
|
|
||||||
|
if let Some(access_token) = cookies.get_private("access_token") {
|
||||||
|
let request_res = reqwest_client
|
||||||
|
.get(format!("{}/users/@me/guilds", DISCORD_API))
|
||||||
|
.bearer_auth(access_token.value())
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match request_res {
|
||||||
|
Ok(response) => {
|
||||||
|
let guilds_res = response.json::<Vec<PartialGuild>>().await;
|
||||||
|
|
||||||
|
match guilds_res {
|
||||||
|
Ok(guilds) => {
|
||||||
|
let reduced_guilds = guilds
|
||||||
|
.iter()
|
||||||
|
.filter(|g| {
|
||||||
|
g.owner
|
||||||
|
|| g.permissions.as_ref().map_or(false, |p| {
|
||||||
|
let permissions =
|
||||||
|
Permissions::from_bits_truncate(p.parse().unwrap());
|
||||||
|
|
||||||
|
permissions.manage_messages()
|
||||||
|
|| permissions.manage_guild()
|
||||||
|
|| permissions.administrator()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(|g| GuildInfo { id: g.id.to_string(), name: g.name.to_string() })
|
||||||
|
.collect::<Vec<GuildInfo>>();
|
||||||
|
|
||||||
|
json!(reduced_guilds)
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error constructing user from request: {:?}", e);
|
||||||
|
|
||||||
|
json!({"error": "Could not get user details"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error getting user guilds: {:?}", e);
|
||||||
|
|
||||||
|
json!({"error": "Could not reach Discord"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
json!({"error": "Not authorized"})
|
||||||
|
}
|
||||||
|
}
|
97
web/src/routes/dashboard/api/user/mod.rs
Normal file
97
web/src/routes/dashboard/api/user/mod.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
mod guilds;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use chrono_tz::Tz;
|
||||||
|
pub use guilds::*;
|
||||||
|
use rocket::{
|
||||||
|
http::CookieJar,
|
||||||
|
serde::json::{json, Json, Value as JsonValue},
|
||||||
|
State,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serenity::{
|
||||||
|
client::Context,
|
||||||
|
model::id::{GuildId, RoleId},
|
||||||
|
};
|
||||||
|
use sqlx::{MySql, Pool};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct UserInfo {
|
||||||
|
name: String,
|
||||||
|
patreon: bool,
|
||||||
|
timezone: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct UpdateUser {
|
||||||
|
timezone: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/api/user")]
|
||||||
|
pub async fn get_user_info(
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonValue {
|
||||||
|
offline!(json!(UserInfo { name: "Discord".to_string(), patreon: true, timezone: None }));
|
||||||
|
|
||||||
|
if let Some(user_id) =
|
||||||
|
cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
|
||||||
|
{
|
||||||
|
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||||
|
.member(&ctx.inner(), user_id)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let timezone = sqlx::query!(
|
||||||
|
"SELECT IFNULL(timezone, 'UTC') AS timezone FROM users WHERE user = ?",
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
.fetch_one(pool.inner())
|
||||||
|
.await
|
||||||
|
.map_or(None, |q| Some(q.timezone));
|
||||||
|
|
||||||
|
let user_info = UserInfo {
|
||||||
|
name: cookies
|
||||||
|
.get_private("username")
|
||||||
|
.map_or("DiscordUser#0000".to_string(), |c| c.value().to_string()),
|
||||||
|
patreon: member_res.map_or(false, |member| {
|
||||||
|
member
|
||||||
|
.roles
|
||||||
|
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||||
|
}),
|
||||||
|
timezone,
|
||||||
|
};
|
||||||
|
|
||||||
|
json!(user_info)
|
||||||
|
} else {
|
||||||
|
json!({"error": "Not authorized"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[patch("/api/user", data = "<user>")]
|
||||||
|
pub async fn update_user_info(
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
user: Json<UpdateUser>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonValue {
|
||||||
|
if let Some(user_id) =
|
||||||
|
cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
|
||||||
|
{
|
||||||
|
if user.timezone.parse::<Tz>().is_ok() {
|
||||||
|
let _ = sqlx::query!(
|
||||||
|
"UPDATE users SET timezone = ? WHERE user = ?",
|
||||||
|
user.timezone,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
|
.execute(pool.inner())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
json!({})
|
||||||
|
} else {
|
||||||
|
json!({"error": "Timezone not recognized"})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
json!({"error": "Not authorized"})
|
||||||
|
}
|
||||||
|
}
|
20
web/src/routes/dashboard/api/user/models.rs
Normal file
20
web/src/routes/dashboard/api/user/models.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
use chrono_tz::Tz;
|
||||||
|
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},
|
||||||
|
permissions::Permissions,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use sqlx::{MySql, Pool};
|
||||||
|
|
||||||
|
use crate::{consts::DISCORD_API, routes::JsonResult};
|
29
web/src/routes/dashboard/api/user/reminders.rs
Normal file
29
web/src/routes/dashboard/api/user/reminders.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
use chrono_tz::Tz;
|
||||||
|
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},
|
||||||
|
permissions::Permissions,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use sqlx::{MySql, Pool};
|
||||||
|
|
||||||
|
use crate::{consts::DISCORD_API, routes::JsonResult};
|
||||||
|
|
||||||
|
#[get("/api/user/reminders")]
|
||||||
|
pub async fn get_reminders(
|
||||||
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
|
pool: &State<Pool<MySql>>,
|
||||||
|
) -> JsonResult {
|
||||||
|
Ok(json! {})
|
||||||
|
}
|
@ -1,650 +1 @@
|
|||||||
use std::env;
|
|
||||||
|
|
||||||
use rocket::{
|
|
||||||
http::CookieJar,
|
|
||||||
serde::json::{json, Json},
|
|
||||||
State,
|
|
||||||
};
|
|
||||||
use serde::Serialize;
|
|
||||||
use serenity::{
|
|
||||||
client::Context,
|
|
||||||
model::{
|
|
||||||
channel::GuildChannel,
|
|
||||||
id::{ChannelId, GuildId, RoleId, UserId},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use sqlx::{MySql, Pool};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
check_authorization, check_guild_subscription, check_subscription,
|
|
||||||
consts::{
|
|
||||||
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_URL_LENGTH, MAX_USERNAME_LENGTH,
|
|
||||||
MIN_INTERVAL,
|
|
||||||
},
|
|
||||||
guards::transaction::Transaction,
|
|
||||||
routes::{
|
|
||||||
dashboard::{
|
|
||||||
create_database_channel, create_reminder, template_name_default, DeleteReminder,
|
|
||||||
DeleteReminderTemplate, PatchReminder, Reminder, ReminderTemplate,
|
|
||||||
},
|
|
||||||
JsonResult,
|
|
||||||
},
|
|
||||||
Database,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct ChannelInfo {
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
webhook_avatar: Option<String>,
|
|
||||||
webhook_name: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api/guild/<id>")]
|
|
||||||
pub async fn get_guild_info(id: u64, cookies: &CookieJar<'_>, ctx: &State<Context>) -> JsonResult {
|
|
||||||
offline!(Ok(json!({ "patreon": true, "name": "Guild" })));
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
|
||||||
Some(guild) => {
|
|
||||||
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
|
||||||
.member(&ctx.inner(), guild.owner_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let patreon = member_res.map_or(false, |member| {
|
|
||||||
member
|
|
||||||
.roles
|
|
||||||
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(json!({ "patreon": patreon, "name": guild.name }))
|
|
||||||
}
|
|
||||||
|
|
||||||
None => json_err!("Bot not in guild"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api/guild/<id>/channels")]
|
|
||||||
pub async fn get_guild_channels(
|
|
||||||
id: u64,
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
) -> JsonResult {
|
|
||||||
offline!(Ok(json!(vec![ChannelInfo {
|
|
||||||
name: "general".to_string(),
|
|
||||||
id: "1".to_string(),
|
|
||||||
webhook_avatar: None,
|
|
||||||
webhook_name: None,
|
|
||||||
}])));
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
|
||||||
Some(guild) => {
|
|
||||||
let mut channels = guild
|
|
||||||
.channels
|
|
||||||
.iter()
|
|
||||||
.filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id.to_owned(), c)))
|
|
||||||
.filter(|(_, channel)| channel.is_text_based())
|
|
||||||
.collect::<Vec<(ChannelId, GuildChannel)>>();
|
|
||||||
|
|
||||||
channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position));
|
|
||||||
|
|
||||||
let channel_info = channels
|
|
||||||
.iter()
|
|
||||||
.map(|(channel_id, channel)| ChannelInfo {
|
|
||||||
name: channel.name.to_string(),
|
|
||||||
id: channel_id.to_string(),
|
|
||||||
webhook_avatar: None,
|
|
||||||
webhook_name: None,
|
|
||||||
})
|
|
||||||
.collect::<Vec<ChannelInfo>>();
|
|
||||||
|
|
||||||
Ok(json!(channel_info))
|
|
||||||
}
|
|
||||||
|
|
||||||
None => json_err!("Bot not in guild"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct RoleInfo {
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api/guild/<id>/roles")]
|
|
||||||
pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State<Context>) -> JsonResult {
|
|
||||||
offline!(Ok(json!(vec![RoleInfo { name: "@everyone".to_string(), id: "1".to_string() }])));
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
let roles_res = ctx.cache.guild_roles(id);
|
|
||||||
|
|
||||||
match roles_res {
|
|
||||||
Some(roles) => {
|
|
||||||
let roles = roles
|
|
||||||
.iter()
|
|
||||||
.map(|(_, r)| RoleInfo { id: r.id.to_string(), name: r.name.to_string() })
|
|
||||||
.collect::<Vec<RoleInfo>>();
|
|
||||||
|
|
||||||
Ok(json!(roles))
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
warn!("Could not fetch roles from {}", id);
|
|
||||||
|
|
||||||
json_err!("Could not get roles")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api/guild/<id>/templates")]
|
|
||||||
pub async fn get_reminder_templates(
|
|
||||||
id: u64,
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
pool: &State<Pool<MySql>>,
|
|
||||||
) -> JsonResult {
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
match sqlx::query_as_unchecked!(
|
|
||||||
ReminderTemplate,
|
|
||||||
"SELECT * FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
|
||||||
id
|
|
||||||
)
|
|
||||||
.fetch_all(pool.inner())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(templates) => Ok(json!(templates)),
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not fetch templates from {}: {:?}", id, e);
|
|
||||||
|
|
||||||
json_err!("Could not get templates")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/api/guild/<id>/templates", data = "<reminder_template>")]
|
|
||||||
pub async fn create_reminder_template(
|
|
||||||
id: u64,
|
|
||||||
reminder_template: Json<ReminderTemplate>,
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
pool: &State<Pool<MySql>>,
|
|
||||||
) -> JsonResult {
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
// validate lengths
|
|
||||||
check_length!(MAX_CONTENT_LENGTH, reminder_template.content);
|
|
||||||
check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder_template.embed_description);
|
|
||||||
check_length!(MAX_EMBED_TITLE_LENGTH, reminder_template.embed_title);
|
|
||||||
check_length!(MAX_EMBED_AUTHOR_LENGTH, reminder_template.embed_author);
|
|
||||||
check_length!(MAX_EMBED_FOOTER_LENGTH, reminder_template.embed_footer);
|
|
||||||
check_length_opt!(MAX_EMBED_FIELDS, reminder_template.embed_fields);
|
|
||||||
if let Some(fields) = &reminder_template.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_USERNAME_LENGTH, reminder_template.username);
|
|
||||||
check_length_opt!(
|
|
||||||
MAX_URL_LENGTH,
|
|
||||||
reminder_template.embed_footer_url,
|
|
||||||
reminder_template.embed_thumbnail_url,
|
|
||||||
reminder_template.embed_author_url,
|
|
||||||
reminder_template.embed_image_url,
|
|
||||||
reminder_template.avatar
|
|
||||||
);
|
|
||||||
|
|
||||||
// validate urls
|
|
||||||
check_url_opt!(
|
|
||||||
reminder_template.embed_footer_url,
|
|
||||||
reminder_template.embed_thumbnail_url,
|
|
||||||
reminder_template.embed_author_url,
|
|
||||||
reminder_template.embed_image_url,
|
|
||||||
reminder_template.avatar
|
|
||||||
);
|
|
||||||
|
|
||||||
let name = if reminder_template.name.is_empty() {
|
|
||||||
template_name_default()
|
|
||||||
} else {
|
|
||||||
reminder_template.name.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
match sqlx::query!(
|
|
||||||
"INSERT INTO reminder_template
|
|
||||||
(guild_id,
|
|
||||||
name,
|
|
||||||
attachment,
|
|
||||||
attachment_name,
|
|
||||||
avatar,
|
|
||||||
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,
|
|
||||||
interval_seconds,
|
|
||||||
interval_days,
|
|
||||||
interval_months,
|
|
||||||
tts,
|
|
||||||
username
|
|
||||||
) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
||||||
?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
reminder_template.attachment,
|
|
||||||
reminder_template.attachment_name,
|
|
||||||
reminder_template.avatar,
|
|
||||||
reminder_template.content,
|
|
||||||
reminder_template.embed_author,
|
|
||||||
reminder_template.embed_author_url,
|
|
||||||
reminder_template.embed_color,
|
|
||||||
reminder_template.embed_description,
|
|
||||||
reminder_template.embed_footer,
|
|
||||||
reminder_template.embed_footer_url,
|
|
||||||
reminder_template.embed_image_url,
|
|
||||||
reminder_template.embed_thumbnail_url,
|
|
||||||
reminder_template.embed_title,
|
|
||||||
reminder_template.embed_fields,
|
|
||||||
reminder_template.interval_seconds,
|
|
||||||
reminder_template.interval_days,
|
|
||||||
reminder_template.interval_months,
|
|
||||||
reminder_template.tts,
|
|
||||||
reminder_template.username,
|
|
||||||
)
|
|
||||||
.fetch_all(pool.inner())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(_) => Ok(json!({})),
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not create template for {}: {:?}", id, e);
|
|
||||||
|
|
||||||
json_err!("Could not create template")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("/api/guild/<id>/templates", data = "<delete_reminder_template>")]
|
|
||||||
pub async fn delete_reminder_template(
|
|
||||||
id: u64,
|
|
||||||
delete_reminder_template: Json<DeleteReminderTemplate>,
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
pool: &State<Pool<MySql>>,
|
|
||||||
) -> JsonResult {
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
match sqlx::query!(
|
|
||||||
"DELETE FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND id = ?",
|
|
||||||
id, delete_reminder_template.id
|
|
||||||
)
|
|
||||||
.fetch_all(pool.inner())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(_) => {
|
|
||||||
Ok(json!({}))
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not delete template from {}: {:?}", id, e);
|
|
||||||
|
|
||||||
json_err!("Could not delete template")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/api/guild/<id>/reminders", data = "<reminder>")]
|
|
||||||
pub async fn create_guild_reminder(
|
|
||||||
id: u64,
|
|
||||||
reminder: Json<Reminder>,
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
mut transaction: Transaction<'_>,
|
|
||||||
) -> JsonResult {
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
let user_id =
|
|
||||||
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
|
|
||||||
|
|
||||||
match create_reminder(
|
|
||||||
ctx.inner(),
|
|
||||||
&mut transaction,
|
|
||||||
GuildId(id),
|
|
||||||
UserId(user_id),
|
|
||||||
reminder.into_inner(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(r) => match transaction.commit().await {
|
|
||||||
Ok(_) => Ok(r),
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could'nt commit transaction: {:?}", e);
|
|
||||||
json_err!("Couldn't commit transaction.")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api/guild/<id>/reminders")]
|
|
||||||
pub async fn get_reminders(
|
|
||||||
id: u64,
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
pool: &State<Pool<MySql>>,
|
|
||||||
) -> JsonResult {
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
|
||||||
|
|
||||||
match channels_res {
|
|
||||||
Ok(channels) => {
|
|
||||||
let channels = channels
|
|
||||||
.keys()
|
|
||||||
.into_iter()
|
|
||||||
.map(|k| k.as_u64().to_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(",");
|
|
||||||
|
|
||||||
sqlx::query_as_unchecked!(
|
|
||||||
Reminder,
|
|
||||||
"SELECT
|
|
||||||
reminders.attachment,
|
|
||||||
reminders.attachment_name,
|
|
||||||
reminders.avatar,
|
|
||||||
channels.channel,
|
|
||||||
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.restartable,
|
|
||||||
reminders.tts,
|
|
||||||
reminders.uid,
|
|
||||||
reminders.username,
|
|
||||||
reminders.utc_time
|
|
||||||
FROM reminders
|
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
|
||||||
WHERE `status` = 'pending' AND FIND_IN_SET(channels.channel, ?)",
|
|
||||||
channels
|
|
||||||
)
|
|
||||||
.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!("Could not fetch channels from {}: {:?}", id, e);
|
|
||||||
|
|
||||||
Ok(json!([]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[patch("/api/guild/<id>/reminders", data = "<reminder>")]
|
|
||||||
pub(crate) async fn edit_reminder(
|
|
||||||
id: u64,
|
|
||||||
reminder: Json<PatchReminder>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
mut transaction: Transaction<'_>,
|
|
||||||
pool: &State<Pool<Database>>,
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
) -> JsonResult {
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
let mut error = vec![];
|
|
||||||
|
|
||||||
let user_id =
|
|
||||||
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
|
|
||||||
|
|
||||||
if reminder.message_ok() {
|
|
||||||
update_field!(transaction.executor(), error, reminder.[
|
|
||||||
content,
|
|
||||||
embed_author,
|
|
||||||
embed_description,
|
|
||||||
embed_footer,
|
|
||||||
embed_title,
|
|
||||||
embed_fields,
|
|
||||||
username
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
error.push("Message exceeds limits.".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
update_field!(transaction.executor(), error, reminder.[
|
|
||||||
attachment,
|
|
||||||
attachment_name,
|
|
||||||
avatar,
|
|
||||||
embed_author_url,
|
|
||||||
embed_color,
|
|
||||||
embed_footer_url,
|
|
||||||
embed_image_url,
|
|
||||||
embed_thumbnail_url,
|
|
||||||
enabled,
|
|
||||||
expires,
|
|
||||||
name,
|
|
||||||
restartable,
|
|
||||||
tts,
|
|
||||||
utc_time
|
|
||||||
]);
|
|
||||||
|
|
||||||
if reminder.interval_days.flatten().is_some()
|
|
||||||
|| reminder.interval_months.flatten().is_some()
|
|
||||||
|| reminder.interval_seconds.flatten().is_some()
|
|
||||||
{
|
|
||||||
if check_guild_subscription(&ctx.inner(), id).await
|
|
||||||
|| check_subscription(&ctx.inner(), user_id).await
|
|
||||||
{
|
|
||||||
let new_interval_length = match reminder.interval_days {
|
|
||||||
Some(interval) => interval.unwrap_or(0),
|
|
||||||
None => sqlx::query!(
|
|
||||||
"SELECT interval_days AS days FROM reminders WHERE uid = ?",
|
|
||||||
reminder.uid
|
|
||||||
)
|
|
||||||
.fetch_one(transaction.executor())
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
warn!("Error updating reminder interval: {:?}", e);
|
|
||||||
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
|
|
||||||
})?
|
|
||||||
.days
|
|
||||||
.unwrap_or(0),
|
|
||||||
} * 86400 + match reminder.interval_months {
|
|
||||||
Some(interval) => interval.unwrap_or(0),
|
|
||||||
None => sqlx::query!(
|
|
||||||
"SELECT interval_months AS months FROM reminders WHERE uid = ?",
|
|
||||||
reminder.uid
|
|
||||||
)
|
|
||||||
.fetch_one(transaction.executor())
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
warn!("Error updating reminder interval: {:?}", e);
|
|
||||||
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
|
|
||||||
})?
|
|
||||||
.months
|
|
||||||
.unwrap_or(0),
|
|
||||||
} * 2592000 + match reminder.interval_seconds {
|
|
||||||
Some(interval) => interval.unwrap_or(0),
|
|
||||||
None => sqlx::query!(
|
|
||||||
"SELECT interval_seconds AS seconds FROM reminders WHERE uid = ?",
|
|
||||||
reminder.uid
|
|
||||||
)
|
|
||||||
.fetch_one(transaction.executor())
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
warn!("Error updating reminder interval: {:?}", e);
|
|
||||||
json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
|
|
||||||
})?
|
|
||||||
.seconds
|
|
||||||
.unwrap_or(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
if new_interval_length < *MIN_INTERVAL {
|
|
||||||
error.push(String::from("New interval is too short."));
|
|
||||||
} else {
|
|
||||||
update_field!(transaction.executor(), error, reminder.[
|
|
||||||
interval_days,
|
|
||||||
interval_months,
|
|
||||||
interval_seconds
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if reminder.channel > 0 {
|
|
||||||
let channel = ChannelId(reminder.channel).to_channel_cached(&ctx.inner());
|
|
||||||
match channel {
|
|
||||||
Some(channel) => {
|
|
||||||
let channel_matches_guild = channel.guild().map_or(false, |c| c.guild_id.0 == id);
|
|
||||||
|
|
||||||
if !channel_matches_guild {
|
|
||||||
warn!(
|
|
||||||
"Error in `edit_reminder`: channel {:?} not found for guild {}",
|
|
||||||
reminder.channel, id
|
|
||||||
);
|
|
||||||
|
|
||||||
return Err(json!({"error": "Channel not found"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let channel = create_database_channel(
|
|
||||||
ctx.inner(),
|
|
||||||
ChannelId(reminder.channel),
|
|
||||||
&mut 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. Please check the bot permissions"}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let channel = channel.unwrap();
|
|
||||||
|
|
||||||
match sqlx::query!(
|
|
||||||
"UPDATE reminders SET channel_id = ? WHERE uid = ?",
|
|
||||||
channel,
|
|
||||||
reminder.uid
|
|
||||||
)
|
|
||||||
.execute(transaction.executor())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error setting channel: {:?}", e);
|
|
||||||
|
|
||||||
error.push("Couldn't set channel".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None => {
|
|
||||||
warn!(
|
|
||||||
"Error in `edit_reminder`: channel {:?} not found for guild {}",
|
|
||||||
reminder.channel, id
|
|
||||||
);
|
|
||||||
|
|
||||||
return Err(json!({"error": "Channel not found"}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = transaction.commit().await {
|
|
||||||
warn!("Couldn't commit transaction: {:?}", e);
|
|
||||||
return json_err!("Couldn't commit transaction");
|
|
||||||
}
|
|
||||||
|
|
||||||
match sqlx::query_as_unchecked!(
|
|
||||||
Reminder,
|
|
||||||
"SELECT reminders.attachment,
|
|
||||||
reminders.attachment_name,
|
|
||||||
reminders.avatar,
|
|
||||||
channels.channel,
|
|
||||||
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.restartable,
|
|
||||||
reminders.tts,
|
|
||||||
reminders.uid,
|
|
||||||
reminders.username,
|
|
||||||
reminders.utc_time
|
|
||||||
FROM reminders
|
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
|
||||||
WHERE uid = ?",
|
|
||||||
reminder.uid
|
|
||||||
)
|
|
||||||
.fetch_one(pool.inner())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(reminder) => Ok(json!({"reminder": reminder, "errors": error})),
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error exiting `edit_reminder': {:?}", e);
|
|
||||||
|
|
||||||
Err(json!({"reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"]}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[delete("/api/guild/<id>/reminders", data = "<reminder>")]
|
|
||||||
pub async fn delete_reminder(
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
id: u64,
|
|
||||||
reminder: Json<DeleteReminder>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
pool: &State<Pool<MySql>>,
|
|
||||||
) -> JsonResult {
|
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
|
||||||
|
|
||||||
match sqlx::query!("UPDATE reminders SET `status` = 'deleted' WHERE uid = ?", reminder.uid)
|
|
||||||
.execute(pool.inner())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(_) => Ok(json!({})),
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error in `delete_reminder`: {:?}", e);
|
|
||||||
|
|
||||||
Err(json!({"error": "Could not delete reminder"}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -25,9 +25,9 @@ use crate::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod api;
|
||||||
pub mod export;
|
pub mod export;
|
||||||
pub mod guild;
|
pub mod guild;
|
||||||
pub mod user;
|
|
||||||
|
|
||||||
type Unset<T> = Option<T>;
|
type Unset<T> = Option<T>;
|
||||||
|
|
||||||
|
@ -1,172 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
|
|
||||||
use chrono_tz::Tz;
|
|
||||||
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},
|
|
||||||
permissions::Permissions,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use sqlx::{MySql, Pool};
|
|
||||||
|
|
||||||
use crate::consts::DISCORD_API;
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct UserInfo {
|
|
||||||
name: String,
|
|
||||||
patreon: bool,
|
|
||||||
timezone: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct UpdateUser {
|
|
||||||
timezone: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct GuildInfo {
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct PartialGuild {
|
|
||||||
pub id: GuildId,
|
|
||||||
pub icon: Option<String>,
|
|
||||||
pub name: String,
|
|
||||||
#[serde(default)]
|
|
||||||
pub owner: bool,
|
|
||||||
#[serde(rename = "permissions_new")]
|
|
||||||
pub permissions: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api/user")]
|
|
||||||
pub async fn get_user_info(
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
ctx: &State<Context>,
|
|
||||||
pool: &State<Pool<MySql>>,
|
|
||||||
) -> JsonValue {
|
|
||||||
offline!(json!(UserInfo { name: "Discord".to_string(), patreon: true, timezone: None }));
|
|
||||||
|
|
||||||
if let Some(user_id) =
|
|
||||||
cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
|
|
||||||
{
|
|
||||||
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
|
||||||
.member(&ctx.inner(), user_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let timezone = sqlx::query!(
|
|
||||||
"SELECT IFNULL(timezone, 'UTC') AS timezone FROM users WHERE user = ?",
|
|
||||||
user_id
|
|
||||||
)
|
|
||||||
.fetch_one(pool.inner())
|
|
||||||
.await
|
|
||||||
.map_or(None, |q| Some(q.timezone));
|
|
||||||
|
|
||||||
let user_info = UserInfo {
|
|
||||||
name: cookies
|
|
||||||
.get_private("username")
|
|
||||||
.map_or("DiscordUser#0000".to_string(), |c| c.value().to_string()),
|
|
||||||
patreon: member_res.map_or(false, |member| {
|
|
||||||
member
|
|
||||||
.roles
|
|
||||||
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
|
||||||
}),
|
|
||||||
timezone,
|
|
||||||
};
|
|
||||||
|
|
||||||
json!(user_info)
|
|
||||||
} else {
|
|
||||||
json!({"error": "Not authorized"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[patch("/api/user", data = "<user>")]
|
|
||||||
pub async fn update_user_info(
|
|
||||||
cookies: &CookieJar<'_>,
|
|
||||||
user: Json<UpdateUser>,
|
|
||||||
pool: &State<Pool<MySql>>,
|
|
||||||
) -> JsonValue {
|
|
||||||
if let Some(user_id) =
|
|
||||||
cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
|
|
||||||
{
|
|
||||||
if user.timezone.parse::<Tz>().is_ok() {
|
|
||||||
let _ = sqlx::query!(
|
|
||||||
"UPDATE users SET timezone = ? WHERE user = ?",
|
|
||||||
user.timezone,
|
|
||||||
user_id,
|
|
||||||
)
|
|
||||||
.execute(pool.inner())
|
|
||||||
.await;
|
|
||||||
|
|
||||||
json!({})
|
|
||||||
} else {
|
|
||||||
json!({"error": "Timezone not recognized"})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
json!({"error": "Not authorized"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/api/user/guilds")]
|
|
||||||
pub async fn get_user_guilds(cookies: &CookieJar<'_>, reqwest_client: &State<Client>) -> JsonValue {
|
|
||||||
offline!(json!(vec![GuildInfo { id: "1".to_string(), name: "Guild".to_string() }]));
|
|
||||||
|
|
||||||
if let Some(access_token) = cookies.get_private("access_token") {
|
|
||||||
let request_res = reqwest_client
|
|
||||||
.get(format!("{}/users/@me/guilds", DISCORD_API))
|
|
||||||
.bearer_auth(access_token.value())
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match request_res {
|
|
||||||
Ok(response) => {
|
|
||||||
let guilds_res = response.json::<Vec<PartialGuild>>().await;
|
|
||||||
|
|
||||||
match guilds_res {
|
|
||||||
Ok(guilds) => {
|
|
||||||
let reduced_guilds = guilds
|
|
||||||
.iter()
|
|
||||||
.filter(|g| {
|
|
||||||
g.owner
|
|
||||||
|| g.permissions.as_ref().map_or(false, |p| {
|
|
||||||
let permissions =
|
|
||||||
Permissions::from_bits_truncate(p.parse().unwrap());
|
|
||||||
|
|
||||||
permissions.manage_messages()
|
|
||||||
|| permissions.manage_guild()
|
|
||||||
|| permissions.administrator()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map(|g| GuildInfo { id: g.id.to_string(), name: g.name.to_string() })
|
|
||||||
.collect::<Vec<GuildInfo>>();
|
|
||||||
|
|
||||||
json!(reduced_guilds)
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error constructing user from request: {:?}", e);
|
|
||||||
|
|
||||||
json!({"error": "Could not get user details"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Error getting user guilds: {:?}", e);
|
|
||||||
|
|
||||||
json!({"error": "Could not reach Discord"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
json!({"error": "Not authorized"})
|
|
||||||
}
|
|
||||||
}
|
|
@ -463,15 +463,16 @@ document.addEventListener("guildSwitched", async (e) => {
|
|||||||
|
|
||||||
let hasError = false;
|
let hasError = false;
|
||||||
|
|
||||||
if ($anchor !== null) {
|
|
||||||
$anchor.classList.add("is-active");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pane() === null) {
|
if (pane() === null) {
|
||||||
window.history.replaceState({}, "", `/dashboard/${guildId()}/reminders`);
|
window.history.replaceState({}, "", `/dashboard/${guildId()}/reminders`);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_pane(pane());
|
switch_pane(pane());
|
||||||
|
|
||||||
|
if ($anchor !== null) {
|
||||||
|
$anchor.classList.add("is-active");
|
||||||
|
}
|
||||||
|
|
||||||
reset_guild_pane();
|
reset_guild_pane();
|
||||||
|
|
||||||
if (globalPatreon || (await fetch_patreon(e.detail.guild_id))) {
|
if (globalPatreon || (await fetch_patreon(e.detail.guild_id))) {
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/reminder_errors.js"></script>
|
<!--<script src="/static/js/reminder_errors.js"></script>-->
|
||||||
|
Loading…
Reference in New Issue
Block a user