diff --git a/config.toml b/config.toml index 6e1a529..5893ddd 100644 --- a/config.toml +++ b/config.toml @@ -1,12 +1,12 @@ #[server] -server = "198.98.52.138" #"irc.supernets.org" +server = "irc.supernets.org" port = 6697 use_ssl = true #[user] nickname = "g1r" realname = "git.supernets.org/sad/g1r" -channels = ["#dev", "#superbowl", "#5000"] +channels = ["#superbowl"] sasl_username = "" sasl_password = "" capabilities = ["sasl"] @@ -15,7 +15,7 @@ capabilities = ["sasl"] use_proxy = false proxy_type = "socks5" proxy_addr = "127.0.0.1" -proxy_port = 1080 +proxy_port = 9050 proxy_username = "" proxy_password = "" @@ -23,4 +23,5 @@ proxy_password = "" kickrejoin = true ascii_art = "./ircart/ircart" pump_delay = 0 # in milliseconds - +reconnect_delay = 5 +reconnect_attempts = 10 diff --git a/src/main.rs b/src/main.rs index 5094c33..7c6b29b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,6 @@ use tokio_native_tls::TlsConnector; use tokio::sync::mpsc; use serde::Deserialize; use std::fs; - use std::sync::atomic::{AtomicBool, Ordering}; use colored::*; use tokio_socks::tcp::Socks5Stream; @@ -21,18 +20,13 @@ struct Config { sasl_username: Option, sasl_password: Option, capabilities: Option>, - reconnect_delay: u64, reconnect_attempts: u64, - - // Proxy use_proxy: bool, - // proxy_type: Option, proxy_addr: Option, proxy_port: Option, proxy_username: Option, proxy_password: Option, - ascii_art: Option, pump_delay: u64, } @@ -45,8 +39,10 @@ mod mods { pub mod sed; pub mod ascii; pub mod vomit; + pub mod drugs; // pub mod invade; } + use mods::proxy::proxy_exec; use mods::tls::tls_exec; use mods::handler::handler; @@ -54,6 +50,7 @@ use mods::sasl::{start_sasl_auth, handle_sasl_messages}; use mods::sed::{SedCommand, MessageBuffer}; use mods::ascii::handle_ascii_command; use mods::vomit::handle_vomit_command; +use mods::drugs::Drugs; //use mods::invade::{handle_invade_command}; #[tokio::main(flavor = "multi_thread", worker_threads = 12)] @@ -119,17 +116,15 @@ async fn main() -> Result<(), Box> { Ok(()) } -/// Load the config file fn loaded_config() -> Result> { let config_contents = fs::read_to_string("config.toml")?; let config: Config = toml::from_str(&config_contents)?; Ok(config) } -/// Read messages from the server async fn readmsg(mut reader: tokio::io::ReadHalf, tx: tokio::sync::mpsc::Sender) where S: AsyncRead + Unpin { let mut buf = vec![0; 4096]; - while let Ok (n) = reader.read(&mut buf).await { + while let Ok(n) = reader.read(&mut buf).await { if n == 0 { break; } let msg_list = String::from_utf8_lossy(&buf[..n]).to_string(); for lines in msg_list.lines() { @@ -145,7 +140,6 @@ async fn readmsg(mut reader: tokio::io::ReadHalf, tx: tokio::sync::mpsc::S static SASL_AUTH: AtomicBool = AtomicBool::new(false); -/// Write messages to the server async fn writemsg(mut writer: tokio::io::WriteHalf, mut rx: tokio::sync::mpsc::Receiver, config: &Config, mut message_buffer: MessageBuffer) where S: AsyncWrite + Unpin { let username = config.sasl_username.clone().unwrap(); let password = config.sasl_password.clone().unwrap(); @@ -162,6 +156,8 @@ async fn writemsg(mut writer: tokio::io::WriteHalf, mut rx: tokio::sync::m writer.flush().await.unwrap(); } + let mut drugs = Drugs::new(); + while let Some(msg) = rx.recv().await { let msg = msg.trim(); if msg.is_empty() { @@ -224,7 +220,6 @@ async fn writemsg(mut writer: tokio::io::WriteHalf, mut rx: tokio::sync::m }; println!("{} {} {} {} {} {} {} {} {}", "DEBUG:".bold().yellow(), "channel:".bold().green(), channel.purple(), "user:".bold().green(), user.purple(), "host:".bold().green(), host.purple(), "msg:".bold().green(), msg_content.yellow()); - // sed if msg_content.starts_with("s/") { if let Some(sed_command) = SedCommand::parse(&msg_content.clone()) { if let Some(response) = message_buffer.apply_sed_command(&sed_command) { @@ -236,27 +231,23 @@ async fn writemsg(mut writer: tokio::io::WriteHalf, mut rx: tokio::sync::m message_buffer.add_message(msg_content.clone().to_string()); } - // ansi art if msg_content.starts_with("%ascii") { let _ = handle_ascii_command(&mut writer, config, &msg_content, channel).await; } - // vomit if msg_content.starts_with("%vomit") { let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await; } - // invade -// if msg_content.starts_with("%invade") { -// let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await; -// } - // other commands here + if ["%chug", "%smoke", "%toke", "%100", "%extendo", "%fatfuck", "%beer"].iter().any(|&prefix| msg_content.starts_with(prefix)) { + drugs.handle_drugs_command(&mut writer, config, &msg_content, channel).await + .unwrap_or_else(|e| eprintln!("Error handling drugs command: {}", e)); + } + } } } - - async fn nickme(writer: &mut W, nickname: &str, realname: &str) -> Result<(), Box> { writer.write_all(format!("NICK {}\r\n", nickname).as_bytes()).await?; writer.flush().await?; @@ -265,6 +256,3 @@ async fn nickme(writer: &mut W, nickname: & Ok(()) } - - - diff --git a/src/mods/drugs.rs b/src/mods/drugs.rs new file mode 100644 index 0000000..23add6d --- /dev/null +++ b/src/mods/drugs.rs @@ -0,0 +1,335 @@ +use tokio::io::AsyncWriteExt; +use rand::prelude::*; +use std::sync::Arc; +use tokio::sync::Mutex; +use crate::Config; + +pub struct Drugs { + pub fat: bool, + pub stats: Arc>, +} + +pub struct Stats { + pub hits: usize, + pub sips: usize, + pub chugged: usize, + pub smoked: usize, + pub toked: usize, + pub chain: usize, + pub drag: f64, +} + +impl Drugs { + pub fn new() -> Self { + Drugs { + fat: false, + stats: Arc::new(Mutex::new(Stats { + hits: 25, + sips: 8, + chugged: 0, + smoked: 0, + toked: 0, + chain: 0, + drag: 0.0, + })), + } + } + + fn color(msg: &str, foreground: &str, background: Option<&str>) -> String { + match background { + Some(bg) => format!("\x03{},{}{}\x0f", foreground, bg, msg), + None => format!("\x03{}{}\x0f", foreground, msg), + } + } + + fn beer() -> String { + let glass = Self::color(" ", "15", Some("15")); // light_grey on light_grey + let content = (0..9) + .map(|_| { + let chars = " :."; + Self::color( + &chars.chars().choose(&mut thread_rng()).unwrap().to_string(), + "07", // orange + Some("08"), // yellow + ) + }) + .collect::(); + format!("{}{}{}", glass, content, glass) + } + + fn cigarette(size: usize) -> String { + let filter = format!( + "{}{}", + Self::color(";.`-,:.`;", "08", Some("07")), // yellow on orange + Self::color(" ", "08", Some("08")), // yellow on yellow + ); + let cigarette = Self::color(&"|".repeat(size), "15", Some("00")); // light_grey on white + let cherry = format!( + "{}{}", + Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("01")), // random color on black + Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("14")), // random color on grey + ); + let smoke_chars = ";:-.,_`~'"; + let smoke = Self::color( + &format!( + "-{}", + (0..Self::random_range(5, 9)) + .map(|_| smoke_chars.chars().choose(&mut thread_rng()).unwrap()) + .collect::() + ), + "14", // grey + None, + ); + format!("{}{}{}{}", filter, cigarette, cherry, smoke) + } + + fn joint(size: usize) -> String { + let joint = Self::color(&"/".repeat(size), "15", Some("00")); // light_grey on white + let cherry = format!( + "{}{}", + Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("01")), // random color on black + Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("14")), // random color on grey + ); + let smoke_chars = ";:-.,_`~'"; + let smoke = Self::color( + &format!( + "-{}", + (0..Self::random_range(5, 9)) + .map(|_| smoke_chars.chars().choose(&mut thread_rng()).unwrap()) + .collect::() + ), + "14", // grey + None, + ); + format!("{}{}{}", joint, cherry, smoke) + } + + fn mug(size: usize) -> Vec { + let glass = Self::color(" ", "15", Some("15")); // light_grey on light_grey + let empty = format!("{} {}", glass, glass); + let foam = format!( + "{}{}{}", + glass, + Self::color(":::::::::", "15", Some("00")), // light_grey on white + glass + ); + let bottom = Self::color(" ", "15", Some("15")); // light_grey on light_grey + let mut mug = vec![ + foam.clone(), + Self::beer(), + Self::beer(), + Self::beer(), + Self::beer(), + Self::beer(), + Self::beer(), + Self::beer(), + ]; + for _ in 0..(8 - size) { + mug.pop(); + mug.insert(0, empty.clone()); + } + for i in 0..mug.len() { + if i == 2 || i == 7 { + mug[i] = format!("{}{}{}", mug[i], glass, glass); + } else if i > 2 && i < 7 { + mug[i] = format!("{} {}", mug[i], glass); + } + } + mug.push(bottom); + mug + } + + pub async fn handle_drugs_command( + &mut self, + writer: &mut W, + config: &Config, + command: &str, + channel: &str, + ) -> Result<(), Box> { + let mut stats = self.stats.lock().await; + let parts: Vec<&str> = command.split_whitespace().collect(); + let action = parts[0].trim_start_matches('%'); + + match action { + "chug" => { + if stats.sips == 0 { + stats.sips = 8; + stats.chugged += 1; + } + for line in Self::mug(stats.sips) { + writer + .write_all(format!("PRIVMSG {} :{}\r\n", channel, line).as_bytes()) + .await?; + } + stats.sips = stats.sips.saturating_sub(Self::random_range(1, 3)); + } + "smoke" | "toke" => { + let option = if action == "smoke" { "smoked" } else { "toked" }; + if stats.hits == 0 { + stats.hits = 25; + if option == "smoked" { + stats.smoked += 1; + } else { + stats.toked += 1; + } + self.fat = false; + } else { + let object = if action == "smoke" { + Self::cigarette(stats.hits) + } else { + Self::joint(stats.hits) + }; + if self.fat { + for _ in 0..3 { + writer + .write_all(format!("PRIVMSG {} :{}\r\n", channel, object).as_bytes()) + .await?; + } + } else { + writer + .write_all(format!("PRIVMSG {} :{}\r\n", channel, object).as_bytes()) + .await?; + } + stats.hits = stats.hits.saturating_sub(Self::random_range(1, 3)); + } + } + "100" | "extendo" | "fatfuck" if Self::luck(100) => { + if action == "fatfuck" { + self.fat = true; + writer + .write_all( + format!( + "PRIVMSG {} :{}{}{}\r\n", + channel, + Self::color(" !!! ", "04", Some("03")), // red on green + Self::color( + "AWWW SHIT, IT'S TIME FOR THAT MARLBORO FATFUCK", + "01", // black + Some("03") // green + ), + Self::color(" !!! ", "04", Some("03")) // red on green + ) + .as_bytes(), + ) + .await?; + } else { + stats.hits = 100; + if action == "100" { + writer + .write_all( + format!( + "PRIVMSG {} :{}{}{}\r\n", + channel, + Self::color(" !!! ", "00", Some("04")), // white on red + Self::color( + "AWWW SHIT, IT'S TIME FOR THAT NEWPORT 100", + "04", // red + Some("00") // white + ), + Self::color(" !!! ", "00", Some("04")) // white on red + ) + .as_bytes(), + ) + .await?; + } else { + writer + .write_all( + format!( + "PRIVMSG {} :{}{}{}\r\n", + channel, + Self::color(" !!! ", "04", Some("03")), // red on green + Self::color( + "OHHH FUCK, IT'S TIME FOR THAT 420 EXTENDO", + "08", // yellow + Some("03") // green + ), + Self::color(" !!! ", "04", Some("03")) // red on green + ) + .as_bytes(), + ) + .await?; + } + } + } + "beer" => { + let target = if parts.len() > 1 { parts[1] } else { channel }; + self.handle_beer_command(writer, target, channel).await?; + } + _ => {} + } + + writer.flush().await?; + Ok(()) + } + + async fn handle_beer_command( + &self, + writer: &mut W, + target: &str, + channel: &str, + ) -> Result<(), Box> { + let (beer_choice, beer_temp) = self.generate_beer(); + let beer = self.format_beer(&beer_choice); + + let action = format!( + "PRIVMSG {} :\x01ACTION throws {} {} {} =)\x01\r\n", + channel, + Self::color(target, "00", None), + beer_temp, + beer + ); + writer.write_all(action.as_bytes()).await?; + + if beer_choice == "bud" && Self::luck(100) { + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + let gay_msg = format!( + "PRIVMSG {} :\x01ACTION suddenly feels more gay...\x01\r\n", + channel + ); + writer.write_all(gay_msg.as_bytes()).await?; + } + + Ok(()) + } + + fn generate_beer(&self) -> (String, String) { + let beer_choice = ["bud", "modelo", "ultra"].choose(&mut thread_rng()).unwrap().to_string(); + let beer_temp = ["a piss warm", "an ice cold", "an empty"].choose(&mut thread_rng()).unwrap().to_string(); + (beer_choice, beer_temp) + } + + fn format_beer(&self, choice: &str) -> String { + match choice { + "bud" => format!( + "{}{}{}", + Self::color(" ", "00", Some("00")), + Self::color(" BUD ", "00", Some(["02", "05"].choose(&mut thread_rng()).unwrap())), + Self::color("c", "14", Some("00")) + ), + "modelo" => format!( + "{}{}{}", + Self::color(" ", "07", Some("07")), + Self::color("Modelo", "02", Some("08")), + Self::color("c", "14", Some("07")) + ), + "ultra" => format!( + "{}{}", + Self::color(" ULTRA ", "02", Some("00")), + Self::color("🬃", "04", Some("00")) + ), + _ => String::new(), + } + } + + fn random_choice(choices: &[T]) -> T { + choices.choose(&mut thread_rng()).unwrap().clone() + } + + fn random_range(start: usize, end: usize) -> usize { + thread_rng().gen_range(start..end) + } + + fn luck(odds: u32) -> bool { + thread_rng().gen_range(1..=odds) == 1 + } +}