Compare commits

...

3 Commits

Author SHA1 Message Date
sad
484cef24c7 fmt 2024-10-04 16:29:49 +00:00
sad
327ad5425f lint 2024-10-04 16:28:57 +00:00
sad
39eb6ee0da fmt 2024-10-04 16:27:25 +00:00
10 changed files with 289 additions and 127 deletions

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
Cargo.lock Cargo.lock
/target /target
./ircart/

View File

@ -1,13 +1,9 @@
use tokio::io::{split, AsyncRead, AsyncWrite, AsyncReadExt, AsyncWriteExt, BufReader, AsyncBufReadExt}; use colored::*;
use tokio::net::TcpStream;
use tokio_native_tls::native_tls::TlsConnector as NTlsConnector;
use tokio_native_tls::TlsConnector;
use tokio::sync::mpsc;
use serde::Deserialize; use serde::Deserialize;
use std::fs; use std::fs;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use colored::*; use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use tokio_socks::tcp::Socks5Stream; use tokio::net::TcpStream;
#[derive(Deserialize, Clone)] #[derive(Deserialize, Clone)]
struct Config { struct Config {
@ -32,25 +28,25 @@ struct Config {
} }
mod mods { mod mods {
pub mod proxy; pub mod ascii;
pub mod tls; pub mod drugs;
pub mod handler; pub mod handler;
pub mod proxy;
pub mod sasl; pub mod sasl;
pub mod sed; pub mod sed;
pub mod ascii; pub mod tls;
pub mod vomit; pub mod vomit;
pub mod drugs; // pub mod invade;
// pub mod invade;
} }
use mods::proxy::proxy_exec;
use mods::tls::tls_exec;
use mods::handler::handler;
use mods::sasl::{start_sasl_auth, handle_sasl_messages};
use mods::sed::{SedCommand, MessageBuffer};
use mods::ascii::handle_ascii_command; use mods::ascii::handle_ascii_command;
use mods::vomit::handle_vomit_command;
use mods::drugs::Drugs; use mods::drugs::Drugs;
use mods::handler::handler;
use mods::proxy::proxy_exec;
use mods::sasl::{handle_sasl_messages, start_sasl_auth};
use mods::sed::{MessageBuffer, SedCommand};
use mods::tls::tls_exec;
use mods::vomit::handle_vomit_command;
//use mods::invade::{handle_invade_command}; //use mods::invade::{handle_invade_command};
#[tokio::main(flavor = "multi_thread", worker_threads = 12)] #[tokio::main(flavor = "multi_thread", worker_threads = 12)]
@ -98,13 +94,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
handler(tcp_stream, config).await.unwrap(); handler(tcp_stream, config).await.unwrap();
} }
Ok::<(), Box<dyn std::error::Error + Send>>(()) Ok::<(), Box<dyn std::error::Error + Send>>(())
}).await.unwrap(); })
.await
.unwrap();
match connection_result { match connection_result {
Ok(_) => { Ok(_) => {
println!("Connection established successfully!"); println!("Connection established successfully!");
reconnect_attempts = 0; reconnect_attempts = 0;
}, }
Err(e) => { Err(e) => {
println!("Error handling connection: {}", e); println!("Error handling connection: {}", e);
reconnect_attempts += 1; reconnect_attempts += 1;
@ -122,14 +120,27 @@ fn loaded_config() -> Result<Config, Box<dyn std::error::Error>> {
Ok(config) Ok(config)
} }
async fn readmsg<S>(mut reader: tokio::io::ReadHalf<S>, tx: tokio::sync::mpsc::Sender<String>) where S: AsyncRead + Unpin { async fn readmsg<S>(mut reader: tokio::io::ReadHalf<S>, tx: tokio::sync::mpsc::Sender<String>)
where
S: AsyncRead + Unpin,
{
let mut buf = vec![0; 4096]; 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; } if n == 0 {
break;
}
let msg_list = String::from_utf8_lossy(&buf[..n]).to_string(); let msg_list = String::from_utf8_lossy(&buf[..n]).to_string();
for lines in msg_list.lines() { for lines in msg_list.lines() {
let msg = lines.to_string(); let msg = lines.to_string();
println!("{}{}{} {}{} {}", "[".green().bold(), ">".yellow().bold(), "]".green().bold(), "DEBUG:".bold().yellow(), ":".bold().green(), msg.trim().purple()); println!(
"{}{}{} {}{} {}",
"[".green().bold(),
">".yellow().bold(),
"]".green().bold(),
"DEBUG:".bold().yellow(),
":".bold().green(),
msg.trim().purple()
);
tx.send(msg).await.unwrap(); tx.send(msg).await.unwrap();
if buf.len() == n { if buf.len() == n {
buf.resize(buf.len() * 2, 0); buf.resize(buf.len() * 2, 0);
@ -140,7 +151,14 @@ async fn readmsg<S>(mut reader: tokio::io::ReadHalf<S>, tx: tokio::sync::mpsc::S
static SASL_AUTH: AtomicBool = AtomicBool::new(false); static SASL_AUTH: AtomicBool = AtomicBool::new(false);
async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::mpsc::Receiver<String>, config: &Config, mut message_buffer: MessageBuffer) where S: AsyncWrite + Unpin { async fn writemsg<S>(
mut writer: tokio::io::WriteHalf<S>,
mut rx: tokio::sync::mpsc::Receiver<String>,
config: &Config,
mut message_buffer: MessageBuffer,
) where
S: AsyncWrite + Unpin,
{
let username = config.sasl_username.clone().unwrap(); let username = config.sasl_username.clone().unwrap();
let password = config.sasl_password.clone().unwrap(); let password = config.sasl_password.clone().unwrap();
let nickname = config.nickname.clone(); let nickname = config.nickname.clone();
@ -148,7 +166,9 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::m
if !password.is_empty() && !SASL_AUTH.load(Ordering::Relaxed) { if !password.is_empty() && !SASL_AUTH.load(Ordering::Relaxed) {
let capabilities = config.capabilities.clone(); let capabilities = config.capabilities.clone();
println!("Starting SASL auth..."); println!("Starting SASL auth...");
start_sasl_auth(&mut writer, "PLAIN", &nickname, &realname, capabilities).await.unwrap(); start_sasl_auth(&mut writer, "PLAIN", &nickname, &realname, capabilities)
.await
.unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
SASL_AUTH.store(true, Ordering::Relaxed); SASL_AUTH.store(true, Ordering::Relaxed);
} else { } else {
@ -167,29 +187,51 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::m
let serv = parts.first().unwrap_or(&""); let serv = parts.first().unwrap_or(&"");
let cmd = parts.get(1).unwrap_or(&""); let cmd = parts.get(1).unwrap_or(&"");
println!("{} {} {} {} {}", "DEBUG:".bold().yellow(), "serv:".bold().green(), serv.purple(), "cmd:".bold().green(), cmd.purple()); println!(
if *serv == "PING" { "{} {} {} {} {}",
"DEBUG:".bold().yellow(),
"serv:".bold().green(),
serv.purple(),
"cmd:".bold().green(),
cmd.purple()
);
if *serv == "PING" {
let response = msg.replace("PING", "PONG") + "\r\n"; let response = msg.replace("PING", "PONG") + "\r\n";
println!("{} {} {}","[%] PONG:".bold().green(), nickname.blue(), response.purple()); println!(
"{} {} {}",
"[%] PONG:".bold().green(),
nickname.blue(),
response.purple()
);
writer.write_all(response.as_bytes()).await.unwrap(); writer.write_all(response.as_bytes()).await.unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
continue; continue;
} }
if (*cmd == "CAP" || msg.starts_with("AUTHENTICATE +") || *cmd == "903") && SASL_AUTH.load(Ordering::Relaxed) { if (*cmd == "CAP" || msg.starts_with("AUTHENTICATE +") || *cmd == "903")
&& SASL_AUTH.load(Ordering::Relaxed)
{
println!("Handling SASL messages..."); println!("Handling SASL messages...");
handle_sasl_messages(&mut writer, msg.trim(), &username, &password, &nickname).await.unwrap(); handle_sasl_messages(&mut writer, msg.trim(), &username, &password, &nickname)
.await
.unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
} }
if *cmd == "001" { if *cmd == "001" {
println!("Setting mode"); println!("Setting mode");
writer.write_all(format!("MODE {} +B\r\n", nickname).as_bytes()).await.unwrap(); writer
.write_all(format!("MODE {} +B\r\n", nickname).as_bytes())
.await
.unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
} }
if *cmd == "376" { if *cmd == "376" {
println!("Joining channels"); println!("Joining channels");
for channel in &config.channels { for channel in &config.channels {
writer.write_all(format!("JOIN {}\r\n", channel).as_bytes()).await.unwrap(); writer
.write_all(format!("JOIN {}\r\n", channel).as_bytes())
.await
.unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
} }
} }
@ -197,13 +239,17 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::m
let channel = parts.get(2).unwrap_or(&""); let channel = parts.get(2).unwrap_or(&"");
let userme = parts.get(3).unwrap_or(&""); let userme = parts.get(3).unwrap_or(&"");
if *userme == nickname { if *userme == nickname {
writer.write_all(format!("JOIN {}\r\n", channel).as_bytes()).await.unwrap(); writer
.write_all(format!("JOIN {}\r\n", channel).as_bytes())
.await
.unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
} }
} }
if *cmd == "PRIVMSG" { if *cmd == "PRIVMSG" {
let channel = &parts.get(2).to_owned().unwrap_or(&""); let channel = &parts.get(2).to_owned().unwrap_or(&"");
let user = parts[0].strip_prefix(':') let user = parts[0]
.strip_prefix(':')
.and_then(|user_with_host| user_with_host.split('!').next()) .and_then(|user_with_host| user_with_host.split('!').next())
.unwrap_or("unknown_user"); .unwrap_or("unknown_user");
let host = parts[0].split('@').nth(1).unwrap_or("unknown_host"); let host = parts[0].split('@').nth(1).unwrap_or("unknown_host");
@ -218,12 +264,26 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::m
} else { } else {
"".to_string() "".to_string()
}; };
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()); 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()
);
if msg_content.starts_with("s/") { if msg_content.starts_with("s/") {
if let Some(sed_command) = SedCommand::parse(&msg_content.clone()) { if let Some(sed_command) = SedCommand::parse(&msg_content.clone()) {
if let Some(response) = message_buffer.apply_sed_command(&sed_command) { if let Some(response) = message_buffer.apply_sed_command(&sed_command) {
writer.write_all(format!("PRIVMSG {} :{}\r\n", channel, response).as_bytes()).await.unwrap(); writer
.write_all(format!("PRIVMSG {} :{}\r\n", channel, response).as_bytes())
.await
.unwrap();
writer.flush().await.unwrap(); writer.flush().await.unwrap();
} }
} }
@ -239,20 +299,33 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::m
let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await; let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await;
} }
if ["%chug", "%smoke", "%toke", "%100", "%extendo", "%fatfuck", "%beer"].iter().any(|&prefix| msg_content.starts_with(prefix)) { if [
drugs.handle_drugs_command(&mut writer, config, &msg_content, channel).await "%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)); .unwrap_or_else(|e| eprintln!("Error handling drugs command: {}", e));
} }
} }
} }
} }
async fn nickme<W: tokio::io::AsyncWriteExt + Unpin>(writer: &mut W, nickname: &str, realname: &str) -> Result<(), Box<dyn std::error::Error>> { async fn nickme<W: tokio::io::AsyncWriteExt + Unpin>(
writer.write_all(format!("NICK {}\r\n", nickname).as_bytes()).await?; writer: &mut W,
nickname: &str,
realname: &str,
) -> Result<(), Box<dyn std::error::Error>> {
writer
.write_all(format!("NICK {}\r\n", nickname).as_bytes())
.await?;
writer.flush().await?; writer.flush().await?;
writer.write_all(format!("USER {} 0 * :{}\r\n", nickname, realname).as_bytes()).await?; writer
.write_all(format!("USER {} 0 * :{}\r\n", nickname, realname).as_bytes())
.await?;
writer.flush().await?; writer.flush().await?;
Ok(()) Ok(())
} }

View File

@ -1,15 +1,21 @@
use tokio::io::{AsyncWriteExt, BufReader}; // mods/ascii.rs
use tokio::fs::File;
use tokio::time::{self, Duration};
use std::fs;
use rand::Rng;
use tokio::io::AsyncBufReadExt;
use std::error::Error;
use crate::Config; use crate::Config;
use rand::Rng;
use std::error::Error;
use std::fs;
use tokio::fs::File;
use tokio::io::AsyncBufReadExt;
use tokio::io::{AsyncWriteExt, BufReader};
use tokio::time::{self, Duration};
const CHUNK_SIZE: usize = 4096; const CHUNK_SIZE: usize = 4096;
async fn send_ansi_art<W: AsyncWriteExt + Unpin>(writer: &mut W, file_path: &str, pump_delay: u64, channel: &str) -> Result<(), Box<dyn Error>> { async fn send_ansi_art<W: AsyncWriteExt + Unpin>(
writer: &mut W,
file_path: &str,
pump_delay: u64,
channel: &str,
) -> Result<(), Box<dyn Error>> {
let file = File::open(file_path).await?; let file = File::open(file_path).await?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
let mut lines = reader.lines(); let mut lines = reader.lines();
@ -20,7 +26,7 @@ async fn send_ansi_art<W: AsyncWriteExt + Unpin>(writer: &mut W, file_path: &str
line_count += 1; line_count += 1;
} }
let mut pump_delay = Duration::from_millis(pump_delay); let mut pump_delay = Duration::from_millis(pump_delay);
if line_count > 500 && pump_delay < Duration::from_millis(100){ if line_count > 500 && pump_delay < Duration::from_millis(100) {
pump_delay = Duration::from_millis(100); pump_delay = Duration::from_millis(100);
} }
let file = File::open(file_path).await?; let file = File::open(file_path).await?;
@ -28,16 +34,25 @@ async fn send_ansi_art<W: AsyncWriteExt + Unpin>(writer: &mut W, file_path: &str
let mut lines = reader.lines(); let mut lines = reader.lines();
while let Some(line) = lines.next_line().await? { while let Some(line) = lines.next_line().await? {
if line.len() > CHUNK_SIZE { if line.len() > CHUNK_SIZE {
for chunk in line.as_bytes().chunks(CHUNK_SIZE) { for chunk in line.as_bytes().chunks(CHUNK_SIZE) {
writer.write_all(format!("PRIVMSG {} :{}\r\n", channel, String::from_utf8_lossy(chunk)).as_bytes()).await?; writer
.write_all(
format!(
"PRIVMSG {} :{}\r\n",
channel,
String::from_utf8_lossy(chunk)
)
.as_bytes(),
)
.await?;
writer.flush().await?; writer.flush().await?;
time::sleep(pump_delay).await; time::sleep(pump_delay).await;
} }
} else { } else {
writer
writer.write_all(format!("PRIVMSG {} :{}\r\n", channel, line).as_bytes()).await?; .write_all(format!("PRIVMSG {} :{}\r\n", channel, line).as_bytes())
.await?;
writer.flush().await?; writer.flush().await?;
time::sleep(pump_delay).await; time::sleep(pump_delay).await;
} }
@ -46,14 +61,17 @@ async fn send_ansi_art<W: AsyncWriteExt + Unpin>(writer: &mut W, file_path: &str
} }
fn select_random_file(dir: &str) -> Option<String> { fn select_random_file(dir: &str) -> Option<String> {
let files = fs::read_dir(dir).ok()?.filter_map(|entry| { let files = fs::read_dir(dir)
let path = entry.ok()?.path(); .ok()?
if path.is_file() { .filter_map(|entry| {
path.to_str().map(ToString::to_string) let path = entry.ok()?.path();
} else { if path.is_file() {
None path.to_str().map(ToString::to_string)
} } else {
}).collect::<Vec<String>>(); None
}
})
.collect::<Vec<String>>();
if files.is_empty() { if files.is_empty() {
None None
@ -66,7 +84,7 @@ fn select_random_file(dir: &str) -> Option<String> {
pub async fn handle_ascii_command<W: AsyncWriteExt + Unpin>( pub async fn handle_ascii_command<W: AsyncWriteExt + Unpin>(
writer: &mut W, writer: &mut W,
config: &Config, config: &Config,
command: &str, command: &str,
channel: &str, channel: &str,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
@ -75,7 +93,7 @@ pub async fn handle_ascii_command<W: AsyncWriteExt + Unpin>(
if *command_type == "random" && parts.len() == 2 { if *command_type == "random" && parts.len() == 2 {
handle_random(writer, config, channel).await?; handle_random(writer, config, channel).await?;
} else if *command_type == "list"{ } else if *command_type == "list" {
handle_list(writer, config, channel, Some(parts.get(2).unwrap_or(&""))).await?; handle_list(writer, config, channel, Some(parts.get(2).unwrap_or(&""))).await?;
} else { } else {
handle_specific_file(writer, config, channel, &parts).await?; handle_specific_file(writer, config, channel, &parts).await?;
@ -93,7 +111,9 @@ async fn handle_random<W: AsyncWriteExt + Unpin>(
if let Some(random_file) = select_random_file(dir) { if let Some(random_file) = select_random_file(dir) {
send_ansi_art(writer, &random_file, config.pump_delay, channel).await?; send_ansi_art(writer, &random_file, config.pump_delay, channel).await?;
} else { } else {
writer.write_all(format!("PRIVMSG {} :No files found\r\n", channel).as_bytes()).await?; writer
.write_all(format!("PRIVMSG {} :No files found\r\n", channel).as_bytes())
.await?;
} }
} }
Ok(()) Ok(())
@ -103,9 +123,12 @@ async fn handle_list<W: AsyncWriteExt + Unpin>(
writer: &mut W, writer: &mut W,
config: &Config, config: &Config,
channel: &str, channel: &str,
parts: Option<&str> parts: Option<&str>,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let base_dir = config.ascii_art.clone().unwrap_or_else(|| "ascii_art".to_string()); let base_dir = config
.ascii_art
.clone()
.unwrap_or_else(|| "ascii_art".to_string());
let dir = if let Some(subdir) = parts { let dir = if let Some(subdir) = parts {
format!("{}/{}", base_dir, subdir) format!("{}/{}", base_dir, subdir)
@ -118,20 +141,31 @@ async fn handle_list<W: AsyncWriteExt + Unpin>(
.filter_map(|entry| entry.ok()) .filter_map(|entry| entry.ok())
.map(|entry| { .map(|entry| {
let path = entry.path(); let path = entry.path();
let display_name = path.file_name().unwrap_or_default().to_string_lossy().into_owned(); let display_name = path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.into_owned();
if path.is_dir() { if path.is_dir() {
format!("{}/", display_name) format!("{}/", display_name)
} else { } else {
display_name.strip_suffix(".txt").unwrap_or(&display_name).to_string() display_name
.strip_suffix(".txt")
.unwrap_or(&display_name)
.to_string()
} }
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(", "); .join(", ");
if entries.is_empty() { if entries.is_empty() {
writer.write_all(format!("PRIVMSG {} :No files or directories found\r\n", channel).as_bytes()).await?; writer
.write_all(format!("PRIVMSG {} :No files or directories found\r\n", channel).as_bytes())
.await?;
} else { } else {
writer.write_all(format!("PRIVMSG {} :{}\r\n", channel, entries).as_bytes()).await?; writer
.write_all(format!("PRIVMSG {} :{}\r\n", channel, entries).as_bytes())
.await?;
} }
Ok(()) Ok(())
@ -151,10 +185,15 @@ async fn handle_specific_file<W: AsyncWriteExt + Unpin>(
}; };
println!("{:?}", file_name); println!("{:?}", file_name);
let file_path = format!("{}/{}.txt", config.ascii_art.clone().unwrap_or_else(|| "ascii_art".to_string()), file_name); let file_path = format!(
"{}/{}.txt",
config
.ascii_art
.clone()
.unwrap_or_else(|| "ascii_art".to_string()),
file_name
);
println!("{:?}", file_path); println!("{:?}", file_path);
send_ansi_art(writer, &file_path, config.pump_delay, channel).await send_ansi_art(writer, &file_path, config.pump_delay, channel).await
} }

View File

@ -1,8 +1,8 @@
use tokio::io::AsyncWriteExt; use crate::Config;
use rand::prelude::*; use rand::prelude::*;
use std::sync::Arc; use std::sync::Arc;
use tokio::io::AsyncWriteExt;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::Config;
pub struct Drugs { pub struct Drugs {
pub fat: bool, pub fat: bool,
@ -49,7 +49,7 @@ impl Drugs {
let chars = " :."; let chars = " :.";
Self::color( Self::color(
&chars.chars().choose(&mut thread_rng()).unwrap().to_string(), &chars.chars().choose(&mut thread_rng()).unwrap().to_string(),
"07", // orange "07", // orange
Some("08"), // yellow Some("08"), // yellow
) )
}) })
@ -61,13 +61,21 @@ impl Drugs {
let filter = format!( let filter = format!(
"{}{}", "{}{}",
Self::color(";.`-,:.`;", "08", Some("07")), // yellow on orange Self::color(";.`-,:.`;", "08", Some("07")), // yellow on orange
Self::color(" ", "08", Some("08")), // yellow on yellow Self::color(" ", "08", Some("08")), // yellow on yellow
); );
let cigarette = Self::color(&"|".repeat(size), "15", Some("00")); // light_grey on white let cigarette = Self::color(&"|".repeat(size), "15", Some("00")); // light_grey on white
let cherry = format!( let cherry = format!(
"{}{}", "{}{}",
Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("01")), // random color on black Self::color(
Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("14")), // random color on grey "\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_chars = ";:-.,_`~'";
let smoke = Self::color( let smoke = Self::color(
@ -87,8 +95,16 @@ impl Drugs {
let joint = Self::color(&"/".repeat(size), "15", Some("00")); // light_grey on white let joint = Self::color(&"/".repeat(size), "15", Some("00")); // light_grey on white
let cherry = format!( let cherry = format!(
"{}{}", "{}{}",
Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("01")), // random color on black Self::color(
Self::color("\u{259A}", Self::random_choice(&["04", "08", "07"]), Some("14")), // random color on grey "\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_chars = ";:-.,_`~'";
let smoke = Self::color( let smoke = Self::color(
@ -182,7 +198,9 @@ impl Drugs {
if self.fat { if self.fat {
for _ in 0..3 { for _ in 0..3 {
writer writer
.write_all(format!("PRIVMSG {} :{}\r\n", channel, object).as_bytes()) .write_all(
format!("PRIVMSG {} :{}\r\n", channel, object).as_bytes(),
)
.await?; .await?;
} }
} else { } else {
@ -204,8 +222,8 @@ impl Drugs {
Self::color(" !!! ", "04", Some("03")), // red on green Self::color(" !!! ", "04", Some("03")), // red on green
Self::color( Self::color(
"AWWW SHIT, IT'S TIME FOR THAT MARLBORO FATFUCK", "AWWW SHIT, IT'S TIME FOR THAT MARLBORO FATFUCK",
"01", // black "01", // black
Some("03") // green Some("03") // green
), ),
Self::color(" !!! ", "04", Some("03")) // red on green Self::color(" !!! ", "04", Some("03")) // red on green
) )
@ -223,8 +241,8 @@ impl Drugs {
Self::color(" !!! ", "00", Some("04")), // white on red Self::color(" !!! ", "00", Some("04")), // white on red
Self::color( Self::color(
"AWWW SHIT, IT'S TIME FOR THAT NEWPORT 100", "AWWW SHIT, IT'S TIME FOR THAT NEWPORT 100",
"04", // red "04", // red
Some("00") // white Some("00") // white
), ),
Self::color(" !!! ", "00", Some("04")) // white on red Self::color(" !!! ", "00", Some("04")) // white on red
) )
@ -240,8 +258,8 @@ impl Drugs {
Self::color(" !!! ", "04", Some("03")), // red on green Self::color(" !!! ", "04", Some("03")), // red on green
Self::color( Self::color(
"OHHH FUCK, IT'S TIME FOR THAT 420 EXTENDO", "OHHH FUCK, IT'S TIME FOR THAT 420 EXTENDO",
"08", // yellow "08", // yellow
Some("03") // green Some("03") // green
), ),
Self::color(" !!! ", "04", Some("03")) // red on green Self::color(" !!! ", "04", Some("03")) // red on green
) )
@ -293,8 +311,14 @@ impl Drugs {
} }
fn generate_beer(&self) -> (String, String) { fn generate_beer(&self) -> (String, String) {
let beer_choice = ["bud", "modelo", "ultra"].choose(&mut thread_rng()).unwrap().to_string(); let beer_choice = ["bud", "modelo", "ultra"]
let beer_temp = ["a piss warm", "an ice cold", "an empty"].choose(&mut thread_rng()).unwrap().to_string(); .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) (beer_choice, beer_temp)
} }
@ -303,7 +327,11 @@ impl Drugs {
"bud" => format!( "bud" => format!(
"{}{}{}", "{}{}{}",
Self::color(" ", "00", Some("00")), Self::color(" ", "00", Some("00")),
Self::color(" BUD ", "00", Some(["02", "05"].choose(&mut thread_rng()).unwrap())), Self::color(
" BUD ",
"00",
Some(["02", "05"].choose(&mut thread_rng()).unwrap())
),
Self::color("c", "14", Some("00")) Self::color("c", "14", Some("00"))
), ),
"modelo" => format!( "modelo" => format!(

View File

@ -1,13 +1,16 @@
// mods/handler.rs // mods/handler.rs
use tokio::io::{AsyncRead, AsyncWrite, split}; use crate::{readmsg, writemsg, Config, MessageBuffer};
use tokio::io::{split, AsyncRead, AsyncWrite};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::{Config, readmsg, writemsg, MessageBuffer};
/// Handle the connection to the server /// Handle the connection to the server
pub async fn handler<S>(stream: S, config: Config) -> Result<(), Box<dyn std::error::Error>> where S: AsyncRead + AsyncWrite + Unpin + Send + 'static { pub async fn handler<S>(stream: S, config: Config) -> Result<(), Box<dyn std::error::Error>>
where
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let (reader, writer) = split(stream); let (reader, writer) = split(stream);
let (tx, rx) = mpsc::channel(1000); let (tx, rx) = mpsc::channel(1000);
let read_task = tokio::spawn(async move { let read_task = tokio::spawn(async move {
readmsg(reader, tx).await; readmsg(reader, tx).await;
}); });
@ -15,10 +18,11 @@ pub async fn handler<S>(stream: S, config: Config) -> Result<(), Box<dyn std::er
let message_buffer = MessageBuffer::new(1000); let message_buffer = MessageBuffer::new(1000);
let write_task = tokio::spawn(async move { let write_task = tokio::spawn(async move {
writemsg(writer, rx, &config, message_buffer).await; writemsg(writer, rx, &config, message_buffer).await;
}); });
//let _ = tokio::try_join!(read_task, write_task); //let _ = tokio::try_join!(read_task, write_task);
tokio::try_join!(read_task, write_task).map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?; tokio::try_join!(read_task, write_task)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error>)?;
Ok(()) Ok(())
} }

View File

@ -1,7 +1,7 @@
// mods/proxy.rs // mods/proxy.rs
use crate::Config;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio_socks::tcp::Socks5Stream; use tokio_socks::tcp::Socks5Stream;
use crate::Config;
/// Establish a connection to the proxy /// Establish a connection to the proxy
pub async fn proxy_exec(config: &Config) -> Result<TcpStream, Box<dyn std::error::Error + Send>> { pub async fn proxy_exec(config: &Config) -> Result<TcpStream, Box<dyn std::error::Error + Send>> {
@ -9,18 +9,20 @@ pub async fn proxy_exec(config: &Config) -> Result<TcpStream, Box<dyn std::error
Some(addr) => addr, Some(addr) => addr,
None => "127.0.0.1", None => "127.0.0.1",
}; };
let proxy_port = config.proxy_port.unwrap_or(9050); let proxy_port = config.proxy_port.unwrap_or(9050);
let proxy = format!("{}:{}", proxy_addr, proxy_port); let proxy = format!("{}:{}", proxy_addr, proxy_port);
let server = format!("{}:{}", config.server, config.port); let server = format!("{}:{}", config.server, config.port);
let proxy_stream = TcpStream::connect(proxy).await.unwrap(); let proxy_stream = TcpStream::connect(proxy).await.unwrap();
let username = config.proxy_username.clone().unwrap(); let username = config.proxy_username.clone().unwrap();
let password = config.proxy_password.clone().unwrap(); let password = config.proxy_password.clone().unwrap();
let tcp_stream = if !&username.is_empty() && !password.is_empty() { let tcp_stream = if !&username.is_empty() && !password.is_empty() {
let tcp_stream = Socks5Stream::connect_with_password_and_socket(proxy_stream, server, &username, &password).await.unwrap(); Socks5Stream::connect_with_password_and_socket(proxy_stream, server, &username, &password)
tcp_stream .await
.unwrap()
} else { } else {
let tcp_stream = Socks5Stream::connect_with_socket(proxy_stream, server).await.unwrap(); Socks5Stream::connect_with_socket(proxy_stream, server)
tcp_stream .await
.unwrap()
}; };
let tcp_stream = tcp_stream.into_inner(); let tcp_stream = tcp_stream.into_inner();

View File

@ -6,7 +6,8 @@ pub async fn start_sasl_auth<W: tokio::io::AsyncWriteExt + Unpin>(
mechanism: &str, mechanism: &str,
nickname: &str, nickname: &str,
realname: &str, realname: &str,
capabilities: Option<Vec<String>>) -> Result<(), Box<dyn std::error::Error>> { capabilities: Option<Vec<String>>,
) -> Result<(), Box<dyn std::error::Error>> {
writer.write_all(b"CAP LS 302\r\n").await?; writer.write_all(b"CAP LS 302\r\n").await?;
nickme(writer, nickname, realname).await?; nickme(writer, nickname, realname).await?;
@ -36,7 +37,9 @@ pub async fn handle_sasl_messages<W: tokio::io::AsyncWriteExt + Unpin>(
} else if message.starts_with("AUTHENTICATE +") { } else if message.starts_with("AUTHENTICATE +") {
let auth_string = format!("\0{}\0{}", username, password); let auth_string = format!("\0{}\0{}", username, password);
let encoded = base64::engine::general_purpose::STANDARD.encode(auth_string); let encoded = base64::engine::general_purpose::STANDARD.encode(auth_string);
writer.write_all(format!("AUTHENTICATE {}\r\n", encoded).as_bytes()).await?; writer
.write_all(format!("AUTHENTICATE {}\r\n", encoded).as_bytes())
.await?;
} else if message.contains("903 * :SASL authentication successful") { } else if message.contains("903 * :SASL authentication successful") {
writer.write_all(b"CAP END\r\n").await?; writer.write_all(b"CAP END\r\n").await?;
} }

View File

@ -1,3 +1,4 @@
// mods/sed.rs
use regex::Regex; use regex::Regex;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -30,9 +31,13 @@ impl SedCommand {
pub fn apply_to(&self, message: &str) -> String { pub fn apply_to(&self, message: &str) -> String {
if self.global { if self.global {
self.pattern.replace_all(message, self.replacement.as_str()).to_string() self.pattern
.replace_all(message, self.replacement.as_str())
.to_string()
} else { } else {
self.pattern.replace(message, self.replacement.as_str()).to_string() self.pattern
.replace(message, self.replacement.as_str())
.to_string()
} }
} }
} }
@ -67,4 +72,3 @@ impl MessageBuffer {
None None
} }
} }

View File

@ -1,12 +1,20 @@
// mods/tls.rs // mods/tls.rs
use tokio::net::TcpStream;
use tokio_native_tls::{TlsConnector, native_tls::TlsConnector as NTlsConnector};
use crate::Config; use crate::Config;
use tokio::net::TcpStream;
use tokio_native_tls::{native_tls::TlsConnector as NTlsConnector, TlsConnector};
// Establish a TLS connection to the server // Establish a TLS connection to the server
pub async fn tls_exec(config: &Config, tcp_stream: TcpStream) -> Result<tokio_native_tls::TlsStream<TcpStream>, Box<dyn std::error::Error + Send>> { pub async fn tls_exec(
let tls_builder = NTlsConnector::builder().danger_accept_invalid_certs(true).build().unwrap(); config: &Config,
tcp_stream: TcpStream,
) -> Result<tokio_native_tls::TlsStream<TcpStream>, Box<dyn std::error::Error + Send>> {
let tls_builder = NTlsConnector::builder()
.danger_accept_invalid_certs(true)
.build()
.unwrap();
let tls_connector = TlsConnector::from(tls_builder); let tls_connector = TlsConnector::from(tls_builder);
Ok(tls_connector.connect(&config.server, tcp_stream).await.unwrap()) Ok(tls_connector
.connect(&config.server, tcp_stream)
.await
.unwrap())
} }

View File

@ -1,8 +1,8 @@
// mods/vomit.rs
use crate::Config;
use rand::prelude::*; use rand::prelude::*;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::time; use tokio::time;
use crate::Config;
async fn generate_random_unicode() -> char { async fn generate_random_unicode() -> char {
let codepoint: u32 = thread_rng().gen_range(0..=0x10FFFF); let codepoint: u32 = thread_rng().gen_range(0..=0x10FFFF);
@ -39,7 +39,7 @@ fn split_into_chunks(s: &str, max_chunk_size: usize) -> Vec<String> {
for char in s.chars() { for char in s.chars() {
if current_chunk.len() + char.len_utf8() > max_chunk_size { if current_chunk.len() + char.len_utf8() > max_chunk_size {
chunks.push(current_chunk.clone()); chunks.push(current_chunk.clone());
current_chunk.clear(); current_chunk.clear();
} }
current_chunk.push(char); current_chunk.push(char);
@ -52,7 +52,6 @@ fn split_into_chunks(s: &str, max_chunk_size: usize) -> Vec<String> {
chunks chunks
} }
const CHUNK_SIZE: usize = 400; const CHUNK_SIZE: usize = 400;
// Function to handle the vomit command // Function to handle the vomit command
pub async fn handle_vomit_command<W: AsyncWriteExt + Unpin>( pub async fn handle_vomit_command<W: AsyncWriteExt + Unpin>(
@ -68,7 +67,9 @@ pub async fn handle_vomit_command<W: AsyncWriteExt + Unpin>(
let chunks = split_into_chunks(&vomit, CHUNK_SIZE); // Adjust if split_into_chunks is async let chunks = split_into_chunks(&vomit, CHUNK_SIZE); // Adjust if split_into_chunks is async
for chunk in chunks { for chunk in chunks {
writer.write_all(format!("PRIVMSG {} :{}\r\n", channel, chunk).as_bytes()).await?; writer
.write_all(format!("PRIVMSG {} :{}\r\n", channel, chunk).as_bytes())
.await?;
writer.flush().await?; writer.flush().await?;
time::sleep(tokio::time::Duration::from_secs(config.pump_delay)).await; time::sleep(tokio::time::Duration::from_secs(config.pump_delay)).await;
} }