Wip commit
This commit is contained in:
parent
cce0de7c75
commit
e4e9af2bb4
202
Cargo.lock
generated
202
Cargo.lock
generated
@ -137,22 +137,6 @@ dependencies = [
|
|||||||
"syn 2.0.42",
|
"syn 2.0.42",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-tungstenite"
|
|
||||||
version = "0.17.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1b71b31561643aa8e7df3effe284fa83ab1a840e52294c5f4bd7bfd8b2becbb"
|
|
||||||
dependencies = [
|
|
||||||
"futures-io",
|
|
||||||
"futures-util",
|
|
||||||
"log",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
"tokio-rustls 0.23.4",
|
|
||||||
"tungstenite 0.17.3",
|
|
||||||
"webpki-roots 0.22.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atoi"
|
name = "atoi"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -1877,15 +1861,6 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ordered-float"
|
|
||||||
version = "2.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "overload"
|
name = "overload"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@ -2102,7 +2077,7 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
"poise_macros",
|
"poise_macros",
|
||||||
"regex",
|
"regex",
|
||||||
"serenity 0.12.0",
|
"serenity",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@ -2142,7 +2117,7 @@ dependencies = [
|
|||||||
"num-integer",
|
"num-integer",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serenity 0.11.7",
|
"serenity",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@ -2346,6 +2321,7 @@ dependencies = [
|
|||||||
"reminder_web",
|
"reminder_web",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
|
"secrecy",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
@ -2369,8 +2345,9 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_dyn_templates",
|
"rocket_dyn_templates",
|
||||||
|
"secrecy",
|
||||||
"serde",
|
"serde",
|
||||||
"serenity 0.11.7",
|
"serenity",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2416,25 +2393,10 @@ dependencies = [
|
|||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-streams",
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"webpki-roots 0.25.3",
|
"webpki-roots",
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ring"
|
|
||||||
version = "0.16.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"spin 0.5.2",
|
|
||||||
"untrusted 0.7.1",
|
|
||||||
"web-sys",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.7"
|
version = "0.17.7"
|
||||||
@ -2445,7 +2407,7 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
"libc",
|
"libc",
|
||||||
"spin 0.9.8",
|
"spin 0.9.8",
|
||||||
"untrusted 0.9.0",
|
"untrusted",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2604,18 +2566,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls"
|
|
||||||
version = "0.20.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"ring 0.16.20",
|
|
||||||
"sct",
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.21.10"
|
version = "0.21.10"
|
||||||
@ -2623,7 +2573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"ring 0.17.7",
|
"ring",
|
||||||
"rustls-webpki 0.101.7",
|
"rustls-webpki 0.101.7",
|
||||||
"sct",
|
"sct",
|
||||||
]
|
]
|
||||||
@ -2635,7 +2585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48"
|
checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"ring 0.17.7",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki 0.102.0",
|
"rustls-webpki 0.102.0",
|
||||||
"subtle",
|
"subtle",
|
||||||
@ -2673,8 +2623,8 @@ version = "0.101.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring 0.17.7",
|
"ring",
|
||||||
"untrusted 0.9.0",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2683,9 +2633,9 @@ version = "0.102.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89"
|
checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring 0.17.7",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"untrusted 0.9.0",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2736,8 +2686,8 @@ version = "0.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring 0.17.7",
|
"ring",
|
||||||
"untrusted 0.9.0",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2791,16 +2741,6 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde-value"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
|
|
||||||
dependencies = [
|
|
||||||
"ordered-float",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.193"
|
version = "1.0.193"
|
||||||
@ -2865,36 +2805,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serenity"
|
|
||||||
version = "0.11.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a7a89cef23483fc9d4caf2df41e6d3928e18aada84c56abd237439d929622c6"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"async-tungstenite",
|
|
||||||
"base64 0.21.5",
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"bytes",
|
|
||||||
"cfg-if",
|
|
||||||
"dashmap",
|
|
||||||
"flate2",
|
|
||||||
"futures",
|
|
||||||
"mime",
|
|
||||||
"mime_guess",
|
|
||||||
"parking_lot",
|
|
||||||
"percent-encoding",
|
|
||||||
"reqwest",
|
|
||||||
"serde",
|
|
||||||
"serde-value",
|
|
||||||
"serde_json",
|
|
||||||
"time",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
"typemap_rev 0.1.5",
|
|
||||||
"url",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serenity"
|
name = "serenity"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@ -2922,22 +2832,11 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"tracing",
|
"tracing",
|
||||||
"typemap_rev 0.3.0",
|
"typemap_rev",
|
||||||
"typesize",
|
"typesize",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha-1"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@ -3135,7 +3034,7 @@ dependencies = [
|
|||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"webpki-roots 0.25.3",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3536,17 +3435,6 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-rustls"
|
|
||||||
version = "0.23.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
|
||||||
dependencies = [
|
|
||||||
"rustls 0.20.9",
|
|
||||||
"tokio",
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.24.1"
|
version = "0.24.1"
|
||||||
@ -3590,8 +3478,8 @@ dependencies = [
|
|||||||
"rustls 0.21.10",
|
"rustls 0.21.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.24.1",
|
"tokio-rustls 0.24.1",
|
||||||
"tungstenite 0.20.1",
|
"tungstenite",
|
||||||
"webpki-roots 0.25.3",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3722,27 +3610,6 @@ version = "0.2.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tungstenite"
|
|
||||||
version = "0.17.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.13.1",
|
|
||||||
"byteorder",
|
|
||||||
"bytes",
|
|
||||||
"http 0.2.11",
|
|
||||||
"httparse",
|
|
||||||
"log",
|
|
||||||
"rand",
|
|
||||||
"rustls 0.20.9",
|
|
||||||
"sha-1",
|
|
||||||
"thiserror",
|
|
||||||
"url",
|
|
||||||
"utf-8",
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tungstenite"
|
name = "tungstenite"
|
||||||
version = "0.20.1"
|
version = "0.20.1"
|
||||||
@ -3763,12 +3630,6 @@ dependencies = [
|
|||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typemap_rev"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed5b74f0a24b5454580a79abb6994393b09adf0ab8070f15827cb666255de155"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typemap_rev"
|
name = "typemap_rev"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -3943,12 +3804,6 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "untrusted"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -4111,25 +3966,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki"
|
|
||||||
version = "0.22.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
|
|
||||||
dependencies = [
|
|
||||||
"ring 0.17.7",
|
|
||||||
"untrusted 0.9.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "webpki-roots"
|
|
||||||
version = "0.22.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
|
|
||||||
dependencies = [
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.25.3"
|
version = "0.25.3"
|
||||||
|
@ -27,6 +27,7 @@ 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.7", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "migrate"]}
|
||||||
base64 = "0.21.0"
|
base64 = "0.21.0"
|
||||||
|
secrecy = "0.8.0"
|
||||||
|
|
||||||
[dependencies.postman]
|
[dependencies.postman]
|
||||||
path = "postman"
|
path = "postman"
|
||||||
|
@ -13,4 +13,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.7", 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.12", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
|
||||||
|
@ -8,11 +8,12 @@ use num_integer::Integer;
|
|||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
builder::CreateEmbed,
|
all::{CreateAttachment, CreateEmbedFooter},
|
||||||
|
builder::{CreateEmbed, CreateEmbedAuthor, CreateMessage, ExecuteWebhook},
|
||||||
http::{CacheHttp, Http, HttpError},
|
http::{CacheHttp, Http, HttpError},
|
||||||
model::{
|
model::{
|
||||||
channel::{Channel, Embed as SerenityEmbed},
|
channel::Channel,
|
||||||
id::ChannelId,
|
id::{ChannelId, MessageId},
|
||||||
webhook::Webhook,
|
webhook::Webhook,
|
||||||
},
|
},
|
||||||
Error, Result,
|
Error, Result,
|
||||||
@ -194,43 +195,36 @@ impl Embed {
|
|||||||
|
|
||||||
impl Into<CreateEmbed> for Embed {
|
impl Into<CreateEmbed> for Embed {
|
||||||
fn into(self) -> CreateEmbed {
|
fn into(self) -> CreateEmbed {
|
||||||
let mut c = CreateEmbed::default();
|
let mut author = CreateEmbedAuthor::new(&self.author);
|
||||||
|
if let Some(author_icon) = &self.author_url {
|
||||||
|
author = author.icon_url(author_icon);
|
||||||
|
}
|
||||||
|
|
||||||
c.title(&self.title)
|
let mut footer = CreateEmbedFooter::new(&self.footer);
|
||||||
|
if let Some(footer_icon) = &self.footer_url {
|
||||||
|
footer = footer.icon_url(footer_icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut embed = CreateEmbed::default()
|
||||||
|
.title(&self.title)
|
||||||
.description(&self.description)
|
.description(&self.description)
|
||||||
.color(self.color)
|
.color(self.color)
|
||||||
.author(|a| {
|
.author(author)
|
||||||
a.name(&self.author);
|
.footer(footer);
|
||||||
|
|
||||||
if let Some(author_icon) = &self.author_url {
|
|
||||||
a.icon_url(author_icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
a
|
|
||||||
})
|
|
||||||
.footer(|f| {
|
|
||||||
f.text(&self.footer);
|
|
||||||
|
|
||||||
if let Some(footer_icon) = &self.footer_url {
|
|
||||||
f.icon_url(footer_icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
f
|
|
||||||
});
|
|
||||||
|
|
||||||
for field in &self.fields.0 {
|
for field in &self.fields.0 {
|
||||||
c.field(&field.title, &field.value, field.inline);
|
embed = embed.field(&field.title, &field.value, field.inline);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(image_url) = &self.image_url {
|
if let Some(image_url) = &self.image_url {
|
||||||
c.image(image_url);
|
embed = embed.image(image_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(thumbnail_url) = &self.thumbnail_url {
|
if let Some(thumbnail_url) = &self.thumbnail_url {
|
||||||
c.thumbnail(thumbnail_url);
|
embed = embed.thumbnail(thumbnail_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
c
|
embed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,8 +459,8 @@ WHERE
|
|||||||
.expect(&format!("Could not delete Reminder {}", self.id));
|
.expect(&format!("Could not delete Reminder {}", self.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn pin_message<M: Into<u64>>(&self, message_id: M, http: impl AsRef<Http>) {
|
async fn pin_message<M: Into<MessageId>>(&self, message_id: M, http: impl AsRef<Http>) {
|
||||||
let _ = http.as_ref().pin_message(self.channel_id, message_id.into(), None).await;
|
let _ = http.as_ref().pin_message(self.channel_id.into(), message_id.into(), None).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
@ -479,28 +473,24 @@ WHERE
|
|||||||
reminder: &Reminder,
|
reminder: &Reminder,
|
||||||
embed: Option<CreateEmbed>,
|
embed: Option<CreateEmbed>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let channel = ChannelId(reminder.channel_id).to_channel(&cache_http).await;
|
let channel = ChannelId::new(reminder.channel_id).to_channel(&cache_http).await;
|
||||||
|
|
||||||
match channel {
|
let mut message = CreateMessage::new().content(&reminder.content).tts(reminder.tts);
|
||||||
Ok(Channel::Guild(channel)) => {
|
|
||||||
match channel
|
|
||||||
.send_message(&cache_http, |m| {
|
|
||||||
m.content(&reminder.content).tts(reminder.tts);
|
|
||||||
|
|
||||||
if let (Some(attachment), Some(name)) =
|
if let (Some(attachment), Some(name)) =
|
||||||
(&reminder.attachment, &reminder.attachment_name)
|
(&reminder.attachment, &reminder.attachment_name)
|
||||||
{
|
{
|
||||||
m.add_file((attachment as &[u8], name.as_str()));
|
message =
|
||||||
|
message.add_file(CreateAttachment::bytes(attachment as &[u8], name.as_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(embed) = embed {
|
if let Some(embed) = embed {
|
||||||
m.set_embed(embed);
|
message = message.embed(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
m
|
match channel {
|
||||||
})
|
Ok(Channel::Guild(channel)) => {
|
||||||
.await
|
match channel.send_message(&cache_http, message).await {
|
||||||
{
|
|
||||||
Ok(m) => {
|
Ok(m) => {
|
||||||
if reminder.pin {
|
if reminder.pin {
|
||||||
reminder.pin_message(m.id, cache_http.http()).await;
|
reminder.pin_message(m.id, cache_http.http()).await;
|
||||||
@ -512,24 +502,7 @@ WHERE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Channel::Private(channel)) => {
|
Ok(Channel::Private(channel)) => {
|
||||||
match channel
|
match channel.send_message(&cache_http.http(), message).await {
|
||||||
.send_message(&cache_http.http(), |m| {
|
|
||||||
m.content(&reminder.content).tts(reminder.tts);
|
|
||||||
|
|
||||||
if let (Some(attachment), Some(name)) =
|
|
||||||
(&reminder.attachment, &reminder.attachment_name)
|
|
||||||
{
|
|
||||||
m.add_file((attachment as &[u8], name.as_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(embed) = embed {
|
|
||||||
m.set_embed(embed);
|
|
||||||
}
|
|
||||||
|
|
||||||
m
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(m) => {
|
Ok(m) => {
|
||||||
if reminder.pin {
|
if reminder.pin {
|
||||||
reminder.pin_message(m.id, cache_http.http()).await;
|
reminder.pin_message(m.id, cache_http.http()).await;
|
||||||
@ -551,35 +524,31 @@ WHERE
|
|||||||
webhook: Webhook,
|
webhook: Webhook,
|
||||||
embed: Option<CreateEmbed>,
|
embed: Option<CreateEmbed>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match webhook
|
let mut builder = ExecuteWebhook::new().content(&reminder.content).tts(reminder.tts);
|
||||||
.execute(&cache_http.http(), reminder.pin || reminder.restartable, |w| {
|
|
||||||
w.content(&reminder.content).tts(reminder.tts);
|
|
||||||
|
|
||||||
if let Some(username) = &reminder.username {
|
if let Some(username) = &reminder.username {
|
||||||
if !username.is_empty() {
|
if !username.is_empty() {
|
||||||
w.username(username);
|
builder = builder.username(username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(avatar) = &reminder.avatar {
|
if let Some(avatar) = &reminder.avatar {
|
||||||
w.avatar_url(avatar);
|
builder = builder.avatar_url(avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Some(attachment), Some(name)) =
|
if let (Some(attachment), Some(name)) =
|
||||||
(&reminder.attachment, &reminder.attachment_name)
|
(&reminder.attachment, &reminder.attachment_name)
|
||||||
{
|
{
|
||||||
w.add_file((attachment as &[u8], name.as_str()));
|
builder =
|
||||||
|
builder.add_file(CreateAttachment::bytes(attachment as &[u8], name.as_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(embed) = embed {
|
if let Some(embed) = embed {
|
||||||
w.embeds(vec![SerenityEmbed::fake(|c| {
|
builder = builder.embeds(vec![embed]);
|
||||||
*c = embed;
|
|
||||||
c
|
|
||||||
})]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w
|
match webhook
|
||||||
})
|
.execute(&cache_http.http(), reminder.pin || reminder.restartable, builder)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(m) => {
|
Ok(m) => {
|
||||||
@ -613,8 +582,10 @@ WHERE
|
|||||||
let result = if let (Some(webhook_id), Some(webhook_token)) =
|
let result = if let (Some(webhook_id), Some(webhook_token)) =
|
||||||
(self.webhook_id, &self.webhook_token)
|
(self.webhook_id, &self.webhook_token)
|
||||||
{
|
{
|
||||||
let webhook_res =
|
let webhook_res = cache_http
|
||||||
cache_http.http().get_webhook_with_token(webhook_id, webhook_token).await;
|
.http()
|
||||||
|
.get_webhook_with_token(webhook_id.into(), 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
|
||||||
@ -630,7 +601,7 @@ WHERE
|
|||||||
|
|
||||||
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(
|
||||||
|
@ -26,7 +26,7 @@ FROM macro
|
|||||||
WHERE
|
WHERE
|
||||||
guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
||||||
AND name LIKE CONCAT(?, '%')",
|
AND name LIKE CONCAT(?, '%')",
|
||||||
ctx.guild_id().unwrap().0,
|
ctx.guild_id().unwrap().get(),
|
||||||
partial,
|
partial,
|
||||||
)
|
)
|
||||||
.fetch_all(&ctx.data().database)
|
.fetch_all(&ctx.data().database)
|
||||||
|
@ -18,7 +18,7 @@ pub async fn delete_macro(
|
|||||||
match sqlx::query!(
|
match sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT id FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?",
|
SELECT id FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?",
|
||||||
ctx.guild_id().unwrap().0,
|
ctx.guild_id().unwrap().get(),
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
.fetch_one(&ctx.data().database)
|
.fetch_one(&ctx.data().database)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use poise::CreateReply;
|
use poise::{
|
||||||
|
serenity_prelude::{CreateEmbed, CreateEmbedFooter},
|
||||||
|
CreateReply,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
component_models::pager::{MacroPager, Pager},
|
component_models::pager::{MacroPager, Pager},
|
||||||
@ -20,11 +23,7 @@ pub async fn list_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
|
|
||||||
let resp = show_macro_page(¯os, 0);
|
let resp = show_macro_page(¯os, 0);
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(resp).await?;
|
||||||
*m = resp;
|
|
||||||
m
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -37,15 +36,12 @@ pub fn show_macro_page<U, E>(macros: &[CommandMacro<U, E>], page: usize) -> Crea
|
|||||||
let pager = MacroPager::new(page);
|
let pager = MacroPager::new(page);
|
||||||
|
|
||||||
if macros.is_empty() {
|
if macros.is_empty() {
|
||||||
let mut reply = CreateReply::default();
|
return CreateReply::default().embed(
|
||||||
|
CreateEmbed::new()
|
||||||
reply.embed(|e| {
|
.title("Macros")
|
||||||
e.title("Macros")
|
|
||||||
.description("No Macros Set Up. Use `/macro record` to get started.")
|
.description("No Macros Set Up. Use `/macro record` to get started.")
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
});
|
);
|
||||||
|
|
||||||
return reply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let pages = max_macro_page(macros);
|
let pages = max_macro_page(macros);
|
||||||
@ -70,20 +66,13 @@ pub fn show_macro_page<U, E>(macros: &[CommandMacro<U, E>], page: usize) -> Crea
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut reply = CreateReply::default();
|
CreateReply::default()
|
||||||
|
.embed(
|
||||||
reply
|
CreateEmbed::new()
|
||||||
.embed(|e| {
|
.title("Macros")
|
||||||
e.title("Macros")
|
|
||||||
.fields(fields)
|
.fields(fields)
|
||||||
.footer(|f| f.text(format!("Page {} of {}", page + 1, pages)))
|
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
})
|
.footer(CreateEmbedFooter::new(format!("Page {} of {}", page + 1, pages))),
|
||||||
.components(|comp| {
|
)
|
||||||
pager.create_button_row(pages, comp);
|
.components(vec![pager.create_button_row(pages)])
|
||||||
|
|
||||||
comp
|
|
||||||
});
|
|
||||||
|
|
||||||
reply
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use lazy_regex::regex;
|
use lazy_regex::regex;
|
||||||
use poise::serenity_prelude::CommandOptionType;
|
use poise::{serenity_prelude::CommandOptionType, CreateReply};
|
||||||
use regex::Captures;
|
use regex::Captures;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
let aliases = sqlx::query_as!(
|
let aliases = sqlx::query_as!(
|
||||||
Alias,
|
Alias,
|
||||||
"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.get()
|
||||||
)
|
)
|
||||||
.fetch_all(&mut *transaction)
|
.fetch_all(&mut *transaction)
|
||||||
.await?;
|
.await?;
|
||||||
@ -37,7 +37,7 @@ pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
Some(cmd_macro) => {
|
Some(cmd_macro) => {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)",
|
"INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)",
|
||||||
cmd_macro.guild_id.0,
|
cmd_macro.guild_id.get(),
|
||||||
cmd_macro.name,
|
cmd_macro.name,
|
||||||
cmd_macro.description,
|
cmd_macro.description,
|
||||||
cmd_macro.commands
|
cmd_macro.commands
|
||||||
@ -54,7 +54,7 @@ pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
|
|
||||||
transaction.commit().await?;
|
transaction.commit().await?;
|
||||||
|
|
||||||
ctx.send(|b| b.content(format!("Added {} macros.", added_aliases))).await?;
|
ctx.send(CreateReply::default().content(format!("Added {} macros.", added_aliases))).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
|
use poise::{serenity_prelude::CreateEmbed, CreateReply};
|
||||||
|
|
||||||
use crate::{consts::THEME_COLOR, models::command_macro::CommandMacro, Context, Error};
|
use crate::{consts::THEME_COLOR, models::command_macro::CommandMacro, Context, Error};
|
||||||
|
|
||||||
/// Start recording up to 5 commands to replay
|
/// Start recording up to 5 commands to replay
|
||||||
@ -32,23 +34,24 @@ pub async fn record_macro(
|
|||||||
let row = sqlx::query!(
|
let row = sqlx::query!(
|
||||||
"
|
"
|
||||||
SELECT 1 as _e FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?",
|
SELECT 1 as _e FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?",
|
||||||
guild_id.0,
|
guild_id.get(),
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
.fetch_one(&ctx.data().database)
|
.fetch_one(&ctx.data().database)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if row.is_ok() {
|
if row.is_ok() {
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("Unique Name Required")
|
CreateEmbed::new()
|
||||||
|
.title("Unique Name Required")
|
||||||
.description(
|
.description(
|
||||||
"A macro already exists under this name.
|
"A macro already exists under this name.
|
||||||
Please select a unique name for your macro.",
|
Please select a unique name for your macro.",
|
||||||
)
|
)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
let okay = {
|
let okay = {
|
||||||
@ -63,28 +66,30 @@ Please select a unique name for your macro.",
|
|||||||
};
|
};
|
||||||
|
|
||||||
if okay {
|
if okay {
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("Macro Recording Started")
|
CreateEmbed::new()
|
||||||
|
.title("Macro Recording Started")
|
||||||
.description(
|
.description(
|
||||||
"Run up to 5 commands, or type `/macro finish` to stop at any point.
|
"Run up to 5 commands, or type `/macro finish` to stop at any point.
|
||||||
Any commands ran as part of recording will be inconsequential",
|
Any commands ran as part of recording will be inconsequential",
|
||||||
)
|
)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("Macro Already Recording")
|
CreateEmbed::new()
|
||||||
|
.title("Macro Already Recording")
|
||||||
.description(
|
.description(
|
||||||
"You are already recording a macro in this server.
|
"You are already recording a macro in this server.
|
||||||
Please use `/macro finish` to end this recording before starting another.",
|
Please use `/macro finish` to end this recording before starting another.",
|
||||||
)
|
)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,13 +113,14 @@ pub async fn finish_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
let contained = lock.get(&key);
|
let contained = lock.get(&key);
|
||||||
|
|
||||||
if contained.map_or(true, |cmacro| cmacro.commands.is_empty()) {
|
if contained.map_or(true, |cmacro| cmacro.commands.is_empty()) {
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.embed(|e| {
|
CreateReply::default().embed(
|
||||||
e.title("No Macro Recorded")
|
CreateEmbed::new()
|
||||||
|
.title("No Macro Recorded")
|
||||||
.description("Use `/macro record` to start recording a macro")
|
.description("Use `/macro record` to start recording a macro")
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
let command_macro = contained.unwrap();
|
let command_macro = contained.unwrap();
|
||||||
@ -122,7 +128,7 @@ pub async fn finish_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)",
|
"INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)",
|
||||||
command_macro.guild_id.0,
|
command_macro.guild_id.get(),
|
||||||
command_macro.name,
|
command_macro.name,
|
||||||
command_macro.description,
|
command_macro.description,
|
||||||
json
|
json
|
||||||
@ -131,13 +137,14 @@ pub async fn finish_macro(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.embed(|e| {
|
CreateReply::default().embed(
|
||||||
e.title("Macro Recorded")
|
CreateEmbed::new()
|
||||||
|
.title("Macro Recorded")
|
||||||
.description("Use `/macro run` to execute the macro")
|
.description("Use `/macro run` to execute the macro")
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
use poise::{
|
||||||
|
serenity_prelude::{CommandOption, CreateEmbed},
|
||||||
|
CreateReply,
|
||||||
|
};
|
||||||
|
|
||||||
use super::super::autocomplete::macro_name_autocomplete;
|
use super::super::autocomplete::macro_name_autocomplete;
|
||||||
use crate::{models::command_macro::guild_command_macro, Context, Data, Error, THEME_COLOR};
|
use crate::{models::command_macro::guild_command_macro, Context, Data, Error, THEME_COLOR};
|
||||||
|
|
||||||
@ -18,15 +23,15 @@ pub async fn run_macro(
|
|||||||
match guild_command_macro(&Context::Application(ctx), &name).await {
|
match guild_command_macro(&Context::Application(ctx), &name).await {
|
||||||
Some(command_macro) => {
|
Some(command_macro) => {
|
||||||
Context::Application(ctx)
|
Context::Application(ctx)
|
||||||
.send(|b| {
|
.send(CreateReply::default().embed(
|
||||||
b.embed(|e| {
|
CreateEmbed::new().title("Running Macro").color(*THEME_COLOR).description(
|
||||||
e.title("Running Macro").color(*THEME_COLOR).description(format!(
|
format!(
|
||||||
"Running macro {} ({} commands)",
|
"Running macro {} ({} commands)",
|
||||||
command_macro.name,
|
command_macro.name,
|
||||||
command_macro.commands.len()
|
command_macro.commands.len()
|
||||||
|
),
|
||||||
|
),
|
||||||
))
|
))
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
for command in command_macro.commands {
|
for command in command_macro.commands {
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
use chrono::offset::Utc;
|
use chrono::offset::Utc;
|
||||||
use poise::{serenity_prelude as serenity, serenity_prelude::Mentionable};
|
use poise::{
|
||||||
|
serenity_prelude as serenity,
|
||||||
|
serenity_prelude::{CreateEmbed, CreateEmbedFooter, Mentionable},
|
||||||
|
CreateReply,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{models::CtxData, Context, Error, THEME_COLOR};
|
use crate::{models::CtxData, Context, Error, THEME_COLOR};
|
||||||
|
|
||||||
fn footer(
|
fn footer(ctx: Context<'_>) -> CreateEmbedFooter {
|
||||||
ctx: Context<'_>,
|
|
||||||
) -> impl FnOnce(&mut serenity::CreateEmbedFooter) -> &mut serenity::CreateEmbedFooter {
|
|
||||||
let shard_count = ctx.serenity_context().cache.shard_count();
|
let shard_count = ctx.serenity_context().cache.shard_count();
|
||||||
let shard = ctx.serenity_context().shard_id;
|
let shard = ctx.serenity_context().shard_id;
|
||||||
|
|
||||||
move |f| {
|
CreateEmbedFooter::new(format!(
|
||||||
f.text(format!(
|
|
||||||
"{}\nshard {} of {}",
|
"{}\nshard {} of {}",
|
||||||
concat!(env!("CARGO_PKG_NAME"), " ver ", env!("CARGO_PKG_VERSION")),
|
concat!(env!("CARGO_PKG_NAME"), " ver ", env!("CARGO_PKG_VERSION")),
|
||||||
shard,
|
shard,
|
||||||
shard_count,
|
shard_count,
|
||||||
))
|
))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an overview of bot commands
|
/// Get an overview of bot commands
|
||||||
@ -24,9 +24,10 @@ fn footer(
|
|||||||
pub async fn help(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn help(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
let footer = footer(ctx);
|
let footer = footer(ctx);
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("Help")
|
CreateEmbed::new()
|
||||||
|
.title("Help")
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
.description(
|
.description(
|
||||||
"__Info Commands__
|
"__Info Commands__
|
||||||
@ -55,9 +56,9 @@ __Advanced Commands__
|
|||||||
`/macro` - Record and replay command sequences
|
`/macro` - Record and replay command sequences
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.footer(footer)
|
.footer(footer),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -69,9 +70,10 @@ pub async fn info(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
let footer = footer(ctx);
|
let footer = footer(ctx);
|
||||||
|
|
||||||
let _ = ctx
|
let _ = ctx
|
||||||
.send(|m| {
|
.send(
|
||||||
m.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("Info")
|
CreateEmbed::new()
|
||||||
|
.title("Info")
|
||||||
.description(
|
.description(
|
||||||
"Help: `/help`
|
"Help: `/help`
|
||||||
|
|
||||||
@ -84,9 +86,9 @@ Invite the bot: https://invite.reminder-bot.com/
|
|||||||
Use our dashboard: https://reminder-bot.com/",
|
Use our dashboard: https://reminder-bot.com/",
|
||||||
)
|
)
|
||||||
.footer(footer)
|
.footer(footer)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -97,8 +99,7 @@ Use our dashboard: https://reminder-bot.com/",
|
|||||||
pub async fn donate(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn donate(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
let footer = footer(ctx);
|
let footer = footer(ctx);
|
||||||
|
|
||||||
ctx.send(|m| m.embed(|e| {
|
ctx.send(CreateReply::default().embed(CreateEmbed::new().title("Donate")
|
||||||
e.title("Donate")
|
|
||||||
.description("Thinking of adding a monthly contribution?
|
.description("Thinking of adding a monthly contribution?
|
||||||
Click below for my Patreon and official bot server :)
|
Click below for my Patreon and official bot server :)
|
||||||
|
|
||||||
@ -117,7 +118,7 @@ Just $2 USD/month!
|
|||||||
*Please note, you must be in the JellyWX Discord server to receive Patreon features*")
|
*Please note, you must be in the JellyWX Discord server to receive Patreon features*")
|
||||||
.footer(footer)
|
.footer(footer)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
}),
|
),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -129,14 +130,15 @@ Just $2 USD/month!
|
|||||||
pub async fn dashboard(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn dashboard(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
let footer = footer(ctx);
|
let footer = footer(ctx);
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("Dashboard")
|
CreateEmbed::new()
|
||||||
|
.title("Dashboard")
|
||||||
.description("**https://reminder-bot.com/dashboard**")
|
.description("**https://reminder-bot.com/dashboard**")
|
||||||
.footer(footer)
|
.footer(footer)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -150,9 +152,11 @@ pub async fn clock(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
let tz = ctx.timezone().await;
|
let tz = ctx.timezone().await;
|
||||||
let now = Utc::now().with_timezone(&tz);
|
let now = Utc::now().with_timezone(&tz);
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(CreateReply::default().ephemeral(true).content(format!(
|
||||||
m.ephemeral(true).content(format!("Time in **{}**: `{}`", tz, now.format("%H:%M")))
|
"Time in **{}**: `{}`",
|
||||||
})
|
tz,
|
||||||
|
now.format("%H:%M")
|
||||||
|
)))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -168,13 +172,11 @@ pub async fn clock_context_menu(ctx: Context<'_>, user: serenity::User) -> Resul
|
|||||||
|
|
||||||
let now = Utc::now().with_timezone(&tz);
|
let now = Utc::now().with_timezone(&tz);
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(CreateReply::default().ephemeral(true).content(format!(
|
||||||
m.ephemeral(true).content(format!(
|
|
||||||
"Time in {}'s timezone: `{}`",
|
"Time in {}'s timezone: `{}`",
|
||||||
user.mention(),
|
user.mention(),
|
||||||
now.format("%H:%M")
|
now.format("%H:%M")
|
||||||
))
|
)))
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2,6 +2,10 @@ use chrono::offset::Utc;
|
|||||||
use chrono_tz::{Tz, TZ_VARIANTS};
|
use chrono_tz::{Tz, TZ_VARIANTS};
|
||||||
use levenshtein::levenshtein;
|
use levenshtein::levenshtein;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
use poise::{
|
||||||
|
serenity_prelude::{CreateEmbed, CreateEmbedFooter},
|
||||||
|
CreateReply,
|
||||||
|
};
|
||||||
|
|
||||||
use super::autocomplete::timezone_autocomplete;
|
use super::autocomplete::timezone_autocomplete;
|
||||||
use crate::{consts::THEME_COLOR, models::CtxData, Context, Error};
|
use crate::{consts::THEME_COLOR, models::CtxData, Context, Error};
|
||||||
@ -26,17 +30,18 @@ pub async fn timezone(
|
|||||||
|
|
||||||
let now = Utc::now().with_timezone(&tz);
|
let now = Utc::now().with_timezone(&tz);
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.embed(|e| {
|
CreateReply::default().embed(
|
||||||
e.title("Timezone Set")
|
CreateEmbed::new()
|
||||||
|
.title("Timezone Set")
|
||||||
.description(format!(
|
.description(format!(
|
||||||
"Timezone has been set to **{}**. Your current time should be `{}`",
|
"Timezone has been set to **{}**. Your current time should be `{}`",
|
||||||
timezone,
|
timezone,
|
||||||
now.format("%H:%M")
|
now.format("%H:%M")
|
||||||
))
|
))
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,16 +65,15 @@ pub async fn timezone(
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(CreateReply::default().embed(CreateEmbed::new()
|
||||||
m.embed(|e| {
|
.title("Timezone Not Recognized")
|
||||||
e.title("Timezone Not Recognized")
|
|
||||||
.description("Possibly you meant one of the following timezones, otherwise click [here](https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee):")
|
.description("Possibly you meant one of the following timezones, otherwise click [here](https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee):")
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
.fields(fields)
|
.fields(fields)
|
||||||
.footer(|f| f.text(footer_text))
|
.footer(CreateEmbedFooter::new(footer_text))
|
||||||
.url("https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee")
|
.url("https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee")
|
||||||
})
|
)
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,9 +82,10 @@ pub async fn timezone(
|
|||||||
(t.to_string(), format!("🕗 `{}`", Utc::now().with_timezone(t).format("%H:%M")), true)
|
(t.to_string(), format!("🕗 `{}`", Utc::now().with_timezone(t).format("%H:%M")), true)
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.embed(|e| {
|
CreateReply::default().embed(
|
||||||
e.title("Timezone Usage")
|
CreateEmbed::new()
|
||||||
|
.title("Timezone Usage")
|
||||||
.description(
|
.description(
|
||||||
"**Usage:**
|
"**Usage:**
|
||||||
`/timezone Name`
|
`/timezone Name`
|
||||||
@ -92,10 +97,10 @@ You may want to use one of the popular timezones below, otherwise click [here](h
|
|||||||
)
|
)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
.fields(popular_timezones_iter)
|
.fields(popular_timezones_iter)
|
||||||
.footer(|f| f.text(footer_text))
|
.footer(CreateEmbedFooter::new(footer_text))
|
||||||
.url("https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee")
|
.url("https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee"),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,13 +141,11 @@ pub async fn set_ephemeral_confirmations(ctx: Context<'_>) -> Result<(), Error>
|
|||||||
guild_data.ephemeral_confirmations = true;
|
guild_data.ephemeral_confirmations = true;
|
||||||
guild_data.commit_changes(&ctx.data().database).await;
|
guild_data.commit_changes(&ctx.data().database).await;
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(CreateReply::default().ephemeral(true).embed(CreateEmbed::new().title("Confirmations ephemeral")
|
||||||
r.ephemeral(true).embed(|e| {
|
|
||||||
e.title("Confirmations ephemeral")
|
|
||||||
.description("Reminder confirmations will be sent privately, and removed when your client restarts.")
|
.description("Reminder confirmations will be sent privately, and removed when your client restarts.")
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
})
|
)
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -160,15 +163,13 @@ pub async fn unset_ephemeral_confirmations(ctx: Context<'_>) -> Result<(), Error
|
|||||||
guild_data.ephemeral_confirmations = false;
|
guild_data.ephemeral_confirmations = false;
|
||||||
guild_data.commit_changes(&ctx.data().database).await;
|
guild_data.commit_changes(&ctx.data().database).await;
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(CreateReply::default().ephemeral(true).embed(CreateEmbed::new().title("Confirmations public")
|
||||||
r.ephemeral(true).embed(|e| {
|
|
||||||
e.title("Confirmations public")
|
|
||||||
.description(
|
.description(
|
||||||
"Reminder confirmations will be sent as regular messages, and won't be removed automatically.",
|
"Reminder confirmations will be sent as regular messages, and won't be removed automatically.",
|
||||||
)
|
)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
})
|
)
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -187,13 +188,14 @@ pub async fn set_allowed_dm(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
user_data.allowed_dm = true;
|
user_data.allowed_dm = true;
|
||||||
user_data.commit_changes(&ctx.data().database).await;
|
user_data.commit_changes(&ctx.data().database).await;
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(
|
||||||
r.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("DMs permitted")
|
CreateEmbed::new()
|
||||||
|
.title("DMs permitted")
|
||||||
.description("You will receive a message if a user sets a DM reminder for you.")
|
.description("You will receive a message if a user sets a DM reminder for you.")
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -206,15 +208,16 @@ pub async fn unset_allowed_dm(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
user_data.allowed_dm = false;
|
user_data.allowed_dm = false;
|
||||||
user_data.commit_changes(&ctx.data().database).await;
|
user_data.commit_changes(&ctx.data().database).await;
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(
|
||||||
r.ephemeral(true).embed(|e| {
|
CreateReply::default().ephemeral(true).embed(
|
||||||
e.title("DMs blocked")
|
CreateEmbed::new()
|
||||||
|
.title("DMs blocked")
|
||||||
.description(
|
.description(
|
||||||
"You can still set DM reminders for yourself or for users with DMs enabled.",
|
"You can still set DM reminders for yourself or for users with DMs enabled.",
|
||||||
)
|
)
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -230,15 +233,13 @@ pub async fn webhook(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
match ctx.channel_data().await {
|
match ctx.channel_data().await {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
if let (Some(id), Some(token)) = (data.webhook_id, data.webhook_token) {
|
if let (Some(id), Some(token)) = (data.webhook_id, data.webhook_token) {
|
||||||
ctx.send(|b| {
|
ctx.send(CreateReply::default().ephemeral(true).content(format!(
|
||||||
b.ephemeral(true).content(format!(
|
|
||||||
"**Warning!**
|
"**Warning!**
|
||||||
This link can be used by users to anonymously send messages, with or without permissions.
|
This link can be used by users to anonymously send messages, with or without permissions.
|
||||||
Do not share it!
|
Do not share it!
|
||||||
|| https://discord.com/api/webhooks/{}/{} ||",
|
|| https://discord.com/api/webhooks/{}/{} ||",
|
||||||
id, token,
|
id, token,
|
||||||
))
|
)))
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
ctx.say("No webhook configured on this channel.").await?;
|
ctx.say("No webhook configured on this channel.").await?;
|
||||||
|
@ -5,7 +5,11 @@ use chrono_tz::Tz;
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
use num_integer::Integer;
|
use num_integer::Integer;
|
||||||
use poise::{
|
use poise::{
|
||||||
serenity_prelude::{builder::CreateEmbed, model::channel::Channel, ButtonStyle, ReactionType},
|
serenity_prelude::{
|
||||||
|
builder::CreateEmbed, model::channel::Channel, ButtonStyle, CreateActionRow, CreateButton,
|
||||||
|
CreateEmbedFooter, CreateSelectMenu, CreateSelectMenuKind, CreateSelectMenuOption,
|
||||||
|
ReactionType,
|
||||||
|
},
|
||||||
CreateReply, Modal,
|
CreateReply, Modal,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -125,21 +129,19 @@ pub async fn offset(
|
|||||||
let channels = guild
|
let channels = guild
|
||||||
.channels
|
.channels
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, channel)| match channel {
|
.filter(|(_, channel)| channel.is_text_based())
|
||||||
Channel::Guild(guild_channel) => guild_channel.is_text_based(),
|
.map(|(id, _)| id.get().to_string())
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
.map(|(id, _)| id.0.to_string())
|
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"
|
"
|
||||||
UPDATE reminders
|
UPDATE reminders
|
||||||
INNER JOIN
|
INNER JOIN `channels`
|
||||||
`channels` ON `channels`.id = reminders.channel_id
|
ON `channels`.id = reminders.channel_id
|
||||||
SET reminders.`utc_time` = DATE_ADD(reminders.`utc_time`, INTERVAL ? SECOND)
|
SET reminders.`utc_time` = DATE_ADD(reminders.`utc_time`, INTERVAL ? SECOND)
|
||||||
WHERE FIND_IN_SET(channels.`channel`, ?)",
|
WHERE FIND_IN_SET(channels.`channel`, ?)
|
||||||
|
",
|
||||||
combined_time as i64,
|
combined_time as i64,
|
||||||
channels
|
channels
|
||||||
)
|
)
|
||||||
@ -148,9 +150,15 @@ WHERE FIND_IN_SET(channels.`channel`, ?)",
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE reminders INNER JOIN `channels` ON `channels`.id = reminders.channel_id SET reminders.`utc_time` = reminders.`utc_time` + ? WHERE channels.`channel` = ?",
|
"
|
||||||
|
UPDATE reminders
|
||||||
|
INNER JOIN `channels`
|
||||||
|
ON `channels`.id = reminders.channel_id
|
||||||
|
SET reminders.`utc_time` = reminders.`utc_time` + ?
|
||||||
|
WHERE channels.`channel` = ?
|
||||||
|
",
|
||||||
combined_time as i64,
|
combined_time as i64,
|
||||||
ctx.channel_id().0
|
ctx.channel_id().get()
|
||||||
)
|
)
|
||||||
.execute(&ctx.data().database)
|
.execute(&ctx.data().database)
|
||||||
.await
|
.await
|
||||||
@ -216,9 +224,9 @@ pub async fn look(
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let channel_opt = ctx.channel_id().to_channel_cached(&ctx);
|
let channel_opt = ctx.channel_id().to_channel_cached(&ctx.cache());
|
||||||
|
|
||||||
let channel_id = if let Some(Channel::Guild(channel)) = channel_opt {
|
let channel_id = if let Some(channel) = channel_opt {
|
||||||
if Some(channel.guild_id) == ctx.guild_id() {
|
if Some(channel.guild_id) == ctx.guild_id() {
|
||||||
flags.channel_id.unwrap_or_else(|| ctx.channel_id())
|
flags.channel_id.unwrap_or_else(|| ctx.channel_id())
|
||||||
} else {
|
} else {
|
||||||
@ -228,11 +236,8 @@ pub async fn look(
|
|||||||
ctx.channel_id()
|
ctx.channel_id()
|
||||||
};
|
};
|
||||||
|
|
||||||
let channel_name = if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {
|
let channel_name =
|
||||||
Some(channel.name)
|
channel_id.to_channel_cached(&ctx.cache()).map(|channel| channel.name.clone());
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let reminders = Reminder::from_channel(&ctx.data().database, channel_id, &flags).await;
|
let reminders = Reminder::from_channel(&ctx.data().database, channel_id, &flags).await;
|
||||||
|
|
||||||
@ -260,23 +265,21 @@ pub async fn look(
|
|||||||
|
|
||||||
let pager = LookPager::new(flags, timezone);
|
let pager = LookPager::new(flags, timezone);
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(
|
||||||
r.ephemeral(true)
|
CreateReply::default()
|
||||||
.embed(|e| {
|
.ephemeral(true)
|
||||||
e.title(format!(
|
.embed(
|
||||||
|
CreateEmbed::new()
|
||||||
|
.title(format!(
|
||||||
"Reminders{}",
|
"Reminders{}",
|
||||||
channel_name.map_or(String::new(), |n| format!(" on #{}", n))
|
channel_name.map_or(String::new(), |n| format!(" on #{}", n))
|
||||||
))
|
))
|
||||||
.description(display)
|
.description(display)
|
||||||
.footer(|f| f.text(format!("Page {} of {}", 1, pages)))
|
.footer(CreateEmbedFooter::new(format!("Page {} of {}", 1, pages)))
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
)
|
||||||
.components(|comp| {
|
.components(vec![pager.create_button_row(pages)]),
|
||||||
pager.create_button_row(pages, comp);
|
)
|
||||||
|
|
||||||
comp
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,11 +301,7 @@ pub async fn delete(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
|
|
||||||
let resp = show_delete_page(&reminders, 0, timezone);
|
let resp = show_delete_page(&reminders, 0, timezone);
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(resp).await?;
|
||||||
*r = resp;
|
|
||||||
r
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -333,16 +332,12 @@ pub fn show_delete_page(reminders: &[Reminder], page: usize, timezone: Tz) -> Cr
|
|||||||
let pager = DelPager::new(page, timezone);
|
let pager = DelPager::new(page, timezone);
|
||||||
|
|
||||||
if reminders.is_empty() {
|
if reminders.is_empty() {
|
||||||
let mut reply = CreateReply::default();
|
let embed = CreateEmbed::new()
|
||||||
|
.title("Delete Reminders")
|
||||||
|
.description("No Reminders")
|
||||||
|
.color(*THEME_COLOR);
|
||||||
|
|
||||||
reply
|
return CreateReply::default().embed(embed).components(vec![pager.create_button_row(0)]);
|
||||||
.embed(|e| e.title("Delete Reminders").description("No Reminders").color(*THEME_COLOR))
|
|
||||||
.components(|comp| {
|
|
||||||
pager.create_button_row(0, comp);
|
|
||||||
comp
|
|
||||||
});
|
|
||||||
|
|
||||||
return reply;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let pages = max_delete_page(reminders, &timezone);
|
let pages = max_delete_page(reminders, &timezone);
|
||||||
@ -391,49 +386,42 @@ pub fn show_delete_page(reminders: &[Reminder], page: usize, timezone: Tz) -> Cr
|
|||||||
|
|
||||||
let del_selector = ComponentDataModel::DelSelector(DelSelector { page, timezone });
|
let del_selector = ComponentDataModel::DelSelector(DelSelector { page, timezone });
|
||||||
|
|
||||||
let mut reply = CreateReply::default();
|
let embed = CreateEmbed::new()
|
||||||
|
.title("Delete Reminders")
|
||||||
reply
|
|
||||||
.embed(|e| {
|
|
||||||
e.title("Delete Reminders")
|
|
||||||
.description(display)
|
.description(display)
|
||||||
.footer(|f| f.text(format!("Page {} of {}", page + 1, pages)))
|
.footer(CreateEmbedFooter::new(format!("Page {} of {}", page + 1, pages)))
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR);
|
||||||
})
|
|
||||||
.components(|comp| {
|
|
||||||
pager.create_button_row(pages, comp);
|
|
||||||
|
|
||||||
comp.create_action_row(|row| {
|
let select_menu = CreateSelectMenu::new(
|
||||||
row.create_select_menu(|menu| {
|
del_selector.to_custom_id(),
|
||||||
menu.custom_id(del_selector.to_custom_id()).options(|opt| {
|
CreateSelectMenuKind::String {
|
||||||
for (count, reminder) in shown_reminders.iter().enumerate() {
|
options: shown_reminders
|
||||||
opt.create_option(|o| {
|
.iter()
|
||||||
o.label(count + first_num).value(reminder.id).description({
|
.enumerate()
|
||||||
|
.map(|(count, reminder)| {
|
||||||
let c = reminder.display_content();
|
let c = reminder.display_content();
|
||||||
|
let description = if c.len() > 100 {
|
||||||
if c.len() > 100 {
|
|
||||||
format!(
|
format!(
|
||||||
"{}...",
|
"{}...",
|
||||||
reminder
|
reminder.display_content().chars().take(97).collect::<String>()
|
||||||
.display_content()
|
|
||||||
.chars()
|
|
||||||
.take(97)
|
|
||||||
.collect::<String>()
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
c.to_string()
|
c.to_string()
|
||||||
}
|
};
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
opt
|
CreateSelectMenuOption::new(
|
||||||
|
(count + first_num).to_string(),
|
||||||
|
reminder.id.to_string(),
|
||||||
|
)
|
||||||
|
.description(description)
|
||||||
})
|
})
|
||||||
})
|
.collect(),
|
||||||
})
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
reply
|
CreateReply::default()
|
||||||
|
.embed(embed)
|
||||||
|
.components(vec![pager.create_button_row(pages), CreateActionRow::SelectMenu(select_menu)])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn time_difference(start_time: DateTime<Utc>) -> String {
|
fn time_difference(start_time: DateTime<Utc>) -> String {
|
||||||
@ -465,19 +453,20 @@ pub async fn timer_base(_ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
default_member_permissions = "MANAGE_GUILD"
|
default_member_permissions = "MANAGE_GUILD"
|
||||||
)]
|
)]
|
||||||
pub async fn list_timer(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn list_timer(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
let owner = ctx.guild_id().map(|g| g.0).unwrap_or_else(|| ctx.author().id.0);
|
let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get());
|
||||||
|
|
||||||
let timers = Timer::from_owner(owner, &ctx.data().database).await;
|
let timers = Timer::from_owner(owner, &ctx.data().database).await;
|
||||||
|
|
||||||
if !timers.is_empty() {
|
if !timers.is_empty() {
|
||||||
ctx.send(|m| {
|
ctx.send(
|
||||||
m.embed(|e| {
|
CreateReply::default().embed(
|
||||||
e.fields(timers.iter().map(|timer| {
|
CreateEmbed::new()
|
||||||
|
.fields(timers.iter().map(|timer| {
|
||||||
(&timer.name, format!("⌚ `{}`", time_difference(timer.start_time)), false)
|
(&timer.name, format!("⌚ `{}`", time_difference(timer.start_time)), false)
|
||||||
}))
|
}))
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
ctx.say("No timers currently. Use `/timer start` to create a new timer").await?;
|
ctx.say("No timers currently. Use `/timer start` to create a new timer").await?;
|
||||||
@ -497,7 +486,7 @@ pub async fn start_timer(
|
|||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "Name for the new timer"] name: String,
|
#[description = "Name for the new timer"] name: String,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let owner = ctx.guild_id().map(|g| g.0).unwrap_or_else(|| ctx.author().id.0);
|
let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get());
|
||||||
|
|
||||||
let count = Timer::count_from_owner(owner, &ctx.data().database).await;
|
let count = Timer::count_from_owner(owner, &ctx.data().database).await;
|
||||||
|
|
||||||
@ -530,7 +519,7 @@ pub async fn delete_timer(
|
|||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
#[description = "Name of timer to delete"] name: String,
|
#[description = "Name of timer to delete"] name: String,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let owner = ctx.guild_id().map(|g| g.0).unwrap_or_else(|| ctx.author().id.0);
|
let owner = ctx.guild_id().map(|g| g.get()).unwrap_or_else(|| ctx.author().id.get());
|
||||||
|
|
||||||
let exists =
|
let exists =
|
||||||
sqlx::query!("SELECT 1 as _r FROM timers WHERE owner = ? AND name = ?", owner, name)
|
sqlx::query!("SELECT 1 as _r FROM timers WHERE owner = ? AND name = ?", owner, name)
|
||||||
@ -604,7 +593,7 @@ pub async fn multiline(
|
|||||||
None => {
|
None => {
|
||||||
warn!("Unexpected None encountered in /multiline");
|
warn!("Unexpected None encountered in /multiline");
|
||||||
Ok(Context::Application(ctx)
|
Ok(Context::Application(ctx)
|
||||||
.send(|m| m.content("Unexpected error.").ephemeral(true))
|
.send(CreateReply::default().content("Unexpected error.").ephemeral(true))
|
||||||
.await
|
.await
|
||||||
.map(|_| ())?)
|
.map(|_| ())?)
|
||||||
}
|
}
|
||||||
@ -682,9 +671,9 @@ async fn create_reminder(
|
|||||||
|
|
||||||
if list.is_empty() {
|
if list.is_empty() {
|
||||||
if ctx.guild_id().is_some() {
|
if ctx.guild_id().is_some() {
|
||||||
vec![ReminderScope::Channel(ctx.channel_id().0)]
|
vec![ReminderScope::Channel(ctx.channel_id().get())]
|
||||||
} else {
|
} else {
|
||||||
vec![ReminderScope::User(ctx.author().id.0)]
|
vec![ReminderScope::User(ctx.author().id.get())]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
list
|
list
|
||||||
@ -709,10 +698,9 @@ async fn create_reminder(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ctx.send(|b| {
|
ctx.send(CreateReply::default().content(
|
||||||
b.content(
|
"`repeat` is only available to Patreon subscribers or self-hosted users",
|
||||||
"`repeat` is only available to Patreon subscribers or self-hosted users")
|
))
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -722,17 +710,16 @@ async fn create_reminder(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if processed_interval.is_none() && interval.is_some() {
|
if processed_interval.is_none() && interval.is_some() {
|
||||||
ctx.send(|b| {
|
ctx.send(CreateReply::default().content(
|
||||||
b.content(
|
"Repeat interval could not be processed. Try similar to `1 hour` or `4 days`",
|
||||||
"Repeat interval could not be processed. Try similar to `1 hour` or `4 days`")
|
))
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
} else if processed_expires.is_none() && expires.is_some() {
|
} else if processed_expires.is_none() && expires.is_some() {
|
||||||
ctx.send(|b| {
|
ctx.send(
|
||||||
b.ephemeral(true).content(
|
CreateReply::default().ephemeral(true).content(
|
||||||
"Expiry time failed to process. Please make it as clear as possible",
|
"Expiry time failed to process. Please make it as clear as possible",
|
||||||
|
),
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
let mut builder = MultiReminderBuilder::new(&ctx, ctx.guild_id())
|
let mut builder = MultiReminderBuilder::new(&ctx, ctx.guild_id())
|
||||||
@ -756,37 +743,20 @@ async fn create_reminder(
|
|||||||
reminder_id: reminder,
|
reminder_id: reminder,
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.send(|m| {
|
ctx.send(CreateReply::default().embed(embed).components(vec![
|
||||||
m.embed(|c| {
|
CreateActionRow::Buttons(vec![
|
||||||
*c = embed;
|
CreateButton::new(undo_button.to_custom_id())
|
||||||
c
|
.emoji(ReactionType::Unicode("🔕".to_string()))
|
||||||
})
|
|
||||||
.components(|c| {
|
|
||||||
c.create_action_row(|r| {
|
|
||||||
r.create_button(|b| {
|
|
||||||
b.emoji(ReactionType::Unicode("🔕".to_string()))
|
|
||||||
.label("Cancel")
|
.label("Cancel")
|
||||||
.style(ButtonStyle::Danger)
|
.style(ButtonStyle::Danger),
|
||||||
.custom_id(undo_button.to_custom_id())
|
CreateButton::new_link("https://beta.reminder-bot.com/dashboard")
|
||||||
})
|
.emoji(ReactionType::Unicode("📝".to_string()))
|
||||||
.create_button(|b| {
|
.label("Edit"),
|
||||||
b.emoji(ReactionType::Unicode("📝".to_string()))
|
]),
|
||||||
.label("Edit")
|
]))
|
||||||
.style(ButtonStyle::Link)
|
|
||||||
.url("https://beta.reminder-bot.com/dashboard")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
ctx.send(|m| {
|
ctx.send(CreateReply::default().embed(embed)).await?;
|
||||||
m.embed(|c| {
|
|
||||||
*c = embed;
|
|
||||||
c
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
use poise::CreateReply;
|
use poise::{
|
||||||
|
serenity_prelude::{
|
||||||
|
CreateActionRow, CreateEmbed, CreateEmbedFooter, CreateSelectMenu, CreateSelectMenuKind,
|
||||||
|
CreateSelectMenuOption,
|
||||||
|
},
|
||||||
|
CreateReply,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
component_models::{
|
component_models::{
|
||||||
@ -48,7 +54,7 @@ pub async fn todo_guild_add(
|
|||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO todos (guild_id, value)
|
"INSERT INTO todos (guild_id, value)
|
||||||
VALUES ((SELECT id FROM guilds WHERE guild = ?), ?)",
|
VALUES ((SELECT id FROM guilds WHERE guild = ?), ?)",
|
||||||
ctx.guild_id().unwrap().0,
|
ctx.guild_id().unwrap().get(),
|
||||||
task
|
task
|
||||||
)
|
)
|
||||||
.execute(&ctx.data().database)
|
.execute(&ctx.data().database)
|
||||||
@ -73,7 +79,7 @@ pub async fn todo_guild_view(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
"SELECT todos.id, value FROM todos
|
"SELECT todos.id, value FROM todos
|
||||||
INNER JOIN guilds ON todos.guild_id = guilds.id
|
INNER JOIN guilds ON todos.guild_id = guilds.id
|
||||||
WHERE guilds.guild = ?",
|
WHERE guilds.guild = ?",
|
||||||
ctx.guild_id().unwrap().0,
|
ctx.guild_id().unwrap().get(),
|
||||||
)
|
)
|
||||||
.fetch_all(&ctx.data().database)
|
.fetch_all(&ctx.data().database)
|
||||||
.await
|
.await
|
||||||
@ -82,13 +88,9 @@ WHERE guilds.guild = ?",
|
|||||||
.map(|row| (row.id as usize, row.value.clone()))
|
.map(|row| (row.id as usize, row.value.clone()))
|
||||||
.collect::<Vec<(usize, String)>>();
|
.collect::<Vec<(usize, String)>>();
|
||||||
|
|
||||||
let resp = show_todo_page(&values, 0, None, None, ctx.guild_id().map(|g| g.0));
|
let resp = show_todo_page(&values, 0, None, None, ctx.guild_id().map(|g| g.get()));
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(resp).await?;
|
||||||
*r = resp;
|
|
||||||
r
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -123,8 +125,8 @@ pub async fn todo_channel_add(
|
|||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO todos (guild_id, channel_id, value)
|
"INSERT INTO todos (guild_id, channel_id, value)
|
||||||
VALUES ((SELECT id FROM guilds WHERE guild = ?), (SELECT id FROM channels WHERE channel = ?), ?)",
|
VALUES ((SELECT id FROM guilds WHERE guild = ?), (SELECT id FROM channels WHERE channel = ?), ?)",
|
||||||
ctx.guild_id().unwrap().0,
|
ctx.guild_id().unwrap().get(),
|
||||||
ctx.channel_id().0,
|
ctx.channel_id().get(),
|
||||||
task
|
task
|
||||||
)
|
)
|
||||||
.execute(&ctx.data().database)
|
.execute(&ctx.data().database)
|
||||||
@ -149,7 +151,7 @@ pub async fn todo_channel_view(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
"SELECT todos.id, value FROM todos
|
"SELECT todos.id, value FROM todos
|
||||||
INNER JOIN channels ON todos.channel_id = channels.id
|
INNER JOIN channels ON todos.channel_id = channels.id
|
||||||
WHERE channels.channel = ?",
|
WHERE channels.channel = ?",
|
||||||
ctx.channel_id().0,
|
ctx.channel_id().get(),
|
||||||
)
|
)
|
||||||
.fetch_all(&ctx.data().database)
|
.fetch_all(&ctx.data().database)
|
||||||
.await
|
.await
|
||||||
@ -158,14 +160,15 @@ WHERE channels.channel = ?",
|
|||||||
.map(|row| (row.id as usize, row.value.clone()))
|
.map(|row| (row.id as usize, row.value.clone()))
|
||||||
.collect::<Vec<(usize, String)>>();
|
.collect::<Vec<(usize, String)>>();
|
||||||
|
|
||||||
let resp =
|
let resp = show_todo_page(
|
||||||
show_todo_page(&values, 0, None, Some(ctx.channel_id().0), ctx.guild_id().map(|g| g.0));
|
&values,
|
||||||
|
0,
|
||||||
|
None,
|
||||||
|
Some(ctx.channel_id().get()),
|
||||||
|
ctx.guild_id().map(|g| g.get()),
|
||||||
|
);
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(resp).await?;
|
||||||
*r = resp;
|
|
||||||
r
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -185,7 +188,7 @@ pub async fn todo_user_add(
|
|||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO todos (user_id, value)
|
"INSERT INTO todos (user_id, value)
|
||||||
VALUES ((SELECT id FROM users WHERE user = ?), ?)",
|
VALUES ((SELECT id FROM users WHERE user = ?), ?)",
|
||||||
ctx.author().id.0,
|
ctx.author().id.get(),
|
||||||
task
|
task
|
||||||
)
|
)
|
||||||
.execute(&ctx.data().database)
|
.execute(&ctx.data().database)
|
||||||
@ -204,7 +207,7 @@ pub async fn todo_user_view(ctx: Context<'_>) -> Result<(), Error> {
|
|||||||
"SELECT todos.id, value FROM todos
|
"SELECT todos.id, value FROM todos
|
||||||
INNER JOIN users ON todos.user_id = users.id
|
INNER JOIN users ON todos.user_id = users.id
|
||||||
WHERE users.user = ?",
|
WHERE users.user = ?",
|
||||||
ctx.author().id.0,
|
ctx.author().id.get(),
|
||||||
)
|
)
|
||||||
.fetch_all(&ctx.data().database)
|
.fetch_all(&ctx.data().database)
|
||||||
.await
|
.await
|
||||||
@ -213,13 +216,9 @@ WHERE users.user = ?",
|
|||||||
.map(|row| (row.id as usize, row.value.clone()))
|
.map(|row| (row.id as usize, row.value.clone()))
|
||||||
.collect::<Vec<(usize, String)>>();
|
.collect::<Vec<(usize, String)>>();
|
||||||
|
|
||||||
let resp = show_todo_page(&values, 0, Some(ctx.author().id.0), None, None);
|
let resp = show_todo_page(&values, 0, Some(ctx.author().id.get()), None, None);
|
||||||
|
|
||||||
ctx.send(|r| {
|
ctx.send(resp).await?;
|
||||||
*r = resp;
|
|
||||||
r
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -306,61 +305,51 @@ pub fn show_todo_page(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if todo_ids.is_empty() {
|
if todo_ids.is_empty() {
|
||||||
let mut reply = CreateReply::default();
|
CreateReply::default().embed(
|
||||||
|
CreateEmbed::new()
|
||||||
reply.embed(|e| {
|
.title(format!("{} Todo List", title))
|
||||||
e.title(format!("{} Todo List", title))
|
|
||||||
.description("Todo List Empty!")
|
.description("Todo List Empty!")
|
||||||
.footer(|f| f.text(format!("Page {} of {}", page + 1, pages)))
|
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
});
|
.footer(CreateEmbedFooter::new(format!("Page {} of {}", page + 1, pages))),
|
||||||
|
)
|
||||||
reply
|
|
||||||
} else {
|
} else {
|
||||||
let todo_selector =
|
let todo_selector =
|
||||||
ComponentDataModel::TodoSelector(TodoSelector { page, user_id, channel_id, guild_id });
|
ComponentDataModel::TodoSelector(TodoSelector { page, user_id, channel_id, guild_id });
|
||||||
|
|
||||||
let mut reply = CreateReply::default();
|
CreateReply::default()
|
||||||
|
.embed(
|
||||||
reply
|
CreateEmbed::new()
|
||||||
.embed(|e| {
|
.title(format!("{} Todo List", title))
|
||||||
e.title(format!("{} Todo List", title))
|
|
||||||
.description(display)
|
.description(display)
|
||||||
.footer(|f| f.text(format!("Page {} of {}", page + 1, pages)))
|
|
||||||
.color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
})
|
.footer(CreateEmbedFooter::new(format!("Page {} of {}", page + 1, pages))),
|
||||||
.components(|comp| {
|
|
||||||
pager.create_button_row(pages, comp);
|
|
||||||
|
|
||||||
comp.create_action_row(|row| {
|
|
||||||
row.create_select_menu(|menu| {
|
|
||||||
menu.custom_id(todo_selector.to_custom_id()).options(|opt| {
|
|
||||||
for (count, (id, disp)) in todo_ids.iter().zip(&display_vec).enumerate()
|
|
||||||
{
|
|
||||||
opt.create_option(|o| {
|
|
||||||
o.label(format!("Mark {} complete", count + first_num))
|
|
||||||
.value(id)
|
|
||||||
.description({
|
|
||||||
let c = disp.split_once(' ').unwrap_or(("", "")).1;
|
|
||||||
|
|
||||||
if c.len() > 100 {
|
|
||||||
format!(
|
|
||||||
"{}...",
|
|
||||||
c.chars().take(97).collect::<String>()
|
|
||||||
)
|
)
|
||||||
|
.components(vec![
|
||||||
|
pager.create_button_row(pages),
|
||||||
|
CreateActionRow::SelectMenu(CreateSelectMenu::new(
|
||||||
|
todo_selector.to_custom_id(),
|
||||||
|
CreateSelectMenuKind::String {
|
||||||
|
options: todo_ids
|
||||||
|
.iter()
|
||||||
|
.zip(&display_vec)
|
||||||
|
.enumerate()
|
||||||
|
.map(|(count, (id, disp))| {
|
||||||
|
let c = disp.split_once(' ').unwrap_or(("", "")).1;
|
||||||
|
let description = if c.len() > 100 {
|
||||||
|
format!("{}...", c.chars().take(97).collect::<String>())
|
||||||
} else {
|
} else {
|
||||||
c.to_string()
|
c.to_string()
|
||||||
}
|
};
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
opt
|
CreateSelectMenuOption::new(
|
||||||
|
format!("Mark {} complete", count + first_num),
|
||||||
|
id.to_string(),
|
||||||
|
)
|
||||||
|
.description(description)
|
||||||
})
|
})
|
||||||
})
|
.collect(),
|
||||||
})
|
},
|
||||||
});
|
)),
|
||||||
|
])
|
||||||
reply
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,8 @@ use log::warn;
|
|||||||
use poise::{
|
use poise::{
|
||||||
serenity_prelude as serenity,
|
serenity_prelude as serenity,
|
||||||
serenity_prelude::{
|
serenity_prelude::{
|
||||||
builder::CreateEmbed,
|
builder::CreateEmbed, ComponentInteraction, ComponentInteractionDataKind, Context,
|
||||||
model::{
|
CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseMessage,
|
||||||
application::interaction::{
|
|
||||||
message_component::MessageComponentInteraction, InteractionResponseType,
|
|
||||||
MessageFlags,
|
|
||||||
},
|
|
||||||
channel::Channel,
|
|
||||||
},
|
|
||||||
Context,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use rmp_serde::Serializer;
|
use rmp_serde::Serializer;
|
||||||
@ -31,7 +24,7 @@ use crate::{
|
|||||||
component_models::pager::{DelPager, LookPager, MacroPager, Pager, TodoPager},
|
component_models::pager::{DelPager, LookPager, MacroPager, Pager, TodoPager},
|
||||||
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
|
consts::{EMBED_DESCRIPTION_MAX_LENGTH, THEME_COLOR},
|
||||||
models::reminder::Reminder,
|
models::reminder::Reminder,
|
||||||
utils::send_as_initial_response,
|
utils::reply_to_interaction_response_message,
|
||||||
Data,
|
Data,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,14 +57,15 @@ impl ComponentDataModel {
|
|||||||
rmp_serde::from_read(cur).unwrap()
|
rmp_serde::from_read(cur).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn act(&self, ctx: &Context, data: &Data, component: &MessageComponentInteraction) {
|
pub async fn act(&self, ctx: &Context, data: &Data, component: &ComponentInteraction) {
|
||||||
match self {
|
match self {
|
||||||
ComponentDataModel::LookPager(pager) => {
|
ComponentDataModel::LookPager(pager) => {
|
||||||
let flags = pager.flags;
|
let flags = pager.flags;
|
||||||
|
|
||||||
let channel_opt = component.channel_id.to_channel_cached(&ctx);
|
let channel_id = {
|
||||||
|
let channel_opt = component.channel_id.to_channel_cached(&ctx.cache);
|
||||||
|
|
||||||
let channel_id = if let Some(Channel::Guild(channel)) = channel_opt {
|
if let Some(channel) = channel_opt {
|
||||||
if Some(channel.guild_id) == component.guild_id {
|
if Some(channel.guild_id) == component.guild_id {
|
||||||
flags.channel_id.unwrap_or(component.channel_id)
|
flags.channel_id.unwrap_or(component.channel_id)
|
||||||
} else {
|
} else {
|
||||||
@ -79,6 +73,7 @@ impl ComponentDataModel {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
component.channel_id
|
component.channel_id
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let reminders = Reminder::from_channel(&data.database, channel_id, &flags).await;
|
let reminders = Reminder::from_channel(&data.database, channel_id, &flags).await;
|
||||||
@ -90,11 +85,7 @@ impl ComponentDataModel {
|
|||||||
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
.div_ceil(EMBED_DESCRIPTION_MAX_LENGTH);
|
||||||
|
|
||||||
let channel_name =
|
let channel_name =
|
||||||
if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx) {
|
channel_id.to_channel_cached(&ctx.cache).map(|channel| channel.name.clone());
|
||||||
Some(channel.name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let next_page = pager.next_page(pages);
|
let next_page = pager.next_page(pages);
|
||||||
|
|
||||||
@ -107,7 +98,7 @@ impl ComponentDataModel {
|
|||||||
.skip_while(|p| {
|
.skip_while(|p| {
|
||||||
skip_char_count += p.len();
|
skip_char_count += p.len();
|
||||||
|
|
||||||
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * next_page as usize
|
skip_char_count < EMBED_DESCRIPTION_MAX_LENGTH * next_page
|
||||||
})
|
})
|
||||||
.take_while(|p| {
|
.take_while(|p| {
|
||||||
char_count += p.len();
|
char_count += p.len();
|
||||||
@ -117,28 +108,24 @@ impl ComponentDataModel {
|
|||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
let mut embed = CreateEmbed::default();
|
let embed = CreateEmbed::default()
|
||||||
embed
|
|
||||||
.title(format!(
|
.title(format!(
|
||||||
"Reminders{}",
|
"Reminders{}",
|
||||||
channel_name.map_or(String::new(), |n| format!(" on #{}", n))
|
channel_name.map_or(String::new(), |n| format!(" on #{}", n))
|
||||||
))
|
))
|
||||||
.description(display)
|
.description(display)
|
||||||
.footer(|f| f.text(format!("Page {} of {}", next_page + 1, pages)))
|
.footer(CreateEmbedFooter::new(format!("Page {} of {}", next_page + 1, pages)))
|
||||||
.color(*THEME_COLOR);
|
.color(*THEME_COLOR);
|
||||||
|
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |r| {
|
.create_response(
|
||||||
r.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
&ctx,
|
||||||
|response| {
|
CreateInteractionResponse::UpdateMessage(
|
||||||
response.set_embeds(vec![embed]).components(|comp| {
|
CreateInteractionResponseMessage::new()
|
||||||
pager.create_button_row(pages, comp);
|
.embed(embed)
|
||||||
|
.components(vec![pager.create_button_row(pages)]),
|
||||||
comp
|
),
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ComponentDataModel::DelPager(pager) => {
|
ComponentDataModel::DelPager(pager) => {
|
||||||
@ -155,21 +142,23 @@ impl ComponentDataModel {
|
|||||||
let resp = show_delete_page(&reminders, pager.next_page(max_pages), pager.timezone);
|
let resp = show_delete_page(&reminders, pager.next_page(max_pages), pager.timezone);
|
||||||
|
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
&ctx,
|
||||||
|d| {
|
CreateInteractionResponse::UpdateMessage(
|
||||||
send_as_initial_response(resp, d);
|
reply_to_interaction_response_message(resp),
|
||||||
d
|
),
|
||||||
},
|
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ComponentDataModel::DelSelector(selector) => {
|
ComponentDataModel::DelSelector(selector) => {
|
||||||
let selected_id = component.data.values.join(",");
|
if let ComponentInteractionDataKind::StringSelect { values } = &component.data.kind
|
||||||
|
{
|
||||||
|
let selected_id = values.join(",");
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE reminders SET `status` = 'deleted' WHERE FIND_IN_SET(id, ?)",
|
"
|
||||||
|
UPDATE reminders SET `status` = 'deleted' WHERE FIND_IN_SET(id, ?)
|
||||||
|
",
|
||||||
selected_id
|
selected_id
|
||||||
)
|
)
|
||||||
.execute(&data.database)
|
.execute(&data.database)
|
||||||
@ -187,23 +176,24 @@ impl ComponentDataModel {
|
|||||||
let resp = show_delete_page(&reminders, selector.page, selector.timezone);
|
let resp = show_delete_page(&reminders, selector.page, selector.timezone);
|
||||||
|
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
&ctx,
|
||||||
|d| {
|
CreateInteractionResponse::UpdateMessage(
|
||||||
send_as_initial_response(resp, d);
|
reply_to_interaction_response_message(resp),
|
||||||
d
|
),
|
||||||
},
|
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
ComponentDataModel::TodoPager(pager) => {
|
ComponentDataModel::TodoPager(pager) => {
|
||||||
if Some(component.user.id.0) == pager.user_id || pager.user_id.is_none() {
|
if Some(component.user.id.get()) == pager.user_id || pager.user_id.is_none() {
|
||||||
let values = if let Some(uid) = pager.user_id {
|
let values = if let Some(uid) = pager.user_id {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT todos.id, value FROM todos
|
"
|
||||||
INNER JOIN users ON todos.user_id = users.id
|
SELECT todos.id, value FROM todos
|
||||||
WHERE users.user = ?",
|
INNER JOIN users ON todos.user_id = users.id
|
||||||
|
WHERE users.user = ?
|
||||||
|
",
|
||||||
uid,
|
uid,
|
||||||
)
|
)
|
||||||
.fetch_all(&data.database)
|
.fetch_all(&data.database)
|
||||||
@ -214,9 +204,11 @@ WHERE users.user = ?",
|
|||||||
.collect::<Vec<(usize, String)>>()
|
.collect::<Vec<(usize, String)>>()
|
||||||
} else if let Some(cid) = pager.channel_id {
|
} else if let Some(cid) = pager.channel_id {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT todos.id, value FROM todos
|
"
|
||||||
INNER JOIN channels ON todos.channel_id = channels.id
|
SELECT todos.id, value FROM todos
|
||||||
WHERE channels.channel = ?",
|
INNER JOIN channels ON todos.channel_id = channels.id
|
||||||
|
WHERE channels.channel = ?
|
||||||
|
",
|
||||||
cid,
|
cid,
|
||||||
)
|
)
|
||||||
.fetch_all(&data.database)
|
.fetch_all(&data.database)
|
||||||
@ -227,9 +219,11 @@ WHERE channels.channel = ?",
|
|||||||
.collect::<Vec<(usize, String)>>()
|
.collect::<Vec<(usize, String)>>()
|
||||||
} else {
|
} else {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"SELECT todos.id, value FROM todos
|
"
|
||||||
INNER JOIN guilds ON todos.guild_id = guilds.id
|
SELECT todos.id, value FROM todos
|
||||||
WHERE guilds.guild = ?",
|
INNER JOIN guilds ON todos.guild_id = guilds.id
|
||||||
|
WHERE guilds.guild = ?
|
||||||
|
",
|
||||||
pager.guild_id,
|
pager.guild_id,
|
||||||
)
|
)
|
||||||
.fetch_all(&data.database)
|
.fetch_all(&data.database)
|
||||||
@ -251,40 +245,48 @@ WHERE guilds.guild = ?",
|
|||||||
);
|
);
|
||||||
|
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::UpdateMessage)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::UpdateMessage(
|
||||||
send_as_initial_response(resp, d);
|
reply_to_interaction_response_message(resp),
|
||||||
d
|
),
|
||||||
})
|
)
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |r| {
|
.create_response(
|
||||||
r.kind(InteractionResponseType::ChannelMessageWithSource)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::Message(
|
||||||
d.flags(
|
CreateInteractionResponseMessage::new()
|
||||||
MessageFlags::EPHEMERAL,
|
.ephemeral(true)
|
||||||
)
|
|
||||||
.content("Only the user who performed the command can use these components")
|
.content("Only the user who performed the command can use these components")
|
||||||
})
|
)
|
||||||
})
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ComponentDataModel::TodoSelector(selector) => {
|
ComponentDataModel::TodoSelector(selector) => {
|
||||||
if Some(component.user.id.0) == selector.user_id || selector.user_id.is_none() {
|
if Some(component.user.id.get()) == selector.user_id || selector.user_id.is_none() {
|
||||||
let selected_id = component.data.values.join(",");
|
if let ComponentInteractionDataKind::StringSelect { values } =
|
||||||
|
&component.data.kind
|
||||||
|
{
|
||||||
|
let selected_id = values.join(",");
|
||||||
|
|
||||||
sqlx::query!("DELETE FROM todos WHERE FIND_IN_SET(id, ?)", selected_id)
|
sqlx::query!(
|
||||||
|
"
|
||||||
|
DELETE FROM todos WHERE FIND_IN_SET(id, ?)
|
||||||
|
",
|
||||||
|
selected_id
|
||||||
|
)
|
||||||
.execute(&data.database)
|
.execute(&data.database)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let values = sqlx::query!(
|
let values = sqlx::query!(
|
||||||
// fucking braindead mysql use <=> instead of = for null comparison
|
// fucking braindead mysql use <=> instead of = for null comparison
|
||||||
"SELECT id, value FROM todos WHERE user_id <=> ? AND channel_id <=> ? AND guild_id <=> ?",
|
"
|
||||||
|
SELECT id, value FROM todos WHERE user_id <=> ? AND channel_id <=> ? AND guild_id <=> ?
|
||||||
|
",
|
||||||
selector.user_id,
|
selector.user_id,
|
||||||
selector.channel_id,
|
selector.channel_id,
|
||||||
selector.guild_id,
|
selector.guild_id,
|
||||||
@ -305,25 +307,24 @@ WHERE guilds.guild = ?",
|
|||||||
);
|
);
|
||||||
|
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::UpdateMessage)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::UpdateMessage(
|
||||||
send_as_initial_response(resp, d);
|
reply_to_interaction_response_message(resp),
|
||||||
d
|
),
|
||||||
})
|
)
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |r| {
|
.create_response(
|
||||||
r.kind(InteractionResponseType::ChannelMessageWithSource)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::Message(
|
||||||
d.flags(
|
CreateInteractionResponseMessage::new()
|
||||||
MessageFlags::EPHEMERAL,
|
.ephemeral(true)
|
||||||
)
|
|
||||||
.content("Only the user who performed the command can use these components")
|
.content("Only the user who performed the command can use these components")
|
||||||
})
|
)
|
||||||
})
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,14 +337,12 @@ WHERE guilds.guild = ?",
|
|||||||
let resp = show_macro_page(¯os, page);
|
let resp = show_macro_page(¯os, page);
|
||||||
|
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::UpdateMessage).interaction_response_data(
|
&ctx,
|
||||||
|d| {
|
CreateInteractionResponse::UpdateMessage(
|
||||||
send_as_initial_response(resp, d);
|
reply_to_interaction_response_message(resp),
|
||||||
d
|
),
|
||||||
},
|
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
ComponentDataModel::UndoReminder(undo_reminder) => {
|
ComponentDataModel::UndoReminder(undo_reminder) => {
|
||||||
@ -355,58 +354,56 @@ WHERE guilds.guild = ?",
|
|||||||
match reminder.delete(&data.database).await {
|
match reminder.delete(&data.database).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::UpdateMessage)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::UpdateMessage(
|
||||||
d.embed(|e| {
|
CreateInteractionResponseMessage::new().embed(
|
||||||
e.title("Reminder Canceled")
|
CreateEmbed::new()
|
||||||
.description(
|
.title("Reminder Canceled")
|
||||||
"This reminder has been canceled.",
|
.description("This reminder has been canceled.")
|
||||||
|
.color(*THEME_COLOR),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.color(*THEME_COLOR)
|
|
||||||
})
|
|
||||||
.components(|c| c)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Error canceling reminder: {:?}", e);
|
warn!("Error canceling reminder: {:?}", e);
|
||||||
|
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::ChannelMessageWithSource)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::Message(
|
||||||
d.content(
|
CreateInteractionResponseMessage::new().content(
|
||||||
"The reminder could not be canceled: it may have already been deleted. Check `/del`!")
|
"An error occurred trying to cancel this reminder.",
|
||||||
.ephemeral(true)
|
).ephemeral(true),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::ChannelMessageWithSource)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::Message(
|
||||||
d.content(
|
CreateInteractionResponseMessage::new().content(
|
||||||
"The reminder could not be canceled: it may have already been deleted. Check `/del`!")
|
"The reminder could not be canceled. It may have already been deleted.",
|
||||||
.ephemeral(true)
|
).ephemeral(true),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let _ = component
|
let _ = component
|
||||||
.create_interaction_response(&ctx, |f| {
|
.create_response(
|
||||||
f.kind(InteractionResponseType::ChannelMessageWithSource)
|
&ctx,
|
||||||
.interaction_response_data(|d| {
|
CreateInteractionResponse::Message(
|
||||||
d.content(
|
CreateInteractionResponseMessage::new()
|
||||||
"Only the user who performed the command can use this button.")
|
.content("Only the user who performed the command can use these components")
|
||||||
.ephemeral(true)
|
.ephemeral(true),
|
||||||
})
|
),
|
||||||
})
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,17 +10,16 @@ pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX
|
|||||||
const THEME_COLOR_FALLBACK: u32 = 0x8fb677;
|
const THEME_COLOR_FALLBACK: u32 = 0x8fb677;
|
||||||
pub const MACRO_MAX_COMMANDS: usize = 5;
|
pub const MACRO_MAX_COMMANDS: usize = 5;
|
||||||
|
|
||||||
|
use poise::serenity_prelude::CreateAttachment;
|
||||||
use std::{collections::HashSet, env, iter::FromIterator};
|
use std::{collections::HashSet, env, iter::FromIterator};
|
||||||
|
|
||||||
use poise::serenity_prelude::model::prelude::AttachmentType;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref DEFAULT_AVATAR: AttachmentType<'static> = (
|
pub static ref DEFAULT_AVATAR: CreateAttachment = CreateAttachment::bytes(
|
||||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/webhook.jpg")) as &[u8],
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/webhook.jpg")) as &[u8],
|
||||||
"webhook.jpg",
|
"webhook.jpg",
|
||||||
)
|
);
|
||||||
.into();
|
|
||||||
pub static ref REGEX_CHANNEL_USER: Regex = Regex::new(r#"\s*<(#|@)(?:!)?(\d+)>\s*"#).unwrap();
|
pub static ref REGEX_CHANNEL_USER: Regex = Regex::new(r#"\s*<(#|@)(?:!)?(\d+)>\s*"#).unwrap();
|
||||||
pub static ref SUBSCRIPTION_ROLES: HashSet<u64> = HashSet::from_iter(
|
pub static ref SUBSCRIPTION_ROLES: HashSet<u64> = HashSet::from_iter(
|
||||||
env::var("PATREON_ROLE_ID")
|
env::var("PATREON_ROLE_ID")
|
||||||
|
@ -1,48 +1,44 @@
|
|||||||
use std::{collections::HashMap, env};
|
use poise::serenity_prelude::ActivityData;
|
||||||
|
|
||||||
use log::error;
|
|
||||||
use poise::{
|
use poise::{
|
||||||
serenity_prelude as serenity,
|
serenity_prelude as serenity,
|
||||||
serenity_prelude::{model::application::interaction::Interaction, utils::shard_id},
|
serenity_prelude::{CreateEmbed, CreateMessage, FullEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{component_models::ComponentDataModel, Data, Error, THEME_COLOR};
|
use crate::{component_models::ComponentDataModel, Data, Error, THEME_COLOR};
|
||||||
|
|
||||||
pub async fn listener(
|
pub async fn listener(
|
||||||
ctx: &serenity::Context,
|
ctx: &serenity::Context,
|
||||||
event: &poise::Event<'_>,
|
event: &FullEvent,
|
||||||
data: &Data,
|
data: &Data,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match event {
|
match event {
|
||||||
poise::Event::Ready { .. } => {
|
FullEvent::Ready { .. } => {
|
||||||
ctx.set_activity(serenity::Activity::watching("for /remind")).await;
|
ctx.set_activity(Some(ActivityData::watching("for /remind")));
|
||||||
}
|
}
|
||||||
poise::Event::ChannelDelete { channel } => {
|
FullEvent::ChannelDelete { channel, .. } => {
|
||||||
sqlx::query!("DELETE FROM channels WHERE channel = ?", channel.id.as_u64())
|
sqlx::query!("DELETE FROM channels WHERE channel = ?", channel.id.get())
|
||||||
.execute(&data.database)
|
.execute(&data.database)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
poise::Event::GuildCreate { guild, is_new } => {
|
FullEvent::GuildCreate { guild, is_new } => {
|
||||||
if *is_new {
|
if is_new.unwrap_or(false) {
|
||||||
let guild_id = guild.id.as_u64().to_owned();
|
let guild_id = guild.id.get().to_owned();
|
||||||
|
|
||||||
sqlx::query!("INSERT IGNORE INTO guilds (guild) VALUES (?)", guild_id)
|
sqlx::query!("INSERT IGNORE INTO guilds (guild) VALUES (?)", guild_id)
|
||||||
.execute(&data.database)
|
.execute(&data.database)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if let Err(e) = post_guild_count(ctx, &data.http, guild_id).await {
|
|
||||||
error!("DiscordBotList: {:?}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let default_channel = guild.default_channel_guaranteed();
|
let default_channel = guild.default_channel_guaranteed();
|
||||||
|
|
||||||
if let Some(default_channel) = default_channel {
|
if let Some(default_channel) = default_channel {
|
||||||
default_channel
|
default_channel.send_message(
|
||||||
.send_message(&ctx, |m| {
|
&ctx,
|
||||||
m.embed(|e| {
|
CreateMessage::new()
|
||||||
e.title("Thank you for adding Reminder Bot!").description(
|
.embed(
|
||||||
"To get started:
|
CreateEmbed::new()
|
||||||
|
.title("Thank you for adding Reminder Bot!")
|
||||||
|
.description("To get started:
|
||||||
• Set your timezone with `/timezone`
|
• Set your timezone with `/timezone`
|
||||||
• Set up permissions in Server Settings 🠚 Integrations 🠚 Reminder Bot (desktop only)
|
• Set up permissions in Server Settings 🠚 Integrations 🠚 Reminder Bot (desktop only)
|
||||||
• Create your first reminder with `/remind`
|
• Create your first reminder with `/remind`
|
||||||
@ -52,24 +48,24 @@ If you need any support, please come and ask us! Join our [Discord](https://disc
|
|||||||
|
|
||||||
__Updates__
|
__Updates__
|
||||||
To stay up to date on the latest features and fixes, join our [Discord](https://discord.jellywx.com).
|
To stay up to date on the latest features and fixes, join our [Discord](https://discord.jellywx.com).
|
||||||
",
|
")
|
||||||
).color(*THEME_COLOR)
|
.color(*THEME_COLOR)
|
||||||
})
|
)
|
||||||
})
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
poise::Event::GuildDelete { incomplete, .. } => {
|
FullEvent::GuildDelete { incomplete, .. } => {
|
||||||
let _ = sqlx::query!("DELETE FROM guilds WHERE guild = ?", incomplete.id.0)
|
let _ = sqlx::query!("DELETE FROM guilds WHERE guild = ?", incomplete.id.get())
|
||||||
.execute(&data.database)
|
.execute(&data.database)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
poise::Event::InteractionCreate { interaction } => {
|
FullEvent::InteractionCreate { interaction } => {
|
||||||
if let Interaction::MessageComponent(component) = interaction {
|
if let Some(component) = interaction.clone().message_component() {
|
||||||
let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id);
|
let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id);
|
||||||
|
|
||||||
component_model.act(ctx, data, component).await;
|
component_model.act(ctx, data, &component).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -77,38 +73,3 @@ To stay up to date on the latest features and fixes, join our [Discord](https://
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_guild_count(
|
|
||||||
ctx: &serenity::Context,
|
|
||||||
http: &reqwest::Client,
|
|
||||||
guild_id: u64,
|
|
||||||
) -> Result<(), reqwest::Error> {
|
|
||||||
if let Ok(token) = env::var("DISCORDBOTS_TOKEN") {
|
|
||||||
let shard_count = ctx.cache.shard_count();
|
|
||||||
let current_shard_id = shard_id(guild_id, shard_count);
|
|
||||||
|
|
||||||
let guild_count = ctx
|
|
||||||
.cache
|
|
||||||
.guilds()
|
|
||||||
.iter()
|
|
||||||
.filter(|g| shard_id(g.as_u64().to_owned(), shard_count) == current_shard_id)
|
|
||||||
.count() as u64;
|
|
||||||
|
|
||||||
let mut hm = HashMap::new();
|
|
||||||
hm.insert("server_count", guild_count);
|
|
||||||
hm.insert("shard_id", current_shard_id);
|
|
||||||
hm.insert("shard_count", shard_count);
|
|
||||||
|
|
||||||
http.post(
|
|
||||||
format!("https://top.gg/api/bots/{}/stats", ctx.cache.current_user_id().as_u64())
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
.header("Authorization", token)
|
|
||||||
.json(&hm)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.map(|_| ())
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
41
src/hooks.rs
41
src/hooks.rs
@ -1,37 +1,37 @@
|
|||||||
use poise::{
|
use poise::{serenity_prelude::model::channel::Channel, CreateReply};
|
||||||
serenity_prelude::model::channel::Channel, ApplicationCommandOrAutocompleteInteraction,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{consts::MACRO_MAX_COMMANDS, models::command_macro::RecordedCommand, Context, Error};
|
use crate::{consts::MACRO_MAX_COMMANDS, models::command_macro::RecordedCommand, Context, Error};
|
||||||
|
|
||||||
async fn macro_check(ctx: Context<'_>) -> bool {
|
async fn macro_check(ctx: Context<'_>) -> bool {
|
||||||
if let Context::Application(app_ctx) = ctx {
|
if let Context::Application(app_ctx) = ctx {
|
||||||
if let ApplicationCommandOrAutocompleteInteraction::ApplicationCommand(_) =
|
|
||||||
app_ctx.interaction
|
|
||||||
{
|
|
||||||
if let Some(guild_id) = ctx.guild_id() {
|
if let Some(guild_id) = ctx.guild_id() {
|
||||||
if ctx.command().identifying_name != "finish_macro" {
|
if ctx.command().identifying_name != "finish_macro" {
|
||||||
let mut lock = ctx.data().recording_macros.write().await;
|
let mut lock = ctx.data().recording_macros.write().await;
|
||||||
|
|
||||||
if let Some(command_macro) = lock.get_mut(&(guild_id, ctx.author().id)) {
|
if let Some(command_macro) = lock.get_mut(&(guild_id, ctx.author().id)) {
|
||||||
if command_macro.commands.len() >= MACRO_MAX_COMMANDS {
|
if command_macro.commands.len() >= MACRO_MAX_COMMANDS {
|
||||||
let _ = ctx.send(|m| {
|
let _ = ctx
|
||||||
m.ephemeral(true).content(
|
.send(
|
||||||
format!("{} commands already recorded. Please use `/macro finish` to end recording.", MACRO_MAX_COMMANDS),
|
CreateReply::default()
|
||||||
|
.ephemeral(true)
|
||||||
|
.content(format!("{} commands already recorded. Please use `/macro finish` to end recording.", MACRO_MAX_COMMANDS))
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
} else {
|
} else {
|
||||||
let recorded = RecordedCommand {
|
let recorded = RecordedCommand {
|
||||||
action: None,
|
action: None,
|
||||||
command_name: ctx.command().identifying_name.clone(),
|
command_name: ctx.command().identifying_name.clone(),
|
||||||
options: Vec::from(app_ctx.args),
|
options: app_ctx.interaction.data.options.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
command_macro.commands.push(recorded);
|
command_macro.commands.push(recorded);
|
||||||
|
|
||||||
let _ = ctx
|
let _ = ctx
|
||||||
.send(|m| m.ephemeral(true).content("Command recorded to macro"))
|
.send(
|
||||||
|
CreateReply::default()
|
||||||
|
.ephemeral(true)
|
||||||
|
.content("Command recorded to macro"),
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,17 +40,18 @@ async fn macro_check(ctx: Context<'_>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_self_permissions(ctx: Context<'_>) -> bool {
|
async fn check_self_permissions(ctx: Context<'_>) -> bool {
|
||||||
if let Some(guild) = ctx.guild() {
|
if let Some(guild_id) = ctx.guild_id() {
|
||||||
let user_id = ctx.serenity_context().cache.current_user_id();
|
let user_id = ctx.serenity_context().cache.current_user().id;
|
||||||
|
|
||||||
let manage_webhooks =
|
let manage_webhooks = guild_id
|
||||||
guild.member_permissions(&ctx, user_id).await.map_or(false, |p| p.manage_webhooks());
|
.current_user_member(&ctx)
|
||||||
|
.await
|
||||||
|
.map_or(false, |m| m.permissions(&ctx).map_or(false, |p| p.manage_webhooks()));
|
||||||
|
|
||||||
let (view_channel, send_messages, embed_links) = ctx
|
let (view_channel, send_messages, embed_links) = ctx
|
||||||
.channel_id()
|
.channel_id()
|
||||||
@ -72,8 +73,7 @@ async fn check_self_permissions(ctx: Context<'_>) -> bool {
|
|||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
let _ = ctx
|
let _ = ctx
|
||||||
.send(|m| {
|
.send(CreateReply::default().content(format!(
|
||||||
m.content(format!(
|
|
||||||
"Please ensure the bot has the correct permissions:
|
"Please ensure the bot has the correct permissions:
|
||||||
|
|
||||||
{} **View Channel**
|
{} **View Channel**
|
||||||
@ -84,8 +84,7 @@ async fn check_self_permissions(ctx: Context<'_>) -> bool {
|
|||||||
if send_messages { "✅" } else { "❌" },
|
if send_messages { "✅" } else { "❌" },
|
||||||
if embed_links { "✅" } else { "❌" },
|
if embed_links { "✅" } else { "❌" },
|
||||||
if manage_webhooks { "✅" } else { "❌" },
|
if manage_webhooks { "✅" } else { "❌" },
|
||||||
))
|
)))
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
false
|
false
|
||||||
|
20
src/main.rs
20
src/main.rs
@ -23,9 +23,12 @@ use std::{
|
|||||||
|
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use poise::serenity_prelude::model::{
|
use poise::serenity_prelude::{
|
||||||
|
model::{
|
||||||
gateway::GatewayIntents,
|
gateway::GatewayIntents,
|
||||||
id::{GuildId, UserId},
|
id::{GuildId, UserId},
|
||||||
|
},
|
||||||
|
ClientBuilder,
|
||||||
};
|
};
|
||||||
use sqlx::{MySql, Pool};
|
use sqlx::{MySql, Pool};
|
||||||
use tokio::sync::{broadcast, broadcast::Sender, RwLock};
|
use tokio::sync::{broadcast, broadcast::Sender, RwLock};
|
||||||
@ -36,7 +39,6 @@ use crate::{
|
|||||||
event_handlers::listener,
|
event_handlers::listener,
|
||||||
hooks::all_checks,
|
hooks::all_checks,
|
||||||
models::command_macro::CommandMacro,
|
models::command_macro::CommandMacro,
|
||||||
utils::register_application_commands,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Database = MySql;
|
type Database = MySql;
|
||||||
@ -213,11 +215,10 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
|||||||
.map(|t| t.timezone.parse::<Tz>().unwrap())
|
.map(|t| t.timezone.parse::<Tz>().unwrap())
|
||||||
.collect::<Vec<Tz>>();
|
.collect::<Vec<Tz>>();
|
||||||
|
|
||||||
poise::Framework::builder()
|
let framework = poise::Framework::builder()
|
||||||
.token(discord_token)
|
|
||||||
.setup(move |ctx, _bot, framework| {
|
.setup(move |ctx, _bot, framework| {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
register_application_commands(ctx, framework, None).await.unwrap();
|
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||||
|
|
||||||
let kill_tx = tx.clone();
|
let kill_tx = tx.clone();
|
||||||
let kill_recv = tx.subscribe();
|
let kill_recv = tx.subscribe();
|
||||||
@ -261,9 +262,12 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.options(options)
|
.options(options)
|
||||||
.intents(GatewayIntents::GUILDS)
|
.build();
|
||||||
.run_autosharded()
|
|
||||||
.await?;
|
let mut client =
|
||||||
|
ClientBuilder::new(&discord_token, GatewayIntents::GUILDS).framework(framework).await?;
|
||||||
|
|
||||||
|
client.start_autosharded().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ impl ChannelData {
|
|||||||
channel: &Channel,
|
channel: &Channel,
|
||||||
pool: &MySqlPool,
|
pool: &MySqlPool,
|
||||||
) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
|
) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
|
||||||
let channel_id = channel.id().as_u64().to_owned();
|
let channel_id = channel.id().get().to_owned();
|
||||||
|
|
||||||
if let Ok(c) = sqlx::query_as_unchecked!(
|
if let Ok(c) = sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
@ -30,7 +30,7 @@ impl ChannelData {
|
|||||||
{
|
{
|
||||||
Ok(c)
|
Ok(c)
|
||||||
} else {
|
} else {
|
||||||
let props = channel.to_owned().guild().map(|g| (g.guild_id.as_u64().to_owned(), g.name));
|
let props = channel.to_owned().guild().map(|g| (g.guild_id.get().to_owned(), g.name));
|
||||||
|
|
||||||
let (guild_id, channel_name) = if let Some((a, b)) = props { (Some(a), Some(b)) } else { (None, None) };
|
let (guild_id, channel_name) = if let Some((a, b)) = props { (Some(a), Some(b)) } else { (None, None) };
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use poise::serenity_prelude::model::{
|
use poise::serenity_prelude::{model::id::GuildId, CommandDataOption};
|
||||||
application::interaction::application_command::CommandDataOption, id::GuildId,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@ -45,7 +43,7 @@ pub async fn guild_command_macro(
|
|||||||
"
|
"
|
||||||
SELECT * FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?
|
SELECT * FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?
|
||||||
",
|
",
|
||||||
ctx.guild_id().unwrap().0,
|
ctx.guild_id().unwrap().get(),
|
||||||
name
|
name
|
||||||
)
|
)
|
||||||
.fetch_one(&ctx.data().database)
|
.fetch_one(&ctx.data().database)
|
||||||
|
@ -14,21 +14,21 @@ impl GuildData {
|
|||||||
if let Ok(c) = sqlx::query_as_unchecked!(
|
if let Ok(c) = sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"SELECT id, ephemeral_confirmations FROM guilds WHERE guild = ?",
|
"SELECT id, ephemeral_confirmations FROM guilds WHERE guild = ?",
|
||||||
guild_id.0
|
guild_id.get()
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(c)
|
Ok(c)
|
||||||
} else {
|
} else {
|
||||||
sqlx::query!("INSERT IGNORE INTO guilds (guild) VALUES (?)", guild_id.0)
|
sqlx::query!("INSERT IGNORE INTO guilds (guild) VALUES (?)", guild_id.get())
|
||||||
.execute(&pool.clone())
|
.execute(&pool.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(sqlx::query_as_unchecked!(
|
Ok(sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"SELECT id, ephemeral_confirmations FROM guilds WHERE guild = ?",
|
"SELECT id, ephemeral_confirmations FROM guilds WHERE guild = ?",
|
||||||
guild_id.0
|
guild_id.get()
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await?)
|
.await?)
|
||||||
|
@ -6,7 +6,7 @@ pub mod timer;
|
|||||||
pub mod user_data;
|
pub mod user_data;
|
||||||
|
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use poise::serenity_prelude::{async_trait, model::id::UserId, ChannelType};
|
use poise::serenity_prelude::{async_trait, model::id::UserId};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData},
|
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData},
|
||||||
@ -53,20 +53,7 @@ impl CtxData for Context<'_> {
|
|||||||
|
|
||||||
async fn channel_data(&self) -> Result<ChannelData, Box<dyn std::error::Error + Sync + Send>> {
|
async fn channel_data(&self) -> Result<ChannelData, Box<dyn std::error::Error + Sync + Send>> {
|
||||||
// If we're in a thread, get the parent channel.
|
// If we're in a thread, get the parent channel.
|
||||||
let recv_channel = self.channel_id().to_channel(&self).await?;
|
let channel = self.channel_id().to_channel(&self).await?;
|
||||||
|
|
||||||
let channel = match recv_channel.guild() {
|
|
||||||
Some(guild_channel) => {
|
|
||||||
if guild_channel.kind == ChannelType::PublicThread {
|
|
||||||
guild_channel.parent_id.unwrap().to_channel_cached(&self).unwrap()
|
|
||||||
} else {
|
|
||||||
self.channel_id().to_channel_cached(&self).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None => self.channel_id().to_channel_cached(&self).unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
ChannelData::from_channel(&channel, &self.data().database).await
|
ChannelData::from_channel(&channel, &self.data().database).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +69,7 @@ impl Data {
|
|||||||
) -> Result<Vec<CommandMacro<Data, Error>>, Error> {
|
) -> Result<Vec<CommandMacro<Data, Error>>, Error> {
|
||||||
let rows = sqlx::query!(
|
let rows = sqlx::query!(
|
||||||
"SELECT name, description, commands FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
"SELECT name, description, commands FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||||
guild_id.0
|
guild_id.get()
|
||||||
)
|
)
|
||||||
.fetch_all(&self.database)
|
.fetch_all(&self.database)
|
||||||
.await?.iter().map(|row| CommandMacro {
|
.await?.iter().map(|row| CommandMacro {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{collections::HashSet, fmt::Display};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use chrono::{Duration, NaiveDateTime, Utc};
|
use chrono::{Duration, NaiveDateTime, Utc};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
@ -9,8 +9,9 @@ use poise::serenity_prelude::{
|
|||||||
id::{ChannelId, GuildId, UserId},
|
id::{ChannelId, GuildId, UserId},
|
||||||
webhook::Webhook,
|
webhook::Webhook,
|
||||||
},
|
},
|
||||||
ChannelType, Result as SerenityResult,
|
ChannelType, CreateWebhook, Result as SerenityResult,
|
||||||
};
|
};
|
||||||
|
use secrecy::ExposeSecret;
|
||||||
use sqlx::MySqlPool;
|
use sqlx::MySqlPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -27,9 +28,9 @@ use crate::{
|
|||||||
async fn create_webhook(
|
async fn create_webhook(
|
||||||
ctx: impl CacheHttp,
|
ctx: impl CacheHttp,
|
||||||
channel: GuildChannel,
|
channel: GuildChannel,
|
||||||
name: impl Display,
|
name: impl Into<String>,
|
||||||
) -> SerenityResult<Webhook> {
|
) -> SerenityResult<Webhook> {
|
||||||
channel.create_webhook_with_avatar(ctx.http(), name, DEFAULT_AVATAR.clone()).await
|
channel.create_webhook(ctx.http(), CreateWebhook::new(name).avatar(&*DEFAULT_AVATAR)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Hash, PartialEq, Eq)]
|
#[derive(Hash, PartialEq, Eq)]
|
||||||
@ -230,7 +231,7 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
let thread_id = None;
|
let thread_id = None;
|
||||||
let db_channel_id = match scope {
|
let db_channel_id = match scope {
|
||||||
ReminderScope::User(user_id) => {
|
ReminderScope::User(user_id) => {
|
||||||
if let Ok(user) = UserId(user_id).to_user(&self.ctx).await {
|
if let Ok(user) = UserId::new(user_id).to_user(&self.ctx).await {
|
||||||
let user_data = UserData::from_user(
|
let user_data = UserData::from_user(
|
||||||
&user,
|
&user,
|
||||||
&self.ctx.serenity_context(),
|
&self.ctx.serenity_context(),
|
||||||
@ -257,7 +258,8 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReminderScope::Channel(channel_id) => {
|
ReminderScope::Channel(channel_id) => {
|
||||||
let channel = ChannelId(channel_id).to_channel(&self.ctx).await.unwrap();
|
let channel =
|
||||||
|
ChannelId::new(channel_id).to_channel(&self.ctx).await.unwrap();
|
||||||
|
|
||||||
if let Some(mut guild_channel) = channel.clone().guild() {
|
if let Some(mut guild_channel) = channel.clone().guild() {
|
||||||
if Some(guild_channel.guild_id) != self.guild_id {
|
if Some(guild_channel.guild_id) != self.guild_id {
|
||||||
@ -290,8 +292,9 @@ impl<'a> MultiReminderBuilder<'a> {
|
|||||||
{
|
{
|
||||||
Ok(webhook) => {
|
Ok(webhook) => {
|
||||||
channel_data.webhook_id =
|
channel_data.webhook_id =
|
||||||
Some(webhook.id.as_u64().to_owned());
|
Some(webhook.id.get().to_owned());
|
||||||
channel_data.webhook_token = webhook.token;
|
channel_data.webhook_token =
|
||||||
|
webhook.token.map(|s| s.expose_secret().clone());
|
||||||
|
|
||||||
channel_data
|
channel_data
|
||||||
.commit_changes(&self.ctx.data().database)
|
.commit_changes(&self.ctx.data().database)
|
||||||
|
@ -54,7 +54,7 @@ impl Reminder {
|
|||||||
sqlx::query_as_unchecked!(
|
sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
reminders.id,
|
reminders.id,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -67,17 +67,17 @@ SELECT
|
|||||||
reminders.content,
|
reminders.content,
|
||||||
reminders.embed_description,
|
reminders.embed_description,
|
||||||
users.user AS set_by
|
users.user AS set_by
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
INNER JOIN
|
INNER JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
reminders.channel_id = channels.id
|
reminders.channel_id = channels.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
users
|
users
|
||||||
ON
|
ON
|
||||||
reminders.set_by = users.id
|
reminders.set_by = users.id
|
||||||
WHERE
|
WHERE
|
||||||
reminders.uid = ?
|
reminders.uid = ?
|
||||||
",
|
",
|
||||||
uid
|
uid
|
||||||
@ -91,7 +91,7 @@ WHERE
|
|||||||
sqlx::query_as_unchecked!(
|
sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
reminders.id,
|
reminders.id,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -104,17 +104,17 @@ SELECT
|
|||||||
reminders.content,
|
reminders.content,
|
||||||
reminders.embed_description,
|
reminders.embed_description,
|
||||||
users.user AS set_by
|
users.user AS set_by
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
INNER JOIN
|
INNER JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
reminders.channel_id = channels.id
|
reminders.channel_id = channels.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
users
|
users
|
||||||
ON
|
ON
|
||||||
reminders.set_by = users.id
|
reminders.set_by = users.id
|
||||||
WHERE
|
WHERE
|
||||||
reminders.id = ?
|
reminders.id = ?
|
||||||
",
|
",
|
||||||
id
|
id
|
||||||
@ -135,7 +135,7 @@ WHERE
|
|||||||
sqlx::query_as_unchecked!(
|
sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
reminders.id,
|
reminders.id,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -148,24 +148,24 @@ SELECT
|
|||||||
reminders.content,
|
reminders.content,
|
||||||
reminders.embed_description,
|
reminders.embed_description,
|
||||||
users.user AS set_by
|
users.user AS set_by
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
INNER JOIN
|
INNER JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
reminders.channel_id = channels.id
|
reminders.channel_id = channels.id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
users
|
users
|
||||||
ON
|
ON
|
||||||
reminders.set_by = users.id
|
reminders.set_by = users.id
|
||||||
WHERE
|
WHERE
|
||||||
`status` = 'pending' AND
|
`status` = 'pending' AND
|
||||||
channels.channel = ? AND
|
channels.channel = ? AND
|
||||||
FIND_IN_SET(reminders.enabled, ?)
|
FIND_IN_SET(reminders.enabled, ?)
|
||||||
ORDER BY
|
ORDER BY
|
||||||
reminders.utc_time
|
reminders.utc_time
|
||||||
",
|
",
|
||||||
channel_id.as_u64(),
|
channel_id.get(),
|
||||||
enabled,
|
enabled,
|
||||||
)
|
)
|
||||||
.fetch_all(pool)
|
.fetch_all(pool)
|
||||||
@ -180,21 +180,26 @@ ORDER BY
|
|||||||
user: UserId,
|
user: UserId,
|
||||||
) -> Vec<Self> {
|
) -> Vec<Self> {
|
||||||
if let Some(guild_id) = guild_id {
|
if let Some(guild_id) = guild_id {
|
||||||
let guild_opt = guild_id.to_guild_cached(cache);
|
let channel_query = if let Some(guild) = guild_id.to_guild_cached(&cache) {
|
||||||
|
Some(
|
||||||
if let Some(guild) = guild_opt {
|
guild
|
||||||
let channels = guild
|
|
||||||
.channels
|
.channels
|
||||||
.keys()
|
.keys()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|k| k.as_u64().to_string())
|
.map(|k| k.get().to_string())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(",");
|
.join(","),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
match channel_query {
|
||||||
|
Some(channel_query) => {
|
||||||
sqlx::query_as_unchecked!(
|
sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
reminders.id,
|
reminders.id,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -207,29 +212,30 @@ SELECT
|
|||||||
reminders.content,
|
reminders.content,
|
||||||
reminders.embed_description,
|
reminders.embed_description,
|
||||||
users.user AS set_by
|
users.user AS set_by
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
channels.id = reminders.channel_id
|
channels.id = reminders.channel_id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
users
|
users
|
||||||
ON
|
ON
|
||||||
reminders.set_by = users.id
|
reminders.set_by = users.id
|
||||||
WHERE
|
WHERE
|
||||||
`status` = 'pending' AND
|
`status` = 'pending' AND
|
||||||
FIND_IN_SET(channels.channel, ?)
|
FIND_IN_SET(channels.channel, ?)
|
||||||
",
|
",
|
||||||
channels
|
channel_query
|
||||||
)
|
)
|
||||||
.fetch_all(pool)
|
.fetch_all(pool)
|
||||||
.await
|
.await
|
||||||
} else {
|
}
|
||||||
|
None => {
|
||||||
sqlx::query_as_unchecked!(
|
sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
reminders.id,
|
reminders.id,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -242,30 +248,31 @@ SELECT
|
|||||||
reminders.content,
|
reminders.content,
|
||||||
reminders.embed_description,
|
reminders.embed_description,
|
||||||
users.user AS set_by
|
users.user AS set_by
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
channels.id = reminders.channel_id
|
channels.id = reminders.channel_id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
users
|
users
|
||||||
ON
|
ON
|
||||||
reminders.set_by = users.id
|
reminders.set_by = users.id
|
||||||
WHERE
|
WHERE
|
||||||
`status` = 'pending' AND
|
`status` = 'pending' AND
|
||||||
channels.guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
channels.guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
||||||
",
|
",
|
||||||
guild_id.as_u64()
|
guild_id.get()
|
||||||
)
|
)
|
||||||
.fetch_all(pool)
|
.fetch_all(pool)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sqlx::query_as_unchecked!(
|
sqlx::query_as_unchecked!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
reminders.id,
|
reminders.id,
|
||||||
reminders.uid,
|
reminders.uid,
|
||||||
channels.channel,
|
channels.channel,
|
||||||
@ -278,21 +285,21 @@ SELECT
|
|||||||
reminders.content,
|
reminders.content,
|
||||||
reminders.embed_description,
|
reminders.embed_description,
|
||||||
users.user AS set_by
|
users.user AS set_by
|
||||||
FROM
|
FROM
|
||||||
reminders
|
reminders
|
||||||
INNER JOIN
|
INNER JOIN
|
||||||
channels
|
channels
|
||||||
ON
|
ON
|
||||||
channels.id = reminders.channel_id
|
channels.id = reminders.channel_id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
users
|
users
|
||||||
ON
|
ON
|
||||||
reminders.set_by = users.id
|
reminders.set_by = users.id
|
||||||
WHERE
|
WHERE
|
||||||
`status` = 'pending' AND
|
`status` = 'pending' AND
|
||||||
channels.id = (SELECT dm_channel FROM users WHERE user = ?)
|
channels.id = (SELECT dm_channel FROM users WHERE user = ?)
|
||||||
",
|
",
|
||||||
user.as_u64()
|
user.get()
|
||||||
)
|
)
|
||||||
.fetch_all(pool)
|
.fetch_all(pool)
|
||||||
.await
|
.await
|
||||||
@ -304,7 +311,12 @@ WHERE
|
|||||||
&self,
|
&self,
|
||||||
db: impl Executor<'_, Database = Database>,
|
db: impl Executor<'_, Database = Database>,
|
||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
sqlx::query!("UPDATE reminders SET `status` = 'deleted' WHERE uid = ?", self.uid)
|
sqlx::query!(
|
||||||
|
"
|
||||||
|
UPDATE reminders SET `status` = 'deleted' WHERE uid = ?
|
||||||
|
",
|
||||||
|
self.uid
|
||||||
|
)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
|
@ -18,7 +18,7 @@ impl UserData {
|
|||||||
where
|
where
|
||||||
U: Into<UserId>,
|
U: Into<UserId>,
|
||||||
{
|
{
|
||||||
let user_id = user.into().as_u64().to_owned();
|
let user_id = user.into().get().to_owned();
|
||||||
|
|
||||||
match sqlx::query!(
|
match sqlx::query!(
|
||||||
"
|
"
|
||||||
@ -50,7 +50,7 @@ SELECT IFNULL(timezone, 'UTC') AS timezone FROM users WHERE user = ?
|
|||||||
SELECT id, user, dm_channel, IF(timezone IS NULL, ?, timezone) AS timezone, allowed_dm FROM users WHERE user = ?
|
SELECT id, user, dm_channel, IF(timezone IS NULL, ?, timezone) AS timezone, allowed_dm FROM users WHERE user = ?
|
||||||
",
|
",
|
||||||
*LOCAL_TIMEZONE,
|
*LOCAL_TIMEZONE,
|
||||||
user_id.0
|
user_id.get()
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
@ -65,7 +65,7 @@ SELECT id, user, dm_channel, IF(timezone IS NULL, ?, timezone) AS timezone, allo
|
|||||||
"
|
"
|
||||||
INSERT IGNORE INTO channels (channel) VALUES (?)
|
INSERT IGNORE INTO channels (channel) VALUES (?)
|
||||||
",
|
",
|
||||||
dm_channel.id.0
|
dm_channel.id.get()
|
||||||
)
|
)
|
||||||
.execute(&pool_c)
|
.execute(&pool_c)
|
||||||
.await?;
|
.await?;
|
||||||
@ -74,8 +74,8 @@ INSERT IGNORE INTO channels (channel) VALUES (?)
|
|||||||
"
|
"
|
||||||
INSERT INTO users (name, user, dm_channel, timezone) VALUES ('', ?, (SELECT id FROM channels WHERE channel = ?), ?)
|
INSERT INTO users (name, user, dm_channel, timezone) VALUES ('', ?, (SELECT id FROM channels WHERE channel = ?), ?)
|
||||||
",
|
",
|
||||||
user_id.0,
|
user_id.get(),
|
||||||
dm_channel.id.0,
|
dm_channel.id.get(),
|
||||||
*LOCAL_TIMEZONE
|
*LOCAL_TIMEZONE
|
||||||
)
|
)
|
||||||
.execute(&pool_c)
|
.execute(&pool_c)
|
||||||
@ -86,7 +86,7 @@ INSERT INTO users (name, user, dm_channel, timezone) VALUES ('', ?, (SELECT id F
|
|||||||
"
|
"
|
||||||
SELECT id, user, dm_channel, timezone, allowed_dm FROM users WHERE user = ?
|
SELECT id, user, dm_channel, timezone, allowed_dm FROM users WHERE user = ?
|
||||||
",
|
",
|
||||||
user_id.0
|
user_id.get()
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await?)
|
.await?)
|
||||||
|
81
src/utils.rs
81
src/utils.rs
@ -1,51 +1,21 @@
|
|||||||
use poise::{
|
use poise::{
|
||||||
serenity_prelude as serenity,
|
|
||||||
serenity_prelude::{
|
serenity_prelude::{
|
||||||
builder::CreateApplicationCommands,
|
|
||||||
http::CacheHttp,
|
http::CacheHttp,
|
||||||
interaction::MessageFlags,
|
|
||||||
model::id::{GuildId, UserId},
|
model::id::{GuildId, UserId},
|
||||||
|
CreateInteractionResponseMessage,
|
||||||
},
|
},
|
||||||
|
CreateReply,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::consts::{CNC_GUILD, SUBSCRIPTION_ROLES};
|
||||||
consts::{CNC_GUILD, SUBSCRIPTION_ROLES},
|
|
||||||
Data, Error,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub async fn register_application_commands(
|
|
||||||
ctx: &serenity::Context,
|
|
||||||
framework: &poise::Framework<Data, Error>,
|
|
||||||
guild_id: Option<GuildId>,
|
|
||||||
) -> Result<(), serenity::Error> {
|
|
||||||
let mut commands_builder = CreateApplicationCommands::default();
|
|
||||||
let commands = &framework.options().commands;
|
|
||||||
for command in commands {
|
|
||||||
if let Some(slash_command) = command.create_as_slash_command() {
|
|
||||||
commands_builder.add_application_command(slash_command);
|
|
||||||
}
|
|
||||||
if let Some(context_menu_command) = command.create_as_context_menu_command() {
|
|
||||||
commands_builder.add_application_command(context_menu_command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let commands_builder = poise::serenity_prelude::json::Value::Array(commands_builder.0);
|
|
||||||
|
|
||||||
if let Some(guild_id) = guild_id {
|
|
||||||
ctx.http.create_guild_application_commands(guild_id.0, &commands_builder).await?;
|
|
||||||
} else {
|
|
||||||
ctx.http.create_global_application_commands(&commands_builder).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
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::new(subscription_guild).member(cache_http, user_id).await;
|
||||||
|
|
||||||
if let Ok(member) = guild_member {
|
if let Ok(member) = guild_member {
|
||||||
for role in member.roles {
|
for role in member.roles {
|
||||||
if SUBSCRIPTION_ROLES.contains(role.as_u64()) {
|
if SUBSCRIPTION_ROLES.contains(&role.get()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,39 +40,14 @@ pub async fn check_guild_subscription(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends the message, specified via [`crate::CreateReply`], to the interaction initial response
|
pub fn reply_to_interaction_response_message(
|
||||||
/// endpoint
|
reply: CreateReply,
|
||||||
pub fn send_as_initial_response(
|
) -> CreateInteractionResponseMessage {
|
||||||
data: poise::CreateReply<'_>,
|
let mut builder = CreateInteractionResponseMessage::new().embeds(reply.embeds);
|
||||||
f: &mut serenity::CreateInteractionResponseData,
|
|
||||||
) {
|
|
||||||
let poise::CreateReply {
|
|
||||||
content,
|
|
||||||
embeds,
|
|
||||||
attachments: _, // serenity doesn't support attachments in initial response yet
|
|
||||||
components,
|
|
||||||
ephemeral,
|
|
||||||
allowed_mentions,
|
|
||||||
reply: _,
|
|
||||||
} = data;
|
|
||||||
|
|
||||||
if let Some(content) = content {
|
if let Some(components) = reply.components {
|
||||||
f.content(content);
|
builder = builder.components(components)
|
||||||
}
|
|
||||||
f.set_embeds(embeds);
|
|
||||||
if let Some(allowed_mentions) = allowed_mentions {
|
|
||||||
f.allowed_mentions(|f| {
|
|
||||||
*f = allowed_mentions.clone();
|
|
||||||
f
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if let Some(components) = components {
|
|
||||||
f.components(|f| {
|
|
||||||
f.0 = components.0;
|
|
||||||
f
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if ephemeral {
|
|
||||||
f.flags(MessageFlags::EPHEMERAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ 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.12", 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 = { version = "0.11", features = ["json"] }
|
||||||
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.7", features = ["runtime-tokio-rustls", "macros", "mysql", "chrono", "json"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
@ -20,3 +20,4 @@ rand = "0.8"
|
|||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
csv = "1.2"
|
csv = "1.2"
|
||||||
prometheus = "0.13.3"
|
prometheus = "0.13.3"
|
||||||
|
secrecy = "0.8.0"
|
||||||
|
@ -23,14 +23,13 @@ pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX
|
|||||||
use std::{collections::HashSet, env, iter::FromIterator};
|
use std::{collections::HashSet, env, iter::FromIterator};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serenity::model::prelude::AttachmentType;
|
use serenity::builder::CreateAttachment;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref DEFAULT_AVATAR: AttachmentType<'static> = (
|
pub static ref DEFAULT_AVATAR: CreateAttachment = CreateAttachment::bytes(
|
||||||
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/webhook.jpg")) as &[u8],
|
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/webhook.jpg")) as &[u8],
|
||||||
"webhook.jpg",
|
"webhook.jpg",
|
||||||
)
|
);
|
||||||
.into();
|
|
||||||
pub static ref SUBSCRIPTION_ROLES: HashSet<u64> = HashSet::from_iter(
|
pub static ref SUBSCRIPTION_ROLES: HashSet<u64> = HashSet::from_iter(
|
||||||
env::var("PATREON_ROLE_ID")
|
env::var("PATREON_ROLE_ID")
|
||||||
.map(|var| var
|
.map(|var| var
|
||||||
|
@ -35,8 +35,8 @@ type Database = MySql;
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
SQLx(sqlx::Error),
|
SQLx,
|
||||||
Serenity(serenity::Error),
|
Serenity,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn initialize(
|
pub async fn initialize(
|
||||||
@ -169,11 +169,11 @@ pub async fn check_subscription(cache_http: impl CacheHttp, user_id: impl Into<U
|
|||||||
offline!(true);
|
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::new(subscription_guild).member(cache_http, user_id).await;
|
||||||
|
|
||||||
if let Ok(member) = guild_member {
|
if let Ok(member) = guild_member {
|
||||||
for role in member.roles {
|
for role in member.roles {
|
||||||
if SUBSCRIPTION_ROLES.contains(role.as_u64()) {
|
if SUBSCRIPTION_ROLES.contains(&role.get()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,9 +191,7 @@ pub async fn check_guild_subscription(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
offline!(true);
|
offline!(true);
|
||||||
|
|
||||||
if let Some(guild) = cache_http.cache().unwrap().guild(guild_id) {
|
if let Some(owner) = cache_http.cache().unwrap().guild(guild_id).map(|guild| guild.owner_id) {
|
||||||
let owner = guild.owner_id;
|
|
||||||
|
|
||||||
check_subscription(&cache_http, owner).await
|
check_subscription(&cache_http, owner).await
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@ -203,7 +201,7 @@ pub async fn check_guild_subscription(
|
|||||||
pub async fn check_authorization(
|
pub async fn check_authorization(
|
||||||
cookies: &CookieJar<'_>,
|
cookies: &CookieJar<'_>,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
guild: u64,
|
guild_id: u64,
|
||||||
) -> Result<(), JsonValue> {
|
) -> Result<(), JsonValue> {
|
||||||
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();
|
||||||
|
|
||||||
@ -217,15 +215,9 @@ pub async fn check_authorization(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
match GuildId(guild).to_guild_cached(ctx) {
|
let guild_id = GuildId::new(guild_id);
|
||||||
Some(guild) => {
|
|
||||||
let member_res = guild.member(ctx, UserId(user_id)).await;
|
|
||||||
|
|
||||||
match member_res {
|
|
||||||
Err(_) => {
|
|
||||||
return Err(json!({"error": "User not in guild"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
match guild_id.member(ctx, UserId::new(user_id)).await {
|
||||||
Ok(member) => {
|
Ok(member) => {
|
||||||
let permissions_res = member.permissions(ctx);
|
let permissions_res = member.permissions(ctx);
|
||||||
|
|
||||||
@ -244,11 +236,9 @@ pub async fn check_authorization(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None => {
|
Err(_) => {
|
||||||
return Err(json!({"error": "Bot not in guild"}));
|
return Err(json!({"error": "User not in guild"}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,13 @@ pub async fn get_guild_channels(
|
|||||||
}])));
|
}])));
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
match GuildId::new(id).to_guild_cached(ctx.inner()) {
|
||||||
Some(guild) => {
|
Some(guild) => {
|
||||||
let mut channels = guild
|
let mut channels = guild
|
||||||
.channels
|
.channels
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(id, channel)| channel.to_owned().guild().map(|c| (id.to_owned(), c)))
|
|
||||||
.filter(|(_, channel)| channel.is_text_based())
|
.filter(|(_, channel)| channel.is_text_based())
|
||||||
|
.map(|(id, channel)| (id.to_owned(), channel.to_owned()))
|
||||||
.collect::<Vec<(ChannelId, GuildChannel)>>();
|
.collect::<Vec<(ChannelId, GuildChannel)>>();
|
||||||
|
|
||||||
channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position));
|
channels.sort_by(|(_, c1), (_, c2)| c1.position.cmp(&c2.position));
|
||||||
|
@ -22,19 +22,22 @@ pub async fn get_guild_info(id: u64, cookies: &CookieJar<'_>, ctx: &State<Contex
|
|||||||
offline!(Ok(json!({ "patreon": true, "name": "Guild" })));
|
offline!(Ok(json!({ "patreon": true, "name": "Guild" })));
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
match GuildId(id).to_guild_cached(ctx.inner()) {
|
match GuildId::new(id)
|
||||||
Some(guild) => {
|
.to_guild_cached(ctx.inner())
|
||||||
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
.map(|guild| (guild.owner_id, guild.name.clone()))
|
||||||
.member(&ctx.inner(), guild.owner_id)
|
{
|
||||||
|
Some((owner_id, name)) => {
|
||||||
|
let member_res = GuildId::new(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||||
|
.member(&ctx.inner(), owner_id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let patreon = member_res.map_or(false, |member| {
|
let patreon = member_res.map_or(false, |member| {
|
||||||
member
|
member
|
||||||
.roles
|
.roles
|
||||||
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
.contains(&RoleId::new(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(json!({ "patreon": patreon, "name": guild.name }))
|
Ok(json!({ "patreon": patreon, "name": name }))
|
||||||
}
|
}
|
||||||
|
|
||||||
None => json_err!("Bot not in guild"),
|
None => json_err!("Bot not in guild"),
|
||||||
|
@ -38,8 +38,8 @@ pub async fn create_guild_reminder(
|
|||||||
match create_reminder(
|
match create_reminder(
|
||||||
ctx.inner(),
|
ctx.inner(),
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
GuildId(id),
|
GuildId::new(id),
|
||||||
UserId(user_id),
|
UserId::new(user_id),
|
||||||
reminder.into_inner(),
|
reminder.into_inner(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
@ -65,14 +65,14 @@ pub async fn get_reminders(
|
|||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||||
|
|
||||||
match channels_res {
|
match channels_res {
|
||||||
Ok(channels) => {
|
Ok(channels) => {
|
||||||
let channels = channels
|
let channels = channels
|
||||||
.keys()
|
.keys()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|k| k.as_u64().to_string())
|
.map(|k| k.get().to_string())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
@ -253,10 +253,12 @@ pub async fn edit_reminder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if reminder.channel > 0 {
|
if reminder.channel > 0 {
|
||||||
let channel = ChannelId(reminder.channel).to_channel_cached(&ctx.inner());
|
let channel_guild = ChannelId::new(reminder.channel)
|
||||||
match channel {
|
.to_channel_cached(&ctx.cache)
|
||||||
Some(channel) => {
|
.map(|channel| channel.guild_id);
|
||||||
let channel_matches_guild = channel.guild().map_or(false, |c| c.guild_id.0 == id);
|
match channel_guild {
|
||||||
|
Some(channel_guild) => {
|
||||||
|
let channel_matches_guild = channel_guild.get() == id;
|
||||||
|
|
||||||
if !channel_matches_guild {
|
if !channel_matches_guild {
|
||||||
warn!(
|
warn!(
|
||||||
@ -269,7 +271,7 @@ pub async fn edit_reminder(
|
|||||||
|
|
||||||
let channel = create_database_channel(
|
let channel = create_database_channel(
|
||||||
ctx.inner(),
|
ctx.inner(),
|
||||||
ChannelId(reminder.channel),
|
ChannelId::new(reminder.channel),
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
@ -39,7 +39,7 @@ pub async fn get_user_info(
|
|||||||
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()
|
||||||
{
|
{
|
||||||
let member_res = GuildId(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
let member_res = GuildId::new(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
|
||||||
.member(&ctx.inner(), user_id)
|
.member(&ctx.inner(), user_id)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ pub async fn get_user_info(
|
|||||||
patreon: member_res.map_or(false, |member| {
|
patreon: member_res.map_or(false, |member| {
|
||||||
member
|
member
|
||||||
.roles
|
.roles
|
||||||
.contains(&RoleId(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
.contains(&RoleId::new(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
|
||||||
}),
|
}),
|
||||||
timezone,
|
timezone,
|
||||||
};
|
};
|
||||||
|
@ -33,14 +33,14 @@ pub async fn export_reminders(
|
|||||||
|
|
||||||
let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]);
|
let mut csv_writer = WriterBuilder::new().quote_style(QuoteStyle::Always).from_writer(vec![]);
|
||||||
|
|
||||||
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||||
|
|
||||||
match channels_res {
|
match channels_res {
|
||||||
Ok(channels) => {
|
Ok(channels) => {
|
||||||
let channels = channels
|
let channels = channels
|
||||||
.keys()
|
.keys()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|k| k.as_u64().to_string())
|
.map(|k| k.get().to_string())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
@ -181,8 +181,8 @@ pub(crate) async fn import_reminders(
|
|||||||
create_reminder(
|
create_reminder(
|
||||||
ctx.inner(),
|
ctx.inner(),
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
GuildId(id),
|
GuildId::new(id),
|
||||||
UserId(user_id),
|
UserId::new(user_id),
|
||||||
reminder,
|
reminder,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -289,7 +289,7 @@ pub async fn import_todos(
|
|||||||
) -> JsonResult {
|
) -> JsonResult {
|
||||||
check_authorization(cookies, ctx.inner(), id).await?;
|
check_authorization(cookies, ctx.inner(), id).await?;
|
||||||
|
|
||||||
let channels_res = GuildId(id).channels(&ctx.inner()).await;
|
let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
|
||||||
|
|
||||||
match channels_res {
|
match channels_res {
|
||||||
Ok(channels) => match base64::decode(&body.body) {
|
Ok(channels) => match base64::decode(&body.body) {
|
||||||
@ -307,7 +307,7 @@ pub async fn import_todos(
|
|||||||
|
|
||||||
match channel_id.parse::<u64>() {
|
match channel_id.parse::<u64>() {
|
||||||
Ok(channel_id) => {
|
Ok(channel_id) => {
|
||||||
if channels.contains_key(&ChannelId(channel_id)) {
|
if channels.contains_key(&ChannelId::new(channel_id)) {
|
||||||
query_params.push((record.value, Some(channel_id), id));
|
query_params.push((record.value, Some(channel_id), id));
|
||||||
} else {
|
} else {
|
||||||
return json_err!(format!(
|
return json_err!(format!(
|
||||||
|
@ -8,10 +8,12 @@ use rocket::{
|
|||||||
response::Redirect,
|
response::Redirect,
|
||||||
serde::json::json,
|
serde::json::json,
|
||||||
};
|
};
|
||||||
|
use secrecy::ExposeSecret;
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serenity::{
|
use serenity::{
|
||||||
|
all::CacheHttp,
|
||||||
|
builder::CreateWebhook,
|
||||||
client::Context,
|
client::Context,
|
||||||
http::Http,
|
|
||||||
model::id::{ChannelId, GuildId, UserId},
|
model::id::{ChannelId, GuildId, UserId},
|
||||||
};
|
};
|
||||||
use sqlx::types::Json;
|
use sqlx::types::Json;
|
||||||
@ -363,12 +365,12 @@ pub(crate) async fn create_reminder(
|
|||||||
reminder: Reminder,
|
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.get())
|
||||||
.fetch_one(transaction.executor())
|
.fetch_one(transaction.executor())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(sqlx::Error::RowNotFound) => {
|
Err(sqlx::Error::RowNotFound) => {
|
||||||
if sqlx::query!("INSERT INTO guilds (guild) VALUES (?)", guild_id.0)
|
if sqlx::query!("INSERT INTO guilds (guild) VALUES (?)", guild_id.get())
|
||||||
.execute(transaction.executor())
|
.execute(transaction.executor())
|
||||||
.await
|
.await
|
||||||
.is_err()
|
.is_err()
|
||||||
@ -379,12 +381,13 @@ pub(crate) async fn create_reminder(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
// validate channel
|
// validate channel
|
||||||
let channel = ChannelId(reminder.channel).to_channel_cached(&ctx);
|
let channel = ChannelId::new(reminder.channel).to_channel_cached(&ctx.cache);
|
||||||
let channel_exists = channel.is_some();
|
let channel_exists = channel.is_some();
|
||||||
|
|
||||||
let channel_matches_guild =
|
let channel_matches_guild =
|
||||||
channel.map_or(false, |c| c.guild().map_or(false, |c| c.guild_id == guild_id));
|
channel.map_or(false, |c| c.guild(&ctx.cache).map_or(false, |c| c.id == guild_id));
|
||||||
|
|
||||||
if !channel_matches_guild || !channel_exists {
|
if !channel_matches_guild || !channel_exists {
|
||||||
warn!(
|
warn!(
|
||||||
@ -394,8 +397,10 @@ pub(crate) async fn create_reminder(
|
|||||||
|
|
||||||
return Err(json!({"error": "Channel not found"}));
|
return Err(json!({"error": "Channel not found"}));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let channel = create_database_channel(&ctx, ChannelId(reminder.channel), transaction).await;
|
let channel =
|
||||||
|
create_database_channel(&ctx, ChannelId::new(reminder.channel), transaction).await;
|
||||||
|
|
||||||
if let Err(e) = channel {
|
if let Err(e) = channel {
|
||||||
warn!("`create_database_channel` returned an error code: {:?}", e);
|
warn!("`create_database_channel` returned an error code: {:?}", e);
|
||||||
@ -590,12 +595,14 @@ pub(crate) async fn create_reminder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn create_database_channel(
|
async fn create_database_channel(
|
||||||
ctx: impl AsRef<Http>,
|
ctx: impl CacheHttp,
|
||||||
channel: ChannelId,
|
channel: ChannelId,
|
||||||
transaction: &mut Transaction<'_>,
|
transaction: &mut Transaction<'_>,
|
||||||
) -> Result<u32, crate::Error> {
|
) -> Result<u32, crate::Error> {
|
||||||
let row =
|
let row = sqlx::query!(
|
||||||
sqlx::query!("SELECT webhook_token, webhook_id FROM channels WHERE channel = ?", channel.0)
|
"SELECT webhook_token, webhook_id FROM channels WHERE channel = ?",
|
||||||
|
channel.get()
|
||||||
|
)
|
||||||
.fetch_one(transaction.executor())
|
.fetch_one(transaction.executor())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -603,19 +610,23 @@ async fn create_database_channel(
|
|||||||
Ok(row) => {
|
Ok(row) => {
|
||||||
if row.webhook_token.is_none() || row.webhook_id.is_none() {
|
if row.webhook_token.is_none() || row.webhook_id.is_none() {
|
||||||
let webhook = channel
|
let webhook = channel
|
||||||
.create_webhook_with_avatar(&ctx, "Reminder", DEFAULT_AVATAR.clone())
|
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::Serenity(e))?;
|
.map_err(|_| Error::Serenity)?;
|
||||||
|
|
||||||
|
let token = webhook.token.unwrap();
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE channels SET webhook_id = ?, webhook_token = ? WHERE channel = ?",
|
"
|
||||||
webhook.id.0,
|
UPDATE channels SET webhook_id = ?, webhook_token = ? WHERE channel = ?
|
||||||
webhook.token,
|
",
|
||||||
channel.0
|
webhook.id.get(),
|
||||||
|
token.expose_secret(),
|
||||||
|
channel.get()
|
||||||
)
|
)
|
||||||
.execute(transaction.executor())
|
.execute(transaction.executor())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::SQLx(e))?;
|
.map_err(|_| Error::SQLx)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -624,35 +635,39 @@ async fn create_database_channel(
|
|||||||
Err(sqlx::Error::RowNotFound) => {
|
Err(sqlx::Error::RowNotFound) => {
|
||||||
// create webhook
|
// create webhook
|
||||||
let webhook = channel
|
let webhook = channel
|
||||||
.create_webhook_with_avatar(&ctx, "Reminder", DEFAULT_AVATAR.clone())
|
.create_webhook(&ctx, CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::Serenity(e))?;
|
.map_err(|_| Error::Serenity)?;
|
||||||
|
|
||||||
|
let token = webhook.token.unwrap();
|
||||||
|
|
||||||
// create database entry
|
// create database entry
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"INSERT INTO channels (
|
"
|
||||||
|
INSERT INTO channels (
|
||||||
webhook_id,
|
webhook_id,
|
||||||
webhook_token,
|
webhook_token,
|
||||||
channel
|
channel
|
||||||
) VALUES (?, ?, ?)",
|
) VALUES (?, ?, ?)
|
||||||
webhook.id.0,
|
",
|
||||||
webhook.token,
|
webhook.id.get(),
|
||||||
channel.0
|
token.expose_secret(),
|
||||||
|
channel.get()
|
||||||
)
|
)
|
||||||
.execute(transaction.executor())
|
.execute(transaction.executor())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::SQLx(e))?;
|
.map_err(|_| Error::SQLx)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e) => Err(Error::SQLx(e)),
|
Err(_) => Err(Error::SQLx),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let row = sqlx::query!("SELECT id FROM channels WHERE channel = ?", channel.0)
|
let row = sqlx::query!("SELECT id FROM channels WHERE channel = ?", channel.get())
|
||||||
.fetch_one(transaction.executor())
|
.fetch_one(transaction.executor())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| Error::SQLx(e))?;
|
.map_err(|_| Error::SQLx)?;
|
||||||
|
|
||||||
Ok(row.id)
|
Ok(row.id)
|
||||||
}
|
}
|
||||||
|
@ -102,10 +102,9 @@ pub async fn discord_callback(
|
|||||||
|
|
||||||
match user_res {
|
match user_res {
|
||||||
Ok(user) => {
|
Ok(user) => {
|
||||||
let user_name = format!("{}#{}", user.name, user.discriminator);
|
let user_id = user.id.get().to_string();
|
||||||
let user_id = user.id.as_u64().to_string();
|
|
||||||
|
|
||||||
cookies.add_private(Cookie::new("username", user_name));
|
cookies.add_private(Cookie::new("username", user.name));
|
||||||
cookies.add_private(Cookie::new("userid", user_id));
|
cookies.add_private(Cookie::new("userid", user_id));
|
||||||
|
|
||||||
Ok(Redirect::to(uri!(super::return_to_same_site("dashboard"))))
|
Ok(Redirect::to(uri!(super::return_to_same_site("dashboard"))))
|
||||||
|
Loading…
Reference in New Issue
Block a user