diff --git a/Cargo.lock b/Cargo.lock index 8398920..ad70d8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + [[package]] name = "aho-corasick" version = "0.7.15" @@ -22,8 +28,10 @@ dependencies = [ "bluearch-recruitment", "dotenv", "env_logger", + "image", "lazy_static", "log", + "reqwest", "serde", "serde_json", "serenity", @@ -104,7 +112,7 @@ dependencies = [ [[package]] name = "bluearch-recruitment" version = "0.1.0" -source = "git+https://github.com/Paoda/bluearch-recruitment.git?branch=main#c209e32bdfcd05023f00f59001150d3389790ade" +source = "git+https://github.com/Paoda/bluearch-recruitment.git?branch=main#c7b275ea2273f0d38ce9917be786feced1ef422e" dependencies = [ "rand 0.8.3", "serde", @@ -118,6 +126,12 @@ version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" +[[package]] +name = "bytemuck" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a4bad0c5981acc24bc09e532f35160f952e35422603f0563cd7a73c2c2e65a0" + [[package]] name = "byteorder" version = "1.4.2" @@ -162,6 +176,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "command_attr" version = "0.3.3" @@ -173,6 +193,28 @@ dependencies = [ "syn", ] +[[package]] +name = "const_fn" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + [[package]] name = "cpuid-bool" version = "0.1.2" @@ -188,6 +230,62 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" +dependencies = [ + "cfg-if", + "const_fn", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + [[package]] name = "digest" version = "0.9.0" @@ -203,6 +301,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "encoding_rs" version = "0.8.28" @@ -214,9 +318,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" dependencies = [ "atty", "humantime", @@ -234,7 +338,7 @@ dependencies = [ "cfg-if", "crc32fast", "libc", - "miniz_oxide", + "miniz_oxide 0.4.3", ] [[package]] @@ -243,6 +347,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.0.0" @@ -368,6 +487,16 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "gif" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "h2" version = "0.3.0" @@ -481,6 +610,19 @@ dependencies = [ "webpki", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.0.1", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "idna" version = "0.2.1" @@ -492,6 +634,25 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293f07a1875fa7e9c5897b51aa68b2d8ed8271b87e1a44cb64b9c3d98aabbc0d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "indexmap" version = "1.6.1" @@ -523,6 +684,15 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.47" @@ -565,6 +735,15 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -581,6 +760,15 @@ dependencies = [ "unicase", ] +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + [[package]] name = "miniz_oxide" version = "0.4.3" @@ -614,6 +802,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "native-tls" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d96b2e1c8da3957d58100b09f102c6d9cfdfced01b7ec5a8974044bb09dbd4" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ntapi" version = "0.3.6" @@ -633,6 +839,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -664,6 +892,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038d43985d1ddca7a9900630d8cd031b56e4794eecc2e9ea39dd17aa04399a70" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -722,6 +983,24 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -839,6 +1118,40 @@ dependencies = [ "rand_core 0.6.1", ] +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.4.3" @@ -857,6 +1170,15 @@ version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "reqwest" version = "0.11.0" @@ -872,12 +1194,14 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "lazy_static", "log", "mime", "mime_guess", + "native-tls", "percent-encoding", "pin-project-lite", "rustls", @@ -885,6 +1209,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", + "tokio-native-tls", "tokio-rustls", "url", "wasm-bindgen", @@ -928,6 +1253,28 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "sct" version = "0.6.0" @@ -938,6 +1285,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.123" @@ -1073,6 +1443,20 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand 0.8.3", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -1091,6 +1475,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" +dependencies = [ + "jpeg-decoder", + "miniz_oxide 0.4.3", + "weezl", +] + [[package]] name = "time" version = "0.1.43" @@ -1143,6 +1538,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.22.0" @@ -1316,6 +1721,12 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4bf03e0ca70d626ecc4ba6b0763b934b6f2976e8c744088bb3c1d646fbb1ad0" +[[package]] +name = "vcpkg" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" + [[package]] name = "version_check" version = "0.9.2" @@ -1450,6 +1861,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "weezl" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 8e3f57f..3b09fed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bluearch-recruitment = { git = "https://github.com/Paoda/bluearch-recruitment.git", branch = "main", version = "^0.1" } +bluearch-recruitment = { git = "https://github.com/Paoda/bluearch-recruitment.git", branch = "main" } dotenv = "^0.15" env_logger = "^0.8" lazy_static = "^1.4" @@ -16,3 +16,5 @@ serde = "^1.0" serde_json = "^1.0" serenity = "^0.10" tokio = { version = "^1.0", features = ["macros", "rt-multi-thread"] } +image = "^0.23" +reqwest = "^0.11" diff --git a/src/main.rs b/src/main.rs index 96eb03d..d25c462 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,28 +3,32 @@ use bluearch_recruitment::gacha::{GachaBuilder, Rarity, Recruitment}; use bluearch_recruitment::i18n::Language; use bluearch_recruitment::student::Student; use dotenv::dotenv; +use image::codecs::jpeg::JpegEncoder; +use image::imageops::FilterType; +use image::io::Reader as ImageReader; +use image::{ColorType, ImageEncoder, Rgba, RgbaImage}; use lazy_static::lazy_static; use log::{debug, error, info, warn}; use serenity::async_trait; use serenity::client::{Client, Context, EventHandler}; +use serenity::framework::standard::macros::{command, group, help}; use serenity::framework::standard::{ - help_commands, - macros::{command, group, help}, - Args, CommandGroup, CommandResult, HelpOptions, StandardFramework, + help_commands, Args, CommandGroup, CommandResult, HelpOptions, StandardFramework, }; use serenity::model::channel::Message; use serenity::model::id::UserId; use serenity::utils::Colour; -use std::collections::HashSet; -use std::env::{self}; -use std::time::{SystemTime, UNIX_EPOCH}; - +use std::collections::{HashMap, HashSet}; +use std::env; +use std::io::Cursor; +use std::sync::Mutex; +use std::time::{Instant, SystemTime, UNIX_EPOCH}; #[group] #[commands(ping, source)] struct General; #[group] -#[commands(roll, banner)] +#[commands(roll, banner, roll10)] struct RecruitmentCommands; struct Handler; @@ -40,9 +44,12 @@ const IMG_SOURCE: &str = "https://thearchive.gg"; const BANNER_IMG_URL: &str = "https://static.wikia.nocookie.net/blue-archive/images/e/e0/Gacha_Banner_01.png/revision/latest/"; const BLUE_ARCHIVE_BLUE: Colour = Colour::from_rgb(0, 215, 251); +type Cache = Mutex>; + lazy_static! { static ref STUDENTS: Vec = serde_json::from_str(STUDENTS_JSON).unwrap(); static ref BANNER: Banner = create_banner(); + static ref CACHE: Cache = Mutex::new(HashMap::new()); } #[tokio::main] @@ -134,6 +141,7 @@ async fn roll(ctx: &Context, msg: &Message) -> CommandResult { let img_url = format!("{}/Characters/{}.png", CDN_URL, url_name); let title_url = format!("https://www.thearchive.gg/characters/{}", url_name); let icon_url = format!("{}/Icons/icon-brand.png", CDN_URL); + let rarity_colour = get_rarity_colour(student.rarity); let rarity_str = match student.rarity { Rarity::One => ":star:", @@ -141,12 +149,6 @@ async fn roll(ctx: &Context, msg: &Message) -> CommandResult { Rarity::Three => ":star::star::star:", }; - let rarity_colour = match student.rarity { - Rarity::One => Colour::from_rgb(227, 234, 240), - Rarity::Two => Colour::from_rgb(255, 248, 124), - Rarity::Three => Colour::from_rgb(253, 198, 229), - }; - channel .send_message(ctx, |m| { m.embed(|embed| { @@ -168,6 +170,169 @@ async fn roll(ctx: &Context, msg: &Message) -> CommandResult { Ok(()) } +#[command] +#[aliases(tenroll)] +async fn roll10(ctx: &Context, msg: &Message) -> CommandResult { + let author_name = format!("{}#{}", msg.author.name, msg.author.discriminator); + info!("{} requested a ten roll", author_name); + let channel = msg.channel_id; + + let typing = channel.start_typing(&ctx.http)?; + + const RESIZE_WIDTH: u32 = 202; // OG: 404 (2020-02-11) + const RESIZE_HEIGHT: u32 = 228; // OG: 456 (2020-02-11) + const IMG_WIDTH: u32 = RESIZE_WIDTH * 5; + const IMG_HEIGHT: u32 = RESIZE_HEIGHT * 2; + + let mut collage = RgbaImage::new(IMG_WIDTH, IMG_HEIGHT); + let mut images: Vec = Vec::with_capacity(10); + + let students = BANNER.roll10(); + let mut max_rarity = Rarity::One; + + let start = Instant::now(); + for student in students.iter() { + let eng_name = student.name.get(Language::English).unwrap(); + let url_name = if eng_name == "Junko" { + "Zunko" + } else { + &eng_name + }; + + max_rarity = max_rarity.max(student.rarity); + + let img_url = format!("{}/Characters/{}.png", CDN_URL, url_name); + let image = get_image_from_url(&img_url, RESIZE_WIDTH, RESIZE_HEIGHT).await; + + images.push(image); + } + let elapsed_ms = (Instant::now() - start).as_millis(); + info!("10-roll, DL, and resize took {}ms", elapsed_ms); + + let start = Instant::now(); + for x in (0..IMG_WIDTH).step_by(RESIZE_WIDTH as usize) { + let index: usize = (((IMG_WIDTH - x) / RESIZE_WIDTH) - 1) as usize; + + // Top Image + image::imageops::overlay(&mut collage, &images[index], x, 0); + + // Bottom Image + image::imageops::overlay(&mut collage, &images[index + 5], x, RESIZE_HEIGHT); + } + let elapsed_ms = (Instant::now() - start).as_millis(); + info!("Collage Build took {}ms", elapsed_ms); + + let mut jpeg = Vec::new(); + let encoder = JpegEncoder::new(&mut jpeg); + + let write_result = encoder.write_image(&collage, IMG_WIDTH, IMG_HEIGHT, ColorType::Rgba8); + + if let Err(err) = write_result { + error!("Failed to Encode JPEG: {:?}", err); + msg.reply( + ctx, + "アロナ failed to perform your 10-roll. Please try again", + ) + .await?; + return Ok(()); + } + + let files = vec![(jpeg.as_slice(), "result.jpeg")]; + let _ = typing.stop(); + channel + .send_files(ctx, files, |m| { + m.embed(|embed| { + embed + .title(format!("{} 10-roll", BANNER.name)) + .description(BANNER.name.get(Language::English).unwrap()) + .attachment("result.jpeg") + .colour(get_rarity_colour(max_rarity)) + }) + }) + .await?; + Ok(()) +} + +async fn get_image_from_url(url: &str, width: u32, height: u32) -> RgbaImage { + if let Some(img) = check_cache(url) { + info!("Cache Hit for {}", url); + return img; + } + + info!("Downloading {}", url); + match reqwest::get(url).await { + Ok(resp) => match resp.bytes().await { + Ok(bytes) => match ImageReader::new(Cursor::new(bytes)).with_guessed_format() { + Ok(img_reader) => match img_reader.decode() { + Ok(dynamic_img) => { + info!("Successfully decoded image from {}", url); + let img = image::imageops::resize( + &dynamic_img, + width, + height, + FilterType::Nearest, + ); + + add_to_cache(url, &img); + img + } + Err(err) => { + warn!("Decoding Error: {}", err); + generate_default_img(width, height) + } + }, + Err(err) => { + error!("Unexpected IO Error Occurred: {}", err); + // We can recover here, but maybe it's worth panicking here? + generate_default_img(width, height) + } + }, + + Err(err) => { + warn!("Response Parse failed: {:}?", err); + generate_default_img(width, height) + } + }, + Err(err) => { + warn!("Download failed: {:?}", err); + generate_default_img(width, height) + } + } +} + +fn get_rarity_colour(rarity: Rarity) -> Colour { + match rarity { + Rarity::One => Colour::from_rgb(227, 234, 240), + Rarity::Two => Colour::from_rgb(255, 248, 124), + Rarity::Three => Colour::from_rgb(253, 198, 229), + } +} + +fn generate_default_img(width: u32, height: u32) -> RgbaImage { + let mut img = RgbaImage::new(width, height); + for x in 0..height { + for y in 0..width { + img.put_pixel(x, y, Rgba([160, 32, 240, 1])); + } + } + + img +} + +fn check_cache(url: &str) -> Option { + if let Ok(lock) = CACHE.lock() { + lock.get(url).cloned() + } else { + None + } +} + +fn add_to_cache(url: &str, img: &RgbaImage) { + if let Ok(ref mut lock) = CACHE.lock() { + lock.insert(url.to_string(), img.clone()); + } +} + #[command] async fn banner(ctx: &Context, msg: &Message) -> CommandResult { let author_name = format!("{}#{}", msg.author.name, msg.author.discriminator); @@ -232,13 +397,13 @@ pub fn create_banner() -> Banner { let pool: Vec = STUDENTS .iter() .filter(|student| student.name != "ノゾミ") - .map(|student| student.clone()) + .cloned() .collect(); let sparkable: Vec = pool .iter() .filter(|student| student.name == "ホシノ" || student.name == "シロコ") - .map(|student| student.clone()) + .cloned() .collect(); let gacha = GachaBuilder::new(79.0, 18.5, 2.5)