Compare commits
7 Commits
82dab53744
...
next
Author | SHA1 | Date | |
---|---|---|---|
d082f63635 | |||
9a51c548d6 | |||
4bc7ae8e23 | |||
6f1ef206df | |||
ec63c942d6 | |||
06165c1b36 | |||
5ee9094bac |
1407
Cargo.lock
generated
1407
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@ -1,18 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "reminder-rs"
|
name = "reminder-rs"
|
||||||
version = "1.6.38"
|
version = "1.6.40"
|
||||||
authors = ["Jude Southworth <judesouthworth@pm.me>"]
|
authors = ["Jude Southworth <judesouthworth@pm.me>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0 only"
|
license = "AGPL-3.0 only"
|
||||||
description = "Reminder Bot for Discord, now in Rust"
|
description = "Reminder Bot for Discord, now in Rust"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
poise = "0.5.5"
|
poise = "0.5"
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
tokio = { version = "1", features = ["process", "full"] }
|
tokio = { version = "1", features = ["process", "full"] }
|
||||||
reqwest = "0.11"
|
reqwest = "0.11"
|
||||||
lazy-regex = "2.3.0"
|
lazy-regex = "3.0"
|
||||||
regex = "1.6"
|
regex = "1.9"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
@ -25,7 +25,7 @@ serde_repr = "0.1"
|
|||||||
rmp-serde = "1.1"
|
rmp-serde = "1.1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
levenshtein = "1.0"
|
levenshtein = "1.0"
|
||||||
sqlx = { version = "0.6", 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.0"
|
base64 = "0.21.0"
|
||||||
|
|
||||||
[dependencies.postman]
|
[dependencies.postman]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
ALTER TABLE reminders ADD COLUMN `status_change_time` DATETIME;
|
ALTER TABLE reminders ADD COLUMN `status_change_time` DATETIME;
|
||||||
|
|
||||||
-- This is a best-guess as to the status change time.
|
-- This is a best-guess as to the status change time.
|
||||||
UPDATE reminders SET `status_change_time` = `utc_time`;
|
UPDATE reminders SET `status_change_time` = `utc_time` WHERE `status` != 'pending';
|
||||||
|
@ -5,12 +5,12 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1", features = ["process", "full"] }
|
tokio = { version = "1", features = ["process", "full"] }
|
||||||
regex = "1.4"
|
regex = "1.9"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
chrono-tz = { version = "0.5", features = ["serde"] }
|
chrono-tz = { version = "0.8", features = ["serde"] }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
num-integer = "0.1"
|
num-integer = "0.1"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "json"]}
|
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "json"]}
|
||||||
serenity = { version = "0.11.1", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
serenity = { version = "0.11", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
||||||
|
@ -237,11 +237,11 @@ impl Into<CreateEmbed> for Embed {
|
|||||||
pub struct Reminder {
|
pub struct Reminder {
|
||||||
id: u32,
|
id: u32,
|
||||||
|
|
||||||
channel_id: u64,
|
channel_id: Option<u64>,
|
||||||
webhook_id: Option<u64>,
|
webhook_id: Option<u64>,
|
||||||
webhook_token: Option<String>,
|
webhook_token: Option<String>,
|
||||||
|
|
||||||
channel_paused: bool,
|
channel_paused: Option<bool>,
|
||||||
channel_paused_until: Option<NaiveDateTime>,
|
channel_paused_until: Option<NaiveDateTime>,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
|
|
||||||
@ -297,7 +297,7 @@ SELECT
|
|||||||
reminders.`username` AS username
|
reminders.`username` AS username
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
INNER JOIN
|
LEFT JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
reminders.channel_id = channels.id
|
reminders.channel_id = channels.id
|
||||||
@ -310,7 +310,6 @@ WHERE
|
|||||||
reminders
|
reminders
|
||||||
WHERE
|
WHERE
|
||||||
reminders.`utc_time` <= NOW() AND
|
reminders.`utc_time` <= NOW() AND
|
||||||
reminders.`channel_id` IS NOT NULL AND
|
|
||||||
`status` = 'pending' AND
|
`status` = 'pending' AND
|
||||||
(
|
(
|
||||||
reminders.`interval_seconds` IS NOT NULL
|
reminders.`interval_seconds` IS NOT NULL
|
||||||
@ -344,7 +343,10 @@ WHERE
|
|||||||
|
|
||||||
async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
async fn reset_webhook(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
||||||
let _ = sqlx::query!(
|
let _ = sqlx::query!(
|
||||||
"UPDATE channels SET webhook_id = NULL, webhook_token = NULL WHERE channel = ?",
|
"
|
||||||
|
UPDATE channels SET webhook_id = NULL, webhook_token = NULL
|
||||||
|
WHERE channel = ?
|
||||||
|
",
|
||||||
self.channel_id
|
self.channel_id
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
@ -416,7 +418,9 @@ WHERE
|
|||||||
self.set_sent(pool).await;
|
self.set_sent(pool).await;
|
||||||
} else {
|
} else {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE reminders SET `utc_time` = ? WHERE `id` = ?",
|
"
|
||||||
|
UPDATE reminders SET `utc_time` = ? WHERE `id` = ?
|
||||||
|
",
|
||||||
updated_reminder_time.with_timezone(&Utc),
|
updated_reminder_time.with_timezone(&Utc),
|
||||||
self.id
|
self.id
|
||||||
)
|
)
|
||||||
@ -449,7 +453,10 @@ WHERE
|
|||||||
|
|
||||||
if *LOG_TO_DATABASE {
|
if *LOG_TO_DATABASE {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO stat (type, reminder_id, message) VALUES ('reminder_failed', ?, ?)",
|
"
|
||||||
|
INSERT INTO stat (type, reminder_id, message)
|
||||||
|
VALUES ('reminder_failed', ?, ?)
|
||||||
|
",
|
||||||
self.id,
|
self.id,
|
||||||
message,
|
message,
|
||||||
)
|
)
|
||||||
@ -462,7 +469,10 @@ WHERE
|
|||||||
async fn log_success(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
async fn log_success(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
||||||
if *LOG_TO_DATABASE {
|
if *LOG_TO_DATABASE {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO stat (type, reminder_id) VALUES ('reminder_sent', ?)",
|
"
|
||||||
|
INSERT INTO stat (type, reminder_id)
|
||||||
|
VALUES ('reminder_sent', ?)
|
||||||
|
",
|
||||||
self.id,
|
self.id,
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
@ -491,7 +501,11 @@ WHERE
|
|||||||
message: &'static str,
|
message: &'static str,
|
||||||
) {
|
) {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE reminders SET `status` = 'failed', `status_message` = ? WHERE `id` = ?",
|
"
|
||||||
|
UPDATE reminders
|
||||||
|
SET `status` = 'failed', `status_message` = ?, `status_change_time` = NOW()
|
||||||
|
WHERE `id` = ?
|
||||||
|
",
|
||||||
message,
|
message,
|
||||||
self.id
|
self.id
|
||||||
)
|
)
|
||||||
@ -501,7 +515,9 @@ WHERE
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn pin_message<M: Into<u64>>(&self, message_id: M, http: impl AsRef<Http>) {
|
async fn pin_message<M: Into<u64>>(&self, message_id: M, http: impl AsRef<Http>) {
|
||||||
let _ = http.as_ref().pin_message(self.channel_id, message_id.into(), None).await;
|
if let Some(channel_id) = self.channel_id {
|
||||||
|
let _ = http.as_ref().pin_message(channel_id, message_id.into(), None).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
@ -511,10 +527,11 @@ WHERE
|
|||||||
) {
|
) {
|
||||||
async fn send_to_channel(
|
async fn send_to_channel(
|
||||||
cache_http: impl CacheHttp,
|
cache_http: impl CacheHttp,
|
||||||
|
channel_id: u64,
|
||||||
reminder: &Reminder,
|
reminder: &Reminder,
|
||||||
embed: Option<CreateEmbed>,
|
embed: Option<CreateEmbed>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let channel = ChannelId(reminder.channel_id).to_channel(&cache_http).await;
|
let channel = ChannelId(channel_id).to_channel(&cache_http).await;
|
||||||
|
|
||||||
match channel {
|
match channel {
|
||||||
Ok(Channel::Guild(channel)) => {
|
Ok(Channel::Guild(channel)) => {
|
||||||
@ -546,6 +563,7 @@ WHERE
|
|||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Channel::Private(channel)) => {
|
Ok(Channel::Private(channel)) => {
|
||||||
match channel
|
match channel
|
||||||
.send_message(&cache_http.http(), |m| {
|
.send_message(&cache_http.http(), |m| {
|
||||||
@ -575,7 +593,9 @@ WHERE
|
|||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
|
|
||||||
_ => Err(Error::Other("Channel not of valid type")),
|
_ => Err(Error::Other("Channel not of valid type")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,14 +650,20 @@ WHERE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match self.channel_id {
|
||||||
|
Some(channel_id) => {
|
||||||
if self.enabled
|
if self.enabled
|
||||||
&& !(self.channel_paused
|
&& !(self.channel_paused.unwrap_or(false)
|
||||||
&& self
|
&& self
|
||||||
.channel_paused_until
|
.channel_paused_until
|
||||||
.map_or(true, |inner| inner >= Utc::now().naive_local()))
|
.map_or(true, |inner| inner >= Utc::now().naive_local()))
|
||||||
{
|
{
|
||||||
let _ = sqlx::query!(
|
let _ = sqlx::query!(
|
||||||
"UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?",
|
"
|
||||||
|
UPDATE `channels`
|
||||||
|
SET paused = 0, paused_until = NULL
|
||||||
|
WHERE `channel` = ?
|
||||||
|
",
|
||||||
self.channel_id
|
self.channel_id
|
||||||
)
|
)
|
||||||
.execute(pool)
|
.execute(pool)
|
||||||
@ -648,8 +674,10 @@ WHERE
|
|||||||
let result = if let (Some(webhook_id), Some(webhook_token)) =
|
let result = if let (Some(webhook_id), Some(webhook_token)) =
|
||||||
(self.webhook_id, &self.webhook_token)
|
(self.webhook_id, &self.webhook_token)
|
||||||
{
|
{
|
||||||
let webhook_res =
|
let webhook_res = cache_http
|
||||||
cache_http.http().get_webhook_with_token(webhook_id, webhook_token).await;
|
.http()
|
||||||
|
.get_webhook_with_token(webhook_id, webhook_token)
|
||||||
|
.await;
|
||||||
|
|
||||||
if let Ok(webhook) = webhook_res {
|
if let Ok(webhook) = webhook_res {
|
||||||
send_to_webhook(cache_http, &self, webhook, embed).await
|
send_to_webhook(cache_http, &self, webhook, embed).await
|
||||||
@ -657,10 +685,10 @@ WHERE
|
|||||||
warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res);
|
warn!("Webhook vanished for reminder {}: {:?}", self.id, webhook_res);
|
||||||
|
|
||||||
self.reset_webhook(pool).await;
|
self.reset_webhook(pool).await;
|
||||||
send_to_channel(cache_http, &self, embed).await
|
send_to_channel(cache_http, channel_id, &self, embed).await
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
send_to_channel(cache_http, &self, embed).await
|
send_to_channel(cache_http, channel_id, &self, embed).await
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
@ -687,7 +715,10 @@ WHERE
|
|||||||
None::<&'static str>,
|
None::<&'static str>,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
self.set_failed(pool, "Could not be sent as guild does not exist")
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as guild does not exist",
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
50001 => {
|
50001 => {
|
||||||
@ -697,7 +728,11 @@ WHERE
|
|||||||
None::<&'static str>,
|
None::<&'static str>,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
self.set_failed(pool, "Could not be sent as missing access").await;
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as missing access",
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
50007 => {
|
50007 => {
|
||||||
self.log_error(
|
self.log_error(
|
||||||
@ -706,7 +741,10 @@ WHERE
|
|||||||
None::<&'static str>,
|
None::<&'static str>,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
self.set_failed(pool, "Could not be sent as user has DMs disabled")
|
self.set_failed(
|
||||||
|
pool,
|
||||||
|
"Could not be sent as user has DMs disabled",
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
50013 => {
|
50013 => {
|
||||||
@ -750,4 +788,13 @@ WHERE
|
|||||||
self.refresh(pool).await;
|
self.refresh(pool).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
info!("Reminder {} is orphaned", self.id);
|
||||||
|
|
||||||
|
self.log_error(pool, "Orphaned", Option::<u8>::None).await;
|
||||||
|
self.set_failed(pool, "Could not be sent as channel was deleted").await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
"SELECT name, command FROM command_aliases WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
"SELECT name, command FROM command_aliases WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||||
guild_id.0
|
guild_id.0
|
||||||
)
|
)
|
||||||
.fetch_all(&mut transaction)
|
.fetch_all(&mut *transaction)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut added_aliases = 0;
|
let mut added_aliases = 0;
|
||||||
@ -42,7 +42,7 @@ pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
cmd_macro.description,
|
cmd_macro.description,
|
||||||
cmd_macro.commands
|
cmd_macro.commands
|
||||||
)
|
)
|
||||||
.execute(&mut transaction)
|
.execute(&mut *transaction)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
added_aliases += 1;
|
added_aliases += 1;
|
||||||
|
@ -75,7 +75,8 @@ impl ChannelData {
|
|||||||
UPDATE channels
|
UPDATE channels
|
||||||
SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?,
|
SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?,
|
||||||
paused = ?, paused_until = ?
|
paused = ?, paused_until = ?
|
||||||
WHERE id = ?",
|
WHERE id = ?
|
||||||
|
",
|
||||||
self.name,
|
self.name,
|
||||||
self.nudge,
|
self.nudge,
|
||||||
self.blacklisted,
|
self.blacklisted,
|
||||||
|
@ -7,14 +7,14 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "master", features = ["tls", "secrets", "json"] }
|
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "master", features = ["tls", "secrets", "json"] }
|
||||||
rocket_dyn_templates = { git = "https://github.com/SergioBenitez/Rocket", branch = "master", features = ["tera"] }
|
rocket_dyn_templates = { git = "https://github.com/SergioBenitez/Rocket", branch = "master", features = ["tera"] }
|
||||||
serenity = { version = "0.11.1", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
serenity = { version = "0.11", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
||||||
oauth2 = "4"
|
oauth2 = "4"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
reqwest = "0.11"
|
reqwest = "0.11"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono", "json"] }
|
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono", "json"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
chrono-tz = "0.5"
|
chrono-tz = "0.8"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
@ -12,8 +12,7 @@ use sqlx::{MySql, Pool};
|
|||||||
|
|
||||||
use crate::routes::{
|
use crate::routes::{
|
||||||
dashboard::{
|
dashboard::{
|
||||||
create_reminder, generate_uid, ImportBody, Reminder, ReminderCsv, ReminderTemplateCsv,
|
create_reminder, ImportBody, ReminderCreate, ReminderCsv, ReminderTemplateCsv, TodoCsv,
|
||||||
TodoCsv,
|
|
||||||
},
|
},
|
||||||
JsonResult,
|
JsonResult,
|
||||||
};
|
};
|
||||||
@ -141,11 +140,11 @@ pub async fn import_reminders(
|
|||||||
|
|
||||||
match channel_id.parse::<u64>() {
|
match channel_id.parse::<u64>() {
|
||||||
Ok(channel_id) => {
|
Ok(channel_id) => {
|
||||||
let reminder = Reminder {
|
let reminder = ReminderCreate {
|
||||||
attachment: record.attachment,
|
attachment: record.attachment,
|
||||||
attachment_name: record.attachment_name,
|
attachment_name: record.attachment_name,
|
||||||
avatar: record.avatar,
|
avatar: record.avatar,
|
||||||
channel: Some(channel_id),
|
channel: channel_id,
|
||||||
content: record.content,
|
content: record.content,
|
||||||
embed_author: record.embed_author,
|
embed_author: record.embed_author,
|
||||||
embed_author_url: record.embed_author_url,
|
embed_author_url: record.embed_author_url,
|
||||||
@ -168,11 +167,8 @@ pub async fn import_reminders(
|
|||||||
name: record.name,
|
name: record.name,
|
||||||
restartable: record.restartable,
|
restartable: record.restartable,
|
||||||
tts: record.tts,
|
tts: record.tts,
|
||||||
uid: generate_uid(),
|
|
||||||
username: record.username,
|
username: record.username,
|
||||||
utc_time: record.utc_time,
|
utc_time: record.utc_time,
|
||||||
status: "pending".to_string(),
|
|
||||||
status_change_time: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
create_reminder(
|
create_reminder(
|
||||||
|
@ -26,7 +26,7 @@ use crate::{
|
|||||||
routes::{
|
routes::{
|
||||||
dashboard::{
|
dashboard::{
|
||||||
create_database_channel, create_reminder, template_name_default, DeleteReminder,
|
create_database_channel, create_reminder, template_name_default, DeleteReminder,
|
||||||
DeleteReminderTemplate, PatchReminder, Reminder, ReminderTemplate,
|
DeleteReminderTemplate, PatchReminder, Reminder, ReminderCreate, ReminderTemplate,
|
||||||
},
|
},
|
||||||
JsonResult,
|
JsonResult,
|
||||||
},
|
},
|
||||||
@ -298,7 +298,7 @@ pub async fn delete_reminder_template(
|
|||||||
#[post("/api/guild/<id>/reminders", data = "<reminder>")]
|
#[post("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||||
pub async fn create_guild_reminder(
|
pub async fn create_guild_reminder(
|
||||||
id: u64,
|
id: u64,
|
||||||
reminder: Json<Reminder>,
|
reminder: Json<ReminderCreate>,
|
||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
serenity_context: &State<Context>,
|
serenity_context: &State<Context>,
|
||||||
pool: &State<Pool<MySql>>,
|
pool: &State<Pool<MySql>>,
|
||||||
@ -361,7 +361,8 @@ pub async fn get_reminders(
|
|||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.status,
|
reminders.status,
|
||||||
reminders.status_change_time
|
reminders.status_change_time,
|
||||||
|
reminders.status_message
|
||||||
FROM reminders
|
FROM reminders
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
WHERE FIND_IN_SET(`status`, ?) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
WHERE FIND_IN_SET(`status`, ?) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||||
@ -549,7 +550,8 @@ pub async fn edit_reminder(
|
|||||||
|
|
||||||
match sqlx::query_as_unchecked!(
|
match sqlx::query_as_unchecked!(
|
||||||
Reminder,
|
Reminder,
|
||||||
"SELECT reminders.attachment,
|
"
|
||||||
|
SELECT reminders.attachment,
|
||||||
reminders.attachment_name,
|
reminders.attachment_name,
|
||||||
reminders.avatar,
|
reminders.avatar,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -576,7 +578,8 @@ pub async fn edit_reminder(
|
|||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.status,
|
reminders.status,
|
||||||
reminders.status_change_time
|
reminders.status_change_time,
|
||||||
|
reminders.status_message
|
||||||
FROM reminders
|
FROM reminders
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
WHERE uid = ?",
|
WHERE uid = ?",
|
||||||
|
@ -118,6 +118,38 @@ pub struct EmbedField {
|
|||||||
inline: bool,
|
inline: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ReminderCreate {
|
||||||
|
#[serde(with = "base64s")]
|
||||||
|
attachment: Option<Vec<u8>>,
|
||||||
|
attachment_name: Option<String>,
|
||||||
|
avatar: Option<String>,
|
||||||
|
#[serde(with = "string")]
|
||||||
|
channel: u64,
|
||||||
|
content: String,
|
||||||
|
embed_author: String,
|
||||||
|
embed_author_url: Option<String>,
|
||||||
|
embed_color: u32,
|
||||||
|
embed_description: String,
|
||||||
|
embed_footer: String,
|
||||||
|
embed_footer_url: Option<String>,
|
||||||
|
embed_image_url: Option<String>,
|
||||||
|
embed_thumbnail_url: Option<String>,
|
||||||
|
embed_title: String,
|
||||||
|
embed_fields: Option<Json<Vec<EmbedField>>>,
|
||||||
|
enabled: bool,
|
||||||
|
expires: Option<NaiveDateTime>,
|
||||||
|
interval_seconds: Option<u32>,
|
||||||
|
interval_days: Option<u32>,
|
||||||
|
interval_months: Option<u32>,
|
||||||
|
#[serde(default = "name_default")]
|
||||||
|
name: String,
|
||||||
|
restartable: bool,
|
||||||
|
tts: bool,
|
||||||
|
username: Option<String>,
|
||||||
|
utc_time: NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Reminder {
|
pub struct Reminder {
|
||||||
#[serde(with = "base64s")]
|
#[serde(with = "base64s")]
|
||||||
@ -151,6 +183,7 @@ pub struct Reminder {
|
|||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
utc_time: NaiveDateTime,
|
utc_time: NaiveDateTime,
|
||||||
status: String,
|
status: String,
|
||||||
|
status_message: Option<String>,
|
||||||
status_change_time: Option<NaiveDateTime>,
|
status_change_time: Option<NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,15 +323,7 @@ pub fn generate_uid() -> String {
|
|||||||
mod string {
|
mod string {
|
||||||
use std::{fmt::Display, str::FromStr};
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serializer};
|
use serde::{de, Deserialize, Deserializer};
|
||||||
|
|
||||||
pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
T: Display,
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
serializer.collect_str(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||||
where
|
where
|
||||||
@ -382,7 +407,7 @@ pub async fn create_reminder(
|
|||||||
pool: impl sqlx::Executor<'_, Database = Database> + Copy,
|
pool: impl sqlx::Executor<'_, Database = Database> + Copy,
|
||||||
guild_id: GuildId,
|
guild_id: GuildId,
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
reminder: Reminder,
|
reminder: ReminderCreate,
|
||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
// check guild in db
|
// check guild in db
|
||||||
match sqlx::query!("SELECT 1 as A FROM guilds WHERE guild = ?", guild_id.0)
|
match sqlx::query!("SELECT 1 as A FROM guilds WHERE guild = ?", guild_id.0)
|
||||||
@ -402,7 +427,7 @@ pub async fn create_reminder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validate channel
|
// validate channel
|
||||||
let channel = reminder.channel.map(|c| ChannelId(c).to_channel_cached(&ctx)).flatten();
|
let channel = ChannelId(reminder.channel).to_channel_cached(&ctx);
|
||||||
let channel_exists = channel.is_some();
|
let channel_exists = channel.is_some();
|
||||||
|
|
||||||
let channel_matches_guild =
|
let channel_matches_guild =
|
||||||
@ -417,7 +442,7 @@ pub async fn create_reminder(
|
|||||||
return Err(json!({"error": "Channel not found"}));
|
return Err(json!({"error": "Channel not found"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let channel = create_database_channel(&ctx, ChannelId(reminder.channel.unwrap()), pool).await;
|
let channel = create_database_channel(&ctx, ChannelId(reminder.channel), pool).await;
|
||||||
|
|
||||||
if let Err(e) = channel {
|
if let Err(e) = channel {
|
||||||
warn!("`create_database_channel` returned an error code: {:?}", e);
|
warn!("`create_database_channel` returned an error code: {:?}", e);
|
||||||
@ -504,7 +529,8 @@ pub async fn create_reminder(
|
|||||||
|
|
||||||
// write to db
|
// write to db
|
||||||
match sqlx::query!(
|
match sqlx::query!(
|
||||||
"INSERT INTO reminders (
|
"
|
||||||
|
INSERT INTO reminders (
|
||||||
uid,
|
uid,
|
||||||
attachment,
|
attachment,
|
||||||
attachment_name,
|
attachment_name,
|
||||||
@ -532,7 +558,9 @@ pub async fn create_reminder(
|
|||||||
tts,
|
tts,
|
||||||
username,
|
username,
|
||||||
`utc_time`
|
`utc_time`
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
) VALUES (?, ?, ?, ?,
|
||||||
|
(SELECT id FROM guilds WHERE guild = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||||
|
?, ?, ?, ?, ?)",
|
||||||
new_uid,
|
new_uid,
|
||||||
attachment_data,
|
attachment_data,
|
||||||
reminder.attachment_name,
|
reminder.attachment_name,
|
||||||
@ -594,7 +622,8 @@ pub async fn create_reminder(
|
|||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time,
|
||||||
reminders.status,
|
reminders.status,
|
||||||
reminders.status_change_time
|
reminders.status_change_time,
|
||||||
|
reminders.status_message
|
||||||
FROM reminders
|
FROM reminders
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
LEFT JOIN channels ON channels.id = reminders.channel_id
|
||||||
WHERE uid = ?",
|
WHERE uid = ?",
|
||||||
|
@ -15,6 +15,18 @@ div.reminderContent.is-collapsed .column.settings {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.reminderContent.is-collapsed .button-row {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.reminderContent.is-collapsed .button-row-edit {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.reminderContent.is-collapsed .reminder-topbar {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
div.reminderContent.is-collapsed .invert-collapses {
|
div.reminderContent.is-collapsed .invert-collapses {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
@ -129,6 +141,12 @@ div.split-controls {
|
|||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reminder-settings > .column {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
div.reminderContent {
|
div.reminderContent {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@ -294,7 +312,7 @@ div.dashboard-sidebar:not(.mobile-sidebar) {
|
|||||||
ul.guildList {
|
ul.guildList {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
overflow: scroll;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.dashboard-sidebar:not(.mobile-sidebar) .aside-footer {
|
div.dashboard-sidebar:not(.mobile-sidebar) .aside-footer {
|
||||||
@ -453,8 +471,7 @@ input.default-width {
|
|||||||
.customizable.is-400x300 img {
|
.customizable.is-400x300 img {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 100px;
|
height: 100px;
|
||||||
max-height: 400px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.customizable.is-32x32 img {
|
.customizable.is-32x32 img {
|
||||||
@ -598,6 +615,14 @@ input.default-width {
|
|||||||
border-bottom: 1px solid #fff;
|
border-bottom: 1px solid #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.channel-selector {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
li.highlight {
|
li.highlight {
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
@ -621,7 +646,22 @@ li.highlight {
|
|||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1408px) {
|
@media only screen and (max-width: 1023px) {
|
||||||
|
p.title.pageTitle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-frame {
|
||||||
|
margin-top: 4rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customizable.thumbnail img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
.button-row {
|
.button-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -639,37 +679,13 @@ li.highlight {
|
|||||||
.button-row button {
|
.button-row button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reminder-settings {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
.tts-row {
|
||||||
.button-row-edit {
|
padding-bottom: 0;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-row-edit > button {
|
|
||||||
width: 100%;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.title.pageTitle {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-frame {
|
|
||||||
margin-top: 4rem !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
|
||||||
.customizable.thumbnail img {
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.customizable.is-24x24 img {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,6 +774,16 @@ div.reminderError .errorHead .reminderTime {
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.reminderError .reminderMessage {
|
||||||
|
font-size: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
color: rgb(54, 54, 54);
|
||||||
|
flex-grow: 1;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* other stuff */
|
/* other stuff */
|
||||||
|
|
||||||
.half-rem {
|
.half-rem {
|
||||||
|
@ -454,17 +454,25 @@ document.addEventListener("guildSwitched", async (e) => {
|
|||||||
.querySelectorAll(".patreon-only")
|
.querySelectorAll(".patreon-only")
|
||||||
.forEach((el) => el.classList.add("is-locked"));
|
.forEach((el) => el.classList.add("is-locked"));
|
||||||
|
|
||||||
let $li = document.querySelector(`li[data-guild="${e.detail.guild_id}"]`);
|
let $li = document.querySelectorAll(`li[data-guild="${e.detail.guild_id}"]`);
|
||||||
|
|
||||||
if ($li === null) {
|
if ($li.length === 0) {
|
||||||
switch_pane("user-error");
|
switch_pane("user-error");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_pane(e.detail.pane);
|
switch_pane(e.detail.pane);
|
||||||
reset_guild_pane();
|
reset_guild_pane();
|
||||||
$li.querySelector("li > a").classList.add("is-active");
|
document
|
||||||
$li.querySelectorAll(`*[data-pane="${e.detail.pane}"]`).forEach((el) => {
|
.querySelectorAll(`li[data-guild="${e.detail.guild_id}"] > a`)
|
||||||
|
.forEach((el) => {
|
||||||
|
el.classList.add("is-active");
|
||||||
|
});
|
||||||
|
document
|
||||||
|
.querySelectorAll(
|
||||||
|
`li[data-guild="${e.detail.guild_id}"] *[data-pane="${e.detail.pane}"]`
|
||||||
|
)
|
||||||
|
.forEach((el) => {
|
||||||
el.classList.add("is-active");
|
el.classList.add("is-active");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -30,6 +30,8 @@ document.addEventListener("paneLoad", (ev) => {
|
|||||||
{ zone: "UTC" }
|
{ zone: "UTC" }
|
||||||
);
|
);
|
||||||
newRow.querySelector(".reminderName").textContent = reminder.name;
|
newRow.querySelector(".reminderName").textContent = reminder.name;
|
||||||
|
newRow.querySelector(".reminderMessage").textContent =
|
||||||
|
reminder.status_message;
|
||||||
newRow.querySelector(".reminderTime").textContent = statusTime
|
newRow.querySelector(".reminderTime").textContent = statusTime
|
||||||
.toLocal()
|
.toLocal()
|
||||||
.toLocaleString(luxon.DateTime.DATETIME_MED);
|
.toLocaleString(luxon.DateTime.DATETIME_MED);
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
<div class="navbar-brand">
|
<div class="navbar-brand">
|
||||||
<a class="navbar-item" href="/">
|
<a class="navbar-item" href="/">
|
||||||
<figure class="image">
|
<figure class="image">
|
||||||
<img src="/static/img/logo_nobg.webp" alt="Reminder Bot Logo">
|
<img width="28px" height="28px" src="/static/img/logo_nobg.webp" alt="Reminder Bot Logo">
|
||||||
</figure>
|
</figure>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@ -234,6 +234,7 @@
|
|||||||
<a href="/">
|
<a href="/">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<img src="/static/img/logo_nobg.webp" alt="Reminder bot logo"
|
<img src="/static/img/logo_nobg.webp" alt="Reminder bot logo"
|
||||||
|
width="52px" height="52px"
|
||||||
class="dashboard-brand">
|
class="dashboard-brand">
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
@ -133,8 +133,6 @@
|
|||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<div class="column settings">
|
<div class="column settings">
|
||||||
<div class="columns">
|
|
||||||
<div class="column">
|
|
||||||
<div class="field channel-field">
|
<div class="field channel-field">
|
||||||
<div class="collapses">
|
<div class="collapses">
|
||||||
<label class="label" for="channelOption">Channel*</label>
|
<label class="label" for="channelOption">Channel*</label>
|
||||||
@ -149,8 +147,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<label class="label collapses">
|
<label class="label collapses">
|
||||||
@ -159,8 +156,6 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="collapses split-controls">
|
<div class="collapses split-controls">
|
||||||
<div>
|
<div>
|
||||||
@ -236,7 +231,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% if creating %}
|
{% if creating %}
|
||||||
<div class="button-row">
|
<div class="button-row">
|
||||||
<div class="button-row-reminder">
|
<div class="button-row-reminder">
|
||||||
@ -270,6 +267,3 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="reminderName">
|
<div class="reminderName">
|
||||||
Reminder
|
Reminder
|
||||||
|
</div>
|
||||||
|
<div class="reminderMessage">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="reminderTime">
|
<div class="reminderTime">
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user