Compare commits
No commits in common. "1eef50c9991055612637064c16766584058aed53" and "7c06906718c97cdb0868a8662d2a2f67dda3adda" have entirely different histories.
1eef50c999
...
7c06906718
|
|
@ -1,2 +0,0 @@
|
||||||
.woodpecker/
|
|
||||||
target/
|
|
||||||
|
|
@ -24,4 +24,3 @@ rust-project.json
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer
|
# End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer
|
||||||
|
|
||||||
*.db*
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
platform: 'linux/amd64'
|
|
||||||
|
|
||||||
branches:
|
|
||||||
exclude: [main]
|
|
||||||
|
|
||||||
pipeline:
|
|
||||||
build:
|
|
||||||
image: 'rust:1.69'
|
|
||||||
commands:
|
|
||||||
- cargo build --verbose
|
|
||||||
- cargo test --verbose
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
platform: 'linux/amd64'
|
|
||||||
|
|
||||||
branches:
|
|
||||||
exclude: [main]
|
|
||||||
|
|
||||||
pipeline:
|
|
||||||
clippy:
|
|
||||||
image: 'rust:1.69'
|
|
||||||
commands:
|
|
||||||
- rustup component add clippy
|
|
||||||
- cargo clippy -- --no-deps -Dwarnings
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
platform: 'linux/amd64'
|
|
||||||
branches: 'main'
|
|
||||||
|
|
||||||
pipeline:
|
|
||||||
release:
|
|
||||||
image: 'plugins/docker'
|
|
||||||
settings:
|
|
||||||
registry: 'git.rustybever.be'
|
|
||||||
repo: 'git.rustybever.be/chewing_bever/affy'
|
|
||||||
tag:
|
|
||||||
- 'latest'
|
|
||||||
mtu: 1300
|
|
||||||
secrets:
|
|
||||||
- 'docker_username'
|
|
||||||
- 'docker_password'
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
image: 'curlimages/curl'
|
|
||||||
secrets:
|
|
||||||
- 'webhook'
|
|
||||||
commands:
|
|
||||||
- curl -XPOST --fail -s "$WEBHOOK"
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
platform: 'linux/amd64'
|
|
||||||
|
|
||||||
branches:
|
|
||||||
exclude: [main]
|
|
||||||
|
|
||||||
pipeline:
|
|
||||||
lint:
|
|
||||||
image: 'rust:1.69'
|
|
||||||
commands:
|
|
||||||
- rustup component add rustfmt
|
|
||||||
- cargo fmt -- --check
|
|
||||||
|
|
@ -18,26 +18,12 @@ dependencies = [
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "affluences-cli"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"affluences-api",
|
|
||||||
"clap",
|
|
||||||
"serde_json",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "affy"
|
name = "affy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"affluences-api",
|
"affluences-api",
|
||||||
"async-minecraft-ping",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"diesel",
|
|
||||||
"diesel_migrations",
|
|
||||||
"libsqlite3-sys",
|
|
||||||
"poise",
|
"poise",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
@ -52,68 +38,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstream"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"anstyle-parse",
|
|
||||||
"anstyle-query",
|
|
||||||
"anstyle-wincon",
|
|
||||||
"colorchoice",
|
|
||||||
"is-terminal",
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-parse"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
|
|
||||||
dependencies = [
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-query"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-wincon"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-minecraft-ping"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "668b459c14dd8d9ef21e296af3f2a3651ff7dc3536e092fb0b09e528daaa6d89"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.68"
|
version = "0.1.68"
|
||||||
|
|
@ -220,48 +144,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap"
|
|
||||||
version = "4.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
|
|
||||||
dependencies = [
|
|
||||||
"clap_builder",
|
|
||||||
"clap_derive",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_builder"
|
|
||||||
version = "4.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
|
|
||||||
dependencies = [
|
|
||||||
"anstream",
|
|
||||||
"anstyle",
|
|
||||||
"bitflags",
|
|
||||||
"clap_lex",
|
|
||||||
"strsim",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_derive"
|
|
||||||
version = "4.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.15",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_lex"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
|
@ -272,12 +154,6 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colorchoice"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
|
@ -416,40 +292,6 @@ dependencies = [
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "diesel"
|
|
||||||
version = "2.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72eb77396836a4505da85bae0712fa324b74acfe1876d7c2f7e694ef3d0ee373"
|
|
||||||
dependencies = [
|
|
||||||
"diesel_derives",
|
|
||||||
"libsqlite3-sys",
|
|
||||||
"r2d2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "diesel_derives"
|
|
||||||
version = "2.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ad74fdcf086be3d4fdd142f67937678fe60ed431c3b2f08599e7687269410c4"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "diesel_migrations"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e9ae22beef5e9d6fab9225ddb073c1c6c1a7a6ded5019d5da11d1e5c5adc34e2"
|
|
||||||
dependencies = [
|
|
||||||
"diesel",
|
|
||||||
"migrations_internals",
|
|
||||||
"migrations_macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
|
@ -469,27 +311,6 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
|
||||||
dependencies = [
|
|
||||||
"errno-dragonfly",
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno-dragonfly"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
|
|
@ -638,12 +459,6 @@ version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
|
|
@ -653,12 +468,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
|
|
@ -780,35 +589,12 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "io-lifetimes"
|
|
||||||
version = "1.0.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.3.1",
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.7.2"
|
version = "2.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
|
checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "is-terminal"
|
|
||||||
version = "0.4.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi 0.3.1",
|
|
||||||
"io-lifetimes",
|
|
||||||
"rustix",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|
@ -830,17 +616,6 @@ version = "0.2.144"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libsqlite3-sys"
|
|
||||||
version = "0.26.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"pkg-config",
|
|
||||||
"vcpkg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "link-cplusplus"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
|
@ -850,12 +625,6 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linux-raw-sys"
|
|
||||||
version = "0.3.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
|
@ -881,27 +650,6 @@ version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "migrations_internals"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c493c09323068c01e54c685f7da41a9ccf9219735c3766fbfd6099806ea08fbc"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"toml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "migrations_macros"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a8ff27a350511de30cdabb77147501c36ef02e0451d957abea2f30caffb2b58"
|
|
||||||
dependencies = [
|
|
||||||
"migrations_internals",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mime"
|
name = "mime"
|
||||||
version = "0.3.17"
|
version = "0.3.17"
|
||||||
|
|
@ -964,7 +712,7 @@ version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi 0.2.6",
|
"hermit-abi",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1024,12 +772,6 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pkg-config"
|
|
||||||
version = "0.3.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "poise"
|
name = "poise"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
|
|
@ -1067,30 +809,6 @@ version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error-attr",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error-attr"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.56"
|
version = "1.0.56"
|
||||||
|
|
@ -1109,17 +827,6 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r2d2"
|
|
||||||
version = "0.8.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"parking_lot",
|
|
||||||
"scheduled-thread-pool",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
|
@ -1231,20 +938,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustix"
|
|
||||||
version = "0.37.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"errno",
|
|
||||||
"io-lifetimes",
|
|
||||||
"libc",
|
|
||||||
"linux-raw-sys",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.20.8"
|
version = "0.20.8"
|
||||||
|
|
@ -1278,15 +971,6 @@ version = "1.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scheduled-thread-pool"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
|
||||||
dependencies = [
|
|
||||||
"parking_lot",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -1610,15 +1294,6 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml"
|
|
||||||
version = "0.5.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
|
@ -1757,12 +1432,6 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8parse"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
|
@ -1772,12 +1441,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vcpkg"
|
|
||||||
version = "0.2.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
|
||||||
24
Cargo.toml
24
Cargo.toml
|
|
@ -2,27 +2,5 @@
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"affluences-api",
|
"affluences-api",
|
||||||
"affluences-cli",
|
"bot",
|
||||||
]
|
]
|
||||||
# Don't build the CLI tool by default
|
|
||||||
default-members = [
|
|
||||||
".",
|
|
||||||
"affluences-api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "affy"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
affluences-api = { path = "./affluences-api" }
|
|
||||||
tokio = { version = "1.28.1", features = ["full"] }
|
|
||||||
chrono = "*"
|
|
||||||
uuid = "*"
|
|
||||||
poise = "0.5.5"
|
|
||||||
async-minecraft-ping = "0.8.0"
|
|
||||||
diesel = { version = "2.0.4", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "r2d2"] }
|
|
||||||
diesel_migrations = { version = "2.0.0", features = [ "sqlite" ] }
|
|
||||||
# Force sqlite3 to be bundled, allowing for a fully static binary
|
|
||||||
libsqlite3-sys = { version = "*", features = ["bundled"] }
|
|
||||||
|
|
|
||||||
35
Dockerfile
35
Dockerfile
|
|
@ -1,35 +0,0 @@
|
||||||
FROM rust:1.69-alpine AS builder
|
|
||||||
|
|
||||||
ARG DI_VER=1.2.5
|
|
||||||
|
|
||||||
RUN apk update && \
|
|
||||||
apk add --no-cache build-base curl
|
|
||||||
|
|
||||||
WORKDIR /build
|
|
||||||
|
|
||||||
# Build dumb-init
|
|
||||||
RUN curl -Lo - "https://github.com/Yelp/dumb-init/archive/refs/tags/v${DI_VER}.tar.gz" | tar -xzf - && \
|
|
||||||
cd "dumb-init-${DI_VER}" && \
|
|
||||||
make SHELL=/bin/sh && \
|
|
||||||
mv dumb-init ..
|
|
||||||
|
|
||||||
WORKDIR /build/affy
|
|
||||||
|
|
||||||
COPY ./ ./
|
|
||||||
|
|
||||||
RUN cargo build --release && \
|
|
||||||
[ "$(readelf -d target/release/affy | grep NEEDED | wc -l)" = 0 ]
|
|
||||||
|
|
||||||
|
|
||||||
FROM busybox:1.36.0
|
|
||||||
|
|
||||||
COPY --from=builder /build/dumb-init /build/affy/target/release/affy /bin/
|
|
||||||
|
|
||||||
WORKDIR /data
|
|
||||||
|
|
||||||
ENV TZ=Europe/Brussels
|
|
||||||
|
|
||||||
USER www-data:www-data
|
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/dumb-init", "--"]
|
|
||||||
CMD ["/bin/affy"]
|
|
||||||
|
|
@ -1,45 +1,3 @@
|
||||||
# Affluences API Reference
|
|
||||||
|
|
||||||
## Terminology
|
|
||||||
|
|
||||||
* `site`
|
|
||||||
* General name for a location/institution
|
|
||||||
* Defined by a UUID and a slug, e.g. `ghent-university`
|
|
||||||
* Can contain one more or more sites, e.g.
|
|
||||||
`ghent-university-library-book-tower` is a child of `ghent-university`
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
The API checks for browser user agents, so your requests should include a valid
|
|
||||||
user agent of a modern browser.
|
|
||||||
|
|
||||||
## API Routes
|
|
||||||
|
|
||||||
### Search for sites
|
|
||||||
|
|
||||||
`GET https://api.affluences.com/app/v3/sites`
|
|
||||||
|
|
||||||
**Body format**
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"selected_categories": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"page": 0,
|
|
||||||
"search_query": "university of ghent"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response format**
|
|
||||||
|
|
||||||
`Data<Vec<SiteData>>`
|
|
||||||
|
|
||||||
### Retrieve time table for a given site
|
|
||||||
|
|
||||||
`GET https://reservation.affluences.com/api/sites/4737e57a-ee05-4f7b-901a-7bb541eeb297`
|
|
||||||
|
|
||||||
|
|
||||||
curl -L 'https://api.affluences.com/app/v3/sites/ghent-university' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/112.0'
|
curl -L 'https://api.affluences.com/app/v3/sites/ghent-university' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/112.0'
|
||||||
|
|
||||||
returnt lijst van alle bibliotheken op ugent, inclusief de uuids die dan nodig zijn om de specifieke requests te sturen naar die bib zijn stuff
|
returnt lijst van alle bibliotheken op ugent, inclusief de uuids die dan nodig zijn om de specifieke requests te sturen naar die bib zijn stuff
|
||||||
|
|
|
||||||
|
|
@ -20,23 +20,6 @@ impl AffluencesClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search(&self, query: String) -> reqwest::Result<SiteSearchResponse> {
|
|
||||||
let url = "https://api.affluences.com/app/v3/sites";
|
|
||||||
let body = SiteSearch {
|
|
||||||
search_query: query,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(self
|
|
||||||
.client
|
|
||||||
.post(url)
|
|
||||||
.json(&body)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json::<Data<SiteSearchResponse>>()
|
|
||||||
.await?
|
|
||||||
.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn available(
|
pub async fn available(
|
||||||
&self,
|
&self,
|
||||||
site_id: uuid::Uuid,
|
site_id: uuid::Uuid,
|
||||||
|
|
@ -89,9 +72,3 @@ impl AffluencesClient {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AffluencesClient {
|
|
||||||
fn default() -> Self {
|
|
||||||
AffluencesClient::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use super::hh_mm_time_format;
|
use super::hh_mm_time_format;
|
||||||
use chrono::{Duration, NaiveTime};
|
use chrono::NaiveTime;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Clone, Copy)]
|
#[derive(Deserialize, Debug, Clone, Copy)]
|
||||||
|
|
@ -41,45 +41,3 @@ pub struct Resource {
|
||||||
pub slots_state: u32,
|
pub slots_state: u32,
|
||||||
pub hours: Vec<HourBlock>,
|
pub hours: Vec<HourBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resource {
|
|
||||||
pub fn condensed_hours(&self) -> Vec<(&HourBlock, Duration)> {
|
|
||||||
if self.hours.is_empty() {
|
|
||||||
return Default::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut start_hour = self.hours.first().unwrap();
|
|
||||||
let mut duration = Duration::minutes(start_hour.granularity.into());
|
|
||||||
let mut out: Vec<(&HourBlock, Duration)> = Default::default();
|
|
||||||
|
|
||||||
for hour in self.hours.iter().skip(1) {
|
|
||||||
if hour.state == start_hour.state {
|
|
||||||
duration = duration + Duration::minutes(hour.granularity.into());
|
|
||||||
} else {
|
|
||||||
out.push((start_hour, duration));
|
|
||||||
start_hour = hour;
|
|
||||||
duration = Duration::minutes(hour.granularity.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push((start_hour, duration));
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn condensed_available_hours(&self) -> Vec<(&HourBlock, Duration)> {
|
|
||||||
self.condensed_hours()
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(hour, _)| hour.state == 1)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether a slot with the given state and time bounds is present in the list of
|
|
||||||
/// hours.
|
|
||||||
pub fn has_slot(&self, start_time: NaiveTime, end_time: NaiveTime, state: u32) -> bool {
|
|
||||||
self.condensed_hours()
|
|
||||||
.into_iter()
|
|
||||||
.filter(|(block, _)| block.state == state)
|
|
||||||
.any(|(block, duration)| start_time >= block.hour && end_time <= block.hour + duration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use chrono::NaiveTime;
|
use chrono::NaiveTime;
|
||||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||||
|
|
||||||
const FORMAT: &str = "%H:%M";
|
const FORMAT: &'static str = "%H:%M";
|
||||||
|
|
||||||
pub fn serialize<S>(time: &NaiveTime, serializer: S) -> Result<S::Ok, S::Error>
|
pub fn serialize<S>(time: &NaiveTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct Data<T> {
|
pub struct Data<T> {
|
||||||
pub data: T,
|
pub data: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataCategory {
|
pub struct SiteDataCategory {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub name_plural: String,
|
pub name_plural: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataLocationCoordinates {
|
pub struct SiteDataLocationCoordinates {
|
||||||
pub latitude: f64,
|
pub latitude: f64,
|
||||||
pub longitude: f64,
|
pub longitude: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataLocationAddress {
|
pub struct SiteDataLocationAddress {
|
||||||
pub route: String,
|
pub route: String,
|
||||||
pub city: String,
|
pub city: String,
|
||||||
|
|
@ -27,47 +27,47 @@ pub struct SiteDataLocationAddress {
|
||||||
pub country_code: String,
|
pub country_code: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataLocation {
|
pub struct SiteDataLocation {
|
||||||
pub coordinates: SiteDataLocationCoordinates,
|
pub coordinates: SiteDataLocationCoordinates,
|
||||||
pub address: SiteDataLocationAddress,
|
pub address: SiteDataLocationAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataForecast {
|
pub struct SiteDataForecast {
|
||||||
pub opened: bool,
|
pub opened: bool,
|
||||||
pub occupancy: Option<u32>,
|
pub occupancy: u32,
|
||||||
// waiting_time
|
// waiting_time
|
||||||
pub waiting_time_overflow: bool,
|
pub waiting_time_overflow: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataNotice {
|
pub struct SiteDataNotice {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataService {
|
pub struct SiteDataService {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataInfo {
|
pub struct SiteDataInfo {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteDataStatus {
|
pub struct SiteDataStatus {
|
||||||
pub state: String,
|
pub state: String,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub color: String,
|
pub color: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct SiteData {
|
pub struct SiteData {
|
||||||
pub id: uuid::Uuid,
|
pub id: uuid::Uuid,
|
||||||
pub slug: String,
|
pub slug: String,
|
||||||
|
|
@ -104,15 +104,3 @@ pub struct SiteData {
|
||||||
#[serde(rename = "publicationStatus")]
|
#[serde(rename = "publicationStatus")]
|
||||||
pub publication_status: String,
|
pub publication_status: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
pub struct SiteSearch {
|
|
||||||
pub search_query: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct SiteSearchResponse {
|
|
||||||
pub page: u32,
|
|
||||||
pub max_size: u32,
|
|
||||||
pub results: Vec<SiteData>,
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
use affluences_api::AffluencesClient;
|
|
||||||
use clap::{Parser, Subcommand};
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
|
||||||
#[command(author, version, about, long_about = None)]
|
|
||||||
struct Cli {
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: Option<Commands>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
enum Commands {
|
|
||||||
/// does testing things
|
|
||||||
SearchSite { query: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
let cli = Cli::parse();
|
|
||||||
let client = AffluencesClient::new();
|
|
||||||
|
|
||||||
match &cli.command {
|
|
||||||
Some(Commands::SearchSite { query }) => {
|
|
||||||
let res = client.search(query.to_string()).await.unwrap();
|
|
||||||
let s = serde_json::to_string_pretty(&res).unwrap();
|
|
||||||
println!("{}", s);
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "affluences-cli"
|
name = "affy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
affluences-api = { path = "../affluences-api" }
|
affluences-api = { path = "../affluences-api" }
|
||||||
clap = { version = "4.2.7", features = ["derive"] }
|
|
||||||
serde_json = "1.0.96"
|
|
||||||
tokio = { version = "1.28.1", features = ["full"] }
|
tokio = { version = "1.28.1", features = ["full"] }
|
||||||
|
chrono = "*"
|
||||||
|
uuid = "*"
|
||||||
|
poise = "0.5.5"
|
||||||
|
|
@ -0,0 +1,160 @@
|
||||||
|
use crate::{Context, Error};
|
||||||
|
use affluences_api::HourBlock;
|
||||||
|
use chrono::{Duration, NaiveDate};
|
||||||
|
use uuid::{uuid, Uuid};
|
||||||
|
|
||||||
|
const STERRE_BIB_ID: Uuid = uuid!("4737e57a-ee05-4f7b-901a-7bb541eeb297");
|
||||||
|
const TIME_FORMAT: &'static str = "%H:%M";
|
||||||
|
|
||||||
|
/// Show this help menu
|
||||||
|
#[poise::command(prefix_command, track_edits, slash_command)]
|
||||||
|
pub async fn help(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "Specific command to show help about"]
|
||||||
|
#[autocomplete = "poise::builtins::autocomplete_command"]
|
||||||
|
command: Option<String>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
poise::builtins::help(
|
||||||
|
ctx,
|
||||||
|
command.as_deref(),
|
||||||
|
poise::builtins::HelpConfiguration {
|
||||||
|
extra_text_at_bottom: "This is an example bot made to showcase features of my custom Discord bot framework",
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vote for something
|
||||||
|
///
|
||||||
|
/// Enter `~vote pumpkin` to vote for pumpkins
|
||||||
|
#[poise::command(prefix_command, slash_command)]
|
||||||
|
pub async fn vote(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "What to vote for"] choice: String,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Lock the Mutex in a block {} so the Mutex isn't locked across an await point
|
||||||
|
let num_votes = {
|
||||||
|
let mut hash_map = ctx.data().votes.lock().unwrap();
|
||||||
|
let num_votes = hash_map.entry(choice.clone()).or_default();
|
||||||
|
*num_votes += 1;
|
||||||
|
*num_votes
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = format!("Successfully voted for {choice}. {choice} now has {num_votes} votes!");
|
||||||
|
ctx.say(response).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve number of votes
|
||||||
|
///
|
||||||
|
/// Retrieve the number of votes either in general, or for a specific choice:
|
||||||
|
/// ```
|
||||||
|
/// ~getvotes
|
||||||
|
/// ~getvotes pumpkin
|
||||||
|
/// ```
|
||||||
|
#[poise::command(prefix_command, track_edits, aliases("votes"), slash_command)]
|
||||||
|
pub async fn getvotes(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "Choice to retrieve votes for"] choice: Option<String>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if let Some(choice) = choice {
|
||||||
|
let num_votes = *ctx.data().votes.lock().unwrap().get(&choice).unwrap_or(&0);
|
||||||
|
let response = match num_votes {
|
||||||
|
0 => format!("Nobody has voted for {} yet", choice),
|
||||||
|
_ => format!("{} people have voted for {}", num_votes, choice),
|
||||||
|
};
|
||||||
|
ctx.say(response).await?;
|
||||||
|
} else {
|
||||||
|
let mut response = String::new();
|
||||||
|
for (choice, num_votes) in ctx.data().votes.lock().unwrap().iter() {
|
||||||
|
response += &format!("{}: {} votes", choice, num_votes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.is_empty() {
|
||||||
|
response += "Nobody has voted for anything yet :(";
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.say(response).await?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List available timeslots for day
|
||||||
|
#[poise::command(prefix_command, slash_command)]
|
||||||
|
pub async fn available(ctx: Context<'_>, date: NaiveDate) -> Result<(), Error> {
|
||||||
|
let client = &ctx.data().client;
|
||||||
|
let resources = client.available(STERRE_BIB_ID, date, 1).await?;
|
||||||
|
let mut fields: Vec<(String, String, bool)> = Default::default();
|
||||||
|
|
||||||
|
for resource in &resources {
|
||||||
|
if resource.hours.len() == 0 {
|
||||||
|
fields.push((
|
||||||
|
resource.resource_name.clone(),
|
||||||
|
"Nothing available.".to_string(),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut lines: Vec<String> = Default::default();
|
||||||
|
|
||||||
|
let mut start_hour_opt: Option<&HourBlock> = None;
|
||||||
|
let mut duration = Duration::seconds(0);
|
||||||
|
|
||||||
|
for hour in &resource.hours {
|
||||||
|
if let Some(start_hour) = start_hour_opt {
|
||||||
|
if hour.state == 1 {
|
||||||
|
duration = duration + Duration::minutes(hour.granularity.into());
|
||||||
|
} else {
|
||||||
|
let end_hour = start_hour.hour + duration;
|
||||||
|
lines.push(format!(
|
||||||
|
"{} - {} ({:02}:{:02})",
|
||||||
|
start_hour.hour.format(TIME_FORMAT),
|
||||||
|
end_hour.format(TIME_FORMAT),
|
||||||
|
duration.num_hours(),
|
||||||
|
duration.num_minutes() % 60
|
||||||
|
));
|
||||||
|
start_hour_opt = None;
|
||||||
|
}
|
||||||
|
} else if hour.state == 1 {
|
||||||
|
start_hour_opt = Some(hour);
|
||||||
|
duration = Duration::minutes(hour.granularity.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print final entry if present
|
||||||
|
if let Some(start_hour) = start_hour_opt {
|
||||||
|
let end_hour = start_hour.hour + duration;
|
||||||
|
lines.push(format!(
|
||||||
|
"{} - {} ({:02}:{:02})",
|
||||||
|
start_hour.hour.format(TIME_FORMAT),
|
||||||
|
end_hour.format(TIME_FORMAT),
|
||||||
|
duration.num_hours(),
|
||||||
|
duration.num_minutes() % 60
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.push((resource.resource_name.clone(), lines.join("\n"), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.send(|f| {
|
||||||
|
f.embed(|e| {
|
||||||
|
e.description(format!("Available booking dates for {}.", date))
|
||||||
|
.fields(fields)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a reservation
|
||||||
|
// #[poise::command(prefix_command, slash_command)]
|
||||||
|
// pub async fn reserve(
|
||||||
|
// ctx: Context<'_>,
|
||||||
|
// date: NaiveDate,
|
||||||
|
// ) -> Result<(), Error> {
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
mod commands;
|
mod commands;
|
||||||
mod db;
|
|
||||||
|
|
||||||
use affluences_api::AffluencesClient;
|
use affluences_api::AffluencesClient;
|
||||||
use diesel::r2d2::{ConnectionManager, Pool};
|
|
||||||
use diesel::sqlite::SqliteConnection;
|
|
||||||
use poise::serenity_prelude as serenity;
|
use poise::serenity_prelude as serenity;
|
||||||
use std::{env::var, time::Duration};
|
use std::{collections::HashMap, env::var, sync::Mutex, time::Duration};
|
||||||
|
|
||||||
// Types used by all command functions
|
// Types used by all command functions
|
||||||
type Error = Box<dyn std::error::Error + Send + Sync>;
|
type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
|
|
@ -13,8 +10,8 @@ type Context<'a> = poise::Context<'a, Data, Error>;
|
||||||
|
|
||||||
// Custom user data passed to all command functions
|
// Custom user data passed to all command functions
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
|
votes: Mutex<HashMap<String, u32>>,
|
||||||
client: AffluencesClient,
|
client: AffluencesClient,
|
||||||
pool: Pool<ConnectionManager<SqliteConnection>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
|
async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
|
||||||
|
|
@ -39,7 +36,12 @@ async fn main() {
|
||||||
// FrameworkOptions contains all of poise's configuration option in one struct
|
// FrameworkOptions contains all of poise's configuration option in one struct
|
||||||
// Every option can be omitted to use its default value
|
// Every option can be omitted to use its default value
|
||||||
let options = poise::FrameworkOptions {
|
let options = poise::FrameworkOptions {
|
||||||
commands: commands::commands(),
|
commands: vec![
|
||||||
|
commands::help(),
|
||||||
|
commands::vote(),
|
||||||
|
commands::getvotes(),
|
||||||
|
commands::available(),
|
||||||
|
],
|
||||||
prefix_options: poise::PrefixFrameworkOptions {
|
prefix_options: poise::PrefixFrameworkOptions {
|
||||||
prefix: Some("~".into()),
|
prefix: Some("~".into()),
|
||||||
edit_tracker: Some(poise::EditTracker::for_timespan(Duration::from_secs(3600))),
|
edit_tracker: Some(poise::EditTracker::for_timespan(Duration::from_secs(3600))),
|
||||||
|
|
@ -84,8 +86,6 @@ async fn main() {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let pool = db::initialize_pool("affy.db").expect("Failed to initialize database.");
|
|
||||||
|
|
||||||
poise::Framework::builder()
|
poise::Framework::builder()
|
||||||
.token(
|
.token(
|
||||||
var("DISCORD_TOKEN")
|
var("DISCORD_TOKEN")
|
||||||
|
|
@ -96,8 +96,8 @@ async fn main() {
|
||||||
println!("Logged in as {}", _ready.user.name);
|
println!("Logged in as {}", _ready.user.name);
|
||||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||||
Ok(Data {
|
Ok(Data {
|
||||||
|
votes: Mutex::new(HashMap::new()),
|
||||||
client: AffluencesClient::new(),
|
client: AffluencesClient::new(),
|
||||||
pool,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# For documentation on how to configure this file,
|
|
||||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
|
||||||
|
|
||||||
[print_schema]
|
|
||||||
file = "src/db/schema.rs"
|
|
||||||
|
|
||||||
[migrations_directory]
|
|
||||||
dir = "migrations"
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
-- This file should undo anything in `up.sql`
|
|
||||||
DROP TABLE users;
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
-- Your SQL goes here
|
|
||||||
CREATE TABLE users (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
||||||
discord_id UNSIGNED BIG INT NOT NULL,
|
|
||||||
guild_id UNSIGNED BIG INT NOT NULL,
|
|
||||||
email TEXT UNIQUE NOT NULL,
|
|
||||||
first_name TEXT NOT NULL,
|
|
||||||
last_name TEXT NOT NULL,
|
|
||||||
|
|
||||||
UNIQUE(discord_id, guild_id)
|
|
||||||
);
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
use crate::commands::{EmbedField, HumanNaiveDate};
|
|
||||||
use crate::db::users::User;
|
|
||||||
use crate::{Context, Error};
|
|
||||||
|
|
||||||
use affluences_api::{Reservation, Resource};
|
|
||||||
use chrono::{NaiveDate, NaiveTime};
|
|
||||||
use uuid::{uuid, Uuid};
|
|
||||||
|
|
||||||
const STERRE_BIB_ID: Uuid = uuid!("4737e57a-ee05-4f7b-901a-7bb541eeb297");
|
|
||||||
const TIME_FORMAT: &str = "%H:%M";
|
|
||||||
|
|
||||||
/// Parent command for all bib-related commands
|
|
||||||
///
|
|
||||||
/// Commands for reservating study rooms in the bib.
|
|
||||||
#[poise::command(prefix_command, slash_command, subcommands("available", "book"))]
|
|
||||||
pub async fn bib(_ctx: Context<'_>) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resource_to_embed_field(resource: Resource) -> EmbedField {
|
|
||||||
let available_hours = resource.condensed_available_hours();
|
|
||||||
let title = format!("{} ({}p)", resource.resource_name, resource.capacity);
|
|
||||||
|
|
||||||
if available_hours.is_empty() {
|
|
||||||
(title, "Nothing available.".to_string(), false)
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
title,
|
|
||||||
available_hours
|
|
||||||
.into_iter()
|
|
||||||
.map(|(start_block, duration)| {
|
|
||||||
format!(
|
|
||||||
"{} - {} ({:02}:{:02})",
|
|
||||||
start_block.hour.format(TIME_FORMAT),
|
|
||||||
(start_block.hour + duration).format(TIME_FORMAT),
|
|
||||||
duration.num_hours(),
|
|
||||||
duration.num_minutes() % 60
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n"),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List available timeslots for day
|
|
||||||
#[poise::command(prefix_command, slash_command)]
|
|
||||||
pub async fn available(ctx: Context<'_>, date: HumanNaiveDate) -> Result<(), Error> {
|
|
||||||
let client = &ctx.data().client;
|
|
||||||
let mut resources = client
|
|
||||||
.available(STERRE_BIB_ID, date.clone().into(), 1)
|
|
||||||
.await?;
|
|
||||||
// Cloning here isn't super efficient, but this list only consists of a handful of elements so
|
|
||||||
// it's fine
|
|
||||||
resources.sort_by_key(|k| k.resource_name.clone());
|
|
||||||
|
|
||||||
ctx.send(|f| {
|
|
||||||
f.embed(|e| {
|
|
||||||
e.description(format!(
|
|
||||||
"Available booking dates for {}.",
|
|
||||||
Into::<NaiveDate>::into(date)
|
|
||||||
))
|
|
||||||
.fields(
|
|
||||||
resources
|
|
||||||
.into_iter()
|
|
||||||
.map(resource_to_embed_field)
|
|
||||||
.collect::<Vec<EmbedField>>(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[poise::command(prefix_command, slash_command)]
|
|
||||||
pub async fn book(
|
|
||||||
ctx: Context<'_>,
|
|
||||||
date: HumanNaiveDate,
|
|
||||||
start_time: NaiveTime,
|
|
||||||
end_time: NaiveTime,
|
|
||||||
#[description = "Minimum seats the room should have."] capacity: Option<u32>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
if ctx.guild_id().is_none() {
|
|
||||||
ctx.say("You have to send this message from a guild.")
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let guild_id = ctx.guild_id().unwrap();
|
|
||||||
let discord_id = ctx.author().id.0 as i64;
|
|
||||||
|
|
||||||
let user = {
|
|
||||||
let mut conn = ctx.data().pool.get()?;
|
|
||||||
User::get(&mut conn, guild_id.into(), discord_id)?
|
|
||||||
};
|
|
||||||
|
|
||||||
if user.is_none() {
|
|
||||||
ctx.say("You have to register before being able to book reservations.")
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let user = user.unwrap();
|
|
||||||
|
|
||||||
let client = &ctx.data().client;
|
|
||||||
let resources = client
|
|
||||||
.available(STERRE_BIB_ID, date.clone().into(), 1)
|
|
||||||
.await?;
|
|
||||||
let chosen_resource = resources
|
|
||||||
.iter()
|
|
||||||
.filter(|r| capacity.is_none() || capacity.unwrap() <= r.capacity)
|
|
||||||
.find(|r| r.has_slot(start_time, end_time, 1));
|
|
||||||
|
|
||||||
if let Some(chosen_resource) = chosen_resource {
|
|
||||||
let reservation = Reservation {
|
|
||||||
auth_type: None,
|
|
||||||
email: user.email.clone(),
|
|
||||||
date: date.clone().into(),
|
|
||||||
start_time,
|
|
||||||
end_time,
|
|
||||||
note: "coworking space".to_string(),
|
|
||||||
user_firstname: user.first_name,
|
|
||||||
user_lastname: user.last_name,
|
|
||||||
user_phone: None,
|
|
||||||
person_count: capacity.unwrap_or(1),
|
|
||||||
};
|
|
||||||
|
|
||||||
client
|
|
||||||
.make_reservation(chosen_resource.resource_id, &reservation)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
ctx.send(|f| {
|
|
||||||
f.embed(|e| {
|
|
||||||
e.description("A new reservation has been made.")
|
|
||||||
.field("when", format!("{} {} - {}", Into::<NaiveDate>::into(date), start_time.format(TIME_FORMAT), end_time.format(TIME_FORMAT)), false)
|
|
||||||
.field("where", &chosen_resource.resource_name, false)
|
|
||||||
.footer(|ft| ft.text(
|
|
||||||
format!("A confirmation mail has been sent to {}. Please check your email and confirm your reservation within two hours.", user.email)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
ctx.say("No slot is available within your requested bounds.")
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let resources = if let Some(capacity) = capacity {
|
|
||||||
// resources.filter(|r| r.capacity >= capacity)
|
|
||||||
// };
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a reservation
|
|
||||||
// #[poise::command(prefix_command, slash_command)]
|
|
||||||
// pub async fn reserve(
|
|
||||||
// ctx: Context<'_>,
|
|
||||||
// date: NaiveDate,
|
|
||||||
// ) -> Result<(), Error> {
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
use crate::{Context, Error};
|
|
||||||
use async_minecraft_ping::ServerDescription;
|
|
||||||
|
|
||||||
const DEFAULT_SERVER: &str = "rustybever.be";
|
|
||||||
|
|
||||||
/// Parent command for Minecraft-related actions.
|
|
||||||
///
|
|
||||||
/// Minecraft-related commands
|
|
||||||
#[poise::command(prefix_command, slash_command, subcommands("ping"))]
|
|
||||||
pub async fn mc(_ctx: Context<'_>) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ping a minecraft server; defaults to our server.
|
|
||||||
#[poise::command(prefix_command, slash_command)]
|
|
||||||
pub async fn ping(
|
|
||||||
ctx: Context<'_>,
|
|
||||||
#[description = "Address of the server"] address: Option<String>,
|
|
||||||
#[description = "Port the server runs on"] port: Option<u16>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let mut full_name = address.unwrap_or(DEFAULT_SERVER.to_string());
|
|
||||||
let mut builder = async_minecraft_ping::ConnectionConfig::build(&full_name);
|
|
||||||
|
|
||||||
if let Some(port) = port {
|
|
||||||
builder = builder.with_port(port);
|
|
||||||
full_name += &format!(":{}", port);
|
|
||||||
}
|
|
||||||
|
|
||||||
let conn = builder.connect().await?;
|
|
||||||
let status = conn.status().await?.status;
|
|
||||||
|
|
||||||
let description = match status.description {
|
|
||||||
ServerDescription::Plain(s) => s,
|
|
||||||
ServerDescription::Object { text } => text,
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.send(|f| {
|
|
||||||
f.embed(|e| {
|
|
||||||
e.description(format!("Server information for {}", full_name))
|
|
||||||
.field("version", status.version.name, false)
|
|
||||||
.field("description", description, false)
|
|
||||||
.field(
|
|
||||||
"players",
|
|
||||||
format!(
|
|
||||||
"{} of {} player(s) online",
|
|
||||||
status.players.online, status.players.max
|
|
||||||
),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
mod bib;
|
|
||||||
mod minecraft;
|
|
||||||
mod users;
|
|
||||||
|
|
||||||
use chrono::Datelike;
|
|
||||||
use core::str;
|
|
||||||
|
|
||||||
use crate::{Context, Data, Error};
|
|
||||||
|
|
||||||
type EmbedField = (String, String, bool);
|
|
||||||
|
|
||||||
const DAY_TERMS: [&str; 3] = ["today", "tomorrow", "overmorrow"];
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct HumanNaiveDate(chrono::NaiveDate);
|
|
||||||
|
|
||||||
impl str::FromStr for HumanNaiveDate {
|
|
||||||
type Err = chrono::format::ParseError;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> chrono::format::ParseResult<Self> {
|
|
||||||
if let Some(days_to_add) = DAY_TERMS.iter().position(|term| s.to_lowercase() == *term) {
|
|
||||||
let now = chrono::Local::now().naive_local().date();
|
|
||||||
|
|
||||||
// days_to_add will never be greater than 2
|
|
||||||
Ok(HumanNaiveDate(
|
|
||||||
now + chrono::Duration::days(days_to_add.try_into().unwrap()),
|
|
||||||
))
|
|
||||||
} else if let Ok(weekday) = s.parse::<chrono::Weekday>() {
|
|
||||||
let now = chrono::Local::now().naive_local().date();
|
|
||||||
let cur_day = now.weekday();
|
|
||||||
let cur_day_index = cur_day.num_days_from_monday();
|
|
||||||
let parsed_day_index = weekday.num_days_from_monday();
|
|
||||||
|
|
||||||
let days_to_add = if cur_day_index <= parsed_day_index {
|
|
||||||
parsed_day_index - cur_day_index
|
|
||||||
} else {
|
|
||||||
7 - (cur_day_index - parsed_day_index)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(HumanNaiveDate(
|
|
||||||
now + chrono::Duration::days(days_to_add.into()),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
chrono::NaiveDate::from_str(s).map(HumanNaiveDate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<HumanNaiveDate> for chrono::NaiveDate {
|
|
||||||
fn from(val: HumanNaiveDate) -> chrono::NaiveDate {
|
|
||||||
val.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn commands() -> Vec<poise::structs::Command<Data, Error>> {
|
|
||||||
vec![
|
|
||||||
help(),
|
|
||||||
bib::bib(),
|
|
||||||
minecraft::mc(),
|
|
||||||
users::register(),
|
|
||||||
users::registered(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Show this help menu
|
|
||||||
#[poise::command(prefix_command, track_edits, slash_command)]
|
|
||||||
pub async fn help(
|
|
||||||
ctx: Context<'_>,
|
|
||||||
#[description = "Specific command to show help about"]
|
|
||||||
#[autocomplete = "poise::builtins::autocomplete_command"]
|
|
||||||
command: Option<String>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
poise::builtins::help(
|
|
||||||
ctx,
|
|
||||||
command.as_deref(),
|
|
||||||
poise::builtins::HelpConfiguration {
|
|
||||||
extra_text_at_bottom: "Brought to you by Doofenshmirtz Evil Incorporated.",
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
use crate::db::users::{NewUser, User};
|
|
||||||
use crate::{Context, Error};
|
|
||||||
use diesel::RunQueryDsl;
|
|
||||||
|
|
||||||
#[poise::command(prefix_command, slash_command)]
|
|
||||||
pub async fn register(
|
|
||||||
ctx: Context<'_>,
|
|
||||||
first_name: String,
|
|
||||||
last_name: String,
|
|
||||||
email: String,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
if let Some(guild_id) = ctx.guild_id() {
|
|
||||||
let discord_id = ctx.author().id.0 as i64;
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut conn = ctx.data().pool.get()?;
|
|
||||||
|
|
||||||
if User::get(&mut conn, guild_id.into(), discord_id)?.is_some() {
|
|
||||||
ctx.say("You've already been registered.").await?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_user = NewUser {
|
|
||||||
discord_id,
|
|
||||||
guild_id: guild_id.into(),
|
|
||||||
first_name,
|
|
||||||
last_name,
|
|
||||||
email,
|
|
||||||
};
|
|
||||||
|
|
||||||
new_user.insert(&mut conn)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.say("You have been registered.").await?;
|
|
||||||
} else {
|
|
||||||
ctx.say("You have to send this message from a guild.")
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[poise::command(prefix_command, slash_command)]
|
|
||||||
pub async fn registered(ctx: Context<'_>) -> Result<(), Error> {
|
|
||||||
if let Some(guild_id) = ctx.guild_id() {
|
|
||||||
let users = {
|
|
||||||
let mut conn = ctx.data().pool.get()?;
|
|
||||||
User::by_guild_id(guild_id.into()).load(&mut conn)?
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.send(|f| {
|
|
||||||
f.embed(|e| {
|
|
||||||
e.description("Registered users").fields(
|
|
||||||
users
|
|
||||||
.into_iter()
|
|
||||||
.map(|u| (format!("{} {}", u.first_name, u.last_name), u.email, false)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
ctx.say("You are not in a guild.").await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
mod schema;
|
|
||||||
pub mod users;
|
|
||||||
|
|
||||||
use diesel::connection::SimpleConnection;
|
|
||||||
use diesel::r2d2::{ConnectionManager, Pool};
|
|
||||||
use diesel::sqlite::{Sqlite, SqliteConnection};
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
|
||||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
|
||||||
|
|
||||||
type DbError = Box<dyn Error + Send + Sync + 'static>;
|
|
||||||
|
|
||||||
fn run_migrations(connection: &mut impl MigrationHarness<Sqlite>) -> Result<(), DbError> {
|
|
||||||
// This will run the necessary migrations.
|
|
||||||
//
|
|
||||||
// See the documentation for `MigrationHarness` for
|
|
||||||
// all available methods.
|
|
||||||
connection.run_pending_migrations(MIGRATIONS)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initialize_db(conn: &mut SqliteConnection) -> Result<(), DbError> {
|
|
||||||
// Enable WAL mode and enforce foreign keys
|
|
||||||
conn.batch_execute(
|
|
||||||
"PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON;",
|
|
||||||
)?;
|
|
||||||
run_migrations(conn)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn initialize_pool(url: &str) -> Result<Pool<ConnectionManager<SqliteConnection>>, DbError> {
|
|
||||||
let manager = ConnectionManager::new(url);
|
|
||||||
|
|
||||||
let pool = Pool::builder().test_on_check_out(true).build(manager)?;
|
|
||||||
|
|
||||||
let mut conn = pool.get()?;
|
|
||||||
initialize_db(&mut conn)?;
|
|
||||||
|
|
||||||
Ok(pool)
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
// @generated automatically by Diesel CLI.
|
|
||||||
|
|
||||||
diesel::table! {
|
|
||||||
users (id) {
|
|
||||||
id -> Integer,
|
|
||||||
discord_id -> BigInt,
|
|
||||||
guild_id -> BigInt,
|
|
||||||
email -> Text,
|
|
||||||
first_name -> Text,
|
|
||||||
last_name -> Text,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
use super::schema::users::{self, dsl::*};
|
|
||||||
use diesel::dsl::Eq;
|
|
||||||
use diesel::dsl::{AsSelect, Select};
|
|
||||||
use diesel::helper_types::Filter;
|
|
||||||
use diesel::prelude::*;
|
|
||||||
use diesel::sqlite::Sqlite;
|
|
||||||
use diesel::sqlite::SqliteConnection;
|
|
||||||
|
|
||||||
#[derive(Queryable, Selectable, AsChangeset)]
|
|
||||||
#[diesel(table_name = users)]
|
|
||||||
pub struct User {
|
|
||||||
pub id: i32,
|
|
||||||
pub discord_id: i64,
|
|
||||||
pub guild_id: i64,
|
|
||||||
pub email: String,
|
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Insertable)]
|
|
||||||
#[diesel(table_name = users)]
|
|
||||||
pub struct NewUser {
|
|
||||||
pub discord_id: i64,
|
|
||||||
pub guild_id: i64,
|
|
||||||
pub email: String,
|
|
||||||
pub first_name: String,
|
|
||||||
pub last_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
type All = Select<users::table, AsSelect<User, Sqlite>>;
|
|
||||||
type WithGuild<T> = Eq<guild_id, T>;
|
|
||||||
type ByGuild<T> = Filter<All, WithGuild<T>>;
|
|
||||||
|
|
||||||
impl User {
|
|
||||||
pub fn all() -> All {
|
|
||||||
users::table.select(User::as_select())
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn by_guild<T>(guild_id_: T) -> ByGuild<T>
|
|
||||||
// where T: AsExpression<BigInt>
|
|
||||||
// {
|
|
||||||
// Self::all().filter(guild_id.eq(guild_id_))
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub fn by_guild_id(guild_id_: i64) -> ByGuild<i64> {
|
|
||||||
Self::all().filter(guild_id.eq(guild_id_))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(
|
|
||||||
conn: &mut SqliteConnection,
|
|
||||||
guild_id_: i64,
|
|
||||||
discord_id_: i64,
|
|
||||||
) -> Result<Option<User>, diesel::result::Error> {
|
|
||||||
Self::all()
|
|
||||||
.filter(guild_id.eq(guild_id_))
|
|
||||||
.filter(discord_id.eq(discord_id_))
|
|
||||||
.first(conn)
|
|
||||||
.optional()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_by_id(
|
|
||||||
conn: &mut SqliteConnection,
|
|
||||||
id_: i32,
|
|
||||||
) -> Result<Option<User>, diesel::result::Error> {
|
|
||||||
Self::all().find(id_).first(conn).optional()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&self, conn: &mut SqliteConnection) -> Result<usize, diesel::result::Error> {
|
|
||||||
diesel::update(users::table).set(self).execute(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NewUser {
|
|
||||||
pub fn insert(&self, conn: &mut SqliteConnection) -> Result<User, diesel::result::Error> {
|
|
||||||
diesel::insert_into(users::table)
|
|
||||||
.values(self)
|
|
||||||
.get_result(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue