diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index f8afc7e..0000000 --- a/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -.woodpecker/ -target/ diff --git a/.env b/.env deleted file mode 100644 index 3e6e71e..0000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -DATABASE_URL=sqlite://affy.db diff --git a/.gitignore b/.gitignore index ac95708..b128699 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,3 @@ rust-project.json # End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer -*.db* diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml deleted file mode 100644 index 5779344..0000000 --- a/.woodpecker/build.yml +++ /dev/null @@ -1,11 +0,0 @@ -platform: 'linux/amd64' - -branches: - exclude: [main] - -pipeline: - build: - image: 'rust:1.69' - commands: - - cargo build --verbose - - cargo test --verbose diff --git a/.woodpecker/clippy.yml b/.woodpecker/clippy.yml deleted file mode 100644 index 92851e5..0000000 --- a/.woodpecker/clippy.yml +++ /dev/null @@ -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 diff --git a/.woodpecker/deploy.yml b/.woodpecker/deploy.yml deleted file mode 100644 index 1fd44e2..0000000 --- a/.woodpecker/deploy.yml +++ /dev/null @@ -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" diff --git a/.woodpecker/lint.yml b/.woodpecker/lint.yml deleted file mode 100644 index ad2b612..0000000 --- a/.woodpecker/lint.yml +++ /dev/null @@ -1,11 +0,0 @@ -platform: 'linux/amd64' - -branches: - exclude: [main] - -pipeline: - lint: - image: 'rust:1.69' - commands: - - rustup component add rustfmt - - cargo fmt -- --check diff --git a/Cargo.lock b/Cargo.lock index 1237a05..afa86eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,26 +18,12 @@ dependencies = [ "uuid", ] -[[package]] -name = "affluences-cli" -version = "0.1.0" -dependencies = [ - "affluences-api", - "clap", - "serde_json", - "tokio", -] - [[package]] name = "affy" version = "0.1.0" dependencies = [ "affluences-api", - "async-minecraft-ping", "chrono", - "diesel", - "diesel_migrations", - "libsqlite3-sys", "poise", "tokio", "uuid", @@ -52,68 +38,6 @@ dependencies = [ "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]] name = "async-trait" version = "0.1.68" @@ -220,48 +144,6 @@ dependencies = [ "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]] name = "codespan-reporting" version = "0.11.1" @@ -272,12 +154,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -416,40 +292,6 @@ dependencies = [ "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]] name = "digest" version = "0.10.6" @@ -469,27 +311,6 @@ dependencies = [ "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]] name = "flate2" version = "1.0.26" @@ -638,12 +459,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "hermit-abi" version = "0.2.6" @@ -653,12 +468,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "http" version = "0.2.9" @@ -780,35 +589,12 @@ dependencies = [ "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]] name = "ipnet" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "itoa" version = "1.0.6" @@ -830,17 +616,6 @@ version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "link-cplusplus" version = "1.0.8" @@ -850,12 +625,6 @@ dependencies = [ "cc", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" - [[package]] name = "lock_api" version = "0.4.9" @@ -881,27 +650,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "mime" version = "0.3.17" @@ -964,7 +712,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -1024,12 +772,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - [[package]] name = "poise" version = "0.5.5" @@ -1067,30 +809,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "proc-macro2" version = "1.0.56" @@ -1109,17 +827,6 @@ dependencies = [ "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]] name = "rand" version = "0.8.5" @@ -1231,20 +938,6 @@ dependencies = [ "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]] name = "rustls" version = "0.20.8" @@ -1278,15 +971,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "scopeguard" version = "1.1.0" @@ -1610,15 +1294,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "tower-service" version = "0.3.2" @@ -1757,12 +1432,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "uuid" version = "1.3.2" @@ -1772,12 +1441,6 @@ dependencies = [ "serde", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 518d125..e07b19a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,27 +2,5 @@ members = [ "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"] } diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b2cbacc..0000000 --- a/Dockerfile +++ /dev/null @@ -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"] diff --git a/affluences-api/API.md b/affluences-api/API.md index 708641e..fc0ecd0 100644 --- a/affluences-api/API.md +++ b/affluences-api/API.md @@ -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>` - -### 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' 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 diff --git a/affluences-api/src/lib.rs b/affluences-api/src/lib.rs index 7994b0d..17c7ace 100644 --- a/affluences-api/src/lib.rs +++ b/affluences-api/src/lib.rs @@ -20,23 +20,6 @@ impl AffluencesClient { } } - pub async fn search(&self, query: String) -> reqwest::Result { - 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::>() - .await? - .data) - } - pub async fn available( &self, site_id: uuid::Uuid, @@ -89,9 +72,3 @@ impl AffluencesClient { .await } } - -impl Default for AffluencesClient { - fn default() -> Self { - AffluencesClient::new() - } -} diff --git a/affluences-api/src/models/available.rs b/affluences-api/src/models/available.rs index e10052c..4b63f5f 100644 --- a/affluences-api/src/models/available.rs +++ b/affluences-api/src/models/available.rs @@ -1,5 +1,5 @@ use super::hh_mm_time_format; -use chrono::{Duration, NaiveTime}; +use chrono::NaiveTime; use serde::Deserialize; #[derive(Deserialize, Debug, Clone, Copy)] @@ -41,45 +41,3 @@ pub struct Resource { pub slots_state: u32, pub hours: Vec, } - -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) - } -} diff --git a/affluences-api/src/models/hh_mm_time_format.rs b/affluences-api/src/models/hh_mm_time_format.rs index 8858d4f..7d535f6 100644 --- a/affluences-api/src/models/hh_mm_time_format.rs +++ b/affluences-api/src/models/hh_mm_time_format.rs @@ -1,7 +1,7 @@ use chrono::NaiveTime; use serde::{self, Deserialize, Deserializer, Serializer}; -const FORMAT: &str = "%H:%M"; +const FORMAT: &'static str = "%H:%M"; pub fn serialize(time: &NaiveTime, serializer: S) -> Result where diff --git a/affluences-api/src/models/site_data.rs b/affluences-api/src/models/site_data.rs index 68f5efb..1d13a22 100644 --- a/affluences-api/src/models/site_data.rs +++ b/affluences-api/src/models/site_data.rs @@ -1,24 +1,24 @@ -use serde::{Deserialize, Serialize}; +use serde::Deserialize; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct Data { pub data: T, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataCategory { pub id: u32, pub name: String, pub name_plural: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataLocationCoordinates { pub latitude: f64, pub longitude: f64, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataLocationAddress { pub route: String, pub city: String, @@ -27,47 +27,47 @@ pub struct SiteDataLocationAddress { pub country_code: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataLocation { pub coordinates: SiteDataLocationCoordinates, pub address: SiteDataLocationAddress, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataForecast { pub opened: bool, - pub occupancy: Option, + pub occupancy: u32, // waiting_time pub waiting_time_overflow: bool, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataNotice { pub message: String, pub url: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataService { pub id: u32, pub name: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataInfo { pub title: String, pub description: String, pub url: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteDataStatus { pub state: String, pub text: String, pub color: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct SiteData { pub id: uuid::Uuid, pub slug: String, @@ -104,15 +104,3 @@ pub struct SiteData { #[serde(rename = "publicationStatus")] 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, -} diff --git a/affluences-cli/src/main.rs b/affluences-cli/src/main.rs deleted file mode 100644 index 3b31cb5..0000000 --- a/affluences-cli/src/main.rs +++ /dev/null @@ -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, -} - -#[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 => {} - } -} diff --git a/affluences-cli/Cargo.toml b/bot/Cargo.toml similarity index 72% rename from affluences-cli/Cargo.toml rename to bot/Cargo.toml index f46a340..41a372d 100644 --- a/affluences-cli/Cargo.toml +++ b/bot/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "affluences-cli" +name = "affy" version = "0.1.0" edition = "2021" @@ -7,6 +7,7 @@ edition = "2021" [dependencies] affluences-api = { path = "../affluences-api" } -clap = { version = "4.2.7", features = ["derive"] } -serde_json = "1.0.96" tokio = { version = "1.28.1", features = ["full"] } +chrono = "*" +uuid = "*" +poise = "0.5.5" diff --git a/bot/src/commands.rs b/bot/src/commands.rs new file mode 100644 index 0000000..60d82d5 --- /dev/null +++ b/bot/src/commands.rs @@ -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, +) -> 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, +) -> 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 = 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> { diff --git a/src/main.rs b/bot/src/main.rs similarity index 91% rename from src/main.rs rename to bot/src/main.rs index 6328584..f6e06bb 100644 --- a/src/main.rs +++ b/bot/src/main.rs @@ -1,11 +1,8 @@ mod commands; -mod db; use affluences_api::AffluencesClient; -use diesel::r2d2::{ConnectionManager, Pool}; -use diesel::sqlite::SqliteConnection; 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 type Error = Box; @@ -13,8 +10,8 @@ type Context<'a> = poise::Context<'a, Data, Error>; // Custom user data passed to all command functions pub struct Data { + votes: Mutex>, client: AffluencesClient, - pool: Pool>, } 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 // Every option can be omitted to use its default value let options = poise::FrameworkOptions { - commands: commands::commands(), + commands: vec![ + commands::help(), + commands::vote(), + commands::getvotes(), + commands::available(), + ], prefix_options: poise::PrefixFrameworkOptions { prefix: Some("~".into()), edit_tracker: Some(poise::EditTracker::for_timespan(Duration::from_secs(3600))), @@ -84,8 +86,6 @@ async fn main() { ..Default::default() }; - let pool = db::initialize_pool("affy.db").expect("Failed to initialize database."); - poise::Framework::builder() .token( var("DISCORD_TOKEN") @@ -96,8 +96,8 @@ async fn main() { println!("Logged in as {}", _ready.user.name); poise::builtins::register_globally(ctx, &framework.options().commands).await?; Ok(Data { + votes: Mutex::new(HashMap::new()), client: AffluencesClient::new(), - pool, }) }) }) diff --git a/build.rs b/build.rs deleted file mode 100644 index 3a8149e..0000000 --- a/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=migrations"); -} diff --git a/diesel.toml b/diesel.toml deleted file mode 100644 index 88db6ba..0000000 --- a/diesel.toml +++ /dev/null @@ -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" diff --git a/migrations/2023-05-15-142901_create_users/down.sql b/migrations/2023-05-15-142901_create_users/down.sql deleted file mode 100644 index dc3714b..0000000 --- a/migrations/2023-05-15-142901_create_users/down.sql +++ /dev/null @@ -1,2 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TABLE users; diff --git a/migrations/2023-05-15-142901_create_users/up.sql b/migrations/2023-05-15-142901_create_users/up.sql deleted file mode 100644 index b1ec088..0000000 --- a/migrations/2023-05-15-142901_create_users/up.sql +++ /dev/null @@ -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) -); diff --git a/src/commands/bib.rs b/src/commands/bib.rs deleted file mode 100644 index 32fbbe2..0000000 --- a/src/commands/bib.rs +++ /dev/null @@ -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::>() - .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::::into(date) - )) - .fields( - resources - .into_iter() - .map(resource_to_embed_field) - .collect::>(), - ) - }) - }) - .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, -) -> 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::::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> { - -// } diff --git a/src/commands/minecraft.rs b/src/commands/minecraft.rs deleted file mode 100644 index b6250d4..0000000 --- a/src/commands/minecraft.rs +++ /dev/null @@ -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, - #[description = "Port the server runs on"] port: Option, -) -> 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(()) -} diff --git a/src/commands/mod.rs b/src/commands/mod.rs deleted file mode 100644 index 32551d4..0000000 --- a/src/commands/mod.rs +++ /dev/null @@ -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 { - 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::() { - 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 for chrono::NaiveDate { - fn from(val: HumanNaiveDate) -> chrono::NaiveDate { - val.0 - } -} - -pub fn commands() -> Vec> { - 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, -) -> 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(()) -} diff --git a/src/commands/users.rs b/src/commands/users.rs deleted file mode 100644 index f552787..0000000 --- a/src/commands/users.rs +++ /dev/null @@ -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(()) -} diff --git a/src/db/mod.rs b/src/db/mod.rs deleted file mode 100644 index 578506e..0000000 --- a/src/db/mod.rs +++ /dev/null @@ -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; - -fn run_migrations(connection: &mut impl MigrationHarness) -> 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>, 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) -} diff --git a/src/db/schema.rs b/src/db/schema.rs deleted file mode 100644 index e43eb3e..0000000 --- a/src/db/schema.rs +++ /dev/null @@ -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, - } -} diff --git a/src/db/users.rs b/src/db/users.rs deleted file mode 100644 index e87f0b9..0000000 --- a/src/db/users.rs +++ /dev/null @@ -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>; -type WithGuild = Eq; -type ByGuild = Filter>; - -impl User { - pub fn all() -> All { - users::table.select(User::as_select()) - } - - // pub fn by_guild(guild_id_: T) -> ByGuild - // where T: AsExpression - // { - // Self::all().filter(guild_id.eq(guild_id_)) - // } - - pub fn by_guild_id(guild_id_: i64) -> ByGuild { - Self::all().filter(guild_id.eq(guild_id_)) - } - - pub fn get( - conn: &mut SqliteConnection, - guild_id_: i64, - discord_id_: i64, - ) -> Result, 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, diesel::result::Error> { - Self::all().find(id_).first(conn).optional() - } - - pub fn update(&self, conn: &mut SqliteConnection) -> Result { - diesel::update(users::table).set(self).execute(conn) - } -} - -impl NewUser { - pub fn insert(&self, conn: &mut SqliteConnection) -> Result { - diesel::insert_into(users::table) - .values(self) - .get_result(conn) - } -}