diff --git a/Cargo.lock b/Cargo.lock index 2487073..0b68364 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,6 +60,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "arrayvec" version = "0.7.4" @@ -86,6 +92,20 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "auto_generate_cdp" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7af08ed49930c50104b2f1699d257e5053fb1809e370647bde9c58b31d65d417" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "serde", + "serde_json", + "ureq", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -222,6 +242,33 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -380,6 +427,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +dependencies = [ + "derive_builder_core", + "syn 2.0.66", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -479,6 +557,31 @@ dependencies = [ "version_check", ] +[[package]] +name = "fantoccini" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d266f4ccd0f172c4daebf7419868bb1948688097ed3b7f746493ed921023a3" +dependencies = [ + "base64 0.22.1", + "cookie 0.18.1", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "hyper-tls", + "hyper-util", + "mime", + "openssl", + "serde", + "serde_json", + "time", + "tokio", + "url", + "webdriver", +] + [[package]] name = "fastrand" version = "2.1.0" @@ -722,6 +825,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "handlebars" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5226a0e122dc74917f3a701484482bed3ee86d016c7356836abbaa033133a157" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -738,6 +855,29 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "headless_chrome" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50752288ae8799c84ed7fc6616611b7493c53d92265198b8a607c7e4256ebd2" +dependencies = [ + "anyhow", + "auto_generate_cdp", + "base64 0.22.1", + "derive_builder", + "log", + "rand", + "regex", + "serde", + "serde_json", + "tempfile", + "thiserror", + "tungstenite 0.23.0", + "url", + "which", + "winreg 0.52.0", +] + [[package]] name = "heck" version = "0.5.0" @@ -756,6 +896,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "html5ever" version = "0.26.0" @@ -1143,8 +1292,11 @@ dependencies = [ "bytes", "chrono", "dotenv", + "fantoccini", "futures", "fuzzy-matcher", + "handlebars", + "headless_chrome", "itertools", "libc", "lru", @@ -1478,6 +1630,51 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "pest_meta" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "phf" version = "0.10.1" @@ -1908,7 +2105,9 @@ version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ + "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki 0.102.4", "subtle", @@ -2230,6 +2429,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -2294,6 +2504,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socks" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b" +dependencies = [ + "byteorder", + "libc", + "winapi", +] + [[package]] name = "spin" version = "0.9.8" @@ -2616,7 +2837,7 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls 0.25.0", - "tungstenite", + "tungstenite 0.21.0", "webpki-roots 0.26.2", ] @@ -2755,6 +2976,24 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typemap_rev" version = "0.3.0" @@ -2796,6 +3035,12 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + [[package]] name = "unicase" version = "2.7.0" @@ -2811,6 +3056,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.13" @@ -2829,6 +3080,23 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls 0.23.10", + "rustls-pki-types", + "socks", + "url", + "webpki-roots 0.26.2", +] + [[package]] name = "url" version = "2.5.1" @@ -2991,6 +3259,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webdriver" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144ab979b12d36d65065635e646549925de229954de2eb3b47459b432a42db71" +dependencies = [ + "base64 0.21.7", + "bytes", + "cookie 0.16.2", + "http 0.2.12", + "log", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "time", + "unicode-segmentation", + "url", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -3006,6 +3294,18 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "which" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3205,6 +3505,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "write16" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index ead40a2..7cb2c83 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "isac-rs" version = "0.1.0" edition = "2021" -rust-version = "1.76" +rust-version = "1.80" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,8 +10,11 @@ rust-version = "1.76" bytes = "1.5.0" chrono = "0.4.31" dotenv = "0.15.0" +fantoccini = "0.21.0" futures = "0.3.28" fuzzy-matcher = "0.3.7" +handlebars = "6.0.0" +headless_chrome = "1.0.12" itertools = "0.13.0" libc = "0.2.153" lru = "0.12.3" diff --git a/renderer/template/overall.hbs b/renderer/template/overall.hbs index e9a69be..4c6f262 100644 --- a/renderer/template/overall.hbs +++ b/renderer/template/overall.hbs @@ -356,9 +356,9 @@ - {{#clan}} + {{#if clan}} [{{clan.tag}}] - {{/clan}} + {{/if}} {{user.ign}} {{user.karma}} diff --git a/renderer/template/recent.hbs b/renderer/template/recent.hbs index b52d22a..41fcbfb 100644 --- a/renderer/template/recent.hbs +++ b/renderer/template/recent.hbs @@ -273,7 +273,7 @@
Frags
Planes
- {{#div.pvp_solo}} + {{#if div.pvp_solo}}
Solo
{{div.pvp_solo.battles}}
@@ -290,8 +290,8 @@
{{div.pvp_solo.planes.value}}
- {{/div.pvp_solo}} - {{#div.pvp_div2}} + {{/if}} + {{#if div.pvp_div2}}
Div 2
{{div.pvp_div2.battles}}
@@ -308,8 +308,8 @@
{{div.pvp_div2.planes.value}}
- {{/div.pvp_div2}} - {{#div.pvp_div3}} + {{/if}} + {{#if div.pvp_div3}}
Div 3
{{div.pvp_div3.battles}}
@@ -326,8 +326,8 @@
{{div.pvp_div3.planes.value}}
- {{/div.pvp_div3}} - {{#div.rank_solo}} + {{/if}} + {{#if div.rank_solo}}
Rank
{{div.rank_solo.battles}} @@ -345,7 +345,7 @@
{{div.rank_solo.planes.value}}
- {{/div.rank_solo}} + {{/if}}
diff --git a/src/cmds/wws.rs b/src/cmds/wws.rs index a777651..9c2a180 100644 --- a/src/cmds/wws.rs +++ b/src/cmds/wws.rs @@ -208,7 +208,7 @@ pub async fn func_wws(ctx: &Context<'_>, partial_player: PartialPlayer) -> Resul clan: clan.clone(), user: player.clone(), }; - let img = overall_data.render(&ctx.data().client).await?; + let img = overall_data.render_test(&ctx.data().renderer).await?; let mut view = WwsView::new(overall_data, partial_player); let mut msg = ctx diff --git a/src/main.rs b/src/main.rs index e9521a2..a89fb1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod cmds; use cmds::*; mod dc_utils; +mod renderer; mod structs; mod tasks; mod template_data; @@ -18,11 +19,11 @@ use tracing::{error, info, warn}; use tracing_subscriber::{prelude::*, EnvFilter}; use crate::{ + renderer::Renderer, structs::{ Banner, ExpectedJs, GuildDefaultRegion, Linked, LittleConstant, Patrons, ShipLeaderboard, ShipsPara, }, - tasks::launch_renderer, utils::{error_handler, LoadSaveFromJson}, }; @@ -147,8 +148,6 @@ async fn main() { let webhook_http = bot.http.clone(); - let _renderer = launch_renderer().await; // it's used in linux specific code below - tokio::spawn(async move { if let Err(why) = bot.start().await { error!("Client error: {:?}", why); @@ -178,11 +177,6 @@ async fn main() { lb.save_json().await; info!("Saved leaderboard.json"); - // close renderer - #[cfg(target_os = "linux")] - if let Some(renderer_pid) = _renderer.id() { - unsafe { libc::kill(renderer_pid as i32, libc::SIGINT) }; - } shard_manager.shutdown_all().await; } @@ -212,6 +206,7 @@ pub struct DataInner { guild_default: tokio::sync::RwLock, banner: tokio::sync::RwLock, leaderboard: Mutex, + renderer: Renderer, } impl Default for DataInner { @@ -227,6 +222,7 @@ impl Default for DataInner { guild_default: tokio::sync::RwLock::new(GuildDefaultRegion::load_json_sync()), banner: tokio::sync::RwLock::new(Banner::load_json_sync()), leaderboard: Mutex::new(ShipLeaderboard::load_json_sync()), + renderer: Renderer::launch(), } } } diff --git a/src/renderer.rs b/src/renderer.rs new file mode 100644 index 0000000..3591d0e --- /dev/null +++ b/src/renderer.rs @@ -0,0 +1,80 @@ +use bytes::Bytes; +use handlebars::Handlebars; +use headless_chrome::{protocol::cdp::Page::CaptureScreenshotFormatOption, Browser, LaunchOptions}; +use serde::Serialize; +use std::fs; + +use crate::utils::{IsacError, IsacInfo}; + +pub struct Renderer { + browser: Browser, + reg: Handlebars<'static>, +} + +impl Renderer { + pub fn launch() -> Self { + const TEMPLATE_PATH: &str = "./renderer/template/"; + // let args = [ + // "-allow-file-access-from-files", + // "-disable-web-security", + // "--no-sandbox", + // ] + // .into_iter() + // .map(OsStr::new) + // .collect(); + + let browser = Browser::new( + LaunchOptions::default_builder() + .devtools(false) + // .args(args) + .build() + .unwrap(), + ) + .unwrap(); + let mut reg = Handlebars::new(); + + let templates = fs::read_dir(TEMPLATE_PATH) + .unwrap() + .filter_map(|entry| { + entry.ok().and_then(|e| { + let path = e.path(); + if path.is_file() && path.extension().unwrap_or_default() == "hbs" { + Some(path) + } else { + None + } + }) + }) + .collect::>(); + for path in templates { + reg.register_template_file(path.file_stem().unwrap().to_str().unwrap(), &path) + .unwrap(); + } + Self { browser, reg } + } + + pub async fn render(&self, template: &str, data: impl Serialize) -> Result { + let js_script = format!( + "document.write(`{}`);", + self.reg + .render(template, &data) + .unwrap() + .replace("`", "\\`") // Escape backticks in the HTML content + ); + let tab = self.browser.new_tab().unwrap(); + tab.evaluate(&js_script, false).unwrap(); + let img = tab + .find_element(".main") + .and_then(|element| element.capture_screenshot(CaptureScreenshotFormatOption::Png)) + .map_err(|_| IsacInfo::GeneralError { + msg: "screenshot failed".to_string(), + })?; + let _ = tab.close_target(); + Ok(Bytes::from(img)) + } +} + +#[test] +fn lauch_renderer() { + Renderer::launch(); +} diff --git a/src/template_data/overall.rs b/src/template_data/overall.rs index ba9b3ce..df7a1f8 100644 --- a/src/template_data/overall.rs +++ b/src/template_data/overall.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use super::Render; use crate::{ + renderer::Renderer, structs::{PartialClan, Player, ShipClass, ShipTier, Statistic}, utils::{IsacError, IsacInfo}, }; @@ -50,6 +51,9 @@ impl OverallTemplate { msg: "screenshot failed".to_string(), })?) } + pub async fn render_test(&self, renderer: &Renderer) -> Result { + renderer.render("overall", &self).await + } } #[derive(Serialize, Deserialize, Debug)]