Compare commits
1 Commits
next
...
jude/confi
Author | SHA1 | Date | |
---|---|---|---|
53aa5ebc55 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@
|
|||||||
/venv
|
/venv
|
||||||
.cargo
|
.cargo
|
||||||
/.idea
|
/.idea
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
1389
Cargo.lock
generated
1389
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -1,18 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "reminder-rs"
|
name = "reminder-rs"
|
||||||
version = "1.6.40"
|
version = "1.6.36"
|
||||||
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"
|
poise = "0.5.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 = "3.0"
|
lazy-regex = "2.3.0"
|
||||||
regex = "1.9"
|
regex = "1.6"
|
||||||
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.7", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "migrate"]}
|
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "migrate"]}
|
||||||
base64 = "0.21.0"
|
base64 = "0.21.0"
|
||||||
|
|
||||||
[dependencies.postman]
|
[dependencies.postman]
|
||||||
@ -42,7 +42,7 @@ assets = [
|
|||||||
["target/release/reminder-rs", "usr/bin/reminder-rs", "755"],
|
["target/release/reminder-rs", "usr/bin/reminder-rs", "755"],
|
||||||
["conf/default.env", "etc/reminder-rs/config.env", "600"],
|
["conf/default.env", "etc/reminder-rs/config.env", "600"],
|
||||||
["conf/Rocket.toml", "etc/reminder-rs/Rocket.toml", "600"],
|
["conf/Rocket.toml", "etc/reminder-rs/Rocket.toml", "600"],
|
||||||
["web/static/**/*", "lib/reminder-rs/static", "644"],
|
["$OUT_DIR/web/static/**/*", "lib/reminder-rs/static", "644"],
|
||||||
["web/templates/**/*", "lib/reminder-rs/templates", "644"],
|
["web/templates/**/*", "lib/reminder-rs/templates", "644"],
|
||||||
["healthcheck", "lib/reminder-rs/healthcheck", "755"],
|
["healthcheck", "lib/reminder-rs/healthcheck", "755"],
|
||||||
["cron.d/reminder_health", "etc/cron.d/reminder_health", "644"],
|
["cron.d/reminder_health", "etc/cron.d/reminder_health", "644"],
|
||||||
|
96
build.rs
96
build.rs
@ -1,3 +1,99 @@
|
|||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
use std::{
|
||||||
|
env, fs,
|
||||||
|
fs::{create_dir_all, DirEntry, File},
|
||||||
|
io,
|
||||||
|
io::Write,
|
||||||
|
path::Path,
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> {
|
||||||
|
if dir.is_dir() {
|
||||||
|
for entry in fs::read_dir(dir)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
visit_dirs(&path, cb)?;
|
||||||
|
} else {
|
||||||
|
cb(&entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
fn process_static(file: &DirEntry) {
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let path = file.path();
|
||||||
|
|
||||||
|
let in_path = path.to_str().unwrap();
|
||||||
|
let art_path = format!("{}/{}", out_dir, in_path);
|
||||||
|
let art_dir = format!("{}/{}", out_dir, path.parent().unwrap().to_str().unwrap());
|
||||||
|
|
||||||
|
match path.extension().map(|o| o.to_str()).flatten() {
|
||||||
|
Some("ts") => {}
|
||||||
|
Some("js") => {
|
||||||
|
create_dir_all(art_dir).unwrap();
|
||||||
|
|
||||||
|
if art_path.ends_with(".min.js") {
|
||||||
|
Command::new("cp").arg(in_path).arg(art_path).spawn().expect("Could not copy");
|
||||||
|
} else {
|
||||||
|
let minified = Command::new("npx")
|
||||||
|
.arg("minify")
|
||||||
|
.arg(in_path)
|
||||||
|
.output()
|
||||||
|
.expect("Could not minify");
|
||||||
|
|
||||||
|
let mut fh = File::create(art_path).expect("Couldn't create file");
|
||||||
|
fh.write(&minified.stdout).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some("css") => {
|
||||||
|
create_dir_all(art_dir).unwrap();
|
||||||
|
|
||||||
|
if art_path.ends_with(".min.css") {
|
||||||
|
Command::new("cp").arg(in_path).arg(art_path).spawn().expect("Could not copy");
|
||||||
|
} else {
|
||||||
|
let minified = Command::new("npx")
|
||||||
|
.arg("minify")
|
||||||
|
.arg(in_path)
|
||||||
|
.output()
|
||||||
|
.expect("Could not minify");
|
||||||
|
|
||||||
|
let mut fh = File::create(art_path).expect("Couldn't create file");
|
||||||
|
fh.write(&minified.stdout).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
create_dir_all(art_dir).unwrap();
|
||||||
|
|
||||||
|
Command::new("cp").arg(in_path).arg(art_path).spawn().expect("Could not copy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn compile_tsc(file: &DirEntry) {
|
||||||
|
// if path.extension() == Some("ts") {
|
||||||
|
// let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
// let path = file.path();
|
||||||
|
//
|
||||||
|
// Command::new("npx")
|
||||||
|
// .arg("tsc")
|
||||||
|
// .arg(in_path)
|
||||||
|
// .arg(art_path)
|
||||||
|
// .spawn()
|
||||||
|
// .expect("Could not compile");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo:rerun-if-changed=migrations");
|
println!("cargo:rerun-if-changed=migrations");
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
visit_dirs("web/static".as_ref(), &process_static).unwrap();
|
||||||
|
|
||||||
|
// visit_dirs("web/static".as_ref(), &compile_tsc).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
-- Drop existing constraint
|
|
||||||
ALTER TABLE `reminders` DROP CONSTRAINT `reminders_ibfk_1`;
|
|
||||||
|
|
||||||
ALTER TABLE `reminders` MODIFY COLUMN `channel_id` INT UNSIGNED;
|
|
||||||
ALTER TABLE `reminders` ADD COLUMN `guild_id` INT UNSIGNED;
|
|
||||||
|
|
||||||
ALTER TABLE `reminders`
|
|
||||||
ADD CONSTRAINT `guild_id_fk`
|
|
||||||
FOREIGN KEY (`guild_id`)
|
|
||||||
REFERENCES `guilds`(`id`)
|
|
||||||
ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE `reminders`
|
|
||||||
ADD CONSTRAINT `channel_id_fk`
|
|
||||||
FOREIGN KEY (`channel_id`)
|
|
||||||
REFERENCES `channels`(`id`)
|
|
||||||
ON DELETE SET NULL;
|
|
||||||
|
|
||||||
UPDATE `reminders` SET `guild_id` = (SELECT guilds.`id` FROM `channels` INNER JOIN `guilds` ON channels.guild_id = guilds.id WHERE reminders.channel_id = channels.id);
|
|
@ -1,4 +0,0 @@
|
|||||||
ALTER TABLE reminders ADD COLUMN `status_change_time` DATETIME;
|
|
||||||
|
|
||||||
-- This is a best-guess as to the status change time.
|
|
||||||
UPDATE reminders SET `status_change_time` = `utc_time` WHERE `status` != 'pending';
|
|
485
package-lock.json
generated
Normal file
485
package-lock.json
generated
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
{
|
||||||
|
"name": "reminder-rs",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"devDependencies": {
|
||||||
|
"minify": "^10.3.0",
|
||||||
|
"prettier": "^3.0.1",
|
||||||
|
"tsc": "^2.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/set-array": "^1.0.1",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/set-array": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/source-map": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
|
||||||
|
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.0",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.4.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||||
|
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
|
||||||
|
"integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@putout/minify": {
|
||||||
|
"version": "1.49.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@putout/minify/-/minify-1.49.0.tgz",
|
||||||
|
"integrity": "sha512-T/eS9rJC0tgq/s8uLpB0cpbsUaY7KSML3UbvPri2qjVCcEK/qwi8+lNWdp8VSyOWiC25Ntrt/DewOu6dXRX1ng==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/acorn": {
|
||||||
|
"version": "8.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
|
||||||
|
"integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/camel-case": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"pascal-case": "^3.1.2",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/clean-css": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": "~0.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "10.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||||
|
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/css-b64-images": {
|
||||||
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-TgQBEdP07adhrDfXvI5o6bHGukKBNMzp2Ngckc/6d09zpjD2gc1Hl3Ca1CKgb8FXjHi88+Phv2Uegs2kTL4zjg==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"css-b64-images": "bin/css-b64-images"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
|
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/dot-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/find-up": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^7.1.0",
|
||||||
|
"path-exists": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/html-minifier-terser": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"camel-case": "^4.1.2",
|
||||||
|
"clean-css": "~5.3.2",
|
||||||
|
"commander": "^10.0.0",
|
||||||
|
"entities": "^4.4.0",
|
||||||
|
"param-case": "^3.0.4",
|
||||||
|
"relateurl": "^0.2.7",
|
||||||
|
"terser": "^5.15.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"html-minifier-terser": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.13.1 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jju": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/locate-path": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lower-case": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minify": {
|
||||||
|
"version": "10.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minify/-/minify-10.3.0.tgz",
|
||||||
|
"integrity": "sha512-eRkx2J1ykkGBVi1gI2sksmovWFzts+GYi2u3Jd/S5eNIkzj0pidciICsWRWdTKTLZVFUP7b6IvoAzasvQkMicg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@putout/minify": "^1.0.4",
|
||||||
|
"clean-css": "^5.0.1",
|
||||||
|
"css-b64-images": "~0.2.5",
|
||||||
|
"debug": "^4.1.0",
|
||||||
|
"find-up": "^6.1.0",
|
||||||
|
"html-minifier-terser": "^7.1.0",
|
||||||
|
"readjson": "^2.2.2",
|
||||||
|
"simport": "^1.2.0",
|
||||||
|
"try-catch": "^3.0.0",
|
||||||
|
"try-to-catch": "^3.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"minify": "bin/minify.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/no-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lower-case": "^2.0.2",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-limit": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"yocto-queue": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-locate": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/param-case": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"dot-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pascal-case": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"no-case": "^3.0.4",
|
||||||
|
"tslib": "^2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-exists": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/prettier": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin/prettier.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/readjson": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readjson/-/readjson-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-PdeC9tsmLWBiL8vMhJvocq+OezQ3HhsH2HrN7YkhfYcTjQSa/iraB15A7Qvt7Xpr0Yd2rDNt6GbFwVQDg3HcAw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"jju": "^1.4.0",
|
||||||
|
"try-catch": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/relateurl": {
|
||||||
|
"version": "0.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
|
||||||
|
"integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/simport": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/simport/-/simport-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-85Bm7pKsqiiQ8rmYCaPDdlXZjJvuW6/k/FY8MTtLFMgU7f8S00CgTHfRtWB6KwSb6ek4p9YyG2enG1+yJbl+CA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"readjson": "^2.2.0",
|
||||||
|
"try-to-catch": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-support": {
|
||||||
|
"version": "0.5.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||||
|
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/terser": {
|
||||||
|
"version": "5.19.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz",
|
||||||
|
"integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
"acorn": "^8.8.2",
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map-support": "~0.5.20"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"terser": "bin/terser"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/terser/node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/try-catch": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/try-catch/-/try-catch-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-91yfXw1rr/P6oLpHSyHDOHm0vloVvUoo9FVdw8YwY05QjJQG9OT0LUxe2VRAzmHG+0CUOmI3nhxDUMLxDN/NEQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/try-to-catch": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/try-to-catch/-/try-to-catch-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-hOY83V84Hx/1sCzDSaJA+Xz2IIQOHRvjxzt+F0OjbQGPZ6yLPLArMA0gw/484MlfUkQbCpKYMLX3VDCAjWKfzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tsc": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/yocto-queue": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.20"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
package.json
Normal file
7
package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"minify": "^10.3.0",
|
||||||
|
"prettier": "^3.0.1",
|
||||||
|
"tsc": "^2.0.4"
|
||||||
|
}
|
||||||
|
}
|
@ -5,12 +5,12 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1", features = ["process", "full"] }
|
tokio = { version = "1", features = ["process", "full"] }
|
||||||
regex = "1.9"
|
regex = "1.4"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
chrono-tz = { version = "0.8", features = ["serde"] }
|
chrono-tz = { version = "0.5", 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.7", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "json"]}
|
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "json"]}
|
||||||
serenity = { version = "0.11", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
serenity = { version = "0.11.1", 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: Option<u64>,
|
channel_id: u64,
|
||||||
webhook_id: Option<u64>,
|
webhook_id: Option<u64>,
|
||||||
webhook_token: Option<String>,
|
webhook_token: Option<String>,
|
||||||
|
|
||||||
channel_paused: Option<bool>,
|
channel_paused: 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
|
||||||
LEFT JOIN
|
INNER JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
reminders.channel_id = channels.id
|
reminders.channel_id = channels.id
|
||||||
@ -343,10 +343,7 @@ 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)
|
||||||
@ -418,9 +415,7 @@ 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
|
||||||
)
|
)
|
||||||
@ -453,10 +448,7 @@ 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,
|
||||||
)
|
)
|
||||||
@ -469,10 +461,7 @@ 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)
|
||||||
@ -482,17 +471,10 @@ WHERE
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn set_sent(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
async fn set_sent(&self, pool: impl Executor<'_, Database = Database> + Copy) {
|
||||||
sqlx::query!(
|
sqlx::query!("UPDATE reminders SET `status` = 'sent' WHERE `id` = ?", self.id)
|
||||||
"
|
.execute(pool)
|
||||||
UPDATE reminders
|
.await
|
||||||
SET `status` = 'sent', `status_change_time` = NOW()
|
.expect(&format!("Could not delete Reminder {}", self.id));
|
||||||
WHERE `id` = ?
|
|
||||||
",
|
|
||||||
self.id
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await
|
|
||||||
.expect(&format!("Could not delete Reminder {}", self.id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_failed(
|
async fn set_failed(
|
||||||
@ -501,11 +483,7 @@ 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
|
||||||
)
|
)
|
||||||
@ -515,9 +493,7 @@ 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>) {
|
||||||
if let Some(channel_id) = self.channel_id {
|
let _ = http.as_ref().pin_message(self.channel_id, message_id.into(), None).await;
|
||||||
let _ = http.as_ref().pin_message(channel_id, message_id.into(), None).await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
@ -527,11 +503,10 @@ 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(channel_id).to_channel(&cache_http).await;
|
let channel = ChannelId(reminder.channel_id).to_channel(&cache_http).await;
|
||||||
|
|
||||||
match channel {
|
match channel {
|
||||||
Ok(Channel::Guild(channel)) => {
|
Ok(Channel::Guild(channel)) => {
|
||||||
@ -563,7 +538,6 @@ 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| {
|
||||||
@ -593,9 +567,7 @@ 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")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -650,151 +622,124 @@ WHERE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.channel_id {
|
if self.enabled
|
||||||
Some(channel_id) => {
|
&& !(self.channel_paused
|
||||||
if self.enabled
|
&& self
|
||||||
&& !(self.channel_paused.unwrap_or(false)
|
.channel_paused_until
|
||||||
&& self
|
.map_or(true, |inner| inner >= Utc::now().naive_local()))
|
||||||
.channel_paused_until
|
{
|
||||||
.map_or(true, |inner| inner >= Utc::now().naive_local()))
|
let _ = sqlx::query!(
|
||||||
{
|
"UPDATE `channels` SET paused = 0, paused_until = NULL WHERE `channel` = ?",
|
||||||
let _ = sqlx::query!(
|
self.channel_id
|
||||||
"
|
)
|
||||||
UPDATE `channels`
|
.execute(pool)
|
||||||
SET paused = 0, paused_until = NULL
|
.await;
|
||||||
WHERE `channel` = ?
|
|
||||||
",
|
|
||||||
self.channel_id
|
|
||||||
)
|
|
||||||
.execute(pool)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let embed = Embed::from_id(pool, self.id).await.map(|e| e.into());
|
let embed = Embed::from_id(pool, self.id).await.map(|e| e.into());
|
||||||
|
|
||||||
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 = cache_http
|
let webhook_res =
|
||||||
.http()
|
cache_http.http().get_webhook_with_token(webhook_id, webhook_token).await;
|
||||||
.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
|
||||||
} else {
|
} else {
|
||||||
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, channel_id, &self, embed).await
|
send_to_channel(cache_http, &self, embed).await
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
send_to_channel(cache_http, channel_id, &self, embed).await
|
send_to_channel(cache_http, &self, embed).await
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
if let Error::Http(error) = e {
|
if let Error::Http(error) = e {
|
||||||
if let HttpError::UnsuccessfulRequest(http_error) = *error {
|
if let HttpError::UnsuccessfulRequest(http_error) = *error {
|
||||||
match http_error.error.code {
|
match http_error.error.code {
|
||||||
10003 => {
|
10003 => {
|
||||||
self.log_error(
|
self.log_error(
|
||||||
pool,
|
pool,
|
||||||
"Could not be sent as channel does not exist",
|
"Could not be sent as channel does not exist",
|
||||||
None::<&'static str>,
|
None::<&'static str>,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
self.set_failed(
|
self.set_failed(
|
||||||
pool,
|
pool,
|
||||||
"Could not be sent as channel does not exist",
|
"Could not be sent as channel does not exist",
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
10004 => {
|
10004 => {
|
||||||
self.log_error(
|
self.log_error(
|
||||||
pool,
|
pool,
|
||||||
"Could not be sent as guild does not exist",
|
"Could not be sent as guild does not exist",
|
||||||
None::<&'static str>,
|
None::<&'static str>,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
self.set_failed(
|
self.set_failed(pool, "Could not be sent as guild does not exist")
|
||||||
pool,
|
.await;
|
||||||
"Could not be sent as guild does not exist",
|
}
|
||||||
)
|
50001 => {
|
||||||
.await;
|
self.log_error(
|
||||||
}
|
pool,
|
||||||
50001 => {
|
"Could not be sent as missing access",
|
||||||
self.log_error(
|
None::<&'static str>,
|
||||||
pool,
|
)
|
||||||
"Could not be sent as missing access",
|
.await;
|
||||||
None::<&'static str>,
|
self.set_failed(pool, "Could not be sent as missing access").await;
|
||||||
)
|
}
|
||||||
.await;
|
50007 => {
|
||||||
self.set_failed(
|
self.log_error(
|
||||||
pool,
|
pool,
|
||||||
"Could not be sent as missing access",
|
"Could not be sent as user has DMs disabled",
|
||||||
)
|
None::<&'static str>,
|
||||||
.await;
|
)
|
||||||
}
|
.await;
|
||||||
50007 => {
|
self.set_failed(pool, "Could not be sent as user has DMs disabled")
|
||||||
self.log_error(
|
.await;
|
||||||
pool,
|
}
|
||||||
"Could not be sent as user has DMs disabled",
|
50013 => {
|
||||||
None::<&'static str>,
|
self.log_error(
|
||||||
)
|
pool,
|
||||||
.await;
|
"Could not be sent as permissions are invalid",
|
||||||
self.set_failed(
|
None::<&'static str>,
|
||||||
pool,
|
)
|
||||||
"Could not be sent as user has DMs disabled",
|
.await;
|
||||||
)
|
self.set_failed(
|
||||||
.await;
|
pool,
|
||||||
}
|
"Could not be sent as permissions are invalid",
|
||||||
50013 => {
|
)
|
||||||
self.log_error(
|
.await;
|
||||||
pool,
|
}
|
||||||
"Could not be sent as permissions are invalid",
|
_ => {
|
||||||
None::<&'static str>,
|
self.log_error(
|
||||||
)
|
pool,
|
||||||
.await;
|
"HTTP error sending reminder",
|
||||||
self.set_failed(
|
Some(http_error),
|
||||||
pool,
|
)
|
||||||
"Could not be sent as permissions are invalid",
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.log_error(
|
|
||||||
pool,
|
|
||||||
"HTTP error sending reminder",
|
|
||||||
Some(http_error),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
self.refresh(pool).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.log_error(pool, "(Likely) a parsing error", Some(error)).await;
|
|
||||||
self.refresh(pool).await;
|
self.refresh(pool).await;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.log_error(pool, "Non-HTTP error", Some(e)).await;
|
|
||||||
self.refresh(pool).await;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.log_success(pool).await;
|
self.log_error(pool, "(Likely) a parsing error", Some(error)).await;
|
||||||
self.refresh(pool).await;
|
self.refresh(pool).await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info!("Reminder {} is paused", self.id);
|
self.log_error(pool, "Non-HTTP error", Some(e)).await;
|
||||||
|
|
||||||
self.refresh(pool).await;
|
self.refresh(pool).await;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
self.log_success(pool).await;
|
||||||
|
self.refresh(pool).await;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
info!("Reminder {} is paused", self.id);
|
||||||
|
|
||||||
None => {
|
self.refresh(pool).await;
|
||||||
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;
|
||||||
|
@ -166,21 +166,15 @@ impl ComponentDataModel {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ComponentDataModel::DelSelector(selector) => {
|
ComponentDataModel::DelSelector(selector) => {
|
||||||
for id in &component.data.values {
|
let selected_id = component.data.values.join(",");
|
||||||
match id.parse::<u32>() {
|
|
||||||
Ok(id) => {
|
|
||||||
if let Some(reminder) = Reminder::from_id(&data.database, id).await {
|
|
||||||
reminder.delete(&data.database).await.unwrap();
|
|
||||||
} else {
|
|
||||||
warn!("Attempt to delete non-existent reminder");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(e) => {
|
sqlx::query!(
|
||||||
warn!("Error casting ID to integer: {:?}.", e);
|
"UPDATE reminders SET `status` = 'pending' WHERE FIND_IN_SET(id, ?)",
|
||||||
}
|
selected_id
|
||||||
}
|
)
|
||||||
}
|
.execute(&data.database)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let reminders = Reminder::from_guild(
|
let reminders = Reminder::from_guild(
|
||||||
&ctx,
|
&ctx,
|
||||||
|
@ -10,7 +10,6 @@ pub struct ChannelData {
|
|||||||
pub webhook_id: Option<u64>,
|
pub webhook_id: Option<u64>,
|
||||||
pub webhook_token: Option<String>,
|
pub webhook_token: Option<String>,
|
||||||
pub paused: bool,
|
pub paused: bool,
|
||||||
pub db_guild_id: Option<u32>,
|
|
||||||
pub paused_until: Option<NaiveDateTime>,
|
pub paused_until: Option<NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,11 +22,7 @@ impl ChannelData {
|
|||||||
|
|
||||||
if let Ok(c) = sqlx::query_as_unchecked!(
|
if let Ok(c) = sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until FROM channels WHERE channel = ?",
|
||||||
SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until,
|
|
||||||
guild_id AS db_guild_id
|
|
||||||
FROM channels WHERE channel = ?
|
|
||||||
",
|
|
||||||
channel_id
|
channel_id
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
@ -35,18 +30,12 @@ impl ChannelData {
|
|||||||
{
|
{
|
||||||
Ok(c)
|
Ok(c)
|
||||||
} else {
|
} else {
|
||||||
let props =
|
let props = channel.to_owned().guild().map(|g| (g.guild_id.as_u64().to_owned(), g.name));
|
||||||
channel.to_owned().guild().map(|g| (g.guild_id.as_u64().to_owned(), g.name));
|
|
||||||
|
|
||||||
let (guild_id, channel_name) =
|
let (guild_id, channel_name) = if let Some((a, b)) = props { (Some(a), Some(b)) } else { (None, None) };
|
||||||
if let Some((a, b)) = props { (Some(a), Some(b)) } else { (None, None) };
|
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"INSERT IGNORE INTO channels (channel, name, guild_id) VALUES (?, ?, (SELECT id FROM guilds WHERE guild = ?))",
|
||||||
INSERT IGNORE INTO channels
|
|
||||||
(channel, name, guild_id)
|
|
||||||
VALUES (?, ?, (SELECT id FROM guilds WHERE guild = ?))
|
|
||||||
",
|
|
||||||
channel_id,
|
channel_id,
|
||||||
channel_name,
|
channel_name,
|
||||||
guild_id
|
guild_id
|
||||||
@ -57,10 +46,7 @@ impl ChannelData {
|
|||||||
Ok(sqlx::query_as_unchecked!(
|
Ok(sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused,
|
SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_until FROM channels WHERE channel = ?
|
||||||
paused_until, guild_id AS db_guild_id
|
|
||||||
FROM channels
|
|
||||||
WHERE channel = ?
|
|
||||||
",
|
",
|
||||||
channel_id
|
channel_id
|
||||||
)
|
)
|
||||||
@ -72,10 +58,8 @@ impl ChannelData {
|
|||||||
pub async fn commit_changes(&self, pool: &MySqlPool) {
|
pub async fn commit_changes(&self, pool: &MySqlPool) {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
UPDATE channels
|
UPDATE channels SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?, paused = ?, paused_until \
|
||||||
SET name = ?, nudge = ?, blacklisted = ?, webhook_id = ?, webhook_token = ?,
|
= ? WHERE id = ?
|
||||||
paused = ?, paused_until = ?
|
|
||||||
WHERE id = ?
|
|
||||||
",
|
",
|
||||||
self.name,
|
self.name,
|
||||||
self.nudge,
|
self.nudge,
|
||||||
|
@ -51,7 +51,6 @@ pub struct ReminderBuilder {
|
|||||||
pool: MySqlPool,
|
pool: MySqlPool,
|
||||||
uid: String,
|
uid: String,
|
||||||
channel: u32,
|
channel: u32,
|
||||||
guild: Option<u32>,
|
|
||||||
thread_id: Option<u64>,
|
thread_id: Option<u64>,
|
||||||
utc_time: NaiveDateTime,
|
utc_time: NaiveDateTime,
|
||||||
timezone: String,
|
timezone: String,
|
||||||
@ -87,7 +86,6 @@ impl ReminderBuilder {
|
|||||||
INSERT INTO reminders (
|
INSERT INTO reminders (
|
||||||
`uid`,
|
`uid`,
|
||||||
`channel_id`,
|
`channel_id`,
|
||||||
`guild_id`,
|
|
||||||
`utc_time`,
|
`utc_time`,
|
||||||
`timezone`,
|
`timezone`,
|
||||||
`interval_seconds`,
|
`interval_seconds`,
|
||||||
@ -112,13 +110,11 @@ INSERT INTO reminders (
|
|||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
?,
|
|
||||||
?
|
?
|
||||||
)
|
)
|
||||||
",
|
",
|
||||||
self.uid,
|
self.uid,
|
||||||
self.channel,
|
self.channel,
|
||||||
self.guild,
|
|
||||||
utc_time,
|
utc_time,
|
||||||
self.timezone,
|
self.timezone,
|
||||||
self.interval_seconds,
|
self.interval_seconds,
|
||||||
@ -251,10 +247,10 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
{
|
{
|
||||||
Err(ReminderError::UserBlockedDm)
|
Err(ReminderError::UserBlockedDm)
|
||||||
} else {
|
} else {
|
||||||
Ok((user_data.dm_channel, None))
|
Ok(user_data.dm_channel)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok((user_data.dm_channel, None))
|
Ok(user_data.dm_channel)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ReminderError::InvalidTag)
|
Err(ReminderError::InvalidTag)
|
||||||
@ -301,13 +297,13 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
.commit_changes(&self.ctx.data().database)
|
.commit_changes(&self.ctx.data().database)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok((channel_data.id, channel_data.db_guild_id))
|
Ok(channel_data.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e) => Err(ReminderError::DiscordError(e.to_string())),
|
Err(e) => Err(ReminderError::DiscordError(e.to_string())),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok((channel_data.id, channel_data.db_guild_id))
|
Ok(channel_data.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -321,8 +317,7 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
let builder = ReminderBuilder {
|
let builder = ReminderBuilder {
|
||||||
pool: self.ctx.data().database.clone(),
|
pool: self.ctx.data().database.clone(),
|
||||||
uid: generate_uid(),
|
uid: generate_uid(),
|
||||||
channel: c.0,
|
channel: c,
|
||||||
guild: c.1,
|
|
||||||
thread_id,
|
thread_id,
|
||||||
utc_time: self.utc_time,
|
utc_time: self.utc_time,
|
||||||
timezone: self.timezone.to_string(),
|
timezone: self.timezone.to_string(),
|
||||||
|
@ -304,13 +304,10 @@ WHERE
|
|||||||
&self,
|
&self,
|
||||||
db: impl Executor<'_, Database = Database>,
|
db: impl Executor<'_, Database = Database>,
|
||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
sqlx::query!(
|
sqlx::query!("UPDATE reminders SET `status` = 'deleted' WHERE uid = ?", self.uid)
|
||||||
"UPDATE reminders SET `status` = 'deleted', `status_change_time` = NOW() WHERE uid = ?",
|
.execute(db)
|
||||||
self.uid
|
.await
|
||||||
)
|
.map(|_| ())
|
||||||
.execute(db)
|
|
||||||
.await
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_content(&self) -> &str {
|
pub fn display_content(&self) -> &str {
|
||||||
|
@ -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", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
serenity = { version = "0.11.1", 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.7", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono", "json"] }
|
sqlx = { version = "0.6", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono", "json"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
chrono-tz = "0.8"
|
chrono-tz = "0.5"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
@ -72,14 +72,10 @@ pub async fn initialize(
|
|||||||
db_pool: Pool<Database>,
|
db_pool: Pool<Database>,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
info!("Checking environment variables...");
|
info!("Checking environment variables...");
|
||||||
|
env::var("OAUTH2_CLIENT_ID").expect("`OAUTH2_CLIENT_ID' not supplied");
|
||||||
if env::var("OFFLINE").map_or(true, |v| v != "1") {
|
env::var("OAUTH2_CLIENT_SECRET").expect("`OAUTH2_CLIENT_SECRET' not supplied");
|
||||||
env::var("OAUTH2_CLIENT_ID").expect("`OAUTH2_CLIENT_ID' not supplied");
|
env::var("OAUTH2_DISCORD_CALLBACK").expect("`OAUTH2_DISCORD_CALLBACK' not supplied");
|
||||||
env::var("OAUTH2_CLIENT_SECRET").expect("`OAUTH2_CLIENT_SECRET' not supplied");
|
env::var("PATREON_GUILD_ID").expect("`PATREON_GUILD_ID' not supplied");
|
||||||
env::var("OAUTH2_DISCORD_CALLBACK").expect("`OAUTH2_DISCORD_CALLBACK' not supplied");
|
|
||||||
env::var("PATREON_GUILD_ID").expect("`PATREON_GUILD_ID' not supplied");
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Done!");
|
info!("Done!");
|
||||||
|
|
||||||
let oauth2_client = BasicClient::new(
|
let oauth2_client = BasicClient::new(
|
||||||
@ -150,8 +146,7 @@ pub async fn initialize(
|
|||||||
.mount(
|
.mount(
|
||||||
"/dashboard",
|
"/dashboard",
|
||||||
routes![
|
routes![
|
||||||
routes::dashboard::dashboard_1,
|
routes::dashboard::dashboard,
|
||||||
routes::dashboard::dashboard_2,
|
|
||||||
routes::dashboard::dashboard_home,
|
routes::dashboard::dashboard_home,
|
||||||
routes::dashboard::user::get_user_info,
|
routes::dashboard::user::get_user_info,
|
||||||
routes::dashboard::user::update_user_info,
|
routes::dashboard::user::update_user_info,
|
||||||
@ -190,8 +185,6 @@ pub async fn initialize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_subscription(cache_http: impl CacheHttp, user_id: impl Into<UserId>) -> bool {
|
pub async fn check_subscription(cache_http: impl CacheHttp, user_id: impl Into<UserId>) -> bool {
|
||||||
offline!(true);
|
|
||||||
|
|
||||||
if let Some(subscription_guild) = *CNC_GUILD {
|
if let Some(subscription_guild) = *CNC_GUILD {
|
||||||
let guild_member = GuildId(subscription_guild).member(cache_http, user_id).await;
|
let guild_member = GuildId(subscription_guild).member(cache_http, user_id).await;
|
||||||
|
|
||||||
@ -213,8 +206,6 @@ pub async fn check_guild_subscription(
|
|||||||
cache_http: impl CacheHttp,
|
cache_http: impl CacheHttp,
|
||||||
guild_id: impl Into<GuildId>,
|
guild_id: impl Into<GuildId>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
offline!(true);
|
|
||||||
|
|
||||||
if let Some(guild) = cache_http.cache().unwrap().guild(guild_id) {
|
if let Some(guild) = cache_http.cache().unwrap().guild(guild_id) {
|
||||||
let owner = guild.owner_id;
|
let owner = guild.owner_id;
|
||||||
|
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
macro_rules! offline {
|
|
||||||
($field:expr) => {
|
|
||||||
if std::env::var("OFFLINE").map_or(false, |v| v == "1") {
|
|
||||||
return $field;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! check_length {
|
macro_rules! check_length {
|
||||||
($max:ident, $field:expr) => {
|
($max:ident, $field:expr) => {
|
||||||
if $field.len() > $max {
|
if $field.len() > $max {
|
||||||
@ -60,45 +52,43 @@ macro_rules! check_authorization {
|
|||||||
|
|
||||||
let user_id = $cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten();
|
let user_id = $cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten();
|
||||||
|
|
||||||
if std::env::var("OFFLINE").map_or(true, |v| v != "1") {
|
match user_id {
|
||||||
match user_id {
|
Some(user_id) => {
|
||||||
Some(user_id) => {
|
match GuildId($guild).to_guild_cached($ctx) {
|
||||||
match GuildId($guild).to_guild_cached($ctx) {
|
Some(guild) => {
|
||||||
Some(guild) => {
|
let member_res = guild.member($ctx, UserId(user_id)).await;
|
||||||
let member_res = guild.member($ctx, UserId(user_id)).await;
|
|
||||||
|
|
||||||
match member_res {
|
match member_res {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(json!({"error": "User not in guild"}));
|
return Err(json!({"error": "User not in guild"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(member) => {
|
Ok(member) => {
|
||||||
let permissions_res = member.permissions($ctx);
|
let permissions_res = member.permissions($ctx);
|
||||||
|
|
||||||
match permissions_res {
|
match permissions_res {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(json!({"error": "Couldn't fetch permissions"}));
|
return Err(json!({"error": "Couldn't fetch permissions"}));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(permissions) => {
|
Ok(permissions) => {
|
||||||
if !(permissions.manage_messages() || permissions.manage_guild() || permissions.administrator()) {
|
if !(permissions.manage_messages() || permissions.manage_guild() || permissions.administrator()) {
|
||||||
return Err(json!({"error": "Incorrect permissions"}));
|
return Err(json!({"error": "Incorrect permissions"}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
return Err(json!({"error": "Bot not in guild"}));
|
return Err(json!({"error": "Bot not in guild"}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
return Err(json!({"error": "User not authorized"}));
|
return Err(json!({"error": "User not authorized"}));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,8 @@ use sqlx::{MySql, Pool};
|
|||||||
|
|
||||||
use crate::routes::{
|
use crate::routes::{
|
||||||
dashboard::{
|
dashboard::{
|
||||||
create_reminder, ImportBody, ReminderCreate, ReminderCsv, ReminderTemplateCsv, TodoCsv,
|
create_reminder, generate_uid, ImportBody, Reminder, ReminderCsv, ReminderTemplateCsv,
|
||||||
|
TodoCsv,
|
||||||
},
|
},
|
||||||
JsonResult,
|
JsonResult,
|
||||||
};
|
};
|
||||||
@ -140,7 +141,7 @@ pub async fn import_reminders(
|
|||||||
|
|
||||||
match channel_id.parse::<u64>() {
|
match channel_id.parse::<u64>() {
|
||||||
Ok(channel_id) => {
|
Ok(channel_id) => {
|
||||||
let reminder = ReminderCreate {
|
let reminder = Reminder {
|
||||||
attachment: record.attachment,
|
attachment: record.attachment,
|
||||||
attachment_name: record.attachment_name,
|
attachment_name: record.attachment_name,
|
||||||
avatar: record.avatar,
|
avatar: record.avatar,
|
||||||
@ -167,6 +168,7 @@ 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,
|
||||||
};
|
};
|
||||||
|
@ -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, ReminderCreate, ReminderTemplate,
|
DeleteReminderTemplate, PatchReminder, Reminder, ReminderTemplate,
|
||||||
},
|
},
|
||||||
JsonResult,
|
JsonResult,
|
||||||
},
|
},
|
||||||
@ -46,7 +46,6 @@ pub async fn get_guild_patreon(
|
|||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
ctx: &State<Context>,
|
ctx: &State<Context>,
|
||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
offline!(Ok(json!({ "patreon": true })));
|
|
||||||
check_authorization!(cookies, ctx.inner(), id);
|
check_authorization!(cookies, ctx.inner(), id);
|
||||||
|
|
||||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
match GuildId(id).to_guild_cached(ctx.inner()) {
|
||||||
@ -74,12 +73,6 @@ pub async fn get_guild_channels(
|
|||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
ctx: &State<Context>,
|
ctx: &State<Context>,
|
||||||
) -> JsonResult {
|
) -> 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);
|
check_authorization!(cookies, ctx.inner(), id);
|
||||||
|
|
||||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
match GuildId(id).to_guild_cached(ctx.inner()) {
|
||||||
@ -118,7 +111,6 @@ struct RoleInfo {
|
|||||||
|
|
||||||
#[get("/api/guild/<id>/roles")]
|
#[get("/api/guild/<id>/roles")]
|
||||||
pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State<Context>) -> JsonResult {
|
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);
|
check_authorization!(cookies, ctx.inner(), id);
|
||||||
|
|
||||||
let roles_res = ctx.cache.guild_roles(id);
|
let roles_res = ctx.cache.guild_roles(id);
|
||||||
@ -298,7 +290,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<ReminderCreate>,
|
reminder: Json<Reminder>,
|
||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
serenity_context: &State<Context>,
|
serenity_context: &State<Context>,
|
||||||
pool: &State<Pool<MySql>>,
|
pool: &State<Pool<MySql>>,
|
||||||
@ -318,65 +310,76 @@ pub async fn create_guild_reminder(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/api/guild/<id>/reminders?<status>")]
|
#[get("/api/guild/<id>/reminders")]
|
||||||
pub async fn get_reminders(
|
pub async fn get_reminders(
|
||||||
id: u64,
|
id: u64,
|
||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
|
ctx: &State<Context>,
|
||||||
serenity_context: &State<Context>,
|
serenity_context: &State<Context>,
|
||||||
pool: &State<Pool<MySql>>,
|
pool: &State<Pool<MySql>>,
|
||||||
status: Option<String>,
|
|
||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
check_authorization!(cookies, serenity_context.inner(), id);
|
check_authorization!(cookies, serenity_context.inner(), id);
|
||||||
|
|
||||||
let status = status.unwrap_or("pending".to_string());
|
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
||||||
|
|
||||||
sqlx::query_as_unchecked!(
|
match channels_res {
|
||||||
Reminder,
|
Ok(channels) => {
|
||||||
"
|
let channels = channels
|
||||||
SELECT
|
.keys()
|
||||||
reminders.attachment,
|
.into_iter()
|
||||||
reminders.attachment_name,
|
.map(|k| k.as_u64().to_string())
|
||||||
reminders.avatar,
|
.collect::<Vec<String>>()
|
||||||
channels.channel,
|
.join(",");
|
||||||
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,
|
|
||||||
reminders.status,
|
|
||||||
reminders.status_change_time,
|
|
||||||
reminders.status_message
|
|
||||||
FROM reminders
|
|
||||||
LEFT JOIN channels ON channels.id = reminders.channel_id
|
|
||||||
WHERE FIND_IN_SET(`status`, ?) AND reminders.guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
|
||||||
status,
|
|
||||||
id
|
|
||||||
)
|
|
||||||
.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")
|
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>")]
|
#[patch("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||||
@ -550,8 +553,7 @@ 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,10 +578,7 @@ pub async fn edit_reminder(
|
|||||||
reminders.tts,
|
reminders.tts,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time
|
||||||
reminders.status,
|
|
||||||
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 = ?",
|
||||||
@ -603,12 +602,9 @@ pub async fn delete_reminder(
|
|||||||
reminder: Json<DeleteReminder>,
|
reminder: Json<DeleteReminder>,
|
||||||
pool: &State<Pool<MySql>>,
|
pool: &State<Pool<MySql>>,
|
||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
match sqlx::query!(
|
match sqlx::query!("UPDATE reminders SET `status` = 'deleted' WHERE uid = ?", reminder.uid)
|
||||||
"UPDATE reminders SET `status` = 'deleted', `status_change_time` = NOW() WHERE uid = ?",
|
.execute(pool.inner())
|
||||||
reminder.uid
|
.await
|
||||||
)
|
|
||||||
.execute(pool.inner())
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
Ok(_) => Ok(json!({})),
|
Ok(_) => Ok(json!({})),
|
||||||
|
|
||||||
|
@ -118,8 +118,8 @@ pub struct EmbedField {
|
|||||||
inline: bool,
|
inline: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct ReminderCreate {
|
pub struct Reminder {
|
||||||
#[serde(with = "base64s")]
|
#[serde(with = "base64s")]
|
||||||
attachment: Option<Vec<u8>>,
|
attachment: Option<Vec<u8>>,
|
||||||
attachment_name: Option<String>,
|
attachment_name: Option<String>,
|
||||||
@ -146,45 +146,10 @@ pub struct ReminderCreate {
|
|||||||
name: String,
|
name: String,
|
||||||
restartable: bool,
|
restartable: bool,
|
||||||
tts: bool,
|
tts: bool,
|
||||||
username: Option<String>,
|
|
||||||
utc_time: NaiveDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Reminder {
|
|
||||||
#[serde(with = "base64s")]
|
|
||||||
attachment: Option<Vec<u8>>,
|
|
||||||
attachment_name: Option<String>,
|
|
||||||
avatar: Option<String>,
|
|
||||||
#[serde(with = "string_opt")]
|
|
||||||
channel: Option<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,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
uid: String,
|
uid: String,
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
utc_time: NaiveDateTime,
|
utc_time: NaiveDateTime,
|
||||||
status: String,
|
|
||||||
status_message: Option<String>,
|
|
||||||
status_change_time: Option<NaiveDateTime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -323,7 +288,15 @@ 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};
|
use serde::{de, Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
|
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
|
||||||
@ -335,34 +308,6 @@ mod string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod string_opt {
|
|
||||||
use std::{fmt::Display, str::FromStr};
|
|
||||||
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serializer};
|
|
||||||
|
|
||||||
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
T: Display,
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
match value {
|
|
||||||
Some(value) => serializer.collect_str(value),
|
|
||||||
None => serializer.serialize_none(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
|
|
||||||
where
|
|
||||||
T: FromStr,
|
|
||||||
T::Err: Display,
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
Option::deserialize(deserializer)?
|
|
||||||
.map(|d: String| d.parse().map_err(de::Error::custom))
|
|
||||||
.transpose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod base64s {
|
mod base64s {
|
||||||
use serde::{de, Deserialize, Deserializer, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
@ -407,7 +352,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: ReminderCreate,
|
reminder: Reminder,
|
||||||
) -> 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)
|
||||||
@ -435,7 +380,7 @@ pub async fn create_reminder(
|
|||||||
|
|
||||||
if !channel_matches_guild || !channel_exists {
|
if !channel_matches_guild || !channel_exists {
|
||||||
warn!(
|
warn!(
|
||||||
"Error in `create_reminder`: channel {:?} not found for guild {} (channel exists: {})",
|
"Error in `create_reminder`: channel {} not found for guild {} (channel exists: {})",
|
||||||
reminder.channel, guild_id, channel_exists
|
reminder.channel, guild_id, channel_exists
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -529,13 +474,11 @@ 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,
|
||||||
channel_id,
|
channel_id,
|
||||||
guild_id,
|
|
||||||
avatar,
|
avatar,
|
||||||
content,
|
content,
|
||||||
embed_author,
|
embed_author,
|
||||||
@ -558,14 +501,11 @@ 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,
|
||||||
channel,
|
channel,
|
||||||
guild_id.0,
|
|
||||||
reminder.avatar,
|
reminder.avatar,
|
||||||
reminder.content,
|
reminder.content,
|
||||||
reminder.embed_author,
|
reminder.embed_author,
|
||||||
@ -620,10 +560,7 @@ pub async fn create_reminder(
|
|||||||
reminders.tts,
|
reminders.tts,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
reminders.username,
|
reminders.username,
|
||||||
reminders.utc_time,
|
reminders.utc_time
|
||||||
reminders.status,
|
|
||||||
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 = ?",
|
||||||
@ -725,17 +662,7 @@ pub async fn dashboard_home(cookies: &CookieJar<'_>) -> Result<Template, Redirec
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<_>")]
|
#[get("/<_>")]
|
||||||
pub async fn dashboard_1(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
pub async fn dashboard(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
||||||
if cookies.get_private("userid").is_some() {
|
|
||||||
let map: HashMap<&str, String> = HashMap::new();
|
|
||||||
Ok(Template::render("dashboard", &map))
|
|
||||||
} else {
|
|
||||||
Err(Redirect::to("/login/discord"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/<_>/<_>")]
|
|
||||||
pub async fn dashboard_2(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
|
||||||
if cookies.get_private("userid").is_some() {
|
if cookies.get_private("userid").is_some() {
|
||||||
let map: HashMap<&str, String> = HashMap::new();
|
let map: HashMap<&str, String> = HashMap::new();
|
||||||
Ok(Template::render("dashboard", &map))
|
Ok(Template::render("dashboard", &map))
|
||||||
|
@ -54,8 +54,6 @@ pub async fn get_user_info(
|
|||||||
ctx: &State<Context>,
|
ctx: &State<Context>,
|
||||||
pool: &State<Pool<MySql>>,
|
pool: &State<Pool<MySql>>,
|
||||||
) -> JsonValue {
|
) -> JsonValue {
|
||||||
offline!(json!(UserInfo { name: "Discord".to_string(), patreon: true, timezone: None }));
|
|
||||||
|
|
||||||
if let Some(user_id) =
|
if let Some(user_id) =
|
||||||
cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
|
cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
|
||||||
{
|
{
|
||||||
@ -118,8 +116,6 @@ pub async fn update_user_info(
|
|||||||
|
|
||||||
#[get("/api/user/guilds")]
|
#[get("/api/user/guilds")]
|
||||||
pub async fn get_user_guilds(cookies: &CookieJar<'_>, reqwest_client: &State<Client>) -> JsonValue {
|
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") {
|
if let Some(access_token) = cookies.get_private("access_token") {
|
||||||
let request_res = reqwest_client
|
let request_res = reqwest_client
|
||||||
.get(format!("{}/users/@me/guilds", DISCORD_API))
|
.get(format!("{}/users/@me/guilds", DISCORD_API))
|
||||||
|
@ -15,18 +15,6 @@ 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;
|
||||||
}
|
}
|
||||||
@ -141,12 +129,6 @@ 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;
|
||||||
@ -267,11 +249,11 @@ div#pageNavbar a {
|
|||||||
|
|
||||||
.navbar-item.pageTitle {
|
.navbar-item.pageTitle {
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
white-space: nowrap;
|
text-wrap: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-burger, .dashboard-burger:active, .dashboard-burger.is-active {
|
.navbar-burger, .navbar-burger:active, .navbar-burger.is-active {
|
||||||
background-color: #adc99c !important;
|
background-color: #adc99c !important;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
@ -309,19 +291,10 @@ div.dashboard-sidebar:not(.mobile-sidebar) {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.guildList {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.dashboard-sidebar:not(.mobile-sidebar) .aside-footer {
|
div.dashboard-sidebar:not(.mobile-sidebar) .aside-footer {
|
||||||
flex-shrink: 0;
|
position: fixed;
|
||||||
flex-grow: 0;
|
bottom: 0;
|
||||||
}
|
width: 226px;
|
||||||
|
|
||||||
div.dashboard-sidebar svg {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.mobile-sidebar {
|
div.mobile-sidebar {
|
||||||
@ -471,7 +444,8 @@ input.default-width {
|
|||||||
.customizable.is-400x300 img {
|
.customizable.is-400x300 img {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100px;
|
min-height: 100px;
|
||||||
|
max-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.customizable.is-32x32 img {
|
.customizable.is-32x32 img {
|
||||||
@ -615,14 +589,6 @@ 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;
|
||||||
}
|
}
|
||||||
@ -646,22 +612,7 @@ li.highlight {
|
|||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1023px) {
|
@media only screen and (max-width: 1408px) {
|
||||||
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;
|
||||||
@ -679,20 +630,41 @@ li.highlight {
|
|||||||
.button-row button {
|
.button-row button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.reminder-settings {
|
@media only screen and (max-width: 768px) {
|
||||||
margin-bottom: 0 !important;
|
.button-row-edit {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tts-row {
|
.button-row-edit > button {
|
||||||
padding-bottom: 0;
|
width: 100%;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.title.pageTitle {
|
||||||
|
visibility: hidden;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
.customizable.thumbnail img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customizable.is-24x24 img {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* loader */
|
/* loader */
|
||||||
#loader {
|
#loader {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
|
||||||
background-color: rgba(255, 255, 255, 0.8);
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
@ -704,86 +676,6 @@ li.highlight {
|
|||||||
|
|
||||||
/* END */
|
/* END */
|
||||||
|
|
||||||
div.reminderError {
|
|
||||||
margin: 10px;
|
|
||||||
padding: 14px;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError .errorHead {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError .errorIcon {
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError .errorIcon .fas {
|
|
||||||
display: none
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError[data-case="deleted"] .errorIcon {
|
|
||||||
background-color: #e7e5e4;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError[data-case="failed"] .errorIcon {
|
|
||||||
background-color: #fecaca;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError[data-case="sent"] .errorIcon {
|
|
||||||
background-color: #d9f99d;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError[data-case="deleted"] .errorIcon .fas.fa-trash {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError[data-case="failed"] .errorIcon .fas.fa-exclamation-triangle {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError[data-case="sent"] .errorIcon .fas.fa-check {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError .errorHead .reminderName {
|
|
||||||
font-size: 1rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
color: rgb(54, 54, 54);
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.reminderError .errorHead .reminderTime {
|
|
||||||
font-size: 1rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-shrink: 1;
|
|
||||||
justify-content: center;
|
|
||||||
color: rgb(54, 54, 54);
|
|
||||||
background-color: #ffffff;
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border-color: #e5e5e5;
|
|
||||||
border-width: 1px;
|
|
||||||
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 {
|
||||||
@ -821,38 +713,15 @@ a.switch-pane {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.guild-submenu {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.guild-submenu li {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.switch-pane.is-active ~ .guild-submenu {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feedback {
|
.feedback {
|
||||||
background-color: #5865F2;
|
background-color: #5865F2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-locked {
|
.is-locked {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
|
||||||
|
|
||||||
.is-locked > :not(.patreon-invert) {
|
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.is-locked .patreon-invert {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.patreon-invert {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.is-locked .foreground {
|
.is-locked .foreground {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
@ -882,5 +751,5 @@ a.switch-pane.is-active ~ .guild-submenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.figure-num {
|
.figure-num {
|
||||||
font-size: 2rem;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ const $downloader = document.querySelector("a#downloader");
|
|||||||
const $uploader = document.querySelector("input#uploader");
|
const $uploader = document.querySelector("input#uploader");
|
||||||
|
|
||||||
let channels = [];
|
let channels = [];
|
||||||
let reminderErrors = [];
|
|
||||||
let guildNames = {};
|
let guildNames = {};
|
||||||
let roles = [];
|
let roles = [];
|
||||||
let templates = {};
|
let templates = {};
|
||||||
@ -34,11 +33,7 @@ let globalPatreon = false;
|
|||||||
let guildPatreon = false;
|
let guildPatreon = false;
|
||||||
|
|
||||||
function guildId() {
|
function guildId() {
|
||||||
return document.querySelector("li > a.is-active").parentElement.dataset["guild"];
|
return document.querySelector(".guildList a.is-active").dataset["guild"];
|
||||||
}
|
|
||||||
|
|
||||||
function guildName() {
|
|
||||||
return guildNames[guildId()];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function colorToInt(r, g, b) {
|
function colorToInt(r, g, b) {
|
||||||
@ -57,7 +52,7 @@ function switch_pane(selector) {
|
|||||||
el.classList.add("is-hidden");
|
el.classList.add("is-hidden");
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector(`*[data-name=${selector}]`).classList.remove("is-hidden");
|
document.getElementById(selector).classList.remove("is-hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_select(sel) {
|
function update_select(sel) {
|
||||||
@ -454,27 +449,21 @@ 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.querySelectorAll(`li[data-guild="${e.detail.guild_id}"]`);
|
let $anchor = document.querySelector(
|
||||||
|
`.switch-pane[data-guild="${e.detail.guild_id}"]`
|
||||||
|
);
|
||||||
|
|
||||||
if ($li.length === 0) {
|
let hasError = false;
|
||||||
|
|
||||||
|
if ($anchor === null) {
|
||||||
switch_pane("user-error");
|
switch_pane("user-error");
|
||||||
|
hasError = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_pane(e.detail.pane);
|
switch_pane($anchor.dataset["pane"]);
|
||||||
reset_guild_pane();
|
reset_guild_pane();
|
||||||
document
|
$anchor.classList.add("is-active");
|
||||||
.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");
|
|
||||||
});
|
|
||||||
|
|
||||||
if (globalPatreon || (await fetch_patreon(e.detail.guild_id))) {
|
if (globalPatreon || (await fetch_patreon(e.detail.guild_id))) {
|
||||||
document
|
document
|
||||||
@ -482,26 +471,15 @@ document.addEventListener("guildSwitched", async (e) => {
|
|||||||
.forEach((el) => el.classList.remove("is-locked"));
|
.forEach((el) => el.classList.remove("is-locked"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const event = new CustomEvent("paneLoad", {
|
hasError = await fetch_channels(e.detail.guild_id);
|
||||||
detail: {
|
|
||||||
guild_id: e.detail.guild_id,
|
|
||||||
pane: e.detail.pane,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("paneLoad", async (ev) => {
|
|
||||||
const hasError = await fetch_channels(ev.detail.guild_id);
|
|
||||||
if (!hasError) {
|
if (!hasError) {
|
||||||
fetch_roles(ev.detail.guild_id);
|
fetch_roles(e.detail.guild_id);
|
||||||
fetch_templates(ev.detail.guild_id);
|
fetch_templates(e.detail.guild_id);
|
||||||
fetch_reminders(ev.detail.guild_id);
|
fetch_reminders(e.detail.guild_id);
|
||||||
|
|
||||||
document.querySelectorAll("p.pageTitle").forEach((el) => {
|
document.querySelectorAll("p.pageTitle").forEach((el) => {
|
||||||
el.textContent = `${guildName()} Reminders`;
|
el.textContent = `${e.detail.guild_name} Reminders`;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelectorAll("select.channel-selector").forEach((el) => {
|
document.querySelectorAll("select.channel-selector").forEach((el) => {
|
||||||
el.addEventListener("change", (e) => {
|
el.addEventListener("change", (e) => {
|
||||||
update_select(e.target);
|
update_select(e.target);
|
||||||
@ -706,56 +684,36 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
"%guildname%",
|
"%guildname%",
|
||||||
guild.name
|
guild.name
|
||||||
);
|
);
|
||||||
|
$anchor.dataset["guild"] = guild.id;
|
||||||
$anchor.dataset["name"] = guild.name;
|
$anchor.dataset["name"] = guild.name;
|
||||||
$anchor.href = `/dashboard/${guild.id}/reminders`;
|
$anchor.href = `/dashboard/${guild.id}?name=${guild.name}`;
|
||||||
|
|
||||||
const $li = $anchor.parentElement;
|
$anchor.addEventListener("click", async (e) => {
|
||||||
$li.dataset["guild"] = guild.id;
|
e.preventDefault();
|
||||||
|
window.history.pushState({}, "", `/dashboard/${guild.id}`);
|
||||||
$li.querySelectorAll("a").forEach((el) => {
|
const event = new CustomEvent("guildSwitched", {
|
||||||
el.addEventListener("click", (e) => {
|
detail: {
|
||||||
const pane = el.dataset["pane"];
|
guild_name: guild.name,
|
||||||
const slug = el.dataset["slug"];
|
guild_id: guild.id,
|
||||||
|
},
|
||||||
if (pane !== undefined && slug !== undefined) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
switch_pane(pane);
|
|
||||||
|
|
||||||
window.history.pushState(
|
|
||||||
{},
|
|
||||||
"",
|
|
||||||
`/dashboard/${guild.id}/${slug}`
|
|
||||||
);
|
|
||||||
const event = new CustomEvent("guildSwitched", {
|
|
||||||
detail: {
|
|
||||||
guild_id: guild.id,
|
|
||||||
pane,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
document.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.dispatchEvent(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
element.append($clone);
|
element.append($clone);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const matches = window.location.href.match(
|
const matches = window.location.href.match(/dashboard\/(\d+)/);
|
||||||
/dashboard\/(\d+)(\/)?([a-zA-Z\-]+)?/
|
|
||||||
);
|
|
||||||
if (matches) {
|
if (matches) {
|
||||||
let id = matches[1];
|
let id = matches[1];
|
||||||
let kind = matches[3];
|
|
||||||
let name = guildNames[id];
|
let name = guildNames[id];
|
||||||
|
|
||||||
const event = new CustomEvent("guildSwitched", {
|
const event = new CustomEvent("guildSwitched", {
|
||||||
detail: {
|
detail: {
|
||||||
guild_name: name,
|
guild_name: name,
|
||||||
guild_id: id,
|
guild_id: id,
|
||||||
pane: kind,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
function loadErrors() {
|
|
||||||
return fetch(
|
|
||||||
`/dashboard/api/guild/${guildId()}/reminders?status=deleted,sent,failed`
|
|
||||||
).then((response) => response.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("paneLoad", (ev) => {
|
|
||||||
if (ev.detail.pane !== "errors") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelectorAll(".reminderError").forEach((el) => el.remove());
|
|
||||||
|
|
||||||
const template = document.getElementById("reminderError");
|
|
||||||
const container = document.getElementById("reminderLog");
|
|
||||||
|
|
||||||
loadErrors()
|
|
||||||
.then((res) => {
|
|
||||||
res = res
|
|
||||||
.filter((r) => r.status_change_time !== null)
|
|
||||||
.sort((a, b) => a.status_change_time < b.status_change_time);
|
|
||||||
|
|
||||||
for (const reminder of res) {
|
|
||||||
const newRow = template.content.cloneNode(true);
|
|
||||||
|
|
||||||
newRow.querySelector(".reminderError").dataset["case"] = reminder.status;
|
|
||||||
|
|
||||||
const statusTime = new luxon.DateTime.fromISO(
|
|
||||||
reminder.status_change_time,
|
|
||||||
{ zone: "UTC" }
|
|
||||||
);
|
|
||||||
newRow.querySelector(".reminderName").textContent = reminder.name;
|
|
||||||
newRow.querySelector(".reminderMessage").textContent =
|
|
||||||
reminder.status_message;
|
|
||||||
newRow.querySelector(".reminderTime").textContent = statusTime
|
|
||||||
.toLocal()
|
|
||||||
.toLocaleString(luxon.DateTime.DATETIME_MED);
|
|
||||||
|
|
||||||
container.appendChild(newRow);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
$loader.classList.add("is-hidden");
|
|
||||||
});
|
|
||||||
});
|
|
19
web/static/js/reminder_errors.ts
Normal file
19
web/static/js/reminder_errors.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
let _reminderErrors = [];
|
||||||
|
|
||||||
|
const reminderErrors = () => {
|
||||||
|
return _reminderErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildId = () => {
|
||||||
|
let selected: HTMLElement = document.querySelector(".guildList a.is-active");
|
||||||
|
return selected.dataset["guild"];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadErrors() {
|
||||||
|
fetch(`/dashboard/api/guild/${guildId()}/errors`).then(response => response.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
|
||||||
|
})
|
@ -40,14 +40,14 @@
|
|||||||
<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 width="28px" height="28px" src="/static/img/logo_nobg.webp" alt="Reminder Bot Logo">
|
<img src="/static/img/logo_nobg.webp" alt="Reminder Bot Logo">
|
||||||
</figure>
|
</figure>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<p class="navbar-item pageTitle">
|
<p class="navbar-item pageTitle">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a role="button" class="dashboard-burger navbar-burger is-right" aria-label="menu" aria-expanded="false"
|
<a role="button" class="navbar-burger is-right" aria-label="menu" aria-expanded="false"
|
||||||
data-target="mobileSidebar">
|
data-target="mobileSidebar">
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
<span aria-hidden="true"></span>
|
<span aria-hidden="true"></span>
|
||||||
@ -234,7 +234,6 @@
|
|||||||
<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>
|
||||||
@ -333,16 +332,16 @@
|
|||||||
<p class="subtitle is-hidden-desktop">Press the <span class="icon"><i class="fal fa-bars"></i></span> to get started</p>
|
<p class="subtitle is-hidden-desktop">Press the <span class="icon"><i class="fal fa-bars"></i></span> to get started</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section data-name="reminders" class="is-hidden">
|
<section id="guild" class="is-hidden">
|
||||||
{% include "reminder_dashboard/reminder_dashboard" %}
|
{% include "reminder_dashboard/reminder_dashboard" %}
|
||||||
</section>
|
</section>
|
||||||
<section data-name="errors" class="is-hidden">
|
<section id="reminder-errors" class="is-hidden">
|
||||||
{% include "reminder_dashboard/reminder_errors" %}
|
{% include "reminder_dashboard/reminder_errors" %}
|
||||||
</section>
|
</section>
|
||||||
<section data-name="guild-error" class="is-hidden">
|
<section id="guild-error" class="is-hidden">
|
||||||
{% include "reminder_dashboard/guild_error" %}
|
{% include "reminder_dashboard/guild_error" %}
|
||||||
</section>
|
</section>
|
||||||
<section data-name="user-error" class="is-hidden">
|
<section id="user-error" class="is-hidden">
|
||||||
{% include "reminder_dashboard/user_error" %}
|
{% include "reminder_dashboard/user_error" %}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@ -376,28 +375,14 @@
|
|||||||
|
|
||||||
<template id="guildListEntry">
|
<template id="guildListEntry">
|
||||||
<li>
|
<li>
|
||||||
<a class="switch-pane" data-pane="reminders" data-slug="reminders">
|
<a class="switch-pane" data-pane="guild">
|
||||||
<span class="icon"><i class="fas fa-map-pin"></i></span> <span class="guild-name">%guildname%</span>
|
<span class="icon"><i class="fas fa-map-pin"></i></span> <span class="guild-name">%guildname%</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="guild-submenu">
|
|
||||||
<li>
|
|
||||||
<a class="switch-pane" data-pane="reminders" data-slug="reminders">
|
|
||||||
<span class="icon"><i class="fas fa-calendar-alt"></i></span> Reminders
|
|
||||||
</a>
|
|
||||||
<a class="switch-pane" data-pane="errors" data-slug="errors">
|
|
||||||
<span class="icon"><i class="fas fa-file-alt"></i></span> Logs
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template id="guildReminder">
|
<template id="guildReminder">
|
||||||
{% include "reminder_dashboard/templates/guild_reminder" %}
|
{% include "reminder_dashboard/guild_reminder" %}
|
||||||
</template>
|
|
||||||
|
|
||||||
<template id="reminderError">
|
|
||||||
{% include "reminder_dashboard/templates/reminder_error" %}
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="/static/js/iro.js"></script>
|
<script src="/static/js/iro.js"></script>
|
||||||
|
@ -133,36 +133,38 @@
|
|||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
<div class="column settings">
|
<div class="column settings">
|
||||||
<div class="field channel-field">
|
<div class="columns">
|
||||||
<div class="collapses">
|
<div class="column">
|
||||||
<label class="label" for="channelOption">Channel*</label>
|
<div class="field channel-field">
|
||||||
</div>
|
<div class="collapses">
|
||||||
<div class="control has-icons-left">
|
<label class="label" for="channelOption">Channel*</label>
|
||||||
<div class="select">
|
</div>
|
||||||
<select name="channel" class="channel-selector">
|
<div class="control has-icons-left">
|
||||||
</select>
|
<div class="select">
|
||||||
</div>
|
<select name="channel" class="channel-selector">
|
||||||
<div class="icon is-small is-left">
|
</select>
|
||||||
<i class="fas fa-hashtag"></i>
|
</div>
|
||||||
|
<div class="icon is-small is-left">
|
||||||
|
<i class="fas fa-hashtag"></i>
|
||||||
|
</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">
|
Time*
|
||||||
Time*
|
<input class="input prefill-now" type="datetime-local" step="1" name="time">
|
||||||
<input class="input prefill-now" type="datetime-local" step="1" name="time">
|
</label>
|
||||||
</label>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="collapses split-controls">
|
<div class="collapses split-controls">
|
||||||
<div>
|
<div>
|
||||||
<div class="patreon-only">
|
<div class="patreon-only">
|
||||||
<div class="patreon-invert foreground">
|
|
||||||
Intervals available on <a href="https://patreon.com/jellywx">Patreon</a> or <a href="https://gitea.jellypro.xyz/jude/reminder-bot">self-hosting</a>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Interval <a class="foreground" href="/help/intervals"><i class="fas fa-question-circle"></i></a></label>
|
<label class="label">Interval <a class="foreground" href="/help/intervals"><i class="fas fa-question-circle"></i></a></label>
|
||||||
<div class="control intervalSelector">
|
<div class="control intervalSelector">
|
||||||
@ -231,39 +233,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if creating %}
|
||||||
|
<div class="button-row">
|
||||||
|
<div class="button-row-reminder">
|
||||||
|
<button class="button is-success" id="createReminder">
|
||||||
|
<span>Create Reminder</span> <span class="icon"><i class="fas fa-sparkles"></i></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="button-row-template">
|
||||||
|
<div>
|
||||||
|
<button class="button is-success is-outlined" id="createTemplate">
|
||||||
|
<span>Create Template</span> <span class="icon"><i class="fas fa-file-spreadsheet"></i></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="button is-outlined show-modal is-pulled-right" data-modal="chooseTemplateModal">
|
||||||
|
Load Template
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="button-row-edit">
|
||||||
|
<button class="button is-success save-btn">
|
||||||
|
<span>Save</span> <span class="icon"><i class="fas fa-save"></i></span>
|
||||||
|
</button>
|
||||||
|
<button class="button is-warning disable-enable">
|
||||||
|
</button>
|
||||||
|
<button class="button is-danger delete-reminder">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if creating %}
|
|
||||||
<div class="button-row">
|
|
||||||
<div class="button-row-reminder">
|
|
||||||
<button class="button is-success" id="createReminder">
|
|
||||||
<span>Create Reminder</span> <span class="icon"><i class="fas fa-sparkles"></i></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="button-row-template">
|
|
||||||
<div>
|
|
||||||
<button class="button is-success is-outlined" id="createTemplate">
|
|
||||||
<span>Create Template</span> <span class="icon"><i class="fas fa-file-spreadsheet"></i></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button class="button is-outlined show-modal is-pulled-right" data-modal="chooseTemplateModal">
|
|
||||||
Load Template
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="button-row-edit">
|
|
||||||
<button class="button is-success save-btn">
|
|
||||||
<span>Save</span> <span class="icon"><i class="fas fa-save"></i></span>
|
|
||||||
</button>
|
|
||||||
<button class="button is-warning disable-enable">
|
|
||||||
</button>
|
|
||||||
<button class="button is-danger delete-reminder">
|
|
||||||
Delete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
@ -2,7 +2,7 @@
|
|||||||
<strong>Create Reminder</strong>
|
<strong>Create Reminder</strong>
|
||||||
<div id="reminderCreator">
|
<div id="reminderCreator">
|
||||||
{% set creating = true %}
|
{% set creating = true %}
|
||||||
{% include "reminder_dashboard/templates/guild_reminder" %}
|
{% include "reminder_dashboard/guild_reminder" %}
|
||||||
{% set creating = false %}
|
{% set creating = false %}
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
@ -46,10 +46,6 @@
|
|||||||
<div id="guildReminders">
|
<div id="guildReminders">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="guildErrors">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/sort.js"></script>
|
<script src="/static/js/sort.js"></script>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div id="reminderLog">
|
<div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
<div class="reminderError" data-case="success">
|
|
||||||
<div class="errorHead">
|
|
||||||
<div class="errorIcon">
|
|
||||||
<span class="icon">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
<i class="fas fa-check"></i>
|
|
||||||
<i class="fas fa-exclamation-triangle"></i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="reminderName">
|
|
||||||
Reminder
|
|
||||||
</div>
|
|
||||||
<div class="reminderMessage">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="reminderTime">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
Reference in New Issue
Block a user