mirror of
https://github.com/ayywrk/fuckircd.git
synced 2024-11-24 07:36:40 +00:00
inital commit
This commit is contained in:
commit
ccec62de1c
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
config.yaml
|
||||
reporting_config.yaml
|
15
Cargo.toml
Normal file
15
Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "fuckircd"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ircie = { path = "../ircie" }
|
||||
tokio = { version = "1.28.2", features = ["full"] }
|
||||
rand = "0.8.5"
|
||||
log = "0.4.19"
|
||||
env_logger = "0.10.0"
|
||||
serde = { version = "1.0.163", features = ["derive"] }
|
||||
serde_yaml = "0.9.21"
|
8
config.example.yaml
Normal file
8
config.example.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
host: 127.0.0.1
|
||||
hostname: you.super.domain.net
|
||||
port: 6667
|
||||
|
||||
ircd_name: ircd-ratbox-3.0.10
|
||||
banner_filepath: your_banner.txt
|
||||
chan_name: HIE
|
||||
privmsg_line: LOLOLOL
|
21
reporting_config.example.yaml
Normal file
21
reporting_config.example.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
host: irc.somewhere.org
|
||||
port: 6697
|
||||
ssl: true
|
||||
|
||||
channels:
|
||||
- "#chats"
|
||||
|
||||
nick: ERIS
|
||||
user: ERIS
|
||||
real: eris.port6667.net/6667
|
||||
|
||||
nickserv_pass: REDACTED
|
||||
nickserv_email: REDACTED
|
||||
|
||||
cmdkey: .
|
||||
|
||||
flood_interval: 1
|
||||
|
||||
owner: "*!*@change.me"
|
||||
|
||||
admins: []
|
706
src/main.rs
Normal file
706
src/main.rs
Normal file
@ -0,0 +1,706 @@
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
io::{ErrorKind, Result},
|
||||
net::SocketAddr,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use env_logger::Env;
|
||||
use ircie::{
|
||||
system_params::{Context, ResMut},
|
||||
Irc,
|
||||
};
|
||||
|
||||
use log::{debug, error, info, trace};
|
||||
use rand::{
|
||||
distributions::{Alphanumeric, DistString},
|
||||
rngs::StdRng,
|
||||
Rng, SeedableRng,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use tokio::{
|
||||
fs::File,
|
||||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpListener,
|
||||
sync::{
|
||||
mpsc::{self},
|
||||
RwLock,
|
||||
},
|
||||
task::JoinHandle,
|
||||
};
|
||||
|
||||
const MAX_MSG_LEN: usize = 512;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Config {
|
||||
host: String,
|
||||
hostname: String,
|
||||
port: u16,
|
||||
ircd_name: String,
|
||||
banner_filepath: String,
|
||||
chan_name: String,
|
||||
privmsg_line: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub async fn load(path: &str) -> Result<Self> {
|
||||
let mut file = File::open(path).await?;
|
||||
let mut content = String::new();
|
||||
file.read_to_string(&mut content).await?;
|
||||
|
||||
let config: Self = serde_yaml::from_str(&content).unwrap();
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Ircd {
|
||||
host: String,
|
||||
port: u16,
|
||||
hostname: String,
|
||||
name: String,
|
||||
motd: Vec<String>,
|
||||
chan_name: String,
|
||||
privmsg_line: String,
|
||||
}
|
||||
|
||||
struct Nerd(mpsc::Receiver<Nick>);
|
||||
|
||||
struct IrcClient<T> {
|
||||
addr: SocketAddr,
|
||||
read_task: JoinHandle<T>,
|
||||
write_task: JoinHandle<T>,
|
||||
rx: mpsc::Receiver<bool>,
|
||||
reporting_tx: mpsc::Sender<Nick>,
|
||||
send_queue: Arc<RwLock<VecDeque<String>>>,
|
||||
recv_queue: Arc<RwLock<VecDeque<String>>>,
|
||||
ircd: Ircd,
|
||||
server_created_on: String,
|
||||
global_users: u32,
|
||||
global_invisibles: u32,
|
||||
global_servers: u32,
|
||||
total_ircops: u32,
|
||||
total_unknown_conns: u32,
|
||||
total_channels: u32,
|
||||
local_users: u32,
|
||||
local_servers: u32,
|
||||
max_local_users: u32,
|
||||
max_global_users: u32,
|
||||
highest_conn_count: u32,
|
||||
total_conns_received: u32,
|
||||
|
||||
nick: Option<Nick>,
|
||||
user: Option<User>,
|
||||
welcomed: bool,
|
||||
quit: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Nick(pub String);
|
||||
|
||||
impl Deref for Nick {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Nick {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct User {
|
||||
username: String,
|
||||
hostname: String,
|
||||
servername: String,
|
||||
realname: String,
|
||||
}
|
||||
|
||||
impl<T> IrcClient<T> {
|
||||
pub fn new(
|
||||
addr: SocketAddr,
|
||||
send_queue: Arc<RwLock<VecDeque<String>>>,
|
||||
recv_queue: Arc<RwLock<VecDeque<String>>>,
|
||||
ircd: Ircd,
|
||||
read_task: JoinHandle<T>,
|
||||
write_task: JoinHandle<T>,
|
||||
rx: mpsc::Receiver<bool>,
|
||||
reporting_tx: mpsc::Sender<Nick>,
|
||||
) -> Self {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let global_users = rng.gen_range(200..8000);
|
||||
let global_invisibles = rng.gen_range(100..global_users);
|
||||
let global_servers = rng.gen_range(2..20);
|
||||
let total_ircops = rng.gen_range(10..(global_users / 10));
|
||||
let total_unknown_conns = rng.gen_range(1..10);
|
||||
let total_channels =
|
||||
rng.gen_range((global_users / 30)..((global_users as f32 * 1.5) as u32));
|
||||
let local_users = rng.gen_range((global_users / 20)..(global_users / 2));
|
||||
let local_servers = rng.gen_range(1..global_servers);
|
||||
let max_local_users = rng.gen_range(local_users..(local_users * 2));
|
||||
let max_global_users = rng.gen_range(max_local_users..(max_local_users * 2));
|
||||
let highest_conn_count = rng.gen_range((max_local_users - 10)..max_local_users + 10);
|
||||
let total_conns_received =
|
||||
rng.gen_range((highest_conn_count * 100)..(highest_conn_count * 1000));
|
||||
|
||||
Self {
|
||||
addr,
|
||||
read_task,
|
||||
write_task,
|
||||
rx,
|
||||
reporting_tx,
|
||||
send_queue: send_queue,
|
||||
recv_queue: recv_queue,
|
||||
ircd,
|
||||
server_created_on: "Sun May 24 2020 at 03:31:19 UTC".to_owned(),
|
||||
global_users,
|
||||
global_invisibles,
|
||||
global_servers,
|
||||
total_ircops,
|
||||
total_unknown_conns,
|
||||
total_channels,
|
||||
local_users,
|
||||
local_servers,
|
||||
max_local_users,
|
||||
max_global_users,
|
||||
highest_conn_count,
|
||||
total_conns_received,
|
||||
nick: None,
|
||||
user: None,
|
||||
welcomed: false,
|
||||
quit: false,
|
||||
}
|
||||
}
|
||||
|
||||
async fn queue(&mut self, data: impl AsRef<str>) {
|
||||
// do not use this trace if you have get_fucked() executing
|
||||
//trace!(">> {}", data.as_ref());
|
||||
self.send_queue
|
||||
.write()
|
||||
.await
|
||||
.push_back(format!("{}\r\n", data.as_ref()));
|
||||
}
|
||||
|
||||
pub async fn process(&mut self) -> Result<()> {
|
||||
self.connection().await?;
|
||||
|
||||
loop {
|
||||
if self.quit {
|
||||
self.quit();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Ok(should_quit) = self.rx.try_recv() {
|
||||
if should_quit {
|
||||
self.quit();
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.process_line().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_line(&mut self) {
|
||||
let Some(line) = self.recv_queue.write().await.pop_front() else { return; };
|
||||
trace!("<< {}", line);
|
||||
self.handle(&line).await;
|
||||
}
|
||||
|
||||
async fn connection(&mut self) -> Result<()> {
|
||||
self.queue("NOTICE AUTH :*** Looking up your hostname...")
|
||||
.await;
|
||||
|
||||
self.queue("NOTICE AUTH :*** Checking ident").await;
|
||||
self.queue("NOTICE AUTH :*** Found your hostname").await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle(&mut self, line: &str) {
|
||||
let args = line.split_whitespace().collect::<Vec<_>>();
|
||||
|
||||
match args[0].to_uppercase().as_str() {
|
||||
"USER" => self.user(args[1], args[2], args[3], args[4]).await,
|
||||
"NICK" => self.nick(args[1]).await,
|
||||
"CAP" => self.cap_ls_302(args[1]).await,
|
||||
"PING" => self.pong(args[1]).await,
|
||||
"QUIT" => self.quit = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn quit(&mut self) {
|
||||
info!(
|
||||
"{}:{} -> connection closed.",
|
||||
self.addr.ip().to_string(),
|
||||
self.addr.port()
|
||||
);
|
||||
self.read_task.abort();
|
||||
self.write_task.abort();
|
||||
}
|
||||
|
||||
async fn pong(&mut self, val: &str) {
|
||||
self.queue(format!(
|
||||
":{} PONG {} :{}",
|
||||
self.ircd.hostname, self.ircd.hostname, val
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn cap_ls_302(&mut self, cmd: &str) {
|
||||
if cmd == "LS" {
|
||||
self.queue(format!(":{} CAP * LS :multi-prefix", self.ircd.hostname))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
if cmd == "REQ" {
|
||||
self.queue(format!(":{} CAP * ACK :multi-prefix", self.ircd.hostname))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async fn user(&mut self, username: &str, hostname: &str, servername: &str, realname: &str) {
|
||||
self.user = Some(User {
|
||||
username: username.to_owned(),
|
||||
hostname: hostname.to_owned(),
|
||||
servername: servername.to_owned(),
|
||||
realname: realname.to_owned(),
|
||||
});
|
||||
self.maybe_welcome().await;
|
||||
}
|
||||
|
||||
async fn nick(&mut self, nick: &str) {
|
||||
self.nick = Some(Nick(nick.to_owned()));
|
||||
self.maybe_welcome().await;
|
||||
}
|
||||
|
||||
async fn maybe_welcome(&mut self) {
|
||||
if self.nick.is_none() || self.user.is_none() || self.welcomed {
|
||||
return;
|
||||
}
|
||||
|
||||
self.queue("NOTICE AUTH :*** No Ident response").await;
|
||||
|
||||
self.queue(format!(
|
||||
":{} 001 {} :Welcome to the EFnet Internet Relay Chat Network bob",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(format!(
|
||||
":{} 002 {} :Your host is {}[{}/{}], running version {}",
|
||||
self.ircd.hostname,
|
||||
self.ircd.host,
|
||||
self.ircd.port,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.ircd.hostname,
|
||||
self.ircd.name
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 003 {} :This server was created {}",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.server_created_on
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 004 {} :{} {} oiwszcrkfydnxbauglZCD biklmnopstveIrS bkloveI",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.ircd.hostname,
|
||||
self.ircd.name
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(
|
||||
&format!(
|
||||
":{} 005 {} CHANTYPES=&# EXCEPTS INVEX CHANMODES=eIb,k,l,imnpstS CHANLIMIT=&#:75 PREFIX=(ov)@+ MAXLIST=beI:100 MODES=4 NETWORK=EFnet KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server",
|
||||
self.ircd.hostname, self.nick.as_ref().unwrap().0
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
self.queue(
|
||||
&format!(
|
||||
":{} 005 {} SAFELIST ELIST=U CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=9 CHANNELLEN=50 TOPICLEN=160 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=60 :are supported by this server",
|
||||
self.ircd.hostname, self.nick.as_ref().unwrap().0
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
self.queue(
|
||||
&format!(
|
||||
":{} 005 {} FNC ACCEPT=20 MAP TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server",
|
||||
self.ircd.hostname, self.nick.as_ref().unwrap().0
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 251 {} :There are {} users and {} invisible on {} servers",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.global_users,
|
||||
self.global_invisibles,
|
||||
self.global_servers
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 252 {} {} :IRC Operators online",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.total_ircops
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 253 {} {} :Unknown connection(s)",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.total_unknown_conns
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 254 {} {} :channels formed",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.total_channels
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 255 {} :I have {} clients and {} servers",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.local_users,
|
||||
self.local_servers
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 265 {} {} {} :Current local users {}, max {}",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.local_users,
|
||||
self.max_local_users,
|
||||
self.local_users,
|
||||
self.max_local_users
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 266 {} {} {} :Current global users {}, max {}",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.global_users + self.global_invisibles,
|
||||
self.max_global_users,
|
||||
self.global_users + self.global_invisibles,
|
||||
self.max_global_users
|
||||
))
|
||||
.await;
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 250 {} :Highest connection count: {} ({} clients) ({} connections received)",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.highest_conn_count,
|
||||
self.max_local_users,
|
||||
self.total_conns_received
|
||||
))
|
||||
.await;
|
||||
|
||||
self.welcomed = true;
|
||||
self.get_fucked().await;
|
||||
}
|
||||
|
||||
async fn get_fucked(&mut self) {
|
||||
let mut rng = StdRng::from_entropy();
|
||||
|
||||
info!(
|
||||
"I'm fucking the shit out of {} ({}:{}) right now...",
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.addr.ip().to_string(),
|
||||
self.addr.port()
|
||||
);
|
||||
|
||||
self.reporting_tx
|
||||
.send(self.nick.clone().unwrap())
|
||||
.await
|
||||
.ok();
|
||||
|
||||
loop {
|
||||
if let Ok(should_quit) = self.rx.try_recv() {
|
||||
if should_quit {
|
||||
self.quit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if rng.gen_bool(1. / 5.) {
|
||||
self.motd().await;
|
||||
}
|
||||
|
||||
let chan = Alphanumeric.sample_string(&mut rng, 6)
|
||||
+ "-"
|
||||
+ &self.ircd.chan_name
|
||||
+ "-"
|
||||
+ &Alphanumeric.sample_string(&mut rng, 6);
|
||||
|
||||
self.queue(format!(
|
||||
":{}!{}@{} JOIN #{}",
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.user.as_ref().unwrap().username,
|
||||
self.user.as_ref().unwrap().hostname,
|
||||
chan
|
||||
))
|
||||
.await;
|
||||
|
||||
let msg = format!(
|
||||
":FUCKYOU{}!B@STARD PRIVMSG #{} :\x03{} {} {}",
|
||||
self.nick.as_ref().unwrap().0,
|
||||
chan,
|
||||
rng.gen_range(2..14),
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.ircd.privmsg_line
|
||||
);
|
||||
|
||||
self.queue(msg).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn motd(&mut self) {
|
||||
self.queue(&format!(
|
||||
":{} 375 {} :- {} Message of the Day -",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
self.ircd.hostname
|
||||
))
|
||||
.await;
|
||||
|
||||
for line in self.ircd.motd.clone() {
|
||||
self.queue(&format!(
|
||||
":{} 372 {} : {}",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0,
|
||||
line
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
self.queue(&format!(
|
||||
":{} 376 {} :End of /MOTD command.",
|
||||
self.ircd.hostname,
|
||||
self.nick.as_ref().unwrap().0
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||
|
||||
info!("fuckircd - made by wrk.");
|
||||
|
||||
let config = Config::load("config.yaml").await?;
|
||||
|
||||
if !std::path::Path::new(&config.banner_filepath).exists() {
|
||||
error!(
|
||||
"motd file '{}` doesn't exists. please make it.",
|
||||
config.banner_filepath
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut banner_file = File::open(&config.banner_filepath).await.unwrap();
|
||||
let mut content = vec![];
|
||||
banner_file.read_to_end(&mut content).await.unwrap();
|
||||
|
||||
let motd = content
|
||||
.split(|&c| c == 0x0a)
|
||||
.map(|line| line.strip_suffix(b"\r").unwrap_or(line))
|
||||
.map(|line| String::from_utf8_lossy(line).to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ircd = Ircd {
|
||||
host: config.host.clone(),
|
||||
port: config.port,
|
||||
hostname: config.hostname.clone(),
|
||||
name: config.ircd_name,
|
||||
motd,
|
||||
chan_name: config.chan_name.clone(),
|
||||
privmsg_line: config.privmsg_line.clone(),
|
||||
};
|
||||
|
||||
let (reporting_tx, reporting_rx) = mpsc::channel(1);
|
||||
|
||||
let mut reporting = Irc::from_config("reporting_config.yaml").await?;
|
||||
|
||||
reporting
|
||||
.add_resource(Nerd(reporting_rx))
|
||||
.await
|
||||
.add_interval_task(Duration::from_millis(50), report_fucker)
|
||||
.await;
|
||||
|
||||
let listener = TcpListener::bind(format!("{}:{}", ircd.host, ircd.port)).await?;
|
||||
|
||||
info!("Starting reporting bot.");
|
||||
tokio::task::spawn(async move { reporting.run().await.unwrap() });
|
||||
|
||||
info!("Listening on {}:{}", ircd.host, ircd.port);
|
||||
|
||||
loop {
|
||||
let (socket, addr) = listener.accept().await?;
|
||||
|
||||
info!(
|
||||
"Got connexion from {}:{}",
|
||||
addr.ip().to_string(),
|
||||
addr.port()
|
||||
);
|
||||
|
||||
let (mut reader, mut writer) = tokio::io::split(socket);
|
||||
let send_queue = Arc::new(RwLock::new(VecDeque::<String>::new()));
|
||||
let recv_queue = Arc::new(RwLock::new(VecDeque::<String>::new()));
|
||||
|
||||
let cloned_send_queue = send_queue.clone();
|
||||
let cloned_recv_queue = recv_queue.clone();
|
||||
|
||||
let (write_tx, rx) = mpsc::channel(1);
|
||||
|
||||
let read_tx = write_tx.clone();
|
||||
|
||||
// WRITE TASK
|
||||
let write_task = tokio::task::spawn(async move {
|
||||
loop {
|
||||
let len;
|
||||
{
|
||||
let queue = cloned_send_queue.read().await;
|
||||
len = queue.len();
|
||||
}
|
||||
if len == 0 {
|
||||
continue;
|
||||
}
|
||||
let mut queue = cloned_send_queue.write().await;
|
||||
let msg = queue.pop_front().unwrap();
|
||||
|
||||
let bytes_written = match writer.write(msg.as_bytes()).await {
|
||||
Ok(bytes_written) => bytes_written,
|
||||
Err(err) => match err.kind() {
|
||||
ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
write_tx.send(true).await.unwrap();
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if bytes_written == 0 {
|
||||
write_tx.send(true).await.unwrap();
|
||||
break;
|
||||
}
|
||||
|
||||
if bytes_written < msg.len() {
|
||||
queue.push_front(msg[bytes_written..].to_owned());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// READ TASK
|
||||
let read_task = tokio::task::spawn(async move {
|
||||
let mut partial_line = String::new();
|
||||
|
||||
loop {
|
||||
let mut buf = [0; MAX_MSG_LEN];
|
||||
let mut lines = vec![];
|
||||
let bytes_read = match reader.read(&mut buf).await {
|
||||
Ok(bytes_read) => bytes_read,
|
||||
Err(err) => match err.kind() {
|
||||
ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
read_tx.send(true).await.unwrap();
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if bytes_read == 0 {
|
||||
read_tx.send(true).await.unwrap();
|
||||
break;
|
||||
}
|
||||
|
||||
let buf = &buf[..bytes_read];
|
||||
|
||||
let buf_str: String =
|
||||
partial_line + String::from_utf8_lossy(buf).into_owned().as_str();
|
||||
|
||||
partial_line = String::new();
|
||||
|
||||
let new_lines: Vec<&str> = buf_str.split("\n").collect();
|
||||
let len = new_lines.len();
|
||||
|
||||
for (index, line) in new_lines.into_iter().enumerate() {
|
||||
if index == len - 1 && &buf[buf.len() - 1..] != b"\n" {
|
||||
partial_line = line.to_owned();
|
||||
break;
|
||||
}
|
||||
if line.len() != 0 {
|
||||
lines.push(line.to_owned());
|
||||
}
|
||||
}
|
||||
let mut queue = cloned_recv_queue.write().await;
|
||||
queue.append(&mut lines.into());
|
||||
}
|
||||
});
|
||||
|
||||
let cloned_ircd = ircd.clone();
|
||||
let cloned_reporting_tx = reporting_tx.clone();
|
||||
tokio::task::spawn(async move {
|
||||
IrcClient::new(
|
||||
addr,
|
||||
send_queue,
|
||||
recv_queue,
|
||||
cloned_ircd,
|
||||
read_task,
|
||||
write_task,
|
||||
rx,
|
||||
cloned_reporting_tx,
|
||||
)
|
||||
.process()
|
||||
.await
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn report_fucker(mut ctx: Context, mut nerd: ResMut<Nerd>) {
|
||||
//TODO: Add ircie config access to systems.
|
||||
|
||||
if let Ok(nerd_nick) = nerd.0.try_recv() {
|
||||
debug!("Sending report to #dev...");
|
||||
|
||||
ctx.privmsg(
|
||||
"#dev",
|
||||
&format!("I'm fucking with {} right now.", nerd_nick.0),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user