implement cancer features

This commit is contained in:
sad 2024-10-02 21:29:15 +00:00
parent 40f9ade80e
commit d9bacc4d5f
3 changed files with 351 additions and 27 deletions

View File

@ -1,12 +1,12 @@
#[server] #[server]
server = "198.98.52.138" #"irc.supernets.org" server = "irc.supernets.org"
port = 6697 port = 6697
use_ssl = true use_ssl = true
#[user] #[user]
nickname = "g1r" nickname = "g1r"
realname = "git.supernets.org/sad/g1r" realname = "git.supernets.org/sad/g1r"
channels = ["#dev", "#superbowl", "#5000"] channels = ["#superbowl"]
sasl_username = "" sasl_username = ""
sasl_password = "" sasl_password = ""
capabilities = ["sasl"] capabilities = ["sasl"]
@ -15,7 +15,7 @@ capabilities = ["sasl"]
use_proxy = false use_proxy = false
proxy_type = "socks5" proxy_type = "socks5"
proxy_addr = "127.0.0.1" proxy_addr = "127.0.0.1"
proxy_port = 1080 proxy_port = 9050
proxy_username = "" proxy_username = ""
proxy_password = "" proxy_password = ""
@ -23,4 +23,5 @@ proxy_password = ""
kickrejoin = true kickrejoin = true
ascii_art = "./ircart/ircart" ascii_art = "./ircart/ircart"
pump_delay = 0 # in milliseconds pump_delay = 0 # in milliseconds
reconnect_delay = 5
reconnect_attempts = 10

View File

@ -5,7 +5,6 @@ use tokio_native_tls::TlsConnector;
use tokio::sync::mpsc; 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 colored::*;
use tokio_socks::tcp::Socks5Stream; use tokio_socks::tcp::Socks5Stream;
@ -21,18 +20,13 @@ struct Config {
sasl_username: Option<String>, sasl_username: Option<String>,
sasl_password: Option<String>, sasl_password: Option<String>,
capabilities: Option<Vec<String>>, capabilities: Option<Vec<String>>,
reconnect_delay: u64, reconnect_delay: u64,
reconnect_attempts: u64, reconnect_attempts: u64,
// Proxy
use_proxy: bool, use_proxy: bool,
// proxy_type: Option<String>,
proxy_addr: Option<String>, proxy_addr: Option<String>,
proxy_port: Option<u16>, proxy_port: Option<u16>,
proxy_username: Option<String>, proxy_username: Option<String>,
proxy_password: Option<String>, proxy_password: Option<String>,
ascii_art: Option<String>, ascii_art: Option<String>,
pump_delay: u64, pump_delay: u64,
} }
@ -45,8 +39,10 @@ mod mods {
pub mod sed; pub mod sed;
pub mod ascii; pub mod ascii;
pub mod vomit; pub mod vomit;
pub mod drugs;
// pub mod invade; // pub mod invade;
} }
use mods::proxy::proxy_exec; use mods::proxy::proxy_exec;
use mods::tls::tls_exec; use mods::tls::tls_exec;
use mods::handler::handler; use mods::handler::handler;
@ -54,6 +50,7 @@ use mods::sasl::{start_sasl_auth, handle_sasl_messages};
use mods::sed::{SedCommand, MessageBuffer}; 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::vomit::handle_vomit_command;
use mods::drugs::Drugs;
//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)]
@ -119,17 +116,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(()) Ok(())
} }
/// Load the config file
fn loaded_config() -> Result<Config, Box<dyn std::error::Error>> { fn loaded_config() -> Result<Config, Box<dyn std::error::Error>> {
let config_contents = fs::read_to_string("config.toml")?; let config_contents = fs::read_to_string("config.toml")?;
let config: Config = toml::from_str(&config_contents)?; let config: Config = toml::from_str(&config_contents)?;
Ok(config) Ok(config)
} }
/// Read messages from the server
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() {
@ -145,7 +140,6 @@ 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);
/// Write messages to the server
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();
@ -162,6 +156,8 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::m
writer.flush().await.unwrap(); writer.flush().await.unwrap();
} }
let mut drugs = Drugs::new();
while let Some(msg) = rx.recv().await { while let Some(msg) = rx.recv().await {
let msg = msg.trim(); let msg = msg.trim();
if msg.is_empty() { if msg.is_empty() {
@ -224,7 +220,6 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, 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()); 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 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) {
@ -236,27 +231,23 @@ async fn writemsg<S>(mut writer: tokio::io::WriteHalf<S>, mut rx: tokio::sync::m
message_buffer.add_message(msg_content.clone().to_string()); message_buffer.add_message(msg_content.clone().to_string());
} }
// ansi art
if msg_content.starts_with("%ascii") { if msg_content.starts_with("%ascii") {
let _ = handle_ascii_command(&mut writer, config, &msg_content, channel).await; let _ = handle_ascii_command(&mut writer, config, &msg_content, channel).await;
} }
// vomit
if msg_content.starts_with("%vomit") { if msg_content.starts_with("%vomit") {
let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await; let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await;
} }
// invade if ["%chug", "%smoke", "%toke", "%100", "%extendo", "%fatfuck", "%beer"].iter().any(|&prefix| msg_content.starts_with(prefix)) {
// if msg_content.starts_with("%invade") { drugs.handle_drugs_command(&mut writer, config, &msg_content, channel).await
// let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await; .unwrap_or_else(|e| eprintln!("Error handling drugs command: {}", e));
// } }
// other commands here
} }
} }
} }
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: &mut W, nickname: &str, realname: &str) -> Result<(), Box<dyn std::error::Error>> {
writer.write_all(format!("NICK {}\r\n", nickname).as_bytes()).await?; writer.write_all(format!("NICK {}\r\n", nickname).as_bytes()).await?;
writer.flush().await?; writer.flush().await?;
@ -265,6 +256,3 @@ async fn nickme<W: tokio::io::AsyncWriteExt + Unpin>(writer: &mut W, nickname: &
Ok(()) Ok(())
} }

335
src/mods/drugs.rs Normal file
View File

@ -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<Mutex<Stats>>,
}
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::<String>();
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::<String>()
),
"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::<String>()
),
"14", // grey
None,
);
format!("{}{}{}", joint, cherry, smoke)
}
fn mug(size: usize) -> Vec<String> {
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<W: AsyncWriteExt + Unpin>(
&mut self,
writer: &mut W,
config: &Config,
command: &str,
channel: &str,
) -> Result<(), Box<dyn std::error::Error>> {
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<W: AsyncWriteExt + Unpin>(
&self,
writer: &mut W,
target: &str,
channel: &str,
) -> Result<(), Box<dyn std::error::Error>> {
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<T: Clone>(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
}
}