diff --git a/Cargo.lock b/Cargo.lock index 76ee9b7..22eb3c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", @@ -315,6 +315,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "command_attr" +version = "0.5.1" +source = "git+https://github.com/nshout/serenity-self#6b62ca61aa592d4fd22dc42848388c5fb3889161" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -1141,12 +1151,12 @@ checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libloading" -version = "0.7.4" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1974,7 +1984,42 @@ dependencies = [ "bitflags 2.4.1", "bytes", "chrono", - "command_attr", + "command_attr 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dashmap", + "flate2", + "futures", + "fxhash", + "levenshtein", + "mime_guess", + "parking_lot", + "percent-encoding", + "reqwest", + "secrecy", + "serde", + "serde_json", + "static_assertions", + "time", + "tokio", + "tokio-tungstenite", + "tracing", + "typemap_rev", + "typesize", + "url", + "uwl", +] + +[[package]] +name = "serenity" +version = "0.13.0-alpha2" +source = "git+https://github.com/nshout/serenity-self#6b62ca61aa592d4fd22dc42848388c5fb3889161" +dependencies = [ + "arrayvec", + "async-trait", + "base64 0.21.5", + "bitflags 2.4.1", + "bytes", + "chrono", + "command_attr 0.5.1 (git+https://github.com/nshout/serenity-self)", "dashmap", "flate2", "futures", @@ -2174,9 +2219,11 @@ version = "0.1.0" dependencies = [ "dotenvy", "image", + "leptess", "regex", + "rusty-tesseract", "serde", - "serenity", + "serenity 0.12.0", "swordfish-common", "tokio", "toml", @@ -2186,16 +2233,22 @@ dependencies = [ name = "swordfish-common" version = "0.1.0" dependencies = [ - "leptess", "log", "mongodb", - "rusty-tesseract", "serde", "tokio", "tracing", "tracing-subscriber", ] +[[package]] +name = "swordfish-user" +version = "0.1.0" +dependencies = [ + "serenity 0.13.0-alpha2", + "swordfish-common", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 002d885..6bdea04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,5 @@ resolver = "1" members = [ "swordfish-common", "swordfish" -] +, "swordfish-user"] default-members = ["swordfish"] diff --git a/swordfish-common/Cargo.toml b/swordfish-common/Cargo.toml index 7ff7012..58eb82a 100644 --- a/swordfish-common/Cargo.toml +++ b/swordfish-common/Cargo.toml @@ -6,9 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -leptess = "0.14.0" log = "0.4.20" -rusty-tesseract = "1.1.9" serde = "1.0.195" tokio = "1.35.1" tracing = "0.1.40" diff --git a/swordfish-common/src/lib.rs b/swordfish-common/src/lib.rs index 2ffa8c0..ce6c26c 100644 --- a/swordfish-common/src/lib.rs +++ b/swordfish-common/src/lib.rs @@ -1,12 +1,12 @@ #![feature(lazy_cell)] #![feature(string_remove_matches)] pub use log; +pub use tokio; pub use tracing::{debug, error, info, trace, warn}; use tracing_subscriber::{self, fmt, EnvFilter}; pub mod constants; pub mod database; pub mod structs; -pub mod tesseract; pub mod utils; pub fn setup_logger(level: &str) -> Result<(), ()> { diff --git a/swordfish-user/Cargo.toml b/swordfish-user/Cargo.toml new file mode 100644 index 0000000..95253bf --- /dev/null +++ b/swordfish-user/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "swordfish-user" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serenity = { git = "https://github.com/nshout/serenity-self", version = "0.13.0-alpha2" } + + +[dependencies.swordfish-common] +path = "../swordfish-common" diff --git a/swordfish-user/src/main.rs b/swordfish-user/src/main.rs new file mode 100644 index 0000000..505e3b3 --- /dev/null +++ b/swordfish-user/src/main.rs @@ -0,0 +1,202 @@ +use std::env; + +use serenity::all::{Embed, MessageUpdateEvent}; +use serenity::async_trait; +use serenity::model::channel::Message; +use serenity::prelude::*; +use swordfish_common::setup_logger; +use swordfish_common::{constants, database, utils}; +use swordfish_common::{debug, tokio}; +use swordfish_common::{error, info, trace}; + +const GITHUB_URL: &str = "https://github.com/teppyboy/swordfish"; + +async fn parse_katana(ctx: &Context, msg: &Message) -> Result<(), String> { + if msg.embeds.len() == 0 { + return Ok(()); + } + let embed = &msg.embeds[0]; + parse_katana_embed(embed).await; + Ok(()) +} + +async fn parse_katana_embed(embed: &Embed) { + match embed.author { + Some(ref author) => match author.name.as_str() { + "Card Collection" => { + let cards = utils::katana::parse_cards_from_katana_kc_ow( + &embed.description.as_ref().unwrap(), + ); + if cards.len() == 0 { + return; + } + debug!("Importing cards from Katana 'Card Collection'"); + match database::katana::write_cards(cards).await { + Ok(_) => { + debug!("Imported successully"); + } + Err(why) => { + error!("Failed to import card: {:?}", why); + } + } + } + _ => {} + }, + None => {} + }; + match embed.title { + Some(ref title) => match title.as_str() { + "Character Lookup" => { + let card = match utils::katana::parse_cards_from_katana_klu_lookup( + &embed.description.as_ref().unwrap(), + ) { + Some(card) => card, + None => { + return; + } + }; + debug!("Importing a card from Katana 'Character Lookup'"); + match database::katana::write_card(card).await { + Ok(_) => { + debug!("Imported successully"); + } + Err(why) => { + error!("Failed to import card: {:?}", why); + } + } + } + "Character Results" => { + let fields = match embed.fields.len() { + 0 => { + return; + } + _ => &embed.fields, + }; + let embed_field = fields.get(0).unwrap(); + let cards = utils::katana::parse_cards_from_katana_klu_results(&embed_field.value); + if cards.len() == 0 { + return; + } + debug!("Importing cards from Katana 'Character Results'"); + match database::katana::write_cards(cards).await { + Ok(_) => { + debug!("Imported successully"); + } + Err(why) => { + error!("Failed to import card: {:?}", why); + } + } + } + _ => {} + }, + None => {} + }; +} + +async fn parse_qingque_event(ctx: &Context, event: MessageUpdateEvent) -> Result<(), String> { + if event.embeds.is_none() || event.embeds.clone().unwrap().len() == 0 { + return Ok(()); + } + let embed = &event.embeds.unwrap()[0]; + let embed_title = match embed.title { + Some(ref title) => title, + None => { + return Ok(()); + } + }; + match embed_title.as_str() { + "Top Wishlist" => { + let cards = utils::katana::parse_cards_from_qingque_atopwl( + &embed.description.as_ref().unwrap(), + ); + debug!("Importing cards from Qingque 'Top Wishlist'"); + match database::katana::write_cards(cards).await { + Ok(_) => { + debug!("Imported successully"); + } + Err(why) => { + error!("Failed to import card: {:?}", why); + } + } + } + _ => { + return Ok(()); + } + } + Ok(()) +} + +async fn parse_katana_event(ctx: &Context, event: MessageUpdateEvent) -> Result<(), String> { + if event.embeds.is_none() || event.embeds.clone().unwrap().len() == 0 { + return Ok(()); + } + let embed = &event.embeds.unwrap()[0]; + parse_katana_embed(embed).await; + Ok(()) +} + +struct Handler; + +#[async_trait] +impl EventHandler for Handler { + async fn message(&self, ctx: Context, msg: Message) { + if msg.author.id.get() == constants::KATANA_ID { + parse_katana(&ctx, &msg).await.unwrap(); + } + } + async fn message_update( + &self, + ctx: Context, + old_if_available: Option, + new: Option, + event: MessageUpdateEvent, + ) { + let author = match event.author { + Some(ref v) => v, + None => { + return; + } + }; + if author.id == ctx.cache.current_user().id { + return; + } + let content = match event.content { + Some(ref v) => v, + None => { + return; + } + }; + trace!("Message update: {}, sender: {}", content, author.id); + match author.id.get() { + constants::KATANA_ID => { + parse_katana_event(&ctx, event).await.unwrap(); + } + constants::QINGQUE_ID => { + parse_qingque_event(&ctx, event).await.unwrap(); + } + _ => {} + } + } +} + +#[tokio::main] +async fn main() { + // Login with a user token from the environment + let log_level = env::var("LOG_LEVEL").unwrap_or("info".to_string()); + setup_logger(log_level.as_str()).expect("Failed to setup logger"); + let token = env::var("DISCORD_TOKEN").expect("Token not found"); + info!("Swordfish v{} - {}", env!("CARGO_PKG_VERSION"), GITHUB_URL); + info!("Log level: {}", log_level); + info!("Initializing database..."); + swordfish_common::database::init().await; + info!("Initializing Discord client..."); + let mut client = Client::builder(token) + .event_handler(Handler) + .await + .expect("Error creating client"); + + // start listening for events by starting a single shard + if let Err(why) = client.start().await { + error!("An error occurred while running the client: {:?}", why); + } +} diff --git a/swordfish/Cargo.toml b/swordfish/Cargo.toml index 3b23b80..dfda7fb 100644 --- a/swordfish/Cargo.toml +++ b/swordfish/Cargo.toml @@ -8,7 +8,9 @@ edition = "2021" [dependencies] dotenvy = "0.15.7" image = "0.24.7" +leptess = "0.14.0" regex = "1.10.2" +rusty-tesseract = "1.1.9" serde = "1.0.193" serenity = { version = "0.12.0", features = ["builder"] } tokio = { version = "1.35.1", features = ["full"] } diff --git a/swordfish/src/katana.rs b/swordfish/src/katana.rs index 6efce8e..d07144e 100644 --- a/swordfish/src/katana.rs +++ b/swordfish/src/katana.rs @@ -1,3 +1,4 @@ +use crate::tesseract::{libtesseract, subprocess}; use crate::CONFIG; use image::imageops::colorops::contrast_in_place; use image::io::Reader as ImageReader; @@ -8,7 +9,6 @@ use std::io::Cursor; use std::sync::LazyLock; use swordfish_common::database::katana as db; use swordfish_common::structs::Card; -use swordfish_common::tesseract::{libtesseract, subprocess}; use swordfish_common::{trace, warn}; use tokio::task; diff --git a/swordfish/src/main.rs b/swordfish/src/main.rs index 4015978..6e8a807 100644 --- a/swordfish/src/main.rs +++ b/swordfish/src/main.rs @@ -19,6 +19,7 @@ mod debug; mod helper; mod katana; mod template; +mod tesseract; const GITHUB_URL: &str = "https://github.com/teppyboy/swordfish"; static CONFIG: OnceLock = OnceLock::new(); @@ -95,10 +96,10 @@ async fn parse_qingque_event(ctx: &Context, event: MessageUpdateEvent) -> Result let cards = utils::katana::parse_cards_from_qingque_atopwl( &embed.description.as_ref().unwrap(), ); - trace!("Begin importing cards"); + debug!("Importing cards from Qingque 'Top Wishlist'"); match database::katana::write_cards(cards).await { Ok(_) => { - trace!("Imported successully"); + debug!("Imported successully"); } Err(why) => { error!("Failed to import card: {:?}", why); @@ -206,10 +207,10 @@ async fn parse_katana_embed(embed: &Embed) { if cards.len() == 0 { return; } - trace!("Begin importing cards"); + debug!("Importing cards from Katana 'Card Collection'"); match database::katana::write_cards(cards).await { Ok(_) => { - trace!("Imported successully"); + debug!("Imported successully"); } Err(why) => { error!("Failed to import card: {:?}", why); @@ -231,10 +232,10 @@ async fn parse_katana_embed(embed: &Embed) { return; } }; - trace!("Begin importing a card"); + debug!("Importing a card from Katana 'Character Lookup'"); match database::katana::write_card(card).await { Ok(_) => { - trace!("Imported successully"); + debug!("Imported successully"); } Err(why) => { error!("Failed to import card: {:?}", why); @@ -253,10 +254,10 @@ async fn parse_katana_embed(embed: &Embed) { if cards.len() == 0 { return; } - trace!("Begin importing cards"); + debug!("Importing cards from Katana 'Character Results'"); match database::katana::write_cards(cards).await { Ok(_) => { - trace!("Imported successully"); + debug!("Imported successully"); } Err(why) => { error!("Failed to import card: {:?}", why); diff --git a/swordfish-common/src/tesseract/libtesseract.rs b/swordfish/src/tesseract/libtesseract.rs similarity index 100% rename from swordfish-common/src/tesseract/libtesseract.rs rename to swordfish/src/tesseract/libtesseract.rs diff --git a/swordfish-common/src/tesseract/mod.rs b/swordfish/src/tesseract/mod.rs similarity index 100% rename from swordfish-common/src/tesseract/mod.rs rename to swordfish/src/tesseract/mod.rs diff --git a/swordfish-common/src/tesseract/subprocess.rs b/swordfish/src/tesseract/subprocess.rs similarity index 100% rename from swordfish-common/src/tesseract/subprocess.rs rename to swordfish/src/tesseract/subprocess.rs