From ba9d20fe15408b8746b62a82146ea67d92ccf6dc Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Sun, 24 May 2026 02:49:21 +0900 Subject: [PATCH 01/10] wip --- Cargo.lock | 1432 +++++++++++++++++++++++++++++++++++-- Cargo.toml | 5 + bot/Cargo.toml | 3 + bot/src/commands.rs | 9 + bot/src/commands/issue.rs | 68 ++ bot/src/commands/ping.rs | 8 + bot/src/lib.rs | 55 +- 7 files changed, 1516 insertions(+), 64 deletions(-) create mode 100644 bot/src/commands.rs create mode 100644 bot/src/commands/issue.rs create mode 100644 bot/src/commands/ping.rs diff --git a/Cargo.lock b/Cargo.lock index f78e9b3..823f4a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,19 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "ahash" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom", + "getrandom 0.2.17", "once_cell", "version_check", ] @@ -93,11 +99,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] [[package]] name = "async-attributes" @@ -399,6 +414,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "byteorder" version = "1.5.0" @@ -411,6 +432,37 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.2.61" @@ -491,6 +543,17 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "command_attr" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8208103c5e25a091226dfa8d61d08d0561cc14f31b25691811ba37d4ec9b157b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -536,6 +599,24 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -581,6 +662,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "strsim", "syn 2.0.117", ] @@ -595,6 +677,26 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", + "serde", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + [[package]] name = "der" version = "0.7.10" @@ -616,6 +718,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -692,6 +805,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + [[package]] name = "etcetera" version = "0.8.0" @@ -742,6 +864,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flume" version = "0.11.1" @@ -851,6 +983,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "futures-sink" version = "0.3.32" @@ -869,8 +1012,10 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -895,8 +1040,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", ] [[package]] @@ -926,6 +1100,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1003,6 +1183,104 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls 0.23.40", + "tokio", + "tokio-rustls 0.26.4", + "tower-service", + "webpki-roots 1.0.7", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -1109,6 +1387,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -1144,6 +1428,8 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.0", + "serde", + "serde_core", ] [[package]] @@ -1157,6 +1443,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1174,6 +1466,11 @@ version = "0.1.0" [[package]] name = "it-bot" version = "0.1.0" +dependencies = [ + "poise", + "serenity", + "tokio", +] [[package]] name = "it-core" @@ -1221,6 +1518,18 @@ dependencies = [ "spin", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "levenshtein" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" + [[package]] name = "libc" version = "0.2.186" @@ -1285,6 +1594,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "mac_address" version = "1.1.8" @@ -1338,6 +1653,47 @@ dependencies = [ "sea-orm-migration", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "mini-moka" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c325dfab65f261f386debee8b0969da215b3fa0037e74c8a1234db7ba986d803" +dependencies = [ + "crossbeam-channel", + "crossbeam-utils", + "dashmap", + "skeptic", + "smallvec", + "tagptr", + "triomphe", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.0" @@ -1392,7 +1748,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -1587,6 +1943,25 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "poise" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711433114bca9ff68582aa58af5cbcf573e83250a8714c2dca1c36f453cad3ae" +dependencies = [ + "async-trait", + "derivative", + "futures-util", + "indexmap", + "parking_lot", + "regex", + "serenity", + "serenity_poise_macros", + "tokio", + "tracing", + "trim-in-place", +] + [[package]] name = "polling" version = "3.11.0" @@ -1625,6 +2000,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -1699,14 +2084,92 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.45" +name = "pulldown-cmark" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "proc-macro2", + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.40", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls 0.23.40", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", ] +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radium" version = "0.7.0" @@ -1720,8 +2183,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -1731,7 +2204,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -1740,7 +2223,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", ] [[package]] @@ -1799,6 +2291,62 @@ dependencies = [ "bytecheck", ] +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.40", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls 0.26.4", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots 1.0.7", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rkyv" version = "0.7.46" @@ -1841,7 +2389,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "spki", "subtle", @@ -1858,13 +2406,19 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand", + "rand 0.8.6", "rkyv", "serde", "serde_json", "wasm-bindgen", ] +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1887,6 +2441,66 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.103.13", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -1899,6 +2513,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1942,7 +2565,7 @@ dependencies = [ "serde_json", "sqlx", "strum", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "url", @@ -2039,7 +2662,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.117", - "thiserror", + "thiserror 2.0.18", ] [[package]] @@ -2071,11 +2694,25 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", +] + [[package]] name = "semver" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] [[package]] name = "serde" @@ -2096,6 +2733,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cow" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7bbbec7196bfde255ab54b65e34087c0849629280028238e67ee25d6a4b7da" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.228" @@ -2132,6 +2778,55 @@ dependencies = [ "serde", ] +[[package]] +name = "serenity" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bde37f42765dfdc34e2a039e0c84afbf79a3101c1941763b0beb816c2f17541" +dependencies = [ + "arrayvec", + "async-trait", + "base64", + "bitflags", + "bytes", + "chrono", + "command_attr", + "dashmap", + "flate2", + "futures", + "levenshtein", + "mime_guess", + "parking_lot", + "percent-encoding", + "reqwest", + "rustc-hash", + "secrecy", + "serde", + "serde_cow", + "serde_json", + "static_assertions", + "time", + "tokio", + "tokio-tungstenite", + "tracing", + "typemap_rev", + "typesize", + "url", + "uwl", +] + +[[package]] +name = "serenity_poise_macros" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bc8407893be0d973a7bb48325e10e0160a0db44f4099467c83179688220e5f5" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2186,15 +2881,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + [[package]] name = "simdutf8" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "slab" version = "0.4.12" @@ -2282,7 +2998,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror", + "thiserror 2.0.18", "time", "tokio", "tokio-stream", @@ -2360,7 +3076,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand", + "rand 0.8.6", "rsa", "rust_decimal", "serde", @@ -2369,7 +3085,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "uuid", @@ -2404,7 +3120,7 @@ dependencies = [ "memchr", "num-bigint", "once_cell", - "rand", + "rand 0.8.6", "rust_decimal", "serde", "serde_json", @@ -2412,7 +3128,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "uuid", @@ -2439,7 +3155,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror", + "thiserror 2.0.18", "time", "tracing", "url", @@ -2509,6 +3225,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -2520,19 +3245,58 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -2613,9 +3377,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -2640,46 +3404,141 @@ dependencies = [ ] [[package]] -name = "tokio-stream" -version = "0.1.18" +name = "tokio-rustls" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ - "futures-core", - "pin-project-lite", + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] [[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +name = "tokio-rustls" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "serde_core", + "rustls 0.23.40", + "tokio", ] [[package]] -name = "toml_edit" -version = "0.25.11+spec-1.1.0" +name = "tokio-stream" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ - "indexmap", - "toml_datetime", - "toml_parser", - "winnow", + "futures-core", + "pin-project-lite", + "tokio", ] [[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" +name = "tokio-tungstenite" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ + "futures-util", + "log", + "rustls 0.22.4", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tungstenite", + "webpki-roots 0.26.11", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + [[package]] name = "tracing" version = "0.1.44" @@ -2755,12 +3614,92 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trim-in-place" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" + +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.6", + "rustls 0.22.4", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "url", + "utf-8", +] + +[[package]] +name = "typemap_rev" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b08b0c1257381af16a5c3605254d529d3e7e109f3c62befc5d168968192998" + [[package]] name = "typenum" version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "typesize" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da66c62c5b7017a2787e77373c03e6a5aafde77a73bff1ff96e91cd2e128179" +dependencies = [ + "chrono", + "dashmap", + "hashbrown 0.14.5", + "mini-moka", + "parking_lot", + "secrecy", + "serde_json", + "time", + "typesize-derive", + "url", +] + +[[package]] +name = "typesize-derive" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "536b6812192bda8551cfa0e52524e328c6a951b48e66529ee4522d6c721243d6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -2794,6 +3733,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.8" @@ -2804,8 +3749,15 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -2829,6 +3781,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "uwl" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4bf03e0ca70d626ecc4ba6b0763b934b6f2976e8c744088bb3c1d646fbb1ad0" + [[package]] name = "valuable" version = "0.1.1" @@ -2853,12 +3811,49 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + [[package]] name = "wasite" version = "0.1.0" @@ -2921,6 +3916,91 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "whoami" version = "1.6.1" @@ -2947,6 +4027,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -3018,7 +4107,25 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -3036,13 +4143,46 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3051,42 +4191,138 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "1.0.2" @@ -3096,6 +4332,100 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + [[package]] name = "writeable" version = "0.6.3" diff --git a/Cargo.toml b/Cargo.toml index b187345..4aeebdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,8 @@ [workspace] resolver = "2" members = ["backend", "boot", "bot", "core", "migration"] + +[workspace.dependencies] +serenity = "0.12" +tokio = { version = "1.52.3", features = ["macros", "rt-multi-thread"] } +poise = "0.6" \ No newline at end of file diff --git a/bot/Cargo.toml b/bot/Cargo.toml index dd9fa4d..4267518 100644 --- a/bot/Cargo.toml +++ b/bot/Cargo.toml @@ -4,3 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] +tokio = {workspace = true} +serenity = { workspace = true } +poise = { workspace = true } \ No newline at end of file diff --git a/bot/src/commands.rs b/bot/src/commands.rs new file mode 100644 index 0000000..b57733c --- /dev/null +++ b/bot/src/commands.rs @@ -0,0 +1,9 @@ +pub mod ping; +pub use ping::ping; + +pub mod issue; +pub use issue::issue; +pub use issue::new; +pub use issue::close; +pub use issue::edit; +pub use issue::list; \ No newline at end of file diff --git a/bot/src/commands/issue.rs b/bot/src/commands/issue.rs new file mode 100644 index 0000000..47ee5ff --- /dev/null +++ b/bot/src/commands/issue.rs @@ -0,0 +1,68 @@ +use crate::{Context, Error}; + +#[allow(unused)] +#[poise::command(slash_command, + subcommands("new", "list", "close", "edit"))] +pub async fn issue(ctx: Context<'_>) -> Result<(), Error> { + Ok(()) +} + +#[allow(unused)] +#[poise::command(slash_command)] +pub async fn new(ctx: Context<'_>, + #[description = "The priority of issue"] + priority: Option +) -> Result<(), Error> { + Ok(()) +} + +#[derive(Debug, poise::ChoiceParameter, PartialEq)] +pub enum IssueStatus { + #[name = "Open"] + Open, + #[name = "In Progress"] + InProgress, + #[name = "Resolved"] + Resolved, + #[name = "Closed"] + Closed, + #[name = "All"] + All, +} + +#[derive(Debug, poise::ChoiceParameter, PartialEq)] +pub enum IssueSort { + #[name = "newest"] + Newest, + #[name = "priority"] + Priority, +} + +#[allow(unused)] +#[poise::command(slash_command)] +pub async fn list( + ctx: Context<'_>, + #[description = "Issue status to filter by (Default: Open)"] status: Option, + #[description = "How to sort the issues"] sort: Option, +) -> Result<(), Error> { + let _status = status.unwrap_or(IssueStatus::Open); + Ok(()) +} + +#[allow(unused)] +#[poise::command(slash_command)] +pub async fn close(ctx: Context<'_>, + #[description = "Issue ID"] + issue: Option +) -> Result<(), Error> { + Ok(()) +} + +#[allow(unused)] +#[poise::command(slash_command)] +pub async fn edit(ctx: Context<'_>, + #[description = "Issue ID"] + issue: Option +) -> Result<(), Error> { + Ok(()) +} \ No newline at end of file diff --git a/bot/src/commands/ping.rs b/bot/src/commands/ping.rs new file mode 100644 index 0000000..4b0360e --- /dev/null +++ b/bot/src/commands/ping.rs @@ -0,0 +1,8 @@ +use crate::{Context, Error}; + +#[poise::command(slash_command)] +pub async fn ping(ctx: Context<'_>) -> Result<(), Error> { + let latency = ctx.ping().await; + ctx.say(format!("Pong! {}ms", latency.as_millis())).await?; + Ok(()) +} diff --git a/bot/src/lib.rs b/bot/src/lib.rs index b93cf3f..590ad57 100644 --- a/bot/src/lib.rs +++ b/bot/src/lib.rs @@ -1,14 +1,43 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +use ::serenity::all::GatewayIntents; +use tokio; +use std::env; + +pub mod commands; + +struct Data {} +type Error = Box; + +#[allow(unused)] +type Context<'a> = poise::Context<'a, Data, Error>; + +#[allow(unused)] +#[tokio::main] +async fn main() { + let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); + let intents + = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; + + let framework = poise::Framework::::builder() + .options(poise::FrameworkOptions { + commands: vec![ + commands::ping() + ], + ..Default::default() + }) + .setup(|_ctx, _ready, _framework| { + Box::pin(async move { + println!("Ready: {}", _ready.user.tag()); + Ok(Data {}) + }) + }) + .build(); + + let mut client + = serenity::Client::builder(token, intents) + .framework(framework) + .await.expect("Error creating client"); + + if let Err(e) = client.start().await { + println!("Client Error {e:?}" ); } -} +} \ No newline at end of file From ce22a5907bb03f94d972234f3c9a9fc264e1dd87 Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Thu, 11 Jun 2026 01:02:13 +0900 Subject: [PATCH 02/10] asdf --- bot/src/commands.rs | 9 --------- bot/src/commands/mod.rs | 2 ++ bot/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 bot/src/commands.rs create mode 100644 bot/src/commands/mod.rs diff --git a/bot/src/commands.rs b/bot/src/commands.rs deleted file mode 100644 index b57733c..0000000 --- a/bot/src/commands.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod ping; -pub use ping::ping; - -pub mod issue; -pub use issue::issue; -pub use issue::new; -pub use issue::close; -pub use issue::edit; -pub use issue::list; \ No newline at end of file diff --git a/bot/src/commands/mod.rs b/bot/src/commands/mod.rs new file mode 100644 index 0000000..4924452 --- /dev/null +++ b/bot/src/commands/mod.rs @@ -0,0 +1,2 @@ +pub mod ping; +pub mod issue; \ No newline at end of file diff --git a/bot/src/lib.rs b/bot/src/lib.rs index 590ad57..1056eca 100644 --- a/bot/src/lib.rs +++ b/bot/src/lib.rs @@ -20,7 +20,7 @@ async fn main() { let framework = poise::Framework::::builder() .options(poise::FrameworkOptions { commands: vec![ - commands::ping() + commands::ping::ping() ], ..Default::default() }) From bed8c3601b7fde81aa1f1cb71267c7ab1dac9365 Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Thu, 11 Jun 2026 01:34:42 +0900 Subject: [PATCH 03/10] Backend base? --- backend/Cargo.toml | 2 ++ backend/src/handlers/issue.rs | 0 backend/src/handlers/mod.rs | 2 ++ backend/src/handlers/zakonim.rs | 0 backend/src/lib.rs | 28 ++++++++++++++++------------ 5 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 backend/src/handlers/issue.rs create mode 100644 backend/src/handlers/mod.rs create mode 100644 backend/src/handlers/zakonim.rs diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 14ce22a..307b22a 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +axum = "0.8.9" +tokio.workspace = true diff --git a/backend/src/handlers/issue.rs b/backend/src/handlers/issue.rs new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/handlers/mod.rs b/backend/src/handlers/mod.rs new file mode 100644 index 0000000..dbb8852 --- /dev/null +++ b/backend/src/handlers/mod.rs @@ -0,0 +1,2 @@ +pub mod issue; +pub mod zakonim; \ No newline at end of file diff --git a/backend/src/handlers/zakonim.rs b/backend/src/handlers/zakonim.rs new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/lib.rs b/backend/src/lib.rs index b93cf3f..8b359fa 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,14 +1,18 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +#[allow(unused_imports)] +use axum::{ + routing::{get, post}, + Router, +}; +use std::env; -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +#[tokio::main] +#[allow(unused)] +async fn main() { + let app = Router::new() + ; + let port = env::var("PORT").unwrap(); + let addr = format!("0.0.0.0:{}", port); + let listner = tokio::net::TcpListener::bind(addr).await.unwrap(); + axum::serve(listner, app); +} \ No newline at end of file From 86df3957e54fb5ca0c018c5cea6d40d471e67f41 Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Thu, 11 Jun 2026 01:35:08 +0900 Subject: [PATCH 04/10] Edit bot source structure --- bot/src/commands/issue.rs | 6 +----- bot/src/commands/mod.rs | 3 ++- bot/src/commands/zakonim.rs | 11 +++++++++++ bot/src/lib.rs | 4 +++- 4 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 bot/src/commands/zakonim.rs diff --git a/bot/src/commands/issue.rs b/bot/src/commands/issue.rs index 47ee5ff..c1969d7 100644 --- a/bot/src/commands/issue.rs +++ b/bot/src/commands/issue.rs @@ -1,13 +1,12 @@ use crate::{Context, Error}; -#[allow(unused)] + #[poise::command(slash_command, subcommands("new", "list", "close", "edit"))] pub async fn issue(ctx: Context<'_>) -> Result<(), Error> { Ok(()) } -#[allow(unused)] #[poise::command(slash_command)] pub async fn new(ctx: Context<'_>, #[description = "The priority of issue"] @@ -38,7 +37,6 @@ pub enum IssueSort { Priority, } -#[allow(unused)] #[poise::command(slash_command)] pub async fn list( ctx: Context<'_>, @@ -49,7 +47,6 @@ pub async fn list( Ok(()) } -#[allow(unused)] #[poise::command(slash_command)] pub async fn close(ctx: Context<'_>, #[description = "Issue ID"] @@ -58,7 +55,6 @@ pub async fn close(ctx: Context<'_>, Ok(()) } -#[allow(unused)] #[poise::command(slash_command)] pub async fn edit(ctx: Context<'_>, #[description = "Issue ID"] diff --git a/bot/src/commands/mod.rs b/bot/src/commands/mod.rs index 4924452..175a273 100644 --- a/bot/src/commands/mod.rs +++ b/bot/src/commands/mod.rs @@ -1,2 +1,3 @@ pub mod ping; -pub mod issue; \ No newline at end of file +pub mod issue; +pub mod zakonim; \ No newline at end of file diff --git a/bot/src/commands/zakonim.rs b/bot/src/commands/zakonim.rs new file mode 100644 index 0000000..8587862 --- /dev/null +++ b/bot/src/commands/zakonim.rs @@ -0,0 +1,11 @@ +use crate::{Context, Error}; +use poise::serenity_prelude as serenity; + +#[poise::command(slash_command)] +pub async fn zakonim(ctx: Context<'_>, + #[description = "Who zakonim"] who : Option, +) -> Result<(), Error> { + let latency = ctx.ping().await; + ctx.say(format!("Pong! {}ms", latency.as_millis())).await?; + Ok(()) +} diff --git a/bot/src/lib.rs b/bot/src/lib.rs index 1056eca..80bfce7 100644 --- a/bot/src/lib.rs +++ b/bot/src/lib.rs @@ -20,7 +20,9 @@ async fn main() { let framework = poise::Framework::::builder() .options(poise::FrameworkOptions { commands: vec![ - commands::ping::ping() + commands::ping::ping(), + commands::issue::issue(), + commands::zakonim::zakonim(), ], ..Default::default() }) From d77c59e94d8feb3a55604ce855f315c7ac272e35 Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Thu, 11 Jun 2026 01:35:19 +0900 Subject: [PATCH 05/10] cargo --- Cargo.lock | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 823f4a9..82bfea4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,58 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "base64" version = "0.22.1" @@ -1222,6 +1274,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.9.0" @@ -1235,6 +1293,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1458,6 +1517,10 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "it-backend" version = "0.1.0" +dependencies = [ + "axum", + "tokio", +] [[package]] name = "it-boot" @@ -1620,6 +1683,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -2766,6 +2835,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3507,6 +3587,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] From 5a7d2412af5f042f34642069e1d8f235a19d5ac0 Mon Sep 17 00:00:00 2001 From: 200mill <6k5etx@zuninemo.com> Date: Thu, 11 Jun 2026 18:17:18 +0900 Subject: [PATCH 06/10] fix something --- backend/Cargo.toml | 2 ++ backend/src/handlers/mod.rs | 3 ++- backend/src/handlers/root.rs | 8 ++++++++ backend/src/handlers/zakonim.rs | 19 +++++++++++++++++++ backend/src/lib.rs | 3 ++- backend/src/startup.rs | 7 +++++++ 6 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 backend/src/handlers/root.rs create mode 100644 backend/src/startup.rs diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 307b22a..6abb9e3 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -5,4 +5,6 @@ edition = "2024" [dependencies] axum = "0.8.9" +serde = "1.0.228" +sqlx = "0.9.0" tokio.workspace = true diff --git a/backend/src/handlers/mod.rs b/backend/src/handlers/mod.rs index dbb8852..a819916 100644 --- a/backend/src/handlers/mod.rs +++ b/backend/src/handlers/mod.rs @@ -1,2 +1,3 @@ pub mod issue; -pub mod zakonim; \ No newline at end of file +pub mod zakonim; +pub mod root; \ No newline at end of file diff --git a/backend/src/handlers/root.rs b/backend/src/handlers/root.rs new file mode 100644 index 0000000..45f495d --- /dev/null +++ b/backend/src/handlers/root.rs @@ -0,0 +1,8 @@ +use axum::{ + http::StatusCode, +}; + + +pub async fn root() -> (StatusCode, &'static str) { + (StatusCode::OK, "asdf") +} \ No newline at end of file diff --git a/backend/src/handlers/zakonim.rs b/backend/src/handlers/zakonim.rs index e69de29..ad25abd 100644 --- a/backend/src/handlers/zakonim.rs +++ b/backend/src/handlers/zakonim.rs @@ -0,0 +1,19 @@ +use axum::{ + http::StatusCode, +}; +use serde::{Deserialize, Serialize}; + +use std::env; + + +pub async fn zakonim(req: ZakonimRequest) -> StatusCode { + + + return StatusCode::OK; +} + +#[derive(Deserialize, Serialize)] +pub struct ZakonimRequest { + pub id: String, + pub reason: String, +} \ No newline at end of file diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 8b359fa..964fa40 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,15 +1,16 @@ -#[allow(unused_imports)] use axum::{ routing::{get, post}, Router, }; use std::env; +mod handlers; #[tokio::main] #[allow(unused)] async fn main() { let app = Router::new() + .route("/", get(handlers::root::root)) ; let port = env::var("PORT").unwrap(); let addr = format!("0.0.0.0:{}", port); diff --git a/backend/src/startup.rs b/backend/src/startup.rs new file mode 100644 index 0000000..4320fc1 --- /dev/null +++ b/backend/src/startup.rs @@ -0,0 +1,7 @@ + + +pub fn init() { + // Database Check + // if -> database not ready -> create database + // else -> nothing +} \ No newline at end of file From b56c912a6d3c0eddbead5f8f2659fe2772204ef1 Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Fri, 12 Jun 2026 16:58:43 +0900 Subject: [PATCH 07/10] meow --- Cargo.lock | 456 +++++++++++++++++++++++++++---- backend/src/etc/mod.rs | 1 + backend/src/{ => etc}/startup.rs | 0 backend/src/lib.rs | 10 +- compose.yml | 0 5 files changed, 417 insertions(+), 50 deletions(-) create mode 100644 backend/src/etc/mod.rs rename backend/src/{ => etc}/startup.rs (100%) create mode 100644 compose.yml diff --git a/Cargo.lock b/Cargo.lock index 82bfea4..ed53698 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,6 +401,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "blocking" version = "1.6.2" @@ -537,6 +546,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.44" @@ -589,6 +609,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "colorchoice" version = "1.0.5" @@ -636,6 +662,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -694,6 +729,24 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "darling" version = "0.20.11" @@ -809,12 +862,23 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", - "crypto-common", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.2", + "ctutils", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -877,6 +941,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "etcetera" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -937,6 +1011,17 @@ dependencies = [ "spin", ] +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -949,6 +1034,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1121,6 +1212,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -1166,7 +1258,18 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", ] [[package]] @@ -1184,6 +1287,15 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "hashlink" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "heck" version = "0.4.1" @@ -1214,7 +1326,16 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" dependencies = [ - "hmac", + "hmac 0.12.1", +] + +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac 0.13.0", ] [[package]] @@ -1223,7 +1344,16 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", ] [[package]] @@ -1280,6 +1410,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.9.0" @@ -1519,6 +1658,8 @@ name = "it-backend" version = "0.1.0" dependencies = [ "axum", + "serde", + "sqlx 0.9.0", "tokio", ] @@ -1696,7 +1837,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "md-5" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +dependencies = [ + "cfg-if", + "digest 0.11.3", ] [[package]] @@ -2266,6 +2417,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -2304,6 +2466,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -2452,7 +2620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", - "digest", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", @@ -2632,7 +2800,7 @@ dependencies = [ "sea-query-binder", "serde", "serde_json", - "sqlx", + "sqlx 0.8.6", "strum", "thiserror 2.0.18", "time", @@ -2715,7 +2883,7 @@ dependencies = [ "rust_decimal", "sea-query", "serde_json", - "sqlx", + "sqlx 0.8.6", "time", "uuid", ] @@ -2914,8 +3082,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -2925,8 +3104,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -2960,7 +3150,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -3041,11 +3231,24 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", + "sqlx-core 0.8.6", + "sqlx-macros 0.8.6", + "sqlx-mysql 0.8.6", + "sqlx-postgres 0.8.6", + "sqlx-sqlite 0.8.6", +] + +[[package]] +name = "sqlx" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" +dependencies = [ + "sqlx-core 0.9.0", + "sqlx-macros 0.9.0", + "sqlx-mysql 0.9.0", + "sqlx-postgres 0.9.0", + "sqlx-sqlite 0.9.0", ] [[package]] @@ -3067,7 +3270,7 @@ dependencies = [ "futures-io", "futures-util", "hashbrown 0.15.5", - "hashlink", + "hashlink 0.10.0", "indexmap", "log", "memchr", @@ -3076,7 +3279,7 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", "thiserror 2.0.18", "time", @@ -3087,6 +3290,38 @@ dependencies = [ "uuid", ] +[[package]] +name = "sqlx-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" +dependencies = [ + "base64", + "bytes", + "cfg-if", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.4.1", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.16.1", + "hashlink 0.11.1", + "indexmap", + "log", + "memchr", + "percent-encoding", + "serde", + "serde_json", + "sha2 0.10.9", + "smallvec", + "thiserror 2.0.18", + "tracing", + "url", +] + [[package]] name = "sqlx-macros" version = "0.8.6" @@ -3095,8 +3330,21 @@ checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" dependencies = [ "proc-macro2", "quote", - "sqlx-core", - "sqlx-macros-core", + "sqlx-core 0.8.6", + "sqlx-macros-core 0.8.6", + "syn 2.0.117", +] + +[[package]] +name = "sqlx-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core 0.9.0", + "sqlx-macros-core 0.9.0", "syn 2.0.117", ] @@ -3115,16 +3363,41 @@ dependencies = [ "quote", "serde", "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", + "sha2 0.10.9", + "sqlx-core 0.8.6", + "sqlx-mysql 0.8.6", + "sqlx-postgres 0.8.6", + "sqlx-sqlite 0.8.6", "syn 2.0.117", "tokio", "url", ] +[[package]] +name = "sqlx-macros-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" +dependencies = [ + "cfg-if", + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "sqlx-core 0.9.0", + "sqlx-mysql 0.9.0", + "sqlx-postgres 0.9.0", + "sqlx-sqlite 0.9.0", + "syn 2.0.117", + "thiserror 2.0.18", + "url", +] + [[package]] name = "sqlx-mysql" version = "0.8.6" @@ -3139,7 +3412,7 @@ dependencies = [ "bytes", "chrono", "crc", - "digest", + "digest 0.10.7", "dotenvy", "either", "futures-channel", @@ -3148,11 +3421,11 @@ dependencies = [ "futures-util", "generic-array", "hex", - "hkdf", - "hmac", + "hkdf 0.12.4", + "hmac 0.12.1", "itoa", "log", - "md-5", + "md-5 0.10.6", "memchr", "once_cell", "percent-encoding", @@ -3160,16 +3433,42 @@ dependencies = [ "rsa", "rust_decimal", "serde", - "sha1", - "sha2", + "sha1 0.10.6", + "sha2 0.10.9", "smallvec", - "sqlx-core", + "sqlx-core 0.8.6", "stringprep", "thiserror 2.0.18", "time", "tracing", "uuid", - "whoami", + "whoami 1.6.1", +] + +[[package]] +name = "sqlx-mysql" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" +dependencies = [ + "bitflags", + "byteorder", + "bytes", + "crc", + "digest 0.11.3", + "dotenvy", + "either", + "futures-core", + "futures-util", + "generic-array", + "log", + "percent-encoding", + "serde", + "sha1 0.11.0", + "sha2 0.11.0", + "sqlx-core 0.9.0", + "thiserror 2.0.18", + "tracing", ] [[package]] @@ -3186,17 +3485,17 @@ dependencies = [ "chrono", "crc", "dotenvy", - "etcetera", + "etcetera 0.8.0", "futures-channel", "futures-core", "futures-util", "hex", - "hkdf", - "hmac", + "hkdf 0.12.4", + "hmac 0.12.1", "home", "itoa", "log", - "md-5", + "md-5 0.10.6", "memchr", "num-bigint", "once_cell", @@ -3204,15 +3503,50 @@ dependencies = [ "rust_decimal", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "smallvec", - "sqlx-core", + "sqlx-core 0.8.6", "stringprep", "thiserror 2.0.18", "time", "tracing", "uuid", - "whoami", + "whoami 1.6.1", +] + +[[package]] +name = "sqlx-postgres" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera 0.11.0", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf 0.13.0", + "hmac 0.13.0", + "itoa", + "log", + "md-5 0.11.0", + "memchr", + "rand 0.10.1", + "serde", + "serde_json", + "sha2 0.11.0", + "smallvec", + "sqlx-core 0.9.0", + "stringprep", + "thiserror 2.0.18", + "tracing", + "whoami 2.1.2", ] [[package]] @@ -3223,7 +3557,7 @@ checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" dependencies = [ "atoi", "chrono", - "flume", + "flume 0.11.1", "futures-channel", "futures-core", "futures-executor", @@ -3234,7 +3568,7 @@ dependencies = [ "percent-encoding", "serde", "serde_urlencoded", - "sqlx-core", + "sqlx-core 0.8.6", "thiserror 2.0.18", "time", "tracing", @@ -3242,6 +3576,30 @@ dependencies = [ "uuid", ] +[[package]] +name = "sqlx-sqlite" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" +dependencies = [ + "atoi", + "flume 0.12.0", + "form_urlencoded", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core 0.9.0", + "thiserror 2.0.18", + "tracing", + "url", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3728,7 +4086,7 @@ dependencies = [ "rand 0.8.6", "rustls 0.22.4", "rustls-pki-types", - "sha1", + "sha1 0.10.6", "thiserror 1.0.69", "url", "utf-8", @@ -4092,6 +4450,12 @@ dependencies = [ "wasite", ] +[[package]] +name = "whoami" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" + [[package]] name = "winapi" version = "0.3.9" diff --git a/backend/src/etc/mod.rs b/backend/src/etc/mod.rs new file mode 100644 index 0000000..f58fb45 --- /dev/null +++ b/backend/src/etc/mod.rs @@ -0,0 +1 @@ +pub mod startup; \ No newline at end of file diff --git a/backend/src/startup.rs b/backend/src/etc/startup.rs similarity index 100% rename from backend/src/startup.rs rename to backend/src/etc/startup.rs diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 964fa40..e06029a 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -2,18 +2,20 @@ use axum::{ routing::{get, post}, Router, }; -use std::env; + mod handlers; +mod etc; #[tokio::main] #[allow(unused)] async fn main() { + + let app = Router::new() .route("/", get(handlers::root::root)) ; - let port = env::var("PORT").unwrap(); - let addr = format!("0.0.0.0:{}", port); - let listner = tokio::net::TcpListener::bind(addr).await.unwrap(); + + let listner = tokio::net::TcpListener::bind("0.0.0.0:80").await.unwrap(); axum::serve(listner, app); } \ No newline at end of file diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..e69de29 From 143879e9837d9094c38be465824bb6201ce016c6 Mon Sep 17 00:00:00 2001 From: 200mill <6k5etx@zuninemo.com> Date: Fri, 12 Jun 2026 18:39:07 +0900 Subject: [PATCH 08/10] asdf --- Cargo.lock | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 393 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed53698..0155d24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ "hybrid-array", ] @@ -557,6 +557,17 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.44" @@ -615,6 +626,12 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "colorchoice" version = "1.0.5" @@ -671,6 +688,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -747,6 +773,24 @@ dependencies = [ "cmov", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "darling" version = "0.20.11" @@ -862,9 +906,11 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ + "block-buffer 0.10.4", "block-buffer 0.10.4", "const-oid", "crypto-common 0.1.7", + "crypto-common 0.1.7", "subtle", ] @@ -874,7 +920,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.12.0", + "block-buffer 0.12.1", "crypto-common 0.2.2", "ctutils", ] @@ -951,6 +997,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "etcetera" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -1022,6 +1078,17 @@ dependencies = [ "spin", ] +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1040,6 +1107,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1213,6 +1286,7 @@ dependencies = [ "libc", "r-efi 6.0.0", "rand_core 0.10.1", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -1261,6 +1335,18 @@ dependencies = [ "foldhash 0.1.5", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -1296,6 +1382,15 @@ dependencies = [ "hashbrown 0.16.1", ] +[[package]] +name = "hashlink" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "heck" version = "0.4.1" @@ -1329,6 +1424,16 @@ dependencies = [ "hmac 0.12.1", ] +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac 0.13.0", + "hmac 0.12.1", +] + [[package]] name = "hkdf" version = "0.13.0" @@ -1347,6 +1452,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", + "digest 0.10.7", +] + [[package]] name = "hmac" version = "0.13.0" @@ -1419,6 +1534,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.9.0" @@ -1660,6 +1784,8 @@ dependencies = [ "axum", "serde", "sqlx 0.9.0", + "serde", + "sqlx 0.9.0", "tokio", ] @@ -1840,6 +1966,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "md-5" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +dependencies = [ + "cfg-if", + "digest 0.11.3", + "digest 0.10.7", +] + [[package]] name = "md-5" version = "0.11.0" @@ -2428,6 +2565,17 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -2472,6 +2620,12 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -2621,6 +2775,7 @@ checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", "digest 0.10.7", + "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", @@ -2801,6 +2956,7 @@ dependencies = [ "serde", "serde_json", "sqlx 0.8.6", + "sqlx 0.8.6", "strum", "thiserror 2.0.18", "time", @@ -2884,6 +3040,7 @@ dependencies = [ "sea-query", "serde_json", "sqlx 0.8.6", + "sqlx 0.8.6", "time", "uuid", ] @@ -3086,6 +3243,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + [[package]] name = "sha1" version = "0.11.0" @@ -3108,6 +3278,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.11.0" @@ -3150,6 +3333,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ + "digest 0.10.7", "digest 0.10.7", "rand_core 0.6.4", ] @@ -3238,6 +3422,24 @@ dependencies = [ "sqlx-sqlite 0.8.6", ] +[[package]] +name = "sqlx" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" +dependencies = [ + "sqlx-core 0.9.0", + "sqlx-macros 0.9.0", + "sqlx-mysql 0.9.0", + "sqlx-postgres 0.9.0", + "sqlx-sqlite 0.9.0", + "sqlx-core 0.8.6", + "sqlx-macros 0.8.6", + "sqlx-mysql 0.8.6", + "sqlx-postgres 0.8.6", + "sqlx-sqlite 0.8.6", +] + [[package]] name = "sqlx" version = "0.9.0" @@ -3271,6 +3473,7 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink 0.10.0", + "hashlink 0.10.0", "indexmap", "log", "memchr", @@ -3280,6 +3483,7 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", + "sha2 0.10.9", "smallvec", "thiserror 2.0.18", "time", @@ -3322,6 +3526,38 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" +dependencies = [ + "base64", + "bytes", + "cfg-if", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.4.1", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.16.1", + "hashlink 0.11.1", + "indexmap", + "log", + "memchr", + "percent-encoding", + "serde", + "serde_json", + "sha2 0.10.9", + "smallvec", + "thiserror 2.0.18", + "tracing", + "url", +] + [[package]] name = "sqlx-macros" version = "0.8.6" @@ -3335,6 +3571,21 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "sqlx-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core 0.9.0", + "sqlx-macros-core 0.9.0", + "sqlx-core 0.8.6", + "sqlx-macros-core 0.8.6", + "syn 2.0.117", +] + [[package]] name = "sqlx-macros" version = "0.9.0" @@ -3368,6 +3619,11 @@ dependencies = [ "sqlx-mysql 0.8.6", "sqlx-postgres 0.8.6", "sqlx-sqlite 0.8.6", + "sha2 0.10.9", + "sqlx-core 0.8.6", + "sqlx-mysql 0.8.6", + "sqlx-postgres 0.8.6", + "sqlx-sqlite 0.8.6", "syn 2.0.117", "tokio", "url", @@ -3398,6 +3654,31 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-macros-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" +dependencies = [ + "cfg-if", + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "sqlx-core 0.9.0", + "sqlx-mysql 0.9.0", + "sqlx-postgres 0.9.0", + "sqlx-sqlite 0.9.0", + "syn 2.0.117", + "thiserror 2.0.18", + "url", +] + [[package]] name = "sqlx-mysql" version = "0.8.6" @@ -3413,6 +3694,7 @@ dependencies = [ "chrono", "crc", "digest 0.10.7", + "digest 0.10.7", "dotenvy", "either", "futures-channel", @@ -3423,9 +3705,12 @@ dependencies = [ "hex", "hkdf 0.12.4", "hmac 0.12.1", + "hkdf 0.12.4", + "hmac 0.12.1", "itoa", "log", "md-5 0.10.6", + "md-5 0.10.6", "memchr", "once_cell", "percent-encoding", @@ -3435,8 +3720,11 @@ dependencies = [ "serde", "sha1 0.10.6", "sha2 0.10.9", + "sha1 0.10.6", + "sha2 0.10.9", "smallvec", "sqlx-core 0.8.6", + "sqlx-core 0.8.6", "stringprep", "thiserror 2.0.18", "time", @@ -3445,6 +3733,33 @@ dependencies = [ "whoami 1.6.1", ] +[[package]] +name = "sqlx-mysql" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" +dependencies = [ + "bitflags", + "byteorder", + "bytes", + "crc", + "digest 0.11.3", + "dotenvy", + "either", + "futures-core", + "futures-util", + "generic-array", + "log", + "percent-encoding", + "serde", + "sha1 0.11.0", + "sha2 0.11.0", + "sqlx-core 0.9.0", + "thiserror 2.0.18", + "tracing", + "whoami 1.6.1", +] + [[package]] name = "sqlx-mysql" version = "0.9.0" @@ -3486,16 +3801,20 @@ dependencies = [ "crc", "dotenvy", "etcetera 0.8.0", + "etcetera 0.8.0", "futures-channel", "futures-core", "futures-util", "hex", "hkdf 0.12.4", "hmac 0.12.1", + "hkdf 0.12.4", + "hmac 0.12.1", "home", "itoa", "log", "md-5 0.10.6", + "md-5 0.10.6", "memchr", "num-bigint", "once_cell", @@ -3504,8 +3823,10 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", + "sha2 0.10.9", "smallvec", "sqlx-core 0.8.6", + "sqlx-core 0.8.6", "stringprep", "thiserror 2.0.18", "time", @@ -3514,6 +3835,42 @@ dependencies = [ "whoami 1.6.1", ] +[[package]] +name = "sqlx-postgres" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera 0.11.0", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf 0.13.0", + "hmac 0.13.0", + "itoa", + "log", + "md-5 0.11.0", + "memchr", + "rand 0.10.1", + "serde", + "serde_json", + "sha2 0.11.0", + "smallvec", + "sqlx-core 0.9.0", + "stringprep", + "thiserror 2.0.18", + "tracing", + "whoami 2.1.2", + "whoami 1.6.1", +] + [[package]] name = "sqlx-postgres" version = "0.9.0" @@ -3558,6 +3915,7 @@ dependencies = [ "atoi", "chrono", "flume 0.11.1", + "flume 0.11.1", "futures-channel", "futures-core", "futures-executor", @@ -3569,6 +3927,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core 0.8.6", + "sqlx-core 0.8.6", "thiserror 2.0.18", "time", "tracing", @@ -3600,6 +3959,30 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-sqlite" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" +dependencies = [ + "atoi", + "flume 0.12.0", + "form_urlencoded", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core 0.9.0", + "thiserror 2.0.18", + "tracing", + "url", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -4087,6 +4470,7 @@ dependencies = [ "rustls 0.22.4", "rustls-pki-types", "sha1 0.10.6", + "sha1 0.10.6", "thiserror 1.0.69", "url", "utf-8", @@ -4456,6 +4840,12 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" +[[package]] +name = "whoami" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" + [[package]] name = "winapi" version = "0.3.9" From 36a76edac93850f55f488fe7d8b502d886132ae0 Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Sun, 14 Jun 2026 01:01:14 +0900 Subject: [PATCH 09/10] Impl --- .dockerignore | 10 + .env.example | 44 ++ Cargo.lock | 220 +++++++- Dockerfile | 26 + backend/Cargo.toml | 17 +- backend/migrations/0001_create_zakonim.sql | 5 + backend/migrations/0002_create_issues.sql | 59 +++ .../migrations/0003_create_message_events.sql | 11 + .../migrations/0004_create_summary_drafts.sql | 28 + backend/src/error.rs | 30 ++ backend/src/etc/anthropic.rs | 86 ++++ backend/src/etc/llm.rs | 85 ++++ backend/src/etc/mod.rs | 5 +- backend/src/etc/openai.rs | 91 ++++ backend/src/etc/startup.rs | 54 +- backend/src/handlers/comment.rs | 60 +++ backend/src/handlers/discord.rs | 64 +++ backend/src/handlers/issue.rs | 268 ++++++++++ backend/src/handlers/mod.rs | 5 +- backend/src/handlers/summary.rs | 156 ++++++ backend/src/handlers/zakonim.rs | 63 ++- backend/src/lib.rs | 21 - backend/src/main.rs | 55 ++ backend/src/models.rs | 98 ++++ bot/Cargo.toml | 11 +- bot/src/api.rs | 310 +++++++++++ bot/src/cache.rs | 47 ++ bot/src/commands/issue.rs | 481 +++++++++++++++++- bot/src/commands/mod.rs | 3 +- bot/src/commands/zakonim.rs | 11 - bot/src/config.rs | 54 ++ bot/src/events.rs | 199 ++++++++ bot/src/lib.rs | 45 -- bot/src/main.rs | 96 ++++ bot/src/render.rs | 64 +++ compose.yml | 51 ++ 36 files changed, 2815 insertions(+), 118 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 backend/migrations/0001_create_zakonim.sql create mode 100644 backend/migrations/0002_create_issues.sql create mode 100644 backend/migrations/0003_create_message_events.sql create mode 100644 backend/migrations/0004_create_summary_drafts.sql create mode 100644 backend/src/error.rs create mode 100644 backend/src/etc/anthropic.rs create mode 100644 backend/src/etc/llm.rs create mode 100644 backend/src/etc/openai.rs create mode 100644 backend/src/handlers/comment.rs create mode 100644 backend/src/handlers/discord.rs create mode 100644 backend/src/handlers/summary.rs delete mode 100644 backend/src/lib.rs create mode 100644 backend/src/main.rs create mode 100644 backend/src/models.rs create mode 100644 bot/src/api.rs create mode 100644 bot/src/cache.rs delete mode 100644 bot/src/commands/zakonim.rs create mode 100644 bot/src/config.rs create mode 100644 bot/src/events.rs delete mode 100644 bot/src/lib.rs create mode 100644 bot/src/main.rs create mode 100644 bot/src/render.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..103cecc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +# Secrets are injected at runtime via compose env_file, never baked into the image. +.env + +# Build output and local tooling — keep the build context small. +target +.git +.direnv +.jj +**/*.log +result diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..26c6886 --- /dev/null +++ b/.env.example @@ -0,0 +1,44 @@ +# Copy to .env and fill in. Compose reads this file for every service. + +# --- Postgres (db service + backend) --- +POSTGRES_USER=zako +POSTGRES_PASSWORD=zako +POSTGRES_DB=zako +# Host is the compose service name `db`. Use localhost:5432 when running outside compose. +DATABASE_URL=postgres://zako:zako@db:5432/zako + +# --- Backend --- +# BIND_ADDR is set to 0.0.0.0:80 by compose; override only when running standalone. + +# --- LLM provider (AI summary flow) --- +# Selects the backend: `anthropic` (default) or `openai` (any OpenAI-compatible API). +# Leave the active provider's API key empty to disable the summary flow (drafts return 502). +LLM_PROVIDER=anthropic + +# Anthropic provider: +ANTHROPIC_API_KEY= +# Optional; defaults to claude-opus-4-8. +ANTHROPIC_MODEL= + +# OpenAI-compatible provider (OpenAI, Ollama, OpenRouter, vLLM, ...): +OPENAI_API_KEY= +# Optional; defaults to https://api.openai.com/v1. For local Ollama use http://localhost:11434/v1. +OPENAI_BASE_URL= +# Optional; defaults to gpt-4o. +OPENAI_MODEL= + +# --- Bot --- +DISCORD_TOKEN= +# BACKEND_URL is set to http://backend:80 by compose; override only when running standalone. +# Optional: register slash commands instantly in one guild (otherwise global, ~1h propagation). +GUILD_ID= +# Channel where confirmed issues are posted. +ISSUE_CHANNEL_ID= +# Forum channel; /issue new inside one of its threads copies the thread to the issue channel. +FORUM_ISSUE_CHANNEL_ID= +# Comma-separated Discord user IDs allowed to close/edit issues. +DEV_USER_IDS= + +# --- Optional Redis author cache --- +# Start Redis with: podman compose --profile cache up +# REDIS_URL=redis://redis:6379 diff --git a/Cargo.lock b/Cargo.lock index 0155d24..0437197 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,6 +105,15 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arc-swap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] + [[package]] name = "arrayvec" version = "0.7.6" @@ -345,6 +354,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "backon" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" +dependencies = [ + "fastrand", +] + [[package]] name = "base64" version = "0.22.1" @@ -575,8 +593,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link", ] @@ -638,6 +658,20 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "command_attr" version = "0.5.4" @@ -1136,6 +1170,7 @@ checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -1597,7 +1632,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -1785,6 +1820,7 @@ dependencies = [ "serde", "sqlx 0.9.0", "serde", + "serde_json", "sqlx 0.9.0", "tokio", ] @@ -1798,6 +1834,10 @@ name = "it-bot" version = "0.1.0" dependencies = [ "poise", + "redis", + "reqwest", + "serde", + "serde_json", "serenity", "tokio", ] @@ -1812,6 +1852,15 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -2464,7 +2513,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.40", - "socket2", + "socket2 0.6.3", "thiserror 2.0.18", "tokio", "tracing", @@ -2501,7 +2550,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -3380,6 +3429,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "socket2" version = "0.6.3" @@ -3558,6 +3617,43 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" +dependencies = [ + "base64", + "bytes", + "cfg-if", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.4.1", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.16.1", + "hashlink 0.11.1", + "indexmap", + "log", + "memchr", + "percent-encoding", + "rustls 0.23.40", + "serde", + "serde_json", + "sha2 0.10.9", + "smallvec", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", + "url", + "webpki-roots 1.0.7", +] + [[package]] name = "sqlx-macros" version = "0.8.6" @@ -3679,6 +3775,32 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-macros-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" +dependencies = [ + "cfg-if", + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "sqlx-core 0.9.0", + "sqlx-mysql 0.9.0", + "sqlx-postgres 0.9.0", + "sqlx-sqlite 0.9.0", + "syn 2.0.117", + "thiserror 2.0.18", + "tokio", + "url", +] + [[package]] name = "sqlx-mysql" version = "0.8.6" @@ -3784,6 +3906,34 @@ dependencies = [ "sqlx-core 0.9.0", "thiserror 2.0.18", "tracing", + "whoami 1.6.1", +] + +[[package]] +name = "sqlx-mysql" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" +dependencies = [ + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "digest 0.11.3", + "dotenvy", + "either", + "futures-core", + "futures-util", + "generic-array", + "log", + "percent-encoding", + "serde", + "sha1 0.11.0", + "sha2 0.11.0", + "sqlx-core 0.9.0", + "thiserror 2.0.18", + "tracing", ] [[package]] @@ -3904,6 +4054,43 @@ dependencies = [ "thiserror 2.0.18", "tracing", "whoami 2.1.2", + "whoami 1.6.1", +] + +[[package]] +name = "sqlx-postgres" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera 0.11.0", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf 0.13.0", + "hmac 0.13.0", + "itoa", + "log", + "md-5 0.11.0", + "memchr", + "rand 0.10.1", + "serde", + "serde_json", + "sha2 0.11.0", + "smallvec", + "sqlx-core 0.9.0", + "stringprep", + "thiserror 2.0.18", + "tracing", + "whoami 2.1.2", ] [[package]] @@ -3983,6 +4170,31 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-sqlite" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" +dependencies = [ + "atoi", + "chrono", + "flume 0.12.0", + "form_urlencoded", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core 0.9.0", + "thiserror 2.0.18", + "tracing", + "url", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -4208,7 +4420,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", ] diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c949d2d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# syntax=docker/dockerfile:1 + +# ---- Build both workspace binaries in one shared stage ---- +FROM rust:1-bookworm AS builder +WORKDIR /app +COPY . . +# Only the two runnable services are built; the legacy SeaORM crates are skipped. +RUN cargo build --release -p it-backend -p it-bot + +# ---- Minimal runtime base (TLS roots for Postgres/Anthropic/Discord) ---- +FROM debian:bookworm-slim AS runtime +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# ---- Backend service ---- +FROM runtime AS backend +COPY --from=builder /app/target/release/it-backend /usr/local/bin/it-backend +ENV BIND_ADDR=0.0.0.0:80 +EXPOSE 80 +CMD ["it-backend"] + +# ---- Bot service ---- +FROM runtime AS bot +COPY --from=builder /app/target/release/it-bot /usr/local/bin/it-bot +CMD ["it-bot"] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 6abb9e3..90a2c67 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -5,6 +5,19 @@ edition = "2024" [dependencies] axum = "0.8.9" -serde = "1.0.228" -sqlx = "0.9.0" +chrono = { version = "0.4", features = ["serde"] } +reqwest = { version = "0.12", default-features = false, features = [ + "json", + "rustls-tls", +] } +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1" +sqlx = { version = "0.9.0", default-features = false, features = [ + "runtime-tokio", + "tls-rustls-ring", + "postgres", + "macros", + "migrate", + "chrono", +] } tokio.workspace = true diff --git a/backend/migrations/0001_create_zakonim.sql b/backend/migrations/0001_create_zakonim.sql new file mode 100644 index 0000000..e551507 --- /dev/null +++ b/backend/migrations/0001_create_zakonim.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS zakonim ( + id TEXT PRIMARY KEY, + description TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); diff --git a/backend/migrations/0002_create_issues.sql b/backend/migrations/0002_create_issues.sql new file mode 100644 index 0000000..3c2c1d8 --- /dev/null +++ b/backend/migrations/0002_create_issues.sql @@ -0,0 +1,59 @@ +CREATE TYPE issue_priority AS ENUM ('P1', 'P2', 'P3', 'P4'); +CREATE TYPE issue_status AS ENUM ('Open', 'InProgress', 'Resolved', 'Closed'); + +-- Bump updated_at on every UPDATE. Shared by issues and comments. +CREATE FUNCTION set_updated_at() RETURNS trigger AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TABLE issues ( + id BIGSERIAL PRIMARY KEY, + title TEXT NOT NULL, + description TEXT NOT NULL DEFAULT '', + priority issue_priority NOT NULL DEFAULT 'P3', + author TEXT NOT NULL, + status issue_status NOT NULL DEFAULT 'Open', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TRIGGER issues_set_updated_at + BEFORE UPDATE ON issues + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + +CREATE INDEX issues_status_idx ON issues (status); +CREATE INDEX issues_priority_idx ON issues (priority); + +CREATE TABLE issue_assignees ( + issue_id BIGINT NOT NULL REFERENCES issues (id) ON DELETE CASCADE, + author_id TEXT NOT NULL, + PRIMARY KEY (issue_id, author_id) +); + +-- The Map from ISSUE.md. key e.g. 'original_reference', 'issue'. +CREATE TABLE issue_discord_messages ( + issue_id BIGINT NOT NULL REFERENCES issues (id) ON DELETE CASCADE, + key TEXT NOT NULL, + guild_id BIGINT NOT NULL, + channel_id BIGINT NOT NULL, + message_id BIGINT NOT NULL, + PRIMARY KEY (issue_id, key) +); + +CREATE TABLE comments ( + id BIGSERIAL PRIMARY KEY, + issue_id BIGINT NOT NULL REFERENCES issues (id) ON DELETE CASCADE, + author TEXT NOT NULL, + content TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TRIGGER comments_set_updated_at + BEFORE UPDATE ON comments + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + +CREATE INDEX comments_issue_id_idx ON comments (issue_id); diff --git a/backend/migrations/0003_create_message_events.sql b/backend/migrations/0003_create_message_events.sql new file mode 100644 index 0000000..0b25533 --- /dev/null +++ b/backend/migrations/0003_create_message_events.sql @@ -0,0 +1,11 @@ +-- Mirror of Discord messages, fed by the bot's message create/edit/delete events. +CREATE TABLE message_events ( + message_id BIGINT PRIMARY KEY, + guild_id BIGINT, + channel_id BIGINT NOT NULL, + author_id TEXT, + content TEXT, + created_at TIMESTAMPTZ NOT NULL, + edited_at TIMESTAMPTZ, + deleted_at TIMESTAMPTZ +); diff --git a/backend/migrations/0004_create_summary_drafts.sql b/backend/migrations/0004_create_summary_drafts.sql new file mode 100644 index 0000000..aa4281a --- /dev/null +++ b/backend/migrations/0004_create_summary_drafts.sql @@ -0,0 +1,28 @@ +CREATE TYPE summary_status AS ENUM ('Pending', 'Confirmed', 'Discarded'); + +-- AI-summary conversation: one draft per temp channel, refined with the dev before becoming an issue. +CREATE TABLE summary_drafts ( + id BIGSERIAL PRIMARY KEY, + temp_channel_id BIGINT NOT NULL, + author_id TEXT NOT NULL, + priority issue_priority, + summary TEXT, + status summary_status NOT NULL DEFAULT 'Pending', + issue_id BIGINT REFERENCES issues (id) ON DELETE SET NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TRIGGER summary_drafts_set_updated_at + BEFORE UPDATE ON summary_drafts + FOR EACH ROW EXECUTE FUNCTION set_updated_at(); + +CREATE TABLE summary_draft_messages ( + id BIGSERIAL PRIMARY KEY, + draft_id BIGINT NOT NULL REFERENCES summary_drafts (id) ON DELETE CASCADE, + role TEXT NOT NULL, + content TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX summary_draft_messages_draft_id_idx ON summary_draft_messages (draft_id); diff --git a/backend/src/error.rs b/backend/src/error.rs new file mode 100644 index 0000000..cd3078b --- /dev/null +++ b/backend/src/error.rs @@ -0,0 +1,30 @@ +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, +}; + +/// Application-wide error type. Wraps database, request, and upstream (AI) failures and +/// renders them as the appropriate HTTP status instead of panicking. +pub enum AppError { + Db(sqlx::Error), + NotFound(String), + BadRequest(String), + Upstream(String), +} + +impl From for AppError { + fn from(err: sqlx::Error) -> Self { + AppError::Db(err) + } +} + +impl IntoResponse for AppError { + fn into_response(self) -> Response { + match self { + AppError::Db(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), + AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg).into_response(), + AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg).into_response(), + AppError::Upstream(msg) => (StatusCode::BAD_GATEWAY, msg).into_response(), + } + } +} diff --git a/backend/src/etc/anthropic.rs b/backend/src/etc/anthropic.rs new file mode 100644 index 0000000..64c644d --- /dev/null +++ b/backend/src/etc/anthropic.rs @@ -0,0 +1,86 @@ +use serde::{Deserialize, Serialize}; + +use crate::error::AppError; + +const API_URL: &str = "https://api.anthropic.com/v1/messages"; +const VERSION: &str = "2023-06-01"; + +#[derive(Serialize)] +struct Message<'a> { + role: &'a str, + content: &'a str, +} + +#[derive(Serialize)] +struct Request<'a> { + model: &'a str, + max_tokens: u32, + system: &'a str, + messages: Vec>, +} + +#[derive(Deserialize)] +struct Block { + #[serde(default)] + text: String, +} + +#[derive(Deserialize)] +struct ResponseBody { + content: Vec, +} + +/// Send the running transcript to Claude and return the assistant's reply text. +pub async fn reply( + http: &reqwest::Client, + api_key: &str, + model: &str, + system: &str, + transcript: &[(String, String)], +) -> Result { + let messages = transcript + .iter() + .map(|(role, content)| Message { + role: role.as_str(), + content: content.as_str(), + }) + .collect(); + + let body = Request { + model, + max_tokens: 1024, + system, + messages, + }; + + let resp = http + .post(API_URL) + .header("x-api-key", api_key) + .header("anthropic-version", VERSION) + .json(&body) + .send() + .await + .map_err(|e| AppError::Upstream(format!("request to Anthropic failed: {e}")))?; + + if !resp.status().is_success() { + let status = resp.status(); + let detail = resp.text().await.unwrap_or_default(); + return Err(AppError::Upstream(format!( + "Anthropic returned {status}: {detail}" + ))); + } + + let parsed: ResponseBody = resp + .json() + .await + .map_err(|e| AppError::Upstream(format!("invalid Anthropic response: {e}")))?; + + let text = parsed + .content + .into_iter() + .map(|b| b.text) + .collect::>() + .join(""); + + Ok(text) +} diff --git a/backend/src/etc/llm.rs b/backend/src/etc/llm.rs new file mode 100644 index 0000000..52b9ead --- /dev/null +++ b/backend/src/etc/llm.rs @@ -0,0 +1,85 @@ +use crate::error::AppError; +use crate::etc::{anthropic, openai}; + +/// System prompt shared by every provider — defines the issue-triage assistant behavior. +pub const SYSTEM_PROMPT: &str = "You are an issue-triage assistant embedded in a Discord bot. \ +Through conversation with a developer, produce and iteratively refine a concise, well-structured \ +issue summary (a short title line followed by a clear description of the problem, repro steps if \ +any, and impact). Ask brief clarifying questions only when essential. When the developer signals \ +they are happy, output the final summary cleanly with no extra commentary."; + +/// Which LLM backend the summary flow talks to. +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Provider { + Anthropic, + OpenAiCompatible, +} + +/// Runtime LLM configuration, assembled from the environment at startup. +#[derive(Clone)] +pub struct LlmConfig { + pub provider: Provider, + pub api_key: String, + pub model: String, + /// Base URL for the OpenAI-compatible API (ignored by the Anthropic provider). + pub base_url: String, +} + +/// Build the LLM configuration from environment variables. +/// +/// `LLM_PROVIDER` selects the backend (`anthropic` default, or `openai` for any +/// OpenAI-compatible API such as OpenAI, Ollama, OpenRouter, or vLLM). +pub fn from_env() -> LlmConfig { + let provider = match std::env::var("LLM_PROVIDER") + .unwrap_or_default() + .to_ascii_lowercase() + .as_str() + { + "openai" => Provider::OpenAiCompatible, + _ => Provider::Anthropic, + }; + + match provider { + Provider::Anthropic => LlmConfig { + provider, + api_key: std::env::var("ANTHROPIC_API_KEY").unwrap_or_default(), + model: std::env::var("ANTHROPIC_MODEL").unwrap_or_else(|_| "claude-opus-4-8".into()), + base_url: String::new(), + }, + Provider::OpenAiCompatible => LlmConfig { + provider, + api_key: std::env::var("OPENAI_API_KEY").unwrap_or_default(), + model: std::env::var("OPENAI_MODEL").unwrap_or_else(|_| "gpt-4o".into()), + base_url: std::env::var("OPENAI_BASE_URL") + .unwrap_or_else(|_| "https://api.openai.com/v1".into()), + }, + } +} + +/// Send the running transcript to the configured provider and return the assistant's reply text. +pub async fn reply( + http: &reqwest::Client, + cfg: &LlmConfig, + transcript: &[(String, String)], +) -> Result { + if cfg.api_key.is_empty() { + return Err(AppError::Upstream("LLM API key is not configured".into())); + } + + match cfg.provider { + Provider::Anthropic => { + anthropic::reply(http, &cfg.api_key, &cfg.model, SYSTEM_PROMPT, transcript).await + } + Provider::OpenAiCompatible => { + openai::reply( + http, + &cfg.base_url, + &cfg.api_key, + &cfg.model, + SYSTEM_PROMPT, + transcript, + ) + .await + } + } +} diff --git a/backend/src/etc/mod.rs b/backend/src/etc/mod.rs index f58fb45..7db7c4b 100644 --- a/backend/src/etc/mod.rs +++ b/backend/src/etc/mod.rs @@ -1 +1,4 @@ -pub mod startup; \ No newline at end of file +pub mod anthropic; +pub mod llm; +pub mod openai; +pub mod startup; diff --git a/backend/src/etc/openai.rs b/backend/src/etc/openai.rs new file mode 100644 index 0000000..e62ea18 --- /dev/null +++ b/backend/src/etc/openai.rs @@ -0,0 +1,91 @@ +use serde::{Deserialize, Serialize}; + +use crate::error::AppError; + +#[derive(Serialize)] +struct Message<'a> { + role: &'a str, + content: &'a str, +} + +#[derive(Serialize)] +struct Request<'a> { + model: &'a str, + max_tokens: u32, + messages: Vec>, +} + +#[derive(Deserialize)] +struct ResponseMessage { + #[serde(default)] + content: String, +} + +#[derive(Deserialize)] +struct Choice { + message: ResponseMessage, +} + +#[derive(Deserialize)] +struct ResponseBody { + choices: Vec, +} + +/// Send the running transcript to an OpenAI-compatible Chat Completions API and return the +/// assistant's reply text. Works with OpenAI, Ollama, OpenRouter, vLLM, etc. via `base_url`. +pub async fn reply( + http: &reqwest::Client, + base_url: &str, + api_key: &str, + model: &str, + system: &str, + transcript: &[(String, String)], +) -> Result { + // Prepend the system prompt, then the running transcript. + let mut messages = Vec::with_capacity(transcript.len() + 1); + messages.push(Message { + role: "system", + content: system, + }); + messages.extend(transcript.iter().map(|(role, content)| Message { + role: role.as_str(), + content: content.as_str(), + })); + + let body = Request { + model, + max_tokens: 1024, + messages, + }; + + let url = format!("{}/chat/completions", base_url.trim_end_matches('/')); + let resp = http + .post(url) + .bearer_auth(api_key) + .json(&body) + .send() + .await + .map_err(|e| AppError::Upstream(format!("request to OpenAI-compatible API failed: {e}")))?; + + if !resp.status().is_success() { + let status = resp.status(); + let detail = resp.text().await.unwrap_or_default(); + return Err(AppError::Upstream(format!( + "OpenAI-compatible API returned {status}: {detail}" + ))); + } + + let parsed: ResponseBody = resp + .json() + .await + .map_err(|e| AppError::Upstream(format!("invalid OpenAI-compatible response: {e}")))?; + + let text = parsed + .choices + .into_iter() + .next() + .map(|c| c.message.content) + .unwrap_or_default(); + + Ok(text) +} diff --git a/backend/src/etc/startup.rs b/backend/src/etc/startup.rs index 4320fc1..35ec946 100644 --- a/backend/src/etc/startup.rs +++ b/backend/src/etc/startup.rs @@ -1,7 +1,51 @@ +use std::time::Duration; +use sqlx::PgPool; +use sqlx::postgres::PgPoolOptions; -pub fn init() { - // Database Check - // if -> database not ready -> create database - // else -> nothing -} \ No newline at end of file +use crate::etc::llm::{self, LlmConfig}; + +/// Shared, cheaply-cloneable application state handed to every handler. +#[derive(Clone)] +pub struct AppState { + pub pool: PgPool, + pub http: reqwest::Client, + pub llm: LlmConfig, +} + +/// Connect to Postgres, run pending migrations, and assemble the shared state. +pub async fn init() -> AppState { + let url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + + let pool = connect_with_retry(&url).await; + + sqlx::migrate!("./migrations") + .run(&pool) + .await + .expect("migrations failed"); + + AppState { + pool, + http: reqwest::Client::new(), + llm: llm::from_env(), + } +} + +/// Connect to Postgres, retrying for a while so the service can start before the database is ready +/// (e.g. under docker-compose). Panics only after exhausting all attempts. +async fn connect_with_retry(url: &str) -> PgPool { + const ATTEMPTS: u32 = 30; + const DELAY: Duration = Duration::from_secs(2); + + for attempt in 1..=ATTEMPTS { + match PgPoolOptions::new().max_connections(5).connect(url).await { + Ok(pool) => return pool, + Err(e) if attempt < ATTEMPTS => { + eprintln!("postgres not ready (attempt {attempt}/{ATTEMPTS}): {e}; retrying..."); + tokio::time::sleep(DELAY).await; + } + Err(e) => panic!("failed to connect to Postgres after {ATTEMPTS} attempts: {e}"), + } + } + unreachable!() +} diff --git a/backend/src/handlers/comment.rs b/backend/src/handlers/comment.rs new file mode 100644 index 0000000..782afeb --- /dev/null +++ b/backend/src/handlers/comment.rs @@ -0,0 +1,60 @@ +use axum::{ + Json, + extract::{Path, State}, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use serde::Deserialize; + +use crate::error::AppError; +use crate::etc::startup::AppState; +use crate::models::Comment; + +#[derive(Deserialize)] +pub struct CreateComment { + pub author: String, + pub content: String, +} + +/// Add a comment to an issue. 404s if the issue does not exist. +pub async fn create( + State(state): State, + Path(issue_id): Path, + Json(req): Json, +) -> Result { + let exists: bool = sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM issues WHERE id = $1)") + .bind(issue_id) + .fetch_one(&state.pool) + .await?; + if !exists { + return Err(AppError::NotFound(format!("issue {issue_id} not found"))); + } + + let comment = sqlx::query_as::<_, Comment>( + "INSERT INTO comments (issue_id, author, content) VALUES ($1, $2, $3) \ + RETURNING id, issue_id, author, content, created_at, updated_at", + ) + .bind(issue_id) + .bind(&req.author) + .bind(&req.content) + .fetch_one(&state.pool) + .await?; + + Ok((StatusCode::CREATED, Json(comment)).into_response()) +} + +/// List an issue's comments, oldest first. +pub async fn list( + State(state): State, + Path(issue_id): Path, +) -> Result>, AppError> { + let rows = sqlx::query_as::<_, Comment>( + "SELECT id, issue_id, author, content, created_at, updated_at \ + FROM comments WHERE issue_id = $1 ORDER BY created_at ASC", + ) + .bind(issue_id) + .fetch_all(&state.pool) + .await?; + + Ok(Json(rows)) +} diff --git a/backend/src/handlers/discord.rs b/backend/src/handlers/discord.rs new file mode 100644 index 0000000..70255de --- /dev/null +++ b/backend/src/handlers/discord.rs @@ -0,0 +1,64 @@ +use axum::{ + Json, + extract::{Path, State}, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use chrono::{DateTime, Utc}; +use serde::Deserialize; + +use crate::error::AppError; +use crate::etc::startup::AppState; +use crate::models::MessageEvent; + +#[derive(Deserialize)] +pub struct UpsertMessage { + pub guild_id: Option, + pub channel_id: i64, + pub author_id: Option, + pub content: Option, + pub created_at: DateTime, + /// Present when this upsert is for an edit rather than the original create. + pub edited_at: Option>, +} + +/// Mirror a Discord message create/edit into the DB (upsert keyed on message_id). +pub async fn upsert( + State(state): State, + Path(message_id): Path, + Json(req): Json, +) -> Result { + let event = sqlx::query_as::<_, MessageEvent>( + "INSERT INTO message_events \ + (message_id, guild_id, channel_id, author_id, content, created_at, edited_at) \ + VALUES ($1, $2, $3, $4, $5, $6, $7) \ + ON CONFLICT (message_id) DO UPDATE SET \ + content = EXCLUDED.content, \ + edited_at = EXCLUDED.edited_at \ + RETURNING message_id, guild_id, channel_id, author_id, content, \ + created_at, edited_at, deleted_at", + ) + .bind(message_id) + .bind(req.guild_id) + .bind(req.channel_id) + .bind(&req.author_id) + .bind(&req.content) + .bind(req.created_at) + .bind(req.edited_at) + .fetch_one(&state.pool) + .await?; + + Ok((StatusCode::OK, Json(event)).into_response()) +} + +/// Soft-delete a mirrored message by stamping deleted_at. No-op if unknown. +pub async fn delete( + State(state): State, + Path(message_id): Path, +) -> Result { + sqlx::query("UPDATE message_events SET deleted_at = now() WHERE message_id = $1") + .bind(message_id) + .execute(&state.pool) + .await?; + Ok(StatusCode::NO_CONTENT) +} diff --git a/backend/src/handlers/issue.rs b/backend/src/handlers/issue.rs index e69de29..d54d9d0 100644 --- a/backend/src/handlers/issue.rs +++ b/backend/src/handlers/issue.rs @@ -0,0 +1,268 @@ +use axum::{ + Json, + extract::{Path, Query, State}, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use serde::Deserialize; + +use crate::error::AppError; +use crate::etc::startup::AppState; +use crate::models::{DiscordMessageRef, Issue, IssueDetail, Priority, Status}; + +#[derive(Deserialize)] +pub struct DiscordMessageInput { + pub key: String, + pub guild_id: i64, + pub channel_id: i64, + pub message_id: i64, +} + +#[derive(Deserialize)] +pub struct CreateIssue { + pub title: String, + #[serde(default)] + pub description: String, + #[serde(default = "default_priority")] + pub priority: Priority, + pub author: String, + #[serde(default)] + pub assignees: Vec, + #[serde(default)] + pub discord_messages: Vec, +} + +fn default_priority() -> Priority { + Priority::P3 +} + +/// Create an issue along with its assignees and Discord message references in one transaction. +pub async fn create( + State(state): State, + Json(req): Json, +) -> Result { + let mut tx = state.pool.begin().await?; + + let issue = sqlx::query_as::<_, Issue>( + "INSERT INTO issues (title, description, priority, author) \ + VALUES ($1, $2, $3, $4) \ + RETURNING id, title, description, priority, author, status, created_at, updated_at", + ) + .bind(&req.title) + .bind(&req.description) + .bind(req.priority) + .bind(&req.author) + .fetch_one(&mut *tx) + .await?; + + for assignee in &req.assignees { + sqlx::query("INSERT INTO issue_assignees (issue_id, author_id) VALUES ($1, $2)") + .bind(issue.id) + .bind(assignee) + .execute(&mut *tx) + .await?; + } + + for m in &req.discord_messages { + sqlx::query( + "INSERT INTO issue_discord_messages (issue_id, key, guild_id, channel_id, message_id) \ + VALUES ($1, $2, $3, $4, $5)", + ) + .bind(issue.id) + .bind(&m.key) + .bind(m.guild_id) + .bind(m.channel_id) + .bind(m.message_id) + .execute(&mut *tx) + .await?; + } + + tx.commit().await?; + + Ok((StatusCode::CREATED, Json(issue)).into_response()) +} + +#[derive(Deserialize)] +pub struct ListQuery { + pub status: Option, + pub sort: Option, +} + +/// List issues. Defaults to Open issues sorted newest-first. `status=All` removes the filter. +pub async fn list( + State(state): State, + Query(q): Query, +) -> Result>, AppError> { + let by_priority = matches!(q.sort.as_deref(), Some("priority")); + + let status = q.status.as_deref().unwrap_or("Open"); + let filter_all = status.eq_ignore_ascii_case("all"); + + // sqlx requires &'static str queries, so enumerate the fixed branches rather than build SQL. + let rows = if filter_all { + let sql = if by_priority { + "SELECT id, title, description, priority, author, status, created_at, updated_at \ + FROM issues ORDER BY priority ASC, created_at DESC" + } else { + "SELECT id, title, description, priority, author, status, created_at, updated_at \ + FROM issues ORDER BY created_at DESC" + }; + sqlx::query_as::<_, Issue>(sql) + .fetch_all(&state.pool) + .await? + } else { + let status = parse_status(status)?; + let sql = if by_priority { + "SELECT id, title, description, priority, author, status, created_at, updated_at \ + FROM issues WHERE status = $1 ORDER BY priority ASC, created_at DESC" + } else { + "SELECT id, title, description, priority, author, status, created_at, updated_at \ + FROM issues WHERE status = $1 ORDER BY created_at DESC" + }; + sqlx::query_as::<_, Issue>(sql) + .bind(status) + .fetch_all(&state.pool) + .await? + }; + + Ok(Json(rows)) +} + +/// Fetch a single issue with its assignees and Discord message references. +pub async fn get( + State(state): State, + Path(id): Path, +) -> Result, AppError> { + let issue = sqlx::query_as::<_, Issue>( + "SELECT id, title, description, priority, author, status, created_at, updated_at \ + FROM issues WHERE id = $1", + ) + .bind(id) + .fetch_optional(&state.pool) + .await? + .ok_or_else(|| AppError::NotFound(format!("issue {id} not found")))?; + + let assignees: Vec = + sqlx::query_scalar("SELECT author_id FROM issue_assignees WHERE issue_id = $1") + .bind(id) + .fetch_all(&state.pool) + .await?; + + let discord_messages = sqlx::query_as::<_, DiscordMessageRef>( + "SELECT key, guild_id, channel_id, message_id \ + FROM issue_discord_messages WHERE issue_id = $1", + ) + .bind(id) + .fetch_all(&state.pool) + .await?; + + Ok(Json(IssueDetail { + issue, + assignees, + discord_messages, + })) +} + +#[derive(Deserialize)] +pub struct EditIssue { + pub title: Option, + pub description: Option, + pub priority: Option, + pub status: Option, + /// When present, replaces the full set of assignees. + pub assignees: Option>, +} + +/// Partially update an issue. Closing is just `status = Closed`. Replaces assignees if given. +pub async fn edit( + State(state): State, + Path(id): Path, + Json(req): Json, +) -> Result, AppError> { + let mut tx = state.pool.begin().await?; + + let issue = sqlx::query_as::<_, Issue>( + "UPDATE issues SET \ + title = COALESCE($2, title), \ + description = COALESCE($3, description), \ + priority = COALESCE($4, priority), \ + status = COALESCE($5, status) \ + WHERE id = $1 \ + RETURNING id, title, description, priority, author, status, created_at, updated_at", + ) + .bind(id) + .bind(req.title) + .bind(req.description) + .bind(req.priority) + .bind(req.status) + .fetch_optional(&mut *tx) + .await? + .ok_or_else(|| AppError::NotFound(format!("issue {id} not found")))?; + + if let Some(assignees) = &req.assignees { + sqlx::query("DELETE FROM issue_assignees WHERE issue_id = $1") + .bind(id) + .execute(&mut *tx) + .await?; + for assignee in assignees { + sqlx::query("INSERT INTO issue_assignees (issue_id, author_id) VALUES ($1, $2)") + .bind(id) + .bind(assignee) + .execute(&mut *tx) + .await?; + } + } + + tx.commit().await?; + Ok(Json(issue)) +} + +#[derive(Deserialize)] +pub struct SetDiscordMessage { + pub guild_id: i64, + pub channel_id: i64, + pub message_id: i64, +} + +/// Upsert a single Discord message reference (e.g. key `issue` or `original_reference`) for an +/// issue. Used by the bot to record where it posted the issue after creation. +pub async fn set_discord_message( + State(state): State, + Path((id, key)): Path<(i64, String)>, + Json(req): Json, +) -> Result { + let result = sqlx::query( + "INSERT INTO issue_discord_messages (issue_id, key, guild_id, channel_id, message_id) \ + VALUES ($1, $2, $3, $4, $5) \ + ON CONFLICT (issue_id, key) DO UPDATE SET \ + guild_id = EXCLUDED.guild_id, \ + channel_id = EXCLUDED.channel_id, \ + message_id = EXCLUDED.message_id", + ) + .bind(id) + .bind(&key) + .bind(req.guild_id) + .bind(req.channel_id) + .bind(req.message_id) + .execute(&state.pool) + .await; + + if let Err(sqlx::Error::Database(e)) = &result + && e.is_foreign_key_violation() + { + return Err(AppError::NotFound(format!("issue {id} not found"))); + } + result?; + + Ok(StatusCode::NO_CONTENT) +} + +fn parse_status(s: &str) -> Result { + match s { + "Open" => Ok(Status::Open), + "InProgress" => Ok(Status::InProgress), + "Resolved" => Ok(Status::Resolved), + "Closed" => Ok(Status::Closed), + other => Err(AppError::BadRequest(format!("unknown status: {other}"))), + } +} diff --git a/backend/src/handlers/mod.rs b/backend/src/handlers/mod.rs index a819916..c9ff455 100644 --- a/backend/src/handlers/mod.rs +++ b/backend/src/handlers/mod.rs @@ -1,3 +1,6 @@ +pub mod comment; +pub mod discord; pub mod issue; +pub mod root; +pub mod summary; pub mod zakonim; -pub mod root; \ No newline at end of file diff --git a/backend/src/handlers/summary.rs b/backend/src/handlers/summary.rs new file mode 100644 index 0000000..6e1fc11 --- /dev/null +++ b/backend/src/handlers/summary.rs @@ -0,0 +1,156 @@ +use axum::{ + Json, + extract::{Path, State}, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use serde::{Deserialize, Serialize}; + +use crate::error::AppError; +use crate::etc::llm; +use crate::etc::startup::AppState; +use crate::models::{DraftMessage, Issue, Priority, SummaryDraft}; + +#[derive(Deserialize)] +pub struct CreateDraft { + pub temp_channel_id: i64, + pub author_id: String, + pub priority: Option, +} + +/// Start a summary draft for a freshly-created temp channel. +pub async fn create( + State(state): State, + Json(req): Json, +) -> Result { + let draft = sqlx::query_as::<_, SummaryDraft>( + "INSERT INTO summary_drafts (temp_channel_id, author_id, priority) \ + VALUES ($1, $2, $3) \ + RETURNING id, temp_channel_id, author_id, priority, summary, status, issue_id, \ + created_at, updated_at", + ) + .bind(req.temp_channel_id) + .bind(&req.author_id) + .bind(req.priority) + .fetch_one(&state.pool) + .await?; + + Ok((StatusCode::CREATED, Json(draft)).into_response()) +} + +#[derive(Deserialize)] +pub struct PostMessage { + pub content: String, +} + +#[derive(Serialize)] +pub struct MessageReply { + pub reply: String, + pub summary: Option, +} + +/// Append the dev's message, ask Claude for a reply, persist both, and treat the reply as the +/// current working summary. +pub async fn message( + State(state): State, + Path(id): Path, + Json(req): Json, +) -> Result, AppError> { + let draft = load_draft(&state, id).await?; + if !matches!(draft.status, crate::models::SummaryStatus::Pending) { + return Err(AppError::BadRequest("draft is no longer pending".into())); + } + + sqlx::query("INSERT INTO summary_draft_messages (draft_id, role, content) VALUES ($1, 'user', $2)") + .bind(id) + .bind(&req.content) + .execute(&state.pool) + .await?; + + let history = sqlx::query_as::<_, DraftMessage>( + "SELECT role, content FROM summary_draft_messages WHERE draft_id = $1 ORDER BY id ASC", + ) + .bind(id) + .fetch_all(&state.pool) + .await?; + let transcript: Vec<(String, String)> = + history.into_iter().map(|m| (m.role, m.content)).collect(); + + let reply = llm::reply(&state.http, &state.llm, &transcript).await?; + + let mut tx = state.pool.begin().await?; + sqlx::query( + "INSERT INTO summary_draft_messages (draft_id, role, content) VALUES ($1, 'assistant', $2)", + ) + .bind(id) + .bind(&reply) + .execute(&mut *tx) + .await?; + sqlx::query("UPDATE summary_drafts SET summary = $2 WHERE id = $1") + .bind(id) + .bind(&reply) + .execute(&mut *tx) + .await?; + tx.commit().await?; + + Ok(Json(MessageReply { + reply: reply.clone(), + summary: Some(reply), + })) +} + +/// Confirm a draft: create the real issue from the working summary and link it back. +pub async fn confirm( + State(state): State, + Path(id): Path, +) -> Result, AppError> { + let draft = load_draft(&state, id).await?; + if !matches!(draft.status, crate::models::SummaryStatus::Pending) { + return Err(AppError::BadRequest("draft is no longer pending".into())); + } + let summary = draft + .summary + .ok_or_else(|| AppError::BadRequest("draft has no summary yet".into()))?; + + // First non-empty line becomes the title; the whole summary is the description. + let title = summary + .lines() + .map(str::trim) + .find(|l| !l.is_empty()) + .unwrap_or("Untitled issue") + .to_string(); + let priority = draft.priority.unwrap_or(Priority::P3); + + let mut tx = state.pool.begin().await?; + let issue = sqlx::query_as::<_, Issue>( + "INSERT INTO issues (title, description, priority, author) VALUES ($1, $2, $3, $4) \ + RETURNING id, title, description, priority, author, status, created_at, updated_at", + ) + .bind(&title) + .bind(&summary) + .bind(priority) + .bind(&draft.author_id) + .fetch_one(&mut *tx) + .await?; + + sqlx::query("UPDATE summary_drafts SET status = 'Confirmed', issue_id = $2 WHERE id = $1") + .bind(id) + .bind(issue.id) + .execute(&mut *tx) + .await?; + tx.commit().await?; + + Ok(Json(issue)) +} + +async fn load_draft(state: &AppState, id: i64) -> Result { + sqlx::query_as::<_, SummaryDraft>( + "SELECT id, temp_channel_id, author_id, priority, summary, status, issue_id, \ + created_at, updated_at \ + FROM summary_drafts WHERE id = $1", + ) + .bind(id) + .fetch_optional(&state.pool) + .await? + .ok_or_else(|| AppError::NotFound(format!("draft {id} not found"))) +} diff --git a/backend/src/handlers/zakonim.rs b/backend/src/handlers/zakonim.rs index ad25abd..84ee151 100644 --- a/backend/src/handlers/zakonim.rs +++ b/backend/src/handlers/zakonim.rs @@ -1,19 +1,72 @@ use axum::{ + Json, + extract::State, http::StatusCode, + response::{IntoResponse, Response}, }; use serde::{Deserialize, Serialize}; -use std::env; +use crate::error::AppError; +use crate::etc::startup::AppState; +/// Register a user as a zakonim. Returns the user's ordinal and the running total. +/// Responds with 409 Conflict if the user is already registered. +pub async fn register( + State(state): State, + Json(req): Json, +) -> Result { + let insert = sqlx::query("INSERT INTO zakonim (id, description) VALUES ($1, $2)") + .bind(&req.id) + .bind(&req.reason) + .execute(&state.pool) + .await; -pub async fn zakonim(req: ZakonimRequest) -> StatusCode { - + if let Err(sqlx::Error::Database(e)) = &insert + && e.is_unique_violation() + { + return Ok((StatusCode::CONFLICT, "already a zako").into_response()); + } + insert?; - return StatusCode::OK; + // Newly inserted row is the most recent, so its ordinal equals the total count. + let count: i64 = sqlx::query_scalar("SELECT count(*) FROM zakonim") + .fetch_one(&state.pool) + .await?; + + let body = Json(ZakonimResponse { + ordinal: count, + total: count, + }); + Ok((StatusCode::CREATED, body).into_response()) +} + +/// List all registered zakonim, oldest first. +pub async fn list(State(state): State) -> Result>, AppError> { + let rows = sqlx::query_as::<_, Zakonim>( + "SELECT id, description, created_at::text AS created_at \ + FROM zakonim ORDER BY created_at ASC", + ) + .fetch_all(&state.pool) + .await?; + + Ok(Json(rows)) } #[derive(Deserialize, Serialize)] pub struct ZakonimRequest { pub id: String, pub reason: String, -} \ No newline at end of file +} + +#[derive(Serialize)] +pub struct ZakonimResponse { + pub ordinal: i64, + pub total: i64, +} + +#[derive(Serialize, sqlx::FromRow)] +pub struct Zakonim { + pub id: String, + pub description: String, + pub created_at: String, +} diff --git a/backend/src/lib.rs b/backend/src/lib.rs deleted file mode 100644 index e06029a..0000000 --- a/backend/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -use axum::{ - routing::{get, post}, - Router, -}; - -mod handlers; -mod etc; - - -#[tokio::main] -#[allow(unused)] -async fn main() { - - - let app = Router::new() - .route("/", get(handlers::root::root)) - ; - - let listner = tokio::net::TcpListener::bind("0.0.0.0:80").await.unwrap(); - axum::serve(listner, app); -} \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..4a05380 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,55 @@ +use axum::{ + Router, + routing::{get, post, put}, +}; + +mod error; +mod etc; +mod handlers; +mod models; + +#[tokio::main] +async fn main() { + let state = etc::startup::init().await; + + let app = Router::new() + .route("/", get(handlers::root::root)) + .route( + "/zakonim", + get(handlers::zakonim::list).post(handlers::zakonim::register), + ) + .route( + "/issues", + get(handlers::issue::list).post(handlers::issue::create), + ) + .route( + "/issues/{id}", + get(handlers::issue::get).patch(handlers::issue::edit), + ) + .route( + "/issues/{id}/comments", + get(handlers::comment::list).post(handlers::comment::create), + ) + .route( + "/issues/{id}/discord-messages/{key}", + put(handlers::issue::set_discord_message), + ) + .route( + "/discord/messages/{message_id}", + put(handlers::discord::upsert).delete(handlers::discord::delete), + ) + .route("/summary/drafts", post(handlers::summary::create)) + .route( + "/summary/drafts/{id}/messages", + post(handlers::summary::message), + ) + .route( + "/summary/drafts/{id}/confirm", + post(handlers::summary::confirm), + ) + .with_state(state); + + let addr = std::env::var("BIND_ADDR").unwrap_or_else(|_| "0.0.0.0:80".to_string()); + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} diff --git a/backend/src/models.rs b/backend/src/models.rs new file mode 100644 index 0000000..f05cf72 --- /dev/null +++ b/backend/src/models.rs @@ -0,0 +1,98 @@ +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, sqlx::Type)] +#[sqlx(type_name = "issue_priority")] +pub enum Priority { + P1, + P2, + P3, + P4, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, sqlx::Type)] +#[sqlx(type_name = "issue_status")] +pub enum Status { + Open, + InProgress, + Resolved, + Closed, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, sqlx::Type)] +#[sqlx(type_name = "summary_status")] +pub enum SummaryStatus { + Pending, + Confirmed, + Discarded, +} + +#[derive(Debug, Serialize, sqlx::FromRow)] +pub struct Issue { + pub id: i64, + pub title: String, + pub description: String, + pub priority: Priority, + pub author: String, + pub status: Status, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +#[derive(Debug, Serialize, sqlx::FromRow)] +pub struct DiscordMessageRef { + pub key: String, + pub guild_id: i64, + pub channel_id: i64, + pub message_id: i64, +} + +/// An issue together with its assignees and Discord message references. +#[derive(Debug, Serialize)] +pub struct IssueDetail { + #[serde(flatten)] + pub issue: Issue, + pub assignees: Vec, + pub discord_messages: Vec, +} + +#[derive(Debug, Serialize, sqlx::FromRow)] +pub struct Comment { + pub id: i64, + pub issue_id: i64, + pub author: String, + pub content: String, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +#[derive(Debug, Serialize, sqlx::FromRow)] +pub struct MessageEvent { + pub message_id: i64, + pub guild_id: Option, + pub channel_id: i64, + pub author_id: Option, + pub content: Option, + pub created_at: DateTime, + pub edited_at: Option>, + pub deleted_at: Option>, +} + +#[derive(Debug, Serialize, sqlx::FromRow)] +pub struct SummaryDraft { + pub id: i64, + pub temp_channel_id: i64, + pub author_id: String, + pub priority: Option, + pub summary: Option, + pub status: SummaryStatus, + pub issue_id: Option, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +#[derive(Debug, Serialize, sqlx::FromRow)] +pub struct DraftMessage { + pub role: String, + pub content: String, +} diff --git a/bot/Cargo.toml b/bot/Cargo.toml index 4267518..ebe72a3 100644 --- a/bot/Cargo.toml +++ b/bot/Cargo.toml @@ -4,6 +4,13 @@ version = "0.1.0" edition = "2024" [dependencies] -tokio = {workspace = true} +tokio = { workspace = true } serenity = { workspace = true } -poise = { workspace = true } \ No newline at end of file +poise = { workspace = true } +reqwest = { version = "0.12", default-features = false, features = [ + "json", + "rustls-tls", +] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +redis = { version = "0.27", features = ["tokio-comp", "connection-manager"] } diff --git a/bot/src/api.rs b/bot/src/api.rs new file mode 100644 index 0000000..3f7115a --- /dev/null +++ b/bot/src/api.rs @@ -0,0 +1,310 @@ +use serde::{Deserialize, Serialize}; + +use crate::Error; + +/// Thin typed client over the backend HTTP API. +#[derive(Clone)] +pub struct Api { + http: reqwest::Client, + base: String, +} + +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub enum Priority { + P1, + P2, + P3, + P4, +} + +impl Priority { + pub fn label(self) -> &'static str { + match self { + Priority::P1 => "P1: Critical", + Priority::P2 => "P2: High", + Priority::P3 => "P3: Medium", + Priority::P4 => "P4: Low", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub enum Status { + Open, + InProgress, + Resolved, + Closed, +} + +impl Status { + pub fn label(self) -> &'static str { + match self { + Status::Open => "Open", + Status::InProgress => "In Progress", + Status::Resolved => "Resolved", + Status::Closed => "Closed", + } + } +} + +#[derive(Debug, Deserialize)] +pub struct Issue { + pub id: i64, + pub title: String, + pub description: String, + pub priority: Priority, + pub author: String, + pub status: Status, + pub created_at: String, + pub updated_at: String, +} + +#[derive(Debug, Deserialize)] +pub struct DiscordMessageRef { + pub key: String, + pub guild_id: i64, + pub channel_id: i64, + pub message_id: i64, +} + +#[derive(Debug, Deserialize)] +pub struct IssueDetail { + #[serde(flatten)] + pub issue: Issue, + pub assignees: Vec, + pub discord_messages: Vec, +} + +#[derive(Debug, Default, Serialize)] +pub struct DiscordMessageInput { + pub key: String, + pub guild_id: i64, + pub channel_id: i64, + pub message_id: i64, +} + +#[derive(Debug, Serialize)] +pub struct CreateIssue { + pub title: String, + pub description: String, + pub priority: Priority, + pub author: String, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub assignees: Vec, + #[serde(skip_serializing_if = "Vec::is_empty")] + pub discord_messages: Vec, +} + +#[derive(Debug, Default, Serialize)] +pub struct EditIssue { + #[serde(skip_serializing_if = "Option::is_none")] + pub title: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub priority: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub status: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub assignees: Option>, +} + +#[derive(Debug, Serialize)] +pub struct MessageEventInput { + pub guild_id: Option, + pub channel_id: i64, + pub author_id: Option, + pub content: Option, + pub created_at: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub edited_at: Option, +} + +#[derive(Debug, Serialize)] +struct SetDiscordMessage { + guild_id: i64, + channel_id: i64, + message_id: i64, +} + +#[derive(Debug, Deserialize)] +pub struct SummaryDraft { + pub id: i64, + pub temp_channel_id: i64, + pub author_id: String, + pub priority: Option, + pub summary: Option, + pub status: String, + pub issue_id: Option, +} + +#[derive(Debug, Serialize)] +struct CreateDraft { + temp_channel_id: i64, + author_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + priority: Option, +} + +#[derive(Debug, Serialize)] +struct PostMessage { + content: String, +} + +#[derive(Debug, Deserialize)] +pub struct MessageReply { + pub reply: String, + pub summary: Option, +} + +impl Api { + pub fn new(base: impl Into) -> Self { + Api { + http: reqwest::Client::new(), + base: base.into(), + } + } + + fn url(&self, path: &str) -> String { + format!("{}{}", self.base, path) + } + + pub async fn create_issue(&self, body: &CreateIssue) -> Result { + let resp = self.http.post(self.url("/issues")).json(body).send().await?; + json_ok(resp).await + } + + pub async fn list_issues( + &self, + status: Option<&str>, + sort: Option<&str>, + ) -> Result, Error> { + let mut req = self.http.get(self.url("/issues")); + let mut query = Vec::new(); + if let Some(s) = status { + query.push(("status", s)); + } + if let Some(s) = sort { + query.push(("sort", s)); + } + if !query.is_empty() { + req = req.query(&query); + } + json_ok(req.send().await?).await + } + + pub async fn get_issue(&self, id: i64) -> Result { + let resp = self.http.get(self.url(&format!("/issues/{id}"))).send().await?; + json_ok(resp).await + } + + pub async fn edit_issue(&self, id: i64, body: &EditIssue) -> Result { + let resp = self + .http + .patch(self.url(&format!("/issues/{id}"))) + .json(body) + .send() + .await?; + json_ok(resp).await + } + + pub async fn set_discord_message( + &self, + id: i64, + key: &str, + guild_id: i64, + channel_id: i64, + message_id: i64, + ) -> Result<(), Error> { + let resp = self + .http + .put(self.url(&format!("/issues/{id}/discord-messages/{key}"))) + .json(&SetDiscordMessage { + guild_id, + channel_id, + message_id, + }) + .send() + .await?; + empty_ok(resp).await + } + + pub async fn upsert_message( + &self, + message_id: u64, + body: &MessageEventInput, + ) -> Result<(), Error> { + let resp = self + .http + .put(self.url(&format!("/discord/messages/{message_id}"))) + .json(body) + .send() + .await?; + empty_ok(resp).await + } + + pub async fn delete_message(&self, message_id: u64) -> Result<(), Error> { + let resp = self + .http + .delete(self.url(&format!("/discord/messages/{message_id}"))) + .send() + .await?; + empty_ok(resp).await + } + + pub async fn create_draft( + &self, + temp_channel_id: i64, + author_id: String, + priority: Option, + ) -> Result { + let resp = self + .http + .post(self.url("/summary/drafts")) + .json(&CreateDraft { + temp_channel_id, + author_id, + priority, + }) + .send() + .await?; + json_ok(resp).await + } + + pub async fn draft_message(&self, draft_id: i64, content: String) -> Result { + let resp = self + .http + .post(self.url(&format!("/summary/drafts/{draft_id}/messages"))) + .json(&PostMessage { content }) + .send() + .await?; + json_ok(resp).await + } + + pub async fn confirm_draft(&self, draft_id: i64) -> Result { + let resp = self + .http + .post(self.url(&format!("/summary/drafts/{draft_id}/confirm"))) + .send() + .await?; + json_ok(resp).await + } +} + +async fn json_ok(resp: reqwest::Response) -> Result { + let status = resp.status(); + if !status.is_success() { + let body = resp.text().await.unwrap_or_default(); + return Err(format!("backend returned {status}: {body}").into()); + } + Ok(resp.json().await?) +} + +async fn empty_ok(resp: reqwest::Response) -> Result<(), Error> { + let status = resp.status(); + if !status.is_success() { + let body = resp.text().await.unwrap_or_default(); + return Err(format!("backend returned {status}: {body}").into()); + } + Ok(()) +} diff --git a/bot/src/cache.rs b/bot/src/cache.rs new file mode 100644 index 0000000..3548a0c --- /dev/null +++ b/bot/src/cache.rs @@ -0,0 +1,47 @@ +use poise::serenity_prelude as serenity; +use redis::AsyncCommands; + +/// Optional Redis-backed cache of author display data, keyed by author id (`d:{user_id}`). +#[derive(Clone)] +pub struct AuthorCache { + conn: Option, +} + +impl AuthorCache { + /// Connect if a URL is provided; otherwise return a no-op cache. + pub async fn connect(url: Option<&str>) -> Self { + let conn = match url { + Some(url) => match redis::Client::open(url) { + Ok(client) => match client.get_connection_manager().await { + Ok(c) => Some(c), + Err(e) => { + eprintln!("redis: failed to connect ({e}); author cache disabled"); + None + } + }, + Err(e) => { + eprintln!("redis: invalid REDIS_URL ({e}); author cache disabled"); + None + } + }, + None => None, + }; + AuthorCache { conn } + } + + /// Cache a user's username/nickname/avatar. Silently no-ops when Redis is unavailable. + pub async fn store(&self, user: &serenity::User, nick: Option<&str>) { + let Some(mut conn) = self.conn.clone() else { + return; + }; + let key = format!("author:d:{}", user.id); + let fields = vec![ + ("username", user.name.clone()), + ("nickname", nick.unwrap_or(&user.name).to_string()), + ("avatar", user.face()), + ]; + if let Err(e) = conn.hset_multiple::<_, _, _, ()>(&key, &fields).await { + eprintln!("redis: failed to cache {key} ({e})"); + } + } +} diff --git a/bot/src/commands/issue.rs b/bot/src/commands/issue.rs index c1969d7..db2cf99 100644 --- a/bot/src/commands/issue.rs +++ b/bot/src/commands/issue.rs @@ -1,18 +1,41 @@ -use crate::{Context, Error}; +use poise::serenity_prelude as serenity; +use serenity::{ + ChannelId, ChannelType, CreateActionRow, CreateButton, CreateChannel, CreateEmbed, + CreateMessage, EditMessage, GuildId, MessageId, PermissionOverwrite, PermissionOverwriteType, + Permissions, RoleId, UserId, +}; +use poise::Modal; -#[poise::command(slash_command, - subcommands("new", "list", "close", "edit"))] -pub async fn issue(ctx: Context<'_>) -> Result<(), Error> { - Ok(()) +use crate::api::{CreateIssue, DiscordMessageInput, EditIssue, Priority}; +use crate::{Context, Data, DraftState, Error, render}; + +/// Format a Discord user id into the canonical author id used by the backend. +pub(crate) fn author_id(user: UserId) -> String { + format!("d:{user}") } -#[poise::command(slash_command)] -pub async fn new(ctx: Context<'_>, - #[description = "The priority of issue"] - priority: Option -) -> Result<(), Error> { - Ok(()) +#[derive(Debug, Clone, Copy, poise::ChoiceParameter)] +pub enum PriorityChoice { + #[name = "P1: Critical"] + P1, + #[name = "P2: High"] + P2, + #[name = "P3: Medium"] + P3, + #[name = "P4: Low"] + P4, +} + +impl From for Priority { + fn from(p: PriorityChoice) -> Self { + match p { + PriorityChoice::P1 => Priority::P1, + PriorityChoice::P2 => Priority::P2, + PriorityChoice::P3 => Priority::P3, + PriorityChoice::P4 => Priority::P4, + } + } } #[derive(Debug, poise::ChoiceParameter, PartialEq)] @@ -29,6 +52,18 @@ pub enum IssueStatus { All, } +impl IssueStatus { + fn as_param(&self) -> &'static str { + match self { + IssueStatus::Open => "Open", + IssueStatus::InProgress => "InProgress", + IssueStatus::Resolved => "Resolved", + IssueStatus::Closed => "Closed", + IssueStatus::All => "All", + } + } +} + #[derive(Debug, poise::ChoiceParameter, PartialEq)] pub enum IssueSort { #[name = "newest"] @@ -37,28 +72,436 @@ pub enum IssueSort { Priority, } +#[derive(Debug, poise::Modal)] +#[name = "New Issue"] +struct NewIssueModal { + #[name = "Title"] + #[placeholder = "Short summary of the issue"] + title: String, + #[name = "Description"] + #[paragraph] + description: Option, +} + +#[derive(Debug, poise::Modal)] +#[name = "Edit Issue"] +struct EditIssueModal { + #[name = "Title"] + title: String, + #[name = "Description"] + #[paragraph] + description: Option, +} + +#[poise::command(slash_command, subcommands("new", "list", "close", "edit"))] +pub async fn issue(_ctx: Context<'_>) -> Result<(), Error> { + Ok(()) +} + +/// Create a new issue. +#[poise::command(slash_command)] +pub async fn new( + ctx: Context<'_>, + #[description = "The priority of the issue"] priority: Option, +) -> Result<(), Error> { + let Some(guild_id) = ctx.guild_id() else { + ctx.say("This command can only be used in a server.").await?; + return Ok(()); + }; + let data = ctx.data(); + let author = ctx.author().id; + let priority = priority.map(Priority::from); + + // Forum issue channel: copy the thread directly into the issue channel, no AI flow. + if let Some((title, content, original)) = forum_source(&ctx, guild_id).await? { + let issue = data + .api + .create_issue(&CreateIssue { + title, + description: content, + priority: priority.unwrap_or(Priority::P3), + author: author_id(author), + assignees: Vec::new(), + discord_messages: vec![original], + }) + .await?; + let posted = post_issue(ctx.serenity_context(), data, guild_id, &issue).await?; + ctx.send( + poise::CreateReply::default() + .ephemeral(true) + .content(format!("Created issue #{}.", issue.id)) + .components(open_link_row(guild_id, posted, "View issue")), + ) + .await?; + return Ok(()); + } + + // Otherwise show a modal and start the AI summary draft flow. + let poise::Context::Application(actx) = ctx else { + ctx.say("This command must be used as a slash command.").await?; + return Ok(()); + }; + let Some(modal) = NewIssueModal::execute(actx).await? else { + return Ok(()); + }; + + let initial = format!( + "Title: {}\n\n{}", + modal.title, + modal.description.unwrap_or_default() + ); + let temp = start_draft(ctx.serenity_context(), data, guild_id, author, priority, Some(initial), None) + .await?; + + ctx.send( + poise::CreateReply::default() + .ephemeral(true) + .content("Started an issue draft. Open the channel to refine it with the assistant.") + .components(open_link_row(guild_id, temp, "Open draft")), + ) + .await?; + Ok(()) +} + +/// List issues. #[poise::command(slash_command)] pub async fn list( ctx: Context<'_>, #[description = "Issue status to filter by (Default: Open)"] status: Option, #[description = "How to sort the issues"] sort: Option, ) -> Result<(), Error> { - let _status = status.unwrap_or(IssueStatus::Open); + let status = status.unwrap_or(IssueStatus::Open); + let sort = match sort { + Some(IssueSort::Priority) => "priority", + _ => "newest", + }; + + let issues = ctx + .data() + .api + .list_issues(Some(status.as_param()), Some(sort)) + .await?; + + ctx.send(poise::CreateReply::default().embed(render::issue_list_embed(&issues, status.as_param()))) + .await?; Ok(()) } +/// Close an issue (devs only, in the issue channel). #[poise::command(slash_command)] -pub async fn close(ctx: Context<'_>, - #[description = "Issue ID"] - issue: Option +pub async fn close( + ctx: Context<'_>, + #[description = "Issue ID"] issue: i64, ) -> Result<(), Error> { + if let Err(msg) = guard_dev_in_issue_channel(&ctx) { + ctx.send(poise::CreateReply::default().ephemeral(true).content(msg)) + .await?; + return Ok(()); + } + + let updated = ctx + .data() + .api + .edit_issue( + issue, + &EditIssue { + status: Some(crate::api::Status::Closed), + ..Default::default() + }, + ) + .await?; + + sync_posted_issue(&ctx, issue).await?; + ctx.send( + poise::CreateReply::default() + .ephemeral(true) + .content(format!("Closed issue #{}.", updated.id)), + ) + .await?; Ok(()) } +/// Edit an issue (devs only, in the issue channel). #[poise::command(slash_command)] -pub async fn edit(ctx: Context<'_>, - #[description = "Issue ID"] - issue: Option +pub async fn edit( + ctx: Context<'_>, + #[description = "Issue ID"] issue: i64, +) -> Result<(), Error> { + if let Err(msg) = guard_dev_in_issue_channel(&ctx) { + ctx.send(poise::CreateReply::default().ephemeral(true).content(msg)) + .await?; + return Ok(()); + } + + let current = ctx.data().api.get_issue(issue).await?; + + let poise::Context::Application(actx) = ctx else { + ctx.say("This command must be used as a slash command.").await?; + return Ok(()); + }; + let defaults = EditIssueModal { + title: current.issue.title.clone(), + description: Some(current.issue.description.clone()), + }; + let Some(modal) = poise::execute_modal(actx, Some(defaults), None).await? else { + return Ok(()); + }; + + let updated = ctx + .data() + .api + .edit_issue( + issue, + &EditIssue { + title: Some(modal.title), + description: Some(modal.description.unwrap_or_default()), + ..Default::default() + }, + ) + .await?; + + sync_posted_issue(&ctx, issue).await?; + ctx.send( + poise::CreateReply::default() + .ephemeral(true) + .content(format!("Updated issue #{}.", updated.id)), + ) + .await?; + Ok(()) +} + +/// Context menu: create an issue from a message via the AI summary draft flow. +#[poise::command(context_menu_command = "Create Issue")] +pub async fn create_issue( + ctx: Context<'_>, + #[description = "Message to base the issue on"] message: serenity::Message, ) -> Result<(), Error> { + let Some(guild_id) = ctx.guild_id() else { + ctx.say("This command can only be used in a server.").await?; + return Ok(()); + }; + let data = ctx.data(); + let author = ctx.author().id; + + let original = DiscordMessageInput { + key: "original_reference".to_string(), + guild_id: guild_id.get() as i64, + channel_id: message.channel_id.get() as i64, + message_id: message.id.get() as i64, + }; + + let temp = start_draft( + ctx.serenity_context(), + data, + guild_id, + author, + None, + Some(message.content.clone()), + Some(original), + ) + .await?; + + ctx.send( + poise::CreateReply::default() + .ephemeral(true) + .content("Started an issue draft from that message.") + .components(open_link_row(guild_id, temp, "Open draft")), + ) + .await?; Ok(()) -} \ No newline at end of file +} + +// ---- helpers ---- + +fn guard_dev_in_issue_channel(ctx: &Context<'_>) -> Result<(), String> { + let data = ctx.data(); + if !data.config.is_dev(ctx.author().id) { + return Err("Only devs can perform this action.".to_string()); + } + if let Some(issue_channel) = data.config.issue_channel_id + && ctx.channel_id() != issue_channel + { + return Err("This command must be used in the issue channel.".to_string()); + } + Ok(()) +} + +/// If invoked inside a thread of the configured forum channel, returns (title, content, ref). +async fn forum_source( + ctx: &Context<'_>, + guild_id: GuildId, +) -> Result, Error> { + let Some(forum) = ctx.data().config.forum_issue_channel_id else { + return Ok(None); + }; + let channel = ctx.channel_id().to_channel(ctx).await?; + let serenity::Channel::Guild(gc) = channel else { + return Ok(None); + }; + if gc.parent_id != Some(forum) { + return Ok(None); + } + + // The thread starter message shares the thread's id. + let starter_id = MessageId::new(gc.id.get()); + let content = gc + .id + .message(ctx.http(), starter_id) + .await + .map(|m| m.content) + .unwrap_or_default(); + + let original = DiscordMessageInput { + key: "original_reference".to_string(), + guild_id: guild_id.get() as i64, + channel_id: gc.id.get() as i64, + message_id: starter_id.get() as i64, + }; + Ok(Some((gc.name.clone(), content, original))) +} + +/// Create the temp channel + backend draft, seed the conversation, and track it. +async fn start_draft( + ctx: &serenity::Context, + data: &Data, + guild_id: GuildId, + author: UserId, + priority: Option, + initial: Option, + original_ref: Option, +) -> Result { + let bot_id = ctx.cache.current_user().id; + + let overwrites = vec![ + PermissionOverwrite { + allow: Permissions::empty(), + deny: Permissions::VIEW_CHANNEL, + kind: PermissionOverwriteType::Role(RoleId::new(guild_id.get())), + }, + PermissionOverwrite { + allow: Permissions::VIEW_CHANNEL + | Permissions::SEND_MESSAGES + | Permissions::READ_MESSAGE_HISTORY, + deny: Permissions::empty(), + kind: PermissionOverwriteType::Member(author), + }, + PermissionOverwrite { + allow: Permissions::VIEW_CHANNEL + | Permissions::SEND_MESSAGES + | Permissions::READ_MESSAGE_HISTORY, + deny: Permissions::empty(), + kind: PermissionOverwriteType::Member(bot_id), + }, + ]; + + let channel = guild_id + .create_channel( + &ctx.http, + CreateChannel::new(format!("issue-draft-{}", author.get())) + .kind(ChannelType::Text) + .permissions(overwrites), + ) + .await?; + + let draft = data + .api + .create_draft(channel.id.get() as i64, author_id(author), priority) + .await?; + + data.drafts.lock().await.insert( + channel.id, + DraftState { + draft_id: draft.id, + owner: author, + original_ref, + }, + ); + + let body = match initial { + Some(text) if !text.trim().is_empty() => match data.api.draft_message(draft.id, text).await { + Ok(reply) => reply.reply, + Err(e) => format!( + "Could not reach the summary assistant ({e}).\nDescribe the issue here and try again." + ), + }, + _ => "Describe the issue and I'll help you write a summary. Click **Confirm** when you're happy." + .to_string(), + }; + + channel + .id + .send_message( + &ctx.http, + CreateMessage::new() + .content(format!("<@{}>", author.get())) + .embed(CreateEmbed::new().title("Issue draft").description(body)) + .components(draft_buttons(draft.id)), + ) + .await?; + + Ok(channel.id) +} + +pub(crate) fn draft_buttons(draft_id: i64) -> Vec { + vec![CreateActionRow::Buttons(vec![ + CreateButton::new(format!("issue_confirm:{draft_id}")) + .label("Confirm") + .style(serenity::ButtonStyle::Success), + CreateButton::new(format!("issue_discard:{draft_id}")) + .label("Discard") + .style(serenity::ButtonStyle::Danger), + ])] +} + +fn open_link_row(guild_id: GuildId, channel: ChannelId, label: &str) -> Vec { + let url = format!("https://discord.com/channels/{guild_id}/{channel}"); + vec![CreateActionRow::Buttons(vec![ + CreateButton::new_link(url).label(label), + ])] +} + +/// Post an issue embed to the configured issue channel and record the message reference. +pub async fn post_issue( + ctx: &serenity::Context, + data: &Data, + guild_id: GuildId, + issue: &crate::api::Issue, +) -> Result { + let channel = data + .config + .issue_channel_id + .ok_or("ISSUE_CHANNEL_ID is not configured")?; + + let msg = channel + .send_message(&ctx.http, CreateMessage::new().embed(render::issue_embed(issue))) + .await?; + + data.api + .set_discord_message( + issue.id, + "issue", + guild_id.get() as i64, + channel.get() as i64, + msg.id.get() as i64, + ) + .await?; + + Ok(channel) +} + +/// Re-render the issue's posted message (if any) after an edit/close. +async fn sync_posted_issue(ctx: &Context<'_>, issue_id: i64) -> Result<(), Error> { + let detail = ctx.data().api.get_issue(issue_id).await?; + let Some(m) = detail.discord_messages.iter().find(|m| m.key == "issue") else { + return Ok(()); + }; + let channel = ChannelId::new(m.channel_id as u64); + channel + .edit_message( + ctx.http(), + MessageId::new(m.message_id as u64), + EditMessage::new().embed(render::issue_embed(&detail.issue)), + ) + .await?; + Ok(()) +} diff --git a/bot/src/commands/mod.rs b/bot/src/commands/mod.rs index 175a273..644cc46 100644 --- a/bot/src/commands/mod.rs +++ b/bot/src/commands/mod.rs @@ -1,3 +1,2 @@ -pub mod ping; pub mod issue; -pub mod zakonim; \ No newline at end of file +pub mod ping; diff --git a/bot/src/commands/zakonim.rs b/bot/src/commands/zakonim.rs deleted file mode 100644 index 8587862..0000000 --- a/bot/src/commands/zakonim.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::{Context, Error}; -use poise::serenity_prelude as serenity; - -#[poise::command(slash_command)] -pub async fn zakonim(ctx: Context<'_>, - #[description = "Who zakonim"] who : Option, -) -> Result<(), Error> { - let latency = ctx.ping().await; - ctx.say(format!("Pong! {}ms", latency.as_millis())).await?; - Ok(()) -} diff --git a/bot/src/config.rs b/bot/src/config.rs new file mode 100644 index 0000000..e05db4b --- /dev/null +++ b/bot/src/config.rs @@ -0,0 +1,54 @@ +use std::collections::HashSet; +use std::env; + +use poise::serenity_prelude as serenity; +use serenity::{ChannelId, GuildId, UserId}; + +/// Runtime configuration sourced from environment variables. +#[derive(Debug, Clone)] +pub struct Config { + /// Base URL of the backend HTTP API, e.g. `http://backend:80`. + pub backend_url: String, + /// Guild used for fast (guild-scoped) command registration. If unset, commands register globally. + pub guild_id: Option, + /// Channel where confirmed issues are posted. + pub issue_channel_id: Option, + /// Forum channel; `/issue new` invoked inside one of its threads copies the thread to the issue channel. + pub forum_issue_channel_id: Option, + /// Users allowed to close/edit issues. + pub dev_user_ids: HashSet, + /// Optional Redis connection for caching author display data. + pub redis_url: Option, +} + +impl Config { + pub fn from_env() -> Self { + Config { + backend_url: env::var("BACKEND_URL") + .unwrap_or_else(|_| "http://localhost:80".to_string()) + .trim_end_matches('/') + .to_string(), + guild_id: parse_id("GUILD_ID").map(GuildId::new), + issue_channel_id: parse_id("ISSUE_CHANNEL_ID").map(ChannelId::new), + forum_issue_channel_id: parse_id("FORUM_ISSUE_CHANNEL_ID").map(ChannelId::new), + dev_user_ids: parse_id_list("DEV_USER_IDS").into_iter().map(UserId::new).collect(), + redis_url: env::var("REDIS_URL").ok().filter(|s| !s.is_empty()), + } + } + + pub fn is_dev(&self, user: UserId) -> bool { + self.dev_user_ids.contains(&user) + } +} + +fn parse_id(key: &str) -> Option { + env::var(key).ok()?.trim().parse().ok() +} + +fn parse_id_list(key: &str) -> Vec { + env::var(key) + .unwrap_or_default() + .split(',') + .filter_map(|s| s.trim().parse().ok()) + .collect() +} diff --git a/bot/src/events.rs b/bot/src/events.rs new file mode 100644 index 0000000..cd5538a --- /dev/null +++ b/bot/src/events.rs @@ -0,0 +1,199 @@ +use poise::serenity_prelude as serenity; +use serenity::{ + CreateEmbed, CreateInteractionResponse, CreateInteractionResponseMessage, CreateMessage, + FullEvent, Interaction, +}; + +use crate::api::MessageEventInput; +use crate::commands::issue::{author_id, draft_buttons, post_issue}; +use crate::{Data, Error}; + +pub async fn handle( + ctx: &serenity::Context, + event: &FullEvent, + _framework: poise::FrameworkContext<'_, Data, Error>, + data: &Data, +) -> Result<(), Error> { + match event { + FullEvent::Message { new_message } => { + if new_message.author.bot { + return Ok(()); + } + propagate_create(data, new_message).await; + data.cache.store(&new_message.author, None).await; + relay_draft(ctx, data, new_message).await?; + } + FullEvent::MessageUpdate { event, .. } => { + if event.author.as_ref().is_some_and(|a| a.bot) { + return Ok(()); + } + propagate_update(data, event).await; + } + FullEvent::MessageDelete { + deleted_message_id, .. + } => { + if let Err(e) = data.api.delete_message(deleted_message_id.get()).await { + eprintln!("propagate delete failed: {e}"); + } + } + FullEvent::InteractionCreate { + interaction: Interaction::Component(mci), + } => { + if let Some(rest) = mci.data.custom_id.strip_prefix("issue_confirm:") { + let _ = rest; + confirm_draft(ctx, data, mci).await?; + } else if mci.data.custom_id.starts_with("issue_discard:") { + discard_draft(ctx, data, mci).await?; + } + } + _ => {} + } + Ok(()) +} + +/// Mirror a newly created message into the backend. +async fn propagate_create(data: &Data, msg: &serenity::Message) { + let body = MessageEventInput { + guild_id: msg.guild_id.map(|g| g.get() as i64), + channel_id: msg.channel_id.get() as i64, + author_id: Some(author_id(msg.author.id)), + content: Some(msg.content.clone()), + created_at: msg.timestamp.to_string(), + edited_at: None, + }; + if let Err(e) = data.api.upsert_message(msg.id.get(), &body).await { + eprintln!("propagate create failed: {e}"); + } +} + +/// Mirror a message edit into the backend. +async fn propagate_update(data: &Data, event: &serenity::MessageUpdateEvent) { + let now = serenity::Timestamp::now(); + let created_at = event + .timestamp + .or(event.edited_timestamp) + .unwrap_or(now) + .to_string(); + let edited_at = Some(event.edited_timestamp.unwrap_or(now).to_string()); + + let body = MessageEventInput { + guild_id: event.guild_id.map(|g| g.get() as i64), + channel_id: event.channel_id.get() as i64, + author_id: event.author.as_ref().map(|a| author_id(a.id)), + content: event.content.clone(), + created_at, + edited_at, + }; + if let Err(e) = data.api.upsert_message(event.id.get(), &body).await { + eprintln!("propagate update failed: {e}"); + } +} + +/// If the message lands in a tracked draft channel from its owner, run it through the assistant. +async fn relay_draft( + ctx: &serenity::Context, + data: &Data, + msg: &serenity::Message, +) -> Result<(), Error> { + let draft_id = { + let drafts = data.drafts.lock().await; + match drafts.get(&msg.channel_id) { + Some(state) if state.owner == msg.author.id => state.draft_id, + _ => return Ok(()), + } + }; + + match data.api.draft_message(draft_id, msg.content.clone()).await { + Ok(reply) => { + msg.channel_id + .send_message( + &ctx.http, + CreateMessage::new() + .embed(CreateEmbed::new().title("Issue draft").description(reply.reply)) + .components(draft_buttons(draft_id)), + ) + .await?; + } + Err(e) => { + msg.channel_id + .say(&ctx.http, format!("Assistant error: {e}")) + .await?; + } + } + Ok(()) +} + +async fn confirm_draft( + ctx: &serenity::Context, + data: &Data, + mci: &serenity::ComponentInteraction, +) -> Result<(), Error> { + let Some(guild_id) = mci.guild_id else { + return Ok(()); + }; + let channel = mci.channel_id; + + let state = data.drafts.lock().await.remove(&channel); + let Some(state) = state else { + ack(ctx, mci, "This draft is no longer active.").await?; + return Ok(()); + }; + + let issue = match data.api.confirm_draft(state.draft_id).await { + Ok(issue) => issue, + Err(e) => { + // Keep the draft so the user can retry. + data.drafts.lock().await.insert(channel, state); + ack(ctx, mci, &format!("Could not create the issue: {e}")).await?; + return Ok(()); + } + }; + + post_issue(ctx, data, guild_id, &issue).await?; + + if let Some(orig) = state.original_ref { + let _ = data + .api + .set_discord_message( + issue.id, + &orig.key, + orig.guild_id, + orig.channel_id, + orig.message_id, + ) + .await; + } + + ack(ctx, mci, &format!("Created issue #{}. Closing this channel.", issue.id)).await?; + let _ = channel.delete(&ctx.http).await; + Ok(()) +} + +async fn discard_draft( + ctx: &serenity::Context, + data: &Data, + mci: &serenity::ComponentInteraction, +) -> Result<(), Error> { + let channel = mci.channel_id; + data.drafts.lock().await.remove(&channel); + ack(ctx, mci, "Draft discarded. Closing this channel.").await?; + let _ = channel.delete(&ctx.http).await; + Ok(()) +} + +async fn ack( + ctx: &serenity::Context, + mci: &serenity::ComponentInteraction, + content: &str, +) -> Result<(), Error> { + mci.create_response( + &ctx.http, + CreateInteractionResponse::Message( + CreateInteractionResponseMessage::new() + .ephemeral(true) + .content(content), + ), + ) + .await?; + Ok(()) +} diff --git a/bot/src/lib.rs b/bot/src/lib.rs deleted file mode 100644 index 80bfce7..0000000 --- a/bot/src/lib.rs +++ /dev/null @@ -1,45 +0,0 @@ -use ::serenity::all::GatewayIntents; -use tokio; -use std::env; - -pub mod commands; - -struct Data {} -type Error = Box; - -#[allow(unused)] -type Context<'a> = poise::Context<'a, Data, Error>; - -#[allow(unused)] -#[tokio::main] -async fn main() { - let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); - let intents - = GatewayIntents::GUILD_MESSAGES | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT; - - let framework = poise::Framework::::builder() - .options(poise::FrameworkOptions { - commands: vec![ - commands::ping::ping(), - commands::issue::issue(), - commands::zakonim::zakonim(), - ], - ..Default::default() - }) - .setup(|_ctx, _ready, _framework| { - Box::pin(async move { - println!("Ready: {}", _ready.user.tag()); - Ok(Data {}) - }) - }) - .build(); - - let mut client - = serenity::Client::builder(token, intents) - .framework(framework) - .await.expect("Error creating client"); - - if let Err(e) = client.start().await { - println!("Client Error {e:?}" ); - } -} \ No newline at end of file diff --git a/bot/src/main.rs b/bot/src/main.rs new file mode 100644 index 0000000..ddbd4cb --- /dev/null +++ b/bot/src/main.rs @@ -0,0 +1,96 @@ +use std::collections::HashMap; +use std::env; +use std::sync::Arc; + +use poise::serenity_prelude as serenity; +use serenity::{ChannelId, GatewayIntents, UserId}; +use tokio::sync::Mutex; + +mod api; +mod cache; +mod commands; +mod config; +mod events; +mod render; + +/// In-flight AI summary draft tied to a temp channel. +pub struct DraftState { + pub draft_id: i64, + pub owner: UserId, + /// The source message an issue was created from, recorded once the issue exists. + pub original_ref: Option, +} + +pub type Drafts = Arc>>; + +pub struct Data { + pub api: api::Api, + pub config: config::Config, + pub cache: cache::AuthorCache, + pub drafts: Drafts, +} + +pub type Error = Box; +pub type Context<'a> = poise::Context<'a, Data, Error>; + +#[tokio::main] +async fn main() { + let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); + let config = config::Config::from_env(); + + let intents = GatewayIntents::GUILDS + | GatewayIntents::GUILD_MESSAGES + | GatewayIntents::DIRECT_MESSAGES + | GatewayIntents::MESSAGE_CONTENT; + + let api = api::Api::new(config.backend_url.clone()); + let cache = cache::AuthorCache::connect(config.redis_url.as_deref()).await; + + let framework = poise::Framework::::builder() + .options(poise::FrameworkOptions { + commands: vec![ + commands::ping::ping(), + commands::issue::issue(), + commands::issue::create_issue(), + ], + event_handler: |ctx, event, framework, data| { + Box::pin(events::handle(ctx, event, framework, data)) + }, + ..Default::default() + }) + .setup(move |ctx, ready, framework| { + Box::pin(async move { + println!("Ready: {}", ready.user.tag()); + match config.guild_id { + Some(guild) => { + poise::builtins::register_in_guild( + ctx, + &framework.options().commands, + guild, + ) + .await?; + } + None => { + poise::builtins::register_globally(ctx, &framework.options().commands) + .await?; + } + } + Ok(Data { + api, + config, + cache, + drafts: Arc::new(Mutex::new(HashMap::new())), + }) + }) + }) + .build(); + + let mut client = serenity::Client::builder(token, intents) + .framework(framework) + .await + .expect("Error creating client"); + + if let Err(e) = client.start().await { + println!("Client Error {e:?}"); + } +} diff --git a/bot/src/render.rs b/bot/src/render.rs new file mode 100644 index 0000000..4b3231b --- /dev/null +++ b/bot/src/render.rs @@ -0,0 +1,64 @@ +use poise::serenity_prelude as serenity; +use serenity::{CreateEmbed, Colour}; + +use crate::api::{Issue, Priority, Status}; + +/// Render an author id (`d:{user_id}`) as a Discord mention where possible. +pub fn mention_author(author: &str) -> String { + match author.strip_prefix("d:") { + Some(id) => format!("<@{id}>"), + None => author.to_string(), + } +} + +fn priority_colour(priority: Priority) -> Colour { + match priority { + Priority::P1 => Colour::RED, + Priority::P2 => Colour::ORANGE, + Priority::P3 => Colour::GOLD, + Priority::P4 => Colour::LIGHT_GREY, + } +} + +/// A full embed for a single issue, used when posting to the issue channel. +pub fn issue_embed(issue: &Issue) -> CreateEmbed { + let description = if issue.description.is_empty() { + "_No description_".to_string() + } else { + issue.description.clone() + }; + + CreateEmbed::new() + .title(format!("#{} · {}", issue.id, issue.title)) + .description(description) + .colour(priority_colour(issue.priority)) + .field("Priority", issue.priority.label(), true) + .field("Status", issue.status.label(), true) + .field("Author", mention_author(&issue.author), true) +} + +/// A compact embed listing many issues. +pub fn issue_list_embed(issues: &[Issue], status_label: &str) -> CreateEmbed { + if issues.is_empty() { + return CreateEmbed::new() + .title(format!("Issues ({status_label})")) + .description("No issues found."); + } + + let lines: Vec = issues + .iter() + .map(|i| { + format!( + "`#{}` **{}** — {} · {}", + i.id, + i.title, + i.priority.label(), + Status::label(i.status), + ) + }) + .collect(); + + CreateEmbed::new() + .title(format!("Issues ({status_label})")) + .description(lines.join("\n")) +} diff --git a/compose.yml b/compose.yml index e69de29..f913f0e 100644 --- a/compose.yml +++ b/compose.yml @@ -0,0 +1,51 @@ +services: + db: + image: postgres:17 + env_file: + - .env + ports: + - "5432:5432" + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 10 + + backend: + build: + context: . + target: backend + env_file: + - .env + environment: + BIND_ADDR: 0.0.0.0:80 + ports: + - "8080:80" + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + bot: + build: + context: . + target: bot + env_file: + - .env + environment: + BACKEND_URL: http://backend:80 + depends_on: + - backend + restart: unless-stopped + + # Optional author display-name cache. Start with: podman compose --profile cache up + redis: + image: redis:7 + profiles: ["cache"] + ports: + - "6379:6379" + +volumes: + pgdata: From 263489c175d20ad0a1667f5cf7224ba27b6e35b9 Mon Sep 17 00:00:00 2001 From: 200mill <200mill@naver.com> Date: Mon, 29 Jun 2026 00:33:41 +0900 Subject: [PATCH 10/10] Rewrite Cargo.lock; formatting --- Cargo.lock | 1072 ++++++------------------------- backend/src/handlers/root.rs | 7 +- backend/src/handlers/summary.rs | 12 +- bot/src/api.rs | 19 +- bot/src/commands/issue.rs | 45 +- bot/src/config.rs | 5 +- bot/src/events.rs | 13 +- bot/src/render.rs | 2 +- 8 files changed, 272 insertions(+), 903 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0437197..55be418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,12 +99,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - [[package]] name = "arc-swap" version = "1.9.1" @@ -116,9 +110,9 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" dependencies = [ "serde", ] @@ -261,7 +255,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -278,7 +272,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -298,9 +292,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "axum" @@ -391,18 +385,18 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" dependencies = [ "serde_core", ] [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -443,9 +437,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" dependencies = [ "borsh-derive", "bytes", @@ -454,22 +448,22 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytecheck" @@ -507,15 +501,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "camino" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" dependencies = [ "serde_core", ] @@ -544,9 +538,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.61" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "shlex", @@ -566,20 +560,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "rand_core 0.10.1", -] - -[[package]] -name = "chacha20" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -588,9 +571,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -631,7 +614,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -646,12 +629,6 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" -[[package]] -name = "cmov" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" - [[package]] name = "colorchoice" version = "1.0.5" @@ -722,15 +699,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - [[package]] name = "crc" version = "3.4.0" @@ -807,24 +775,6 @@ dependencies = [ "cmov", ] -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "ctutils" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" -dependencies = [ - "cmov", -] - [[package]] name = "darling" version = "0.20.11" @@ -846,7 +796,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -857,7 +807,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -897,7 +847,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -930,7 +879,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -940,11 +889,9 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", "block-buffer 0.10.4", "const-oid", "crypto-common 0.1.7", - "crypto-common 0.1.7", "subtle", ] @@ -961,13 +908,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -978,9 +925,9 @@ checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" dependencies = [ "serde", ] @@ -1031,16 +978,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "etcetera" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" -dependencies = [ - "cfg-if", - "windows-sys 0.61.2", -] - [[package]] name = "event-listener" version = "2.5.3" @@ -1112,17 +1049,6 @@ dependencies = [ "spin", ] -[[package]] -name = "flume" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1141,12 +1067,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1242,7 +1162,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1313,17 +1233,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "rand_core 0.10.1", - "wasip2", - "wasip3", ] [[package]] @@ -1370,18 +1287,6 @@ dependencies = [ "foldhash 0.1.5", ] -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", - "foldhash 0.1.5", -] - [[package]] name = "hashbrown" version = "0.16.1" @@ -1395,9 +1300,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hashlink" @@ -1417,15 +1322,6 @@ dependencies = [ "hashbrown 0.16.1", ] -[[package]] -name = "hashlink" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" -dependencies = [ - "hashbrown 0.16.1", -] - [[package]] name = "heck" version = "0.4.1" @@ -1459,16 +1355,6 @@ dependencies = [ "hmac 0.12.1", ] -[[package]] -name = "hkdf" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" -dependencies = [ - "hmac 0.13.0", - "hmac 0.12.1", -] - [[package]] name = "hkdf" version = "0.13.0" @@ -1487,16 +1373,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "hmac" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" -dependencies = [ - "digest 0.11.3", - "digest 0.10.7", -] - [[package]] name = "hmac" version = "0.13.0" @@ -1517,9 +1393,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1562,27 +1438,18 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" -dependencies = [ - "typenum", -] - -[[package]] -name = "hybrid-array" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ "typenum", ] [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -1608,11 +1475,11 @@ dependencies = [ "http", "hyper", "hyper-util", - "rustls 0.23.40", + "rustls 0.23.41", "tokio", "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 1.0.7", + "webpki-roots 1.0.8", ] [[package]] @@ -1632,7 +1499,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.6.4", "tokio", "tower-service", "tracing", @@ -1744,12 +1611,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -1784,9 +1645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", + "hashbrown 0.17.1", ] [[package]] @@ -1797,7 +1656,7 @@ checksum = "c727f80bfa4a6c6e2508d2f05b6f4bfce242030bd88ed15ae5331c5b5d30fba7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1817,8 +1676,8 @@ name = "it-backend" version = "0.1.0" dependencies = [ "axum", - "serde", - "sqlx 0.9.0", + "chrono", + "reqwest", "serde", "serde_json", "sqlx 0.9.0", @@ -1869,13 +1728,12 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.97" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -1897,12 +1755,6 @@ dependencies = [ "spin", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "levenshtein" version = "1.0.5" @@ -1923,14 +1775,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" dependencies = [ "bitflags", "libc", "plain", - "redox_syscall 0.7.4", + "redox_syscall 0.8.1", ] [[package]] @@ -1966,9 +1818,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" dependencies = [ "value-bag", ] @@ -2015,17 +1867,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "md-5" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" -dependencies = [ - "cfg-if", - "digest 0.11.3", - "digest 0.10.7", -] - [[package]] name = "md-5" version = "0.11.0" @@ -2038,9 +1879,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memoffset" @@ -2102,9 +1943,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -2161,9 +2002,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -2237,7 +2078,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2286,9 +2127,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pgvector" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc58e2d255979a31caa7cabfa7aac654af0354220719ab7a68520ae7a91e8c0b" +checksum = "3673cba5b9a124916096a423b806a9f29620972c6c97b08db5f2053e9428b481" dependencies = [ "serde", ] @@ -2406,16 +2247,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -2444,7 +2275,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2464,7 +2295,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "version_check", "yansi", ] @@ -2502,9 +2333,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -2512,8 +2343,8 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.40", - "socket2 0.6.3", + "rustls 0.23.41", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -2522,9 +2353,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" dependencies = [ "bytes", "getrandom 0.3.4", @@ -2532,7 +2363,7 @@ dependencies = [ "rand 0.9.4", "ring", "rustc-hash", - "rustls 0.23.40", + "rustls 0.23.41", "rustls-pki-types", "slab", "thiserror 2.0.18", @@ -2550,16 +2381,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.3", + "socket2 0.6.4", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -2610,18 +2441,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", - "getrandom 0.4.2", - "rand_core 0.10.1", -] - -[[package]] -name = "rand" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" -dependencies = [ - "chacha20", - "getrandom 0.4.2", + "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -2670,10 +2490,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] -name = "rand_core" -version = "0.10.1" +name = "redis" +version = "0.27.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +checksum = "09d8f99a4090c89cc489a94833c901ead69bfbf3877b4867d5482e321ee875bc" +dependencies = [ + "arc-swap", + "async-trait", + "backon", + "bytes", + "combine", + "futures", + "futures-util", + "itertools", + "itoa", + "num-bigint", + "percent-encoding", + "pin-project-lite", + "ryu", + "sha1_smol", + "socket2 0.5.10", + "tokio", + "tokio-util", + "url", +] [[package]] name = "redox_syscall" @@ -2686,18 +2526,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -2718,9 +2558,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rend" @@ -2753,7 +2593,7 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.40", + "rustls 0.23.41", "rustls-pki-types", "serde", "serde_json", @@ -2770,7 +2610,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.7", + "webpki-roots 1.0.8", ] [[package]] @@ -2824,7 +2664,6 @@ checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", "digest 0.10.7", - "digest 0.10.7", "num-bigint-dig", "num-integer", "num-traits", @@ -2839,9 +2678,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.41.0" +version = "1.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce901f9a19d251159075a4c37af514c3b8ef99c22e02dd8c19161cf397ee94a" +checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" dependencies = [ "arrayvec", "borsh", @@ -2898,9 +2737,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" +version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ "once_cell", "ring", @@ -2979,7 +2818,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3005,7 +2844,6 @@ dependencies = [ "serde", "serde_json", "sqlx 0.8.6", - "sqlx 0.8.6", "strum", "thiserror 2.0.18", "time", @@ -3040,7 +2878,7 @@ dependencies = [ "proc-macro2", "quote", "sea-bae", - "syn 2.0.117", + "syn 2.0.118", "unicode-ident", ] @@ -3089,7 +2927,6 @@ dependencies = [ "sea-query", "serde_json", "sqlx 0.8.6", - "sqlx 0.8.6", "time", "uuid", ] @@ -3104,7 +2941,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "thiserror 2.0.18", ] @@ -3128,7 +2965,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3193,14 +3030,14 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", @@ -3278,7 +3115,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3301,20 +3138,13 @@ dependencies = [ "cfg-if", "cpufeatures 0.3.0", "digest 0.11.3", - "cpufeatures 0.2.17", - "digest 0.10.7", ] [[package]] -name = "sha1" -version = "0.11.0" +name = "sha1_smol" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "digest 0.11.3", -] +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -3327,19 +3157,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.0", - "digest 0.11.3", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - [[package]] name = "sha2" version = "0.11.0" @@ -3362,9 +3179,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "signal-hook-registry" @@ -3382,7 +3199,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest 0.10.7", "digest 0.10.7", "rand_core 0.6.4", ] @@ -3422,9 +3238,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" dependencies = [ "serde", ] @@ -3441,9 +3257,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -3481,24 +3297,6 @@ dependencies = [ "sqlx-sqlite 0.8.6", ] -[[package]] -name = "sqlx" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" -dependencies = [ - "sqlx-core 0.9.0", - "sqlx-macros 0.9.0", - "sqlx-mysql 0.9.0", - "sqlx-postgres 0.9.0", - "sqlx-sqlite 0.9.0", - "sqlx-core 0.8.6", - "sqlx-macros 0.8.6", - "sqlx-mysql 0.8.6", - "sqlx-postgres 0.8.6", - "sqlx-sqlite 0.8.6", -] - [[package]] name = "sqlx" version = "0.9.0" @@ -3532,7 +3330,6 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink 0.10.0", - "hashlink 0.10.0", "indexmap", "log", "memchr", @@ -3542,7 +3339,6 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "sha2 0.10.9", "smallvec", "thiserror 2.0.18", "time", @@ -3553,70 +3349,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "sqlx-core" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" -dependencies = [ - "base64", - "bytes", - "cfg-if", - "crc", - "crossbeam-queue", - "either", - "event-listener 5.4.1", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.16.1", - "hashlink 0.11.1", - "indexmap", - "log", - "memchr", - "percent-encoding", - "serde", - "serde_json", - "sha2 0.10.9", - "smallvec", - "thiserror 2.0.18", - "tracing", - "url", -] - -[[package]] -name = "sqlx-core" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" -dependencies = [ - "base64", - "bytes", - "cfg-if", - "crc", - "crossbeam-queue", - "either", - "event-listener 5.4.1", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.16.1", - "hashlink 0.11.1", - "indexmap", - "log", - "memchr", - "percent-encoding", - "serde", - "serde_json", - "sha2 0.10.9", - "smallvec", - "thiserror 2.0.18", - "tracing", - "url", -] - [[package]] name = "sqlx-core" version = "0.9.0" @@ -3641,7 +3373,7 @@ dependencies = [ "log", "memchr", "percent-encoding", - "rustls 0.23.40", + "rustls 0.23.41", "serde", "serde_json", "sha2 0.10.9", @@ -3651,7 +3383,7 @@ dependencies = [ "tokio-stream", "tracing", "url", - "webpki-roots 1.0.7", + "webpki-roots 1.0.8", ] [[package]] @@ -3664,22 +3396,7 @@ dependencies = [ "quote", "sqlx-core 0.8.6", "sqlx-macros-core 0.8.6", - "syn 2.0.117", -] - -[[package]] -name = "sqlx-macros" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core 0.9.0", - "sqlx-macros-core 0.9.0", - "sqlx-core 0.8.6", - "sqlx-macros-core 0.8.6", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3692,7 +3409,7 @@ dependencies = [ "quote", "sqlx-core 0.9.0", "sqlx-macros-core 0.9.0", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3715,12 +3432,7 @@ dependencies = [ "sqlx-mysql 0.8.6", "sqlx-postgres 0.8.6", "sqlx-sqlite 0.8.6", - "sha2 0.10.9", - "sqlx-core 0.8.6", - "sqlx-mysql 0.8.6", - "sqlx-postgres 0.8.6", - "sqlx-sqlite 0.8.6", - "syn 2.0.117", + "syn 2.0.118", "tokio", "url", ] @@ -3745,57 +3457,7 @@ dependencies = [ "sqlx-mysql 0.9.0", "sqlx-postgres 0.9.0", "sqlx-sqlite 0.9.0", - "syn 2.0.117", - "thiserror 2.0.18", - "url", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" -dependencies = [ - "cfg-if", - "dotenvy", - "either", - "heck 0.5.0", - "hex", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2 0.10.9", - "sqlx-core 0.9.0", - "sqlx-mysql 0.9.0", - "sqlx-postgres 0.9.0", - "sqlx-sqlite 0.9.0", - "syn 2.0.117", - "thiserror 2.0.18", - "url", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" -dependencies = [ - "cfg-if", - "dotenvy", - "either", - "heck 0.5.0", - "hex", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2 0.10.9", - "sqlx-core 0.9.0", - "sqlx-mysql 0.9.0", - "sqlx-postgres 0.9.0", - "sqlx-sqlite 0.9.0", - "syn 2.0.117", + "syn 2.0.118", "thiserror 2.0.18", "tokio", "url", @@ -3816,7 +3478,6 @@ dependencies = [ "chrono", "crc", "digest 0.10.7", - "digest 0.10.7", "dotenvy", "either", "futures-channel", @@ -3827,12 +3488,9 @@ dependencies = [ "hex", "hkdf 0.12.4", "hmac 0.12.1", - "hkdf 0.12.4", - "hmac 0.12.1", "itoa", "log", "md-5 0.10.6", - "md-5 0.10.6", "memchr", "once_cell", "percent-encoding", @@ -3842,11 +3500,8 @@ dependencies = [ "serde", "sha1 0.10.6", "sha2 0.10.9", - "sha1 0.10.6", - "sha2 0.10.9", "smallvec", "sqlx-core 0.8.6", - "sqlx-core 0.8.6", "stringprep", "thiserror 2.0.18", "time", @@ -3855,60 +3510,6 @@ dependencies = [ "whoami 1.6.1", ] -[[package]] -name = "sqlx-mysql" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" -dependencies = [ - "bitflags", - "byteorder", - "bytes", - "crc", - "digest 0.11.3", - "dotenvy", - "either", - "futures-core", - "futures-util", - "generic-array", - "log", - "percent-encoding", - "serde", - "sha1 0.11.0", - "sha2 0.11.0", - "sqlx-core 0.9.0", - "thiserror 2.0.18", - "tracing", - "whoami 1.6.1", -] - -[[package]] -name = "sqlx-mysql" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" -dependencies = [ - "bitflags", - "byteorder", - "bytes", - "crc", - "digest 0.11.3", - "dotenvy", - "either", - "futures-core", - "futures-util", - "generic-array", - "log", - "percent-encoding", - "serde", - "sha1 0.11.0", - "sha2 0.11.0", - "sqlx-core 0.9.0", - "thiserror 2.0.18", - "tracing", - "whoami 1.6.1", -] - [[package]] name = "sqlx-mysql" version = "0.9.0" @@ -3951,20 +3552,16 @@ dependencies = [ "crc", "dotenvy", "etcetera 0.8.0", - "etcetera 0.8.0", "futures-channel", "futures-core", "futures-util", "hex", "hkdf 0.12.4", "hmac 0.12.1", - "hkdf 0.12.4", - "hmac 0.12.1", "home", "itoa", "log", "md-5 0.10.6", - "md-5 0.10.6", "memchr", "num-bigint", "once_cell", @@ -3973,10 +3570,8 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "sha2 0.10.9", "smallvec", "sqlx-core 0.8.6", - "sqlx-core 0.8.6", "stringprep", "thiserror 2.0.18", "time", @@ -3985,78 +3580,6 @@ dependencies = [ "whoami 1.6.1", ] -[[package]] -name = "sqlx-postgres" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" -dependencies = [ - "atoi", - "base64", - "bitflags", - "byteorder", - "crc", - "dotenvy", - "etcetera 0.11.0", - "futures-channel", - "futures-core", - "futures-util", - "hex", - "hkdf 0.13.0", - "hmac 0.13.0", - "itoa", - "log", - "md-5 0.11.0", - "memchr", - "rand 0.10.1", - "serde", - "serde_json", - "sha2 0.11.0", - "smallvec", - "sqlx-core 0.9.0", - "stringprep", - "thiserror 2.0.18", - "tracing", - "whoami 2.1.2", - "whoami 1.6.1", -] - -[[package]] -name = "sqlx-postgres" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" -dependencies = [ - "atoi", - "base64", - "bitflags", - "byteorder", - "crc", - "dotenvy", - "etcetera 0.11.0", - "futures-channel", - "futures-core", - "futures-util", - "hex", - "hkdf 0.13.0", - "hmac 0.13.0", - "itoa", - "log", - "md-5 0.11.0", - "memchr", - "rand 0.10.1", - "serde", - "serde_json", - "sha2 0.11.0", - "smallvec", - "sqlx-core 0.9.0", - "stringprep", - "thiserror 2.0.18", - "tracing", - "whoami 2.1.2", - "whoami 1.6.1", -] - [[package]] name = "sqlx-postgres" version = "0.9.0" @@ -4102,7 +3625,6 @@ dependencies = [ "atoi", "chrono", "flume 0.11.1", - "flume 0.11.1", "futures-channel", "futures-core", "futures-executor", @@ -4114,7 +3636,6 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core 0.8.6", - "sqlx-core 0.8.6", "thiserror 2.0.18", "time", "tracing", @@ -4122,54 +3643,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "sqlx-sqlite" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" -dependencies = [ - "atoi", - "flume 0.12.0", - "form_urlencoded", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "sqlx-core 0.9.0", - "thiserror 2.0.18", - "tracing", - "url", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" -dependencies = [ - "atoi", - "flume 0.12.0", - "form_urlencoded", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "sqlx-core 0.9.0", - "thiserror 2.0.18", - "tracing", - "url", -] - [[package]] name = "sqlx-sqlite" version = "0.9.0" @@ -4249,9 +3722,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -4275,7 +3748,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4297,7 +3770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4329,7 +3802,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4340,7 +3813,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4354,12 +3827,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -4369,15 +3841,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", @@ -4420,7 +3892,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.3", + "socket2 0.6.4", "tokio-macros", "windows-sys 0.61.2", ] @@ -4433,7 +3905,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4453,7 +3925,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.40", + "rustls 0.23.41", "tokio", ] @@ -4508,9 +3980,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap", "toml_datetime", @@ -4593,7 +4065,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4682,7 +4154,6 @@ dependencies = [ "rustls 0.22.4", "rustls-pki-types", "sha1 0.10.6", - "sha1 0.10.6", "thiserror 1.0.69", "url", "utf-8", @@ -4696,15 +4167,15 @@ checksum = "74b08b0c1257381af16a5c3605254d529d3e7e109f3c62befc5d168968192998" [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "typesize" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da66c62c5b7017a2787e77373c03e6a5aafde77a73bff1ff96e91cd2e128179" +checksum = "a313188364f7e10138257cb288c64e9b0e282510abefc91c796799559fa14762" dependencies = [ "chrono", "dashmap", @@ -4726,7 +4197,7 @@ checksum = "536b6812192bda8551cfa0e52524e328c6a951b48e66529ee4522d6c721243d6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4807,9 +4278,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ "js-sys", "serde_core", @@ -4873,20 +4344,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -4897,9 +4359,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -4911,9 +4373,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.70" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -4921,9 +4383,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4931,48 +4393,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.120" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.4.2" @@ -4986,23 +4426,11 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.97" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -5024,14 +4452,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.7", + "webpki-roots 1.0.8", ] [[package]] name = "webpki-roots" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -5052,12 +4480,6 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" -[[package]] -name = "whoami" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" - [[package]] name = "winapi" version = "0.3.9" @@ -5110,7 +4532,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5121,7 +4543,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5372,107 +4794,19 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "writeable" version = "0.6.3" @@ -5496,9 +4830,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -5513,35 +4847,35 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -5554,15 +4888,15 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" @@ -5594,7 +4928,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] diff --git a/backend/src/handlers/root.rs b/backend/src/handlers/root.rs index 45f495d..15a7006 100644 --- a/backend/src/handlers/root.rs +++ b/backend/src/handlers/root.rs @@ -1,8 +1,5 @@ -use axum::{ - http::StatusCode, -}; - +use axum::http::StatusCode; pub async fn root() -> (StatusCode, &'static str) { (StatusCode::OK, "asdf") -} \ No newline at end of file +} diff --git a/backend/src/handlers/summary.rs b/backend/src/handlers/summary.rs index 6e1fc11..64c6add 100644 --- a/backend/src/handlers/summary.rs +++ b/backend/src/handlers/summary.rs @@ -61,11 +61,13 @@ pub async fn message( return Err(AppError::BadRequest("draft is no longer pending".into())); } - sqlx::query("INSERT INTO summary_draft_messages (draft_id, role, content) VALUES ($1, 'user', $2)") - .bind(id) - .bind(&req.content) - .execute(&state.pool) - .await?; + sqlx::query( + "INSERT INTO summary_draft_messages (draft_id, role, content) VALUES ($1, 'user', $2)", + ) + .bind(id) + .bind(&req.content) + .execute(&state.pool) + .await?; let history = sqlx::query_as::<_, DraftMessage>( "SELECT role, content FROM summary_draft_messages WHERE draft_id = $1 ORDER BY id ASC", diff --git a/bot/src/api.rs b/bot/src/api.rs index 3f7115a..7ada590 100644 --- a/bot/src/api.rs +++ b/bot/src/api.rs @@ -170,7 +170,12 @@ impl Api { } pub async fn create_issue(&self, body: &CreateIssue) -> Result { - let resp = self.http.post(self.url("/issues")).json(body).send().await?; + let resp = self + .http + .post(self.url("/issues")) + .json(body) + .send() + .await?; json_ok(resp).await } @@ -194,7 +199,11 @@ impl Api { } pub async fn get_issue(&self, id: i64) -> Result { - let resp = self.http.get(self.url(&format!("/issues/{id}"))).send().await?; + let resp = self + .http + .get(self.url(&format!("/issues/{id}"))) + .send() + .await?; json_ok(resp).await } @@ -271,7 +280,11 @@ impl Api { json_ok(resp).await } - pub async fn draft_message(&self, draft_id: i64, content: String) -> Result { + pub async fn draft_message( + &self, + draft_id: i64, + content: String, + ) -> Result { let resp = self .http .post(self.url(&format!("/summary/drafts/{draft_id}/messages"))) diff --git a/bot/src/commands/issue.rs b/bot/src/commands/issue.rs index db2cf99..9fa1779 100644 --- a/bot/src/commands/issue.rs +++ b/bot/src/commands/issue.rs @@ -105,7 +105,8 @@ pub async fn new( #[description = "The priority of the issue"] priority: Option, ) -> Result<(), Error> { let Some(guild_id) = ctx.guild_id() else { - ctx.say("This command can only be used in a server.").await?; + ctx.say("This command can only be used in a server.") + .await?; return Ok(()); }; let data = ctx.data(); @@ -138,7 +139,8 @@ pub async fn new( // Otherwise show a modal and start the AI summary draft flow. let poise::Context::Application(actx) = ctx else { - ctx.say("This command must be used as a slash command.").await?; + ctx.say("This command must be used as a slash command.") + .await?; return Ok(()); }; let Some(modal) = NewIssueModal::execute(actx).await? else { @@ -150,8 +152,16 @@ pub async fn new( modal.title, modal.description.unwrap_or_default() ); - let temp = start_draft(ctx.serenity_context(), data, guild_id, author, priority, Some(initial), None) - .await?; + let temp = start_draft( + ctx.serenity_context(), + data, + guild_id, + author, + priority, + Some(initial), + None, + ) + .await?; ctx.send( poise::CreateReply::default() @@ -182,17 +192,16 @@ pub async fn list( .list_issues(Some(status.as_param()), Some(sort)) .await?; - ctx.send(poise::CreateReply::default().embed(render::issue_list_embed(&issues, status.as_param()))) - .await?; + ctx.send( + poise::CreateReply::default().embed(render::issue_list_embed(&issues, status.as_param())), + ) + .await?; Ok(()) } /// Close an issue (devs only, in the issue channel). #[poise::command(slash_command)] -pub async fn close( - ctx: Context<'_>, - #[description = "Issue ID"] issue: i64, -) -> Result<(), Error> { +pub async fn close(ctx: Context<'_>, #[description = "Issue ID"] issue: i64) -> Result<(), Error> { if let Err(msg) = guard_dev_in_issue_channel(&ctx) { ctx.send(poise::CreateReply::default().ephemeral(true).content(msg)) .await?; @@ -223,10 +232,7 @@ pub async fn close( /// Edit an issue (devs only, in the issue channel). #[poise::command(slash_command)] -pub async fn edit( - ctx: Context<'_>, - #[description = "Issue ID"] issue: i64, -) -> Result<(), Error> { +pub async fn edit(ctx: Context<'_>, #[description = "Issue ID"] issue: i64) -> Result<(), Error> { if let Err(msg) = guard_dev_in_issue_channel(&ctx) { ctx.send(poise::CreateReply::default().ephemeral(true).content(msg)) .await?; @@ -236,7 +242,8 @@ pub async fn edit( let current = ctx.data().api.get_issue(issue).await?; let poise::Context::Application(actx) = ctx else { - ctx.say("This command must be used as a slash command.").await?; + ctx.say("This command must be used as a slash command.") + .await?; return Ok(()); }; let defaults = EditIssueModal { @@ -277,7 +284,8 @@ pub async fn create_issue( #[description = "Message to base the issue on"] message: serenity::Message, ) -> Result<(), Error> { let Some(guild_id) = ctx.guild_id() else { - ctx.say("This command can only be used in a server.").await?; + ctx.say("This command can only be used in a server.") + .await?; return Ok(()); }; let data = ctx.data(); @@ -473,7 +481,10 @@ pub async fn post_issue( .ok_or("ISSUE_CHANNEL_ID is not configured")?; let msg = channel - .send_message(&ctx.http, CreateMessage::new().embed(render::issue_embed(issue))) + .send_message( + &ctx.http, + CreateMessage::new().embed(render::issue_embed(issue)), + ) .await?; data.api diff --git a/bot/src/config.rs b/bot/src/config.rs index e05db4b..ed583f3 100644 --- a/bot/src/config.rs +++ b/bot/src/config.rs @@ -31,7 +31,10 @@ impl Config { guild_id: parse_id("GUILD_ID").map(GuildId::new), issue_channel_id: parse_id("ISSUE_CHANNEL_ID").map(ChannelId::new), forum_issue_channel_id: parse_id("FORUM_ISSUE_CHANNEL_ID").map(ChannelId::new), - dev_user_ids: parse_id_list("DEV_USER_IDS").into_iter().map(UserId::new).collect(), + dev_user_ids: parse_id_list("DEV_USER_IDS") + .into_iter() + .map(UserId::new) + .collect(), redis_url: env::var("REDIS_URL").ok().filter(|s| !s.is_empty()), } } diff --git a/bot/src/events.rs b/bot/src/events.rs index cd5538a..1013f0a 100644 --- a/bot/src/events.rs +++ b/bot/src/events.rs @@ -109,7 +109,11 @@ async fn relay_draft( .send_message( &ctx.http, CreateMessage::new() - .embed(CreateEmbed::new().title("Issue draft").description(reply.reply)) + .embed( + CreateEmbed::new() + .title("Issue draft") + .description(reply.reply), + ) .components(draft_buttons(draft_id)), ) .await?; @@ -164,7 +168,12 @@ async fn confirm_draft( .await; } - ack(ctx, mci, &format!("Created issue #{}. Closing this channel.", issue.id)).await?; + ack( + ctx, + mci, + &format!("Created issue #{}. Closing this channel.", issue.id), + ) + .await?; let _ = channel.delete(&ctx.http).await; Ok(()) } diff --git a/bot/src/render.rs b/bot/src/render.rs index 4b3231b..ee0e49e 100644 --- a/bot/src/render.rs +++ b/bot/src/render.rs @@ -1,5 +1,5 @@ use poise::serenity_prelude as serenity; -use serenity::{CreateEmbed, Colour}; +use serenity::{Colour, CreateEmbed}; use crate::api::{Issue, Priority, Status};