From 7bae2e8a4d1c79e341507a18b0162b7845700da8 Mon Sep 17 00:00:00 2001 From: Wynd Date: Mon, 27 Mar 2023 18:51:15 +0300 Subject: [PATCH] Initial commit --- .editorconfig | 17 +++++ .gitignore | 17 +++++ Cargo.toml | 12 ++++ rustfmt.toml | 4 ++ src/main.rs | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 225 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 rustfmt.toml create mode 100644 src/main.rs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5df031b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = tab +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aadddec --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Self explanatory really +.env \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dc5ab3c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "mochiless" +version = "0.1.0" +authors = ["Wynd"] +edition = "2021" + +[dependencies] +tokio = { version = "1.18.1", features = ["macros", "rt-multi-thread"] } +serenity = { version = "0.11.1", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "framework", "standard_framework"] } +dotenv = { version = "0.15.0" } +regex = { version = "1.5.5" } +lazy_static = { version = "1.4.0" } \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..c577f2d --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +unstable_features = true +reorder_imports = true +brace_style = "AlwaysNextLine" +max_width = 80 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..22399bb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,175 @@ +#![feature(test)] + +extern crate test; + +use std::collections::HashMap; + +use dotenv::dotenv; +use regex::Regex; +use lazy_static::lazy_static; + +use serenity::async_trait; +use serenity::framework::standard::macros::{command, group}; +use serenity::model::channel::Message; +use serenity::model::gateway::Ready; +use serenity::prelude::*; +use serenity::framework::standard::{StandardFramework, CommandResult}; +use serenity::utils::MessageBuilder; + +const QUESTION_WORDS: &str = "when|wen|how|is|will|add|will|can|has"; +const END_WORDS: &str = "\\?|when|wen|added"; + +// Trying my best to not use lookaheads here :'( +lazy_static! { + static ref LEET_CHARS: HashMap> = { + let mut m = HashMap::new(); + m.insert('A', vec!["@", "4", "/\\", "/-\\", "^", "(L", "Д", "Á‎", "Ă‎", "Ǎ‎", "Â", "Ä‎", "À", "Ā", "Ą‎", "Å‎", "Ꞻ‎", "Ⱥ", "Ã‎", "Ạ", "Α", "А"]); + m.insert('B', vec!["I3", "8", "13", "|3", "ß", "!3", "(3", "/3", ")3", "|-]", "j3", "Ꞗ", "Ɓ", "Ƀ‎", "Ƃ‎", "Β", "ʙ", "В", "в", "Ᏼ", "ᏼ", "ᗷ", "ᛒ", "ℬ", "ꓐ", "Ꞵ", "B", "𐊂", "𐊡", "𐌁", "𝐁", "𝐵", "𝑩", "𝓑", "𝔅", "𝔹", "𝕭", "𝖡", "𝗕", "𝘉", "𝘽", "𝙱", "𝚩", "𝛣", "𝜝", "𝝗", "𝞑"]); + m.insert('C', vec!["[", "¢", "<", "(", "©", "Ć‎", "Č", "Ç", "Ĉ‎", "ɕ‎", "Ċ‎", "Ƈ‎", "Ꞔ‎", "C̱", "Ȼ", "С", "Ϲ", "Ꮯ", "ᑕ", "ℂ", "ℭ", "Ⅽ", "⊂", "Ⲥ", "⸦", "ꓚ", "C", "𐊢", "𐌂", "𐐕", "𐔜", "𑣩", "𑣲", "𝐂", "𝐶", "𝑪", "𝒞", "𝓒", "𝕮", "𝖢", "𝗖", "𝘊", "𝘾", "𝙲", "🝌"]); + m.insert('D', vec![]); + m.insert('E', vec!["3", "Ε", "Ξ", "Σ"]); + m.insert('F', vec![]); + m.insert('G', vec![]); + m.insert('H', vec!["#", "/-/", "\\-\\", "[-]", "]-[", ")-(", "(-)", ":-:", "|~|", "|-|", "]~[", "}{", "!-!", "1-1", "\\-/", "I+I", "Һ", "ʜ", "Η", "Н", "н", "Ꮋ", "ᕼ", "ℋ", "ℌ", "ℍ", "Ⲏ", "ꓧ", "ꮋ", "H", "𐋏", "𝐇", "𝐻", "𝑯", "𝓗", "𝕳", "𝖧", "𝗛", "𝘏", "𝙃", "𝙷", "𝚮", "𝛨", "𝜢", "𝝜", "𝞖"]); + m.insert('I', vec!["!", "1", "|", "][", "eye", "Í", "Ĭ", "Î‎", "Ï‎", "Ï", "Ϊ", "Ì‎", "Ī", "Į", "Ꞽ", "Ɨ‎", "Ĩ‎", "Ị‎", "Ι", "І", "Ɩ", "ǀ", "Ӏ", "׀", "ا", "١", "۱", "ߊ", "ᛁ", "ℐ", "ℑ", "ℓ", "Ⅰ", "ⅼ", "∣", "⏽", "Ⲓ", "ⵏ", "ꓲ", "ﺍ", "ﺎ", "1", "I", "l", "│", "𐊊", "𐌉", "𐌠", "𖼨", "𝐈", "𝐥", "𝐼", "𝑙", "𝑰", "𝒍", "𝓁", "𝓘", "𝓵", "𝔩", "𝕀", "𝕝", "𝕴", "𝖑", "𝖨", "𝗅", "𝗜", "𝗹", "𝘐", "𝘭", "𝙄", "𝙡", "𝙸", "𝚕", "𝚰", "𝛪", "𝜤", "𝝞", "𝞘", "𝟏", "𝟙", "𝟣", "𝟭", "𝟷", "𞣇", "Ί"]); + m.insert('J', vec![]); + m.insert('K', vec!["|<", "Κ"]); + m.insert('L', vec!["£", "|_", "Ỻ‎", "Ɬ", "Ļ", "Ľ", "Ŀ", "ɭ", "Ɫ", "Ł‎", "ʟ", "Ꮮ", "ᒪ", "ℒ", "Ⳑ", "ⳑ", "ꓡ", "ꮮ", "𐐛", "𐑃", "𐔦", "𑢣", "𑢲", "𖼖", "𝈪", "𝐋", "𝐿", "𝑳", "𝓛", "𝔏", "𝕃", "𝕷", "𝖫", "𝗟", "𝘓", "𝙇", "𝙻"]); + m.insert('M', vec!["/\\/\\", "/V\\", "[V]", "|\\/|", "^^", "<\\/>", "{V}", "(V)", "|\\|\\", "]\\/[", "NN", "M̈", "Ṁ‎", "Ɱ", "M̃", "Μ", "Ϻ", "М", "Ꮇ", "ᗰ", "ᛖ", "ℳ", "Ⅿ", "Ⲙ", "ꓟ", "M", "𐊰", "𐌑", "𝐌", "𝑀", "𝑴", "𝓜", "𝔐", "𝕄", "𝕸", "𝖬", "𝗠", "𝘔", "𝙈", "𝙼", "𝚳", "𝛭", "𝜧", "𝝡", "𝞛"]); + m.insert('N', vec!["Ν", "Π"]); + m.insert('O', vec!["0", "()", "oh", "[]", "<>", "Ø", "Ó", "Ŏ", "Ô‎", "Ö", "Ő‎", "Ò", "Ơ", "Ō", "Ǫ", "Ɵ", "Õ", "Ọ", "Œ", "Ο", "Θ", "Φ", "Ω", "О", "Օ", "Ȯ", "Ỏ", "໐", "ဝ", "၀", "Ჿ", "ዐ", "ᴏ", "ᴑ", "ℴ", "Ⲟ", "Ⲟ", "ⵔ", "〇", "ꓳ", "0", "O", "O", "𐊒", "𐊫", "𐐄", "𐐄", "𐓂", "𐓂", "𐔖", "𑓐", "𑢵", "𑢷", "𑣠", "𝐎", "𝐨", "𝑂", "𝑜", "𝑶", "𝒐", "𝒪", "𝓞", "𝓸", "𝔒", "𝔬", "𝕆", "𝕠", "𝕺", "𝖔", "𝖮", "𝗈", "𝗢", "𝗼", "𝘖", "𝘰", "𝙊", "𝙤", "𝙾", "𝚘", "𝚶", "𝛐", "𝛔", "𝛰", "𝜊", "𝜎", "𝜪", "𝝄", "𝝈", "𝝤", "𝝾", "𝞂", "𝞞", "𝞸", "𝞼", "𝟎", "𝟘", "𝟢", "𝟬", "𝟶", "🯰", "Ό"]); + m.insert('U', vec!["(_)", "|_|", "v", "L|", "บ", "Ú‎", "Ŭ", "Û‎", "Ṳ", "Ü", "Ű", "Ù", "ʮ", "Ư", "Ū", "Ų", "Ů", "Ꞿ", "Ʉ", "ʯ‎", "Ũ", "Ụ"]); + m.insert('P', vec!["Ρ"]); + m.insert('Q', vec![]); + m.insert('R', vec!["I2", "9", "|`", "|~", "|?", "/2", "|^", "lz", "7", "2", "12", "®", "[z", "Я", "|2", "ꝶ‎", "Ŕ", "Ř‎", "ɾ", "Ɍ", "℞", "ꝵ‎", "Ꞧ", "Ɽ", "Ʀ", "ʀ", "Ꭱ", "Ꮢ", "ᖇ", "ᚱ", "ℛ", "ℜ", "ℝ", "ꓣ", "ꭱ", "ꮢ", "R", "𐒴", "𖼵", "𝈖", "𝐑", "𝑅", "𝑹", "𝓡", "𝕽", "𝖱", "𝗥", "𝘙", "𝙍", "𝚁"]); + m.insert('S', vec!["z"]); + m.insert('T', vec!["7", "Τ"]); + m.insert('V', vec![]); + m.insert('X', vec!["><", "}{", "ecks", "×", ")(", "][", "X̱", "cks", "Χ", "Х", "᙭", "ᚷ", "Ⅹ", "╳", "Ⲭ", "ⵝ", "ꓫ", "Ꭓ", "X", "𐊐", "𐊴", "𐌗", "𐌢", "𐔧", "𑣬", "𝐗", "𝑋", "𝑿", "𝒳", "𝓧", "𝔛", "𝕏", "𝖃", "𝖷", "𝗫", "𝘟", "𝙓", "𝚇", "𝚾", "𝛸", "𝜲", "𝝬", "𝞦"]); + return m; + }; +} + +#[group] +#[commands(ping)] +struct General; + +struct Handler; + +#[async_trait] +impl EventHandler for Handler { + + async fn message(&self, ctx: Context, msg: Message) { + + let mut message = String::from(&msg.content).to_uppercase(); + message.retain(|c| !c.is_whitespace()); // Remove the whitespaces + message = translate_from_leet(message).to_lowercase(); // Transform as many l33t characters into normal english + + //println!("{}", message); + + if message.contains("mochi") { + + let case1 = "(".to_owned() + QUESTION_WORDS + ").*mochi.*(" + END_WORDS + "|)"; + let case2 = "(".to_owned() + QUESTION_WORDS + ").*mochi"; + let case3 = "^mochi.*(".to_owned() + END_WORDS + "|)"; + let case4 = "^mochi$"; + let regex = format!(r"{}|{}|{}|{}", case1, case2, case3, case4); + let re = Regex::new(®ex).unwrap(); + + if re.is_match(&message) { + + let reply = MessageBuilder::new() + .push("https://tenor.com/view/will-smith-chris-rock-jada-pinkett-smith-oscars2022-smack-gif-25234614 ") + .mention(&msg.author.mention()) + .push_bold_safe(" N O") + .build(); + + if let Err(err) = msg.channel_id.say(ctx, reply).await { + println!("Error sending message: {:?}", err); + } + } + } + else if message.contains("roblox") { + + if let Err(err) = msg.delete(ctx).await { + println!("Error sending message: {:?}", err); + } + } + } + + async fn ready(&self, _: Context, ready: Ready) { + println!("{} is connected!", ready.user.name); + } +} + +#[tokio::main] +async fn main() { + + // Loading in the .env file that holds the environment variables + dotenv().ok(); + + // Loading the discord token from the .env file + let token = dotenv::var("DISCORD_TOKEN").expect("Expected a Discord Token"); + + // Setting up the intents + let intents = + GatewayIntents::GUILD_MESSAGES + | GatewayIntents::DIRECT_MESSAGES + | GatewayIntents::MESSAGE_CONTENT; + + // Setting up the framework for commands setting the prefix and groups assigned to it + let framework = StandardFramework::new() + .configure(|c| c + .with_whitespace(true) + .prefix("~") + ); + //.group(&GENERAL_GROUP); + + let mut client = Client::builder(token, intents) + .event_handler(Handler) + .framework(framework) + .await + .expect("Error creating the client"); + + if let Err(err) = client.start().await { + println!("Startup Error: {:?}", err); + } +} + +fn translate_from_leet(mut text: String) -> String { + for (key, leet) in LEET_CHARS.iter() { + for s in leet { + if !text.contains(s) { + continue; + } + text = text.replace(s, key.to_string().as_str()); + } + }; + return text; +} + +#[command] +async fn ping(ctx: &Context, msg: &Message) -> CommandResult { + + msg.channel_id.say(&ctx.http, "Pong!").await.expect("Cannot send message!"); + + return Ok(()); +} + +#[cfg(test)] +mod tests { + + use super::*; + use test::Bencher; + + #[test] + fn translator_test() { + let res = translate_from_leet(String::from("m0<|-|!")); + assert_eq!(&res, "mochi"); + } + + #[bench] + fn translator_bench(b: &mut Bencher) { + b.iter(|| translate_from_leet(String::from("m0<|-|!"))); + } +} \ No newline at end of file