diff --git a/Cargo.toml b/Cargo.toml index 14efc21..9f03691 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -openssl = "0.10.45" -async-openai = "0.9.3" +openssl = "0.10.2" +async-openai = "0.10.2" tokio = { version = "1.23.0", features = ["full"] } rand = "0.8.4" regex = "1.7.1" @@ -23,6 +23,8 @@ irc = "0.15.0" futures = "0.3.27" futures-util = "0.3.27" +base64 = "0.21" + lazy_static = "1.4.0" anyhow = "1.0.70" tokio-native-tls = "0.3" diff --git a/proxies.txt b/proxies.txt deleted file mode 100644 index 290dda7..0000000 --- a/proxies.txt +++ /dev/null @@ -1,78 +0,0 @@ -184.185.2.12:4145 -174.138.33.62:59166 -206.220.175.2:4145 -96.126.113.216:59166 -205.240.77.164:4145 -174.75.211.222:4145 -72.210.252.137:4145 -192.111.129.145:16894 -68.71.254.6:4145 -159.203.13.82:59166 -107.170.81.141:59166 -159.65.220.89:59166 -192.241.143.216:59166 -149.56.247.67:59166 -157.245.223.201:59166 -203.57.114.228:59166 -192.111.135.18:18301 -192.252.216.81:4145 -68.183.90.210:59166 -123.171.245.162:1080 -72.195.34.58:4145 -208.111.40.144:59166 -98.162.25.29:31679 -192.252.214.20:15864 -95.213.228.10:59166 -142.93.77.151:59166 -72.195.114.169:4145 -192.111.135.17:18302 -75.119.157.170:59166 -75.127.13.195:59166 -167.71.10.234:59166 -51.255.219.244:59166 -51.178.82.49:59166 -184.178.172.14:4145 -68.71.247.130:4145 -192.111.137.37:18762 -176.58.125.165:59166 -205.186.162.190:59166 -47.243.95.228:10080 -142.54.228.193:4145 -168.196.160.61:59166 -138.68.124.120:59166 -174.138.63.144:59166 -74.119.147.209:4145 -68.183.20.254:59166 -67.205.181.126:59166 -139.144.31.132:59166 -159.89.49.172:59166 -43.226.26.120:59166 -23.253.253.26:59166 -206.189.157.253:59166 -184.170.248.5:4145 -159.203.78.174:59166 -46.101.37.189:59166 -184.170.249.65:4145 -192.111.137.34:18765 -167.71.190.131:59166 -174.64.199.79:4145 -184.178.172.25:15291 -45.79.46.53:59166 -40.87.45.45:59166 -199.58.184.97:4145 -198.8.94.170:4145 -192.252.208.67:14287 -184.170.245.148:4145 -69.164.224.53:59166 -68.71.249.153:48606 -143.198.229.170:59166 -165.227.153.96:59166 -192.252.220.89:4145 -138.197.185.192:59166 -151.80.45.47:59166 -69.194.181.6:7497 -170.210.156.33:59166 -209.94.62.123:59166 -198.8.94.174:39078 -184.178.172.23:4145 -72.221.232.155:4145 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 25ee5e0..40075ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,7 +37,7 @@ struct Config { channels: Vec, admin_users: Vec, ignore_users: Vec, - + //ignore_channels: Vec, } fn main() { let _rt = tokio::runtime::Builder::new_multi_thread() @@ -56,10 +56,14 @@ fn main() { let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); let mut ssl_stream = connector.connect(&config.server, stream).unwrap(); let nick_command = format!("NICK {}_\r\n", config.nick); //setup passwords - let user_command = format!("USER {} 0 * :{}\r\n", config.nick, config.nick); ssl_stream.write_all(nick_command.as_bytes()).unwrap(); ssl_stream.write_all(user_command.as_bytes()).unwrap(); + + + + + let identify_command = format!("PRIVMSG NickServ :IDENTIFY {} {}\r\n", config.nick, config.password); ssl_stream.write(identify_command.as_bytes()).unwrap(); let channels = config.channels.join(","); @@ -67,6 +71,9 @@ fn main() { let admin_users = config.admin_users; // ADMINS let ignored_users = config.ignore_users; // IGNORED + // Create a HashSet of ignored channels for efficient lookup + //let ignored_channels = config.ignore_channels; + // ... ssl_stream.write_all(join_command.as_bytes()).unwrap(); @@ -83,13 +90,19 @@ fn main() { // RESPOND TO PINGS + + if message.starts_with("PING") { + let response = message.replace("PING", "PONG"); + println!("{} {}","[%] PONG:".bold().green(), config.nick.blue()); + ssl_stream.write_all(response.as_bytes()).unwrap(); + continue; + } if message.starts_with("PING") { println!("{} {}","[%] PONG:".bold().green(), config.nick.blue()); // DEBUG ssl_stream.write_all("PONG ircd.chat\r\n".as_bytes()).unwrap(); continue; // skip processing the PING message further } - // MODULES let mut ping_command = PingCommand; let mut kill_command = KillCommand; @@ -123,6 +136,11 @@ fn main() { for response in invade_command.handle(message) { ssl_stream.write_all(response.as_bytes()).unwrap(); } + } else if message.contains("PRIVMSG") && message.contains(":%join") { // fix so commands get picked up faster + let parts: Vec<&str> = message.splitn(3, ":%join").collect(); + let invade_channel = parts[1]; + let response = format!("JOIN {} \r\n", invade_channel); + ssl_stream.write_all(response.as_bytes()).unwrap(); } } @@ -139,6 +157,10 @@ fn main() { println!("[!] IGNORED: {}", username.red()); continue; } + //if ignored_channels.contains(&channel.to_string()) { + // continue; + //} + for response in ai.handle(message, ) { ssl_stream.write_all(response.as_bytes()).unwrap(); } diff --git a/src/modules/invade.rs b/src/modules/invade.rs index b9ea9c7..c714592 100644 --- a/src/modules/invade.rs +++ b/src/modules/invade.rs @@ -10,6 +10,11 @@ use leetspeak; use regex::Regex; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, mpsc, Mutex}; +use rand::seq::SliceRandom; +use rand::thread_rng; +// add proxy support + +// add support for multiple servers #[derive(Clone, Deserialize)] struct Config { @@ -41,7 +46,33 @@ impl Command for InvadeCommand { let num_invaders = parts[4].parse::().unwrap_or(1) as usize; let channel = parts[2]; let invadechannel = parts[5]; - let scream = if parts.len() > 6 { parts[6] } else { "" }; + let re = Regex::new(r#""([^"]*)""#).unwrap(); + let scream = re.captures(message).and_then(|cap| cap.get(1)).map_or("", |m| m.as_str()); + let delay_str = if parts.len() > 7 { parts[7] } else { "5s" }; + let mut loop_scream = false; + let mut nuke = false; + let mut reconnect = false; + let mut ping_all = false; + let mut rainbow = false; + let mut wait_join_duration = std::time::Duration::from_secs(0); // Default wait duration + for part in &parts[8..] { + match *part { + "loop" => loop_scream = true, + "nuke" => nuke = true, + "reconnect" => reconnect = true, + "pingall" => ping_all = true, + "rainbow" => rainbow = true, + option if option.starts_with("waitjoin=") => { + let wait_duration = option.trim_start_matches("waitjoin=").trim_end_matches('s'); + let wait_secs = wait_duration.parse::().unwrap_or(0); + wait_join_duration = std::time::Duration::from_secs(wait_secs); + } + _ => {} + } + } + // Parsing the delay duration (Assumes delay string ends with 's' for seconds) + let delay_secs = delay_str.trim_end_matches('s').parse::().unwrap_or(0); + let delay_duration = std::time::Duration::from_secs(delay_secs); let config_str = std::fs::read_to_string("config.toml").unwrap(); let config_value = config_str.parse::().unwrap(); @@ -62,7 +93,13 @@ impl Command for InvadeCommand { let kill_flag_clone = Arc::clone(&self.kill_flag); let kill_receiver = Arc::clone(&kill_receiver); + // fix this so it only delays for connection not entire invader processing + std::thread::sleep(delay_duration); + std::thread::spawn(move || { + std::thread::sleep(delay_duration); + std::thread::sleep(wait_join_duration); // Wait before connecting to the channel + let stream = TcpStream::connect((config_clone.server.as_str(), config_clone.port)).unwrap(); let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); let mut ssl_stream = connector.connect(config_clone.server.as_str(), stream).unwrap(); @@ -75,26 +112,98 @@ impl Command for InvadeCommand { let commander = format!("JOIN {} \r\n", command_channel); ssl_stream.write_all(commander.as_bytes()).unwrap(); ssl_stream.write_all(join_command.as_bytes()).unwrap(); + std::thread::sleep(wait_join_duration); // Wait before sending the message to the channel let msg = format!("PRIVMSG {} :{}\r\n", thread_channel, screaming); ssl_stream.write_all(msg.as_bytes()).unwrap(); loop { if kill_flag_clone.load(Ordering::SeqCst) { - break; + ssl_stream.shutdown().unwrap(); + return; } - if let Ok(_) = kill_receiver.lock().unwrap().try_recv() { - break; + ssl_stream.shutdown().unwrap(); + return; } - + ssl_stream.write_all(msg.as_bytes()).unwrap(); + if loop_scream && !kill_flag_clone.load(Ordering::SeqCst) { + let msg = format!("PRIVMSG {} :{}\r\n", thread_channel, screaming); + ssl_stream.write_all(msg.as_bytes()).unwrap(); + } + if nuke && !kill_flag_clone.load(Ordering::SeqCst) { + let join_command = format!("JOIN {} \r\n", thread_channel); + ssl_stream.write_all(join_command.as_bytes()).unwrap(); + let msg = format!("PRIVMSG {} :{}\r\n", thread_channel, screaming); + ssl_stream.write_all(msg.as_bytes()).unwrap(); + let part_command = format!("PART {} \r\n", thread_channel); + ssl_stream.write_all(part_command.as_bytes()).unwrap(); + std::thread::sleep(delay_duration); + } else if loop_scream && !kill_flag_clone.load(Ordering::SeqCst) { + let msg = format!("PRIVMSG {} :{}\r\n", thread_channel, screaming); + ssl_stream.write_all(msg.as_bytes()).unwrap(); + + } + if ping_all { + let names_command = format!("NAMES {}\r\n", thread_channel); + ssl_stream.write_all(names_command.as_bytes()).unwrap(); + + let mut nicknames: Vec = Vec::new(); + + loop { + let mut buf = [0; 512]; + match ssl_stream.ssl_read(&mut buf) { + Ok(0) => break, + Ok(n) => { + let received = String::from_utf8_lossy(&buf[0..n]); + let message = received.trim(); + + if message.starts_with(&format!(":{} 353", config_clone.server.as_str())) { // Numeric reply for NAMES command + let name_list = message.split(":").last().unwrap_or(""); + nicknames.extend(name_list.split_whitespace().map(|s| s.to_string())); + } else if message.starts_with(&format!(":{} 366", config_clone.server.as_str())) { // Numeric reply for end of NAMES + break; + } + } + Err(_) => break, + } + } + + + + + // Construct a message by joining all the names together with commas and prefix each name with '@' + let nicknames_message = nicknames.iter().map(|name| format!("@{}", name)).collect::>().join(", "); + let ping_message = format!("PRIVMSG {} :{}\r\n", thread_channel, nicknames_message); + ssl_stream.write_all(ping_message.as_bytes()).unwrap(); + + if loop_scream { + loop { + if kill_flag_clone.load(Ordering::SeqCst) { + ssl_stream.shutdown().unwrap(); + return; + } + + if let Ok(_) = kill_receiver.lock().unwrap().try_recv() { + ssl_stream.shutdown().unwrap(); + return; + } + + ssl_stream.write_all(ping_message.as_bytes()).unwrap(); + std::thread::sleep(delay_duration); + } + } + } + + let mut buf = [0; 512]; match ssl_stream.ssl_read(&mut buf) { Ok(0) => break, Ok(n) => { + let received = String::from_utf8_lossy(&buf[0..n]); let message = received.trim(); @@ -106,7 +215,7 @@ impl Command for InvadeCommand { println!("{} {}","[%] PONG:".bold().green(), thread_invader.blue()); ssl_stream.write_all(response.as_bytes()).unwrap(); } - if message.starts_with(":ircd.chat 433") { // Numeric reply for nickname in use + if message.starts_with(&format!(":{} 433", config_clone.server.as_str())) { // Numeric reply for nickname in use let leet_nick = leetspeak::translate_with_level(&thread_invader, &leetspeak::Level::One); let nick_command = format!("NICK {}\r\n", leet_nick); let user_command = format!("USER {} 0 * :{}\r\n", thread_invader, thread_invader); @@ -120,7 +229,7 @@ impl Command for InvadeCommand { ssl_stream.write_all(join_command.as_bytes()).unwrap(); ssl_stream.write_all(msg.as_bytes()).unwrap(); - //break; + return; } // turn to mods // setup so these will only run from the server admin to avoid handle/host conflicts @@ -148,12 +257,44 @@ impl Command for InvadeCommand { ssl_stream.write_all(response.as_bytes()).unwrap(); } } + if loop_scream && !kill_flag_clone.load(Ordering::SeqCst) { + let msg = format!("PRIVMSG {} :{}\r\n", thread_channel, screaming); + ssl_stream.write_all(msg.as_bytes()).unwrap(); + } + + //if message.contains("PRIVMSG") && message.contains(":%%kill") { + // std::thread::process::exit(0); +// + //} } // ...1 } Err(e) => { eprintln!("{} {}","[!] ERROR FROM SERVER:".on_red(), e); - break; + + if reconnect { + // You might want to sleep for a short duration before trying to reconnect + // to avoid flooding the server with connection attempts. + std::thread::sleep(std::time::Duration::from_secs(5)); + + let stream = TcpStream::connect((config_clone.server.as_str(), config_clone.port)).unwrap(); + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + ssl_stream = connector.connect(config_clone.server.as_str(), stream).unwrap(); + + let nick_command = format!("NICK {}\r\n", thread_invader); + let user_command = format!("USER {} 0 * :{}\r\n", thread_invader, thread_invader); + ssl_stream.write_all(nick_command.as_bytes()).unwrap(); + ssl_stream.write_all(user_command.as_bytes()).unwrap(); + + let join_command = format!("JOIN {} \r\n", thread_channel); + let commander = format!("JOIN {} \r\n", command_channel); + ssl_stream.write_all(commander.as_bytes()).unwrap(); + ssl_stream.write_all(join_command.as_bytes()).unwrap(); + let msg = format!("PRIVMSG {} :{}\r\n", thread_channel, screaming); + ssl_stream.write_all(msg.as_bytes()).unwrap(); + } else { + break; + } } } }