Compare commits
4 Commits
40f9ade80e
...
484cef24c7
Author | SHA1 | Date | |
---|---|---|---|
484cef24c7 | |||
327ad5425f | |||
39eb6ee0da | |||
d9bacc4d5f |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
|||||||
Cargo.lock
|
Cargo.lock
|
||||||
/target
|
/target
|
||||||
|
./ircart/
|
||||||
|
@ -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
|
||||||
|
181
src/main.rs
181
src/main.rs
@ -1,14 +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 {
|
||||||
@ -21,38 +16,36 @@ 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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::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::vomit::handle_vomit_command;
|
||||||
//use mods::invade::{handle_invade_command};
|
//use mods::invade::{handle_invade_command};
|
||||||
|
|
||||||
@ -101,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;
|
||||||
@ -119,22 +114,33 @@ 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>)
|
||||||
async fn readmsg<S>(mut reader: tokio::io::ReadHalf<S>, tx: tokio::sync::mpsc::Sender<String>) where S: AsyncRead + Unpin {
|
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);
|
||||||
@ -145,8 +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);
|
||||||
|
|
||||||
/// Write messages to the server
|
async fn writemsg<S>(
|
||||||
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 {
|
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();
|
||||||
@ -154,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 {
|
||||||
@ -162,6 +176,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() {
|
||||||
@ -171,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!(
|
||||||
|
"{} {} {} {} {}",
|
||||||
|
"DEBUG:".bold().yellow(),
|
||||||
|
"serv:".bold().green(),
|
||||||
|
serv.purple(),
|
||||||
|
"cmd:".bold().green(),
|
||||||
|
cmd.purple()
|
||||||
|
);
|
||||||
if *serv == "PING" {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,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");
|
||||||
@ -222,13 +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()
|
||||||
|
);
|
||||||
|
|
||||||
// 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) {
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,35 +291,41 @@ 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 [
|
||||||
// if msg_content.starts_with("%invade") {
|
"%chug", "%smoke", "%toke", "%100", "%extendo", "%fatfuck", "%beer",
|
||||||
// let _ = handle_vomit_command(&mut writer, config, &msg_content, channel).await;
|
]
|
||||||
// }
|
.iter()
|
||||||
// other commands here
|
.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<W: tokio::io::AsyncWriteExt + Unpin>(
|
||||||
|
writer: &mut W,
|
||||||
async fn nickme<W: tokio::io::AsyncWriteExt + Unpin>(writer: &mut W, nickname: &str, realname: &str) -> Result<(), Box<dyn std::error::Error>> {
|
nickname: &str,
|
||||||
writer.write_all(format!("NICK {}\r\n", nickname).as_bytes()).await?;
|
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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
.ok()?
|
||||||
|
.filter_map(|entry| {
|
||||||
let path = entry.ok()?.path();
|
let path = entry.ok()?.path();
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
path.to_str().map(ToString::to_string)
|
path.to_str().map(ToString::to_string)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).collect::<Vec<String>>();
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
if files.is_empty() {
|
if files.is_empty() {
|
||||||
None
|
None
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
363
src/mods/drugs.rs
Normal file
363
src/mods/drugs.rs
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
use crate::Config;
|
||||||
|
use rand::prelude::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,13 @@
|
|||||||
// 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);
|
||||||
|
|
||||||
@ -19,6 +22,7 @@ pub async fn handler<S>(stream: S, config: Config) -> Result<(), Box<dyn std::er
|
|||||||
});
|
});
|
||||||
|
|
||||||
//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(())
|
||||||
}
|
}
|
||||||
|
@ -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>> {
|
||||||
@ -16,11 +16,13 @@ pub async fn proxy_exec(config: &Config) -> Result<TcpStream, Box<dyn std::error
|
|||||||
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();
|
||||||
|
|
||||||
|
@ -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?;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user