Split the code again and cleaned up a little.

This commit is contained in:
Thelie 2021-05-18 20:54:22 +02:00
parent 5cd35e2e8e
commit a8ddf20098
2 changed files with 181 additions and 169 deletions

View file

@ -17,180 +17,14 @@
pub mod connect; pub mod connect;
pub mod config; pub mod config;
pub mod servers;
use std::{ use std::{
env::args, env::args,
path::PathBuf, path::PathBuf,
collections::VecDeque, sync::mpsc::channel,
sync::mpsc::{
channel,
Sender,
},
thread, thread,
}; };
use irc_proto::command::{
CapSubCommand,
Command,
};
use irc_proto::message::Message;
use irc_stream::{
IrcRead,
IrcWrite,
};
use toml::value::Value;
trait IrcReadWrite: IrcRead + IrcWrite {}
impl<T: IrcRead + IrcWrite> IrcReadWrite for T {}
/// Constructs a VecDeque with the messages
/// required to identify with an IRC server.
/// Uses the credentials set in -`config.toml`.
fn get_irc_identify_messages (config: &Value)
-> Result<VecDeque<Message>, Box<dyn std::error::Error>> {
let mut queue = VecDeque::new();
queue.push_back(Message::from(
Command::CAP(
None,
CapSubCommand::END,
None,
None
)));
match config.get("password") {
Some(p) => queue.push_back(Message::from(
Command::PASS(
String::from(p.as_str().ok_or("Could not parse password.")?)
)
)),
None => ()
}
let nick: String;
match config.get("nickname") {
Some(n) => {
nick = String::from(n.as_str().ok_or("Could not parse nickname.")?);
queue.push_back(Message::from(Command::NICK(nick.clone())));
},
None => return Err("No nickname supplied!".into()),
}
match config.get("username") {
Some(u) => queue.push_back(Message::from(
Command::USER(
String::from(u.as_str().ok_or("Could not parse username.")?),
"0".to_owned(),
String::from(u.as_str().ok_or("Could not parse username.")?)
)
)),
None => queue.push_back(Message::from(
// nick.clone() is only used once because the value
// can be moved the second time
Command::USER(nick.clone(), "0".to_owned(), nick)
))
}
Ok(queue)
}
/// Appends a given VecDeque to include the messages
/// used to join the channels defined in `config.toml`.
fn get_irc_join_messages(config: &Value, queue: VecDeque<Message>)
-> Result<VecDeque<Message>, Box<dyn std::error::Error>> {
let mut queue = queue;
match config.get("channels") {
Some(c) => {
for channel in c.as_array().ok_or("Could not parse channels.")? {
queue.push_back(Message::from(
Command::JOIN(
String::from(channel.as_str().ok_or("Could not parse one of the channels")?),
None,
None
)
))
}
},
None => ()
}
Ok(queue)
}
fn handle_message(message: Message) -> (Option<Message>, Option<[String; 3]>) {
let sender = match message.clone().source_nickname() {
Some(s) => String::from(s),
None => String::from("anonymous")
};
match message.clone().command {
Command::PING(ref data, _) => {
return (
Some(Message::from(
Command::PONG(data.to_owned(), None)
)),
None
);
},
Command::PRIVMSG(ref rec, ref msg) => {
return (None, Some([rec.clone(), sender.clone(), msg.clone()]))
},
_ => println!("{}", message.clone().to_string())
}
return (None, None)
}
fn handle_server(config: Value, tx: Sender<[String; 4]>)
-> Result<(), Box<dyn std::error::Error>> {
let server_name = config.get("server").unwrap().as_str()
.ok_or("Could not get server adress from config")?;
let mut stream = connect::connect_irc(&config).unwrap();
let mut message_queue = get_irc_identify_messages(&config).unwrap();
while let Some(message) = message_queue.pop_front() {
stream.write(message).unwrap();
}
message_queue = get_irc_join_messages(&config, message_queue).unwrap();
// Wait for first ping and join channels after sending pong.
loop {
let message = stream.read()?;
match message.command {
Command::PING(ref data, _) => {
stream.write(Message::from(
Command::PONG(data.to_owned(), None)
)).unwrap();
while let Some(message) = message_queue.pop_front() {
stream.write(message).unwrap();
}
break;
}
_ => ()
}
}
// Handle all incoming messages after joining the channels.
loop {
let message = stream.read()?;
let (answer, data) = handle_message(message);
match answer {
Some(a) => stream.write(a).unwrap(),
None => ()
}
match data {
Some(d) => {
// There must be a better way to do this…
let d = [
server_name.to_owned(),
d[0].clone(),
d[1].clone(),
d[2].clone()
];
tx.send(d)?
},
None => (),
}
}
}
fn main() { fn main() {
let mut server_conf_flag = false; let mut server_conf_flag = false;
@ -235,7 +69,7 @@ fn main() {
server_threads.push(thread::Builder::new() server_threads.push(thread::Builder::new()
.name("server name here".to_string()) .name("server name here".to_string())
.spawn(move || { .spawn(move || {
handle_server(s_conf, t).unwrap(); servers::handle_server(s_conf, t).unwrap();
}) })
); );
} }

178
src/servers.rs Normal file
View file

@ -0,0 +1,178 @@
/* Copyright 2021 Daniel Mowitz
* This file is part of Mention2Mail.
*
* Mention2Mail is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Mention2Mail is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Mention2Mail. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{
collections::VecDeque,
sync::mpsc::Sender,
};
use irc_proto::command::{
CapSubCommand,
Command,
};
use irc_proto::message::Message;
use toml::value::Value;
/// Constructs a VecDeque with the messages
/// required to identify with an IRC server.
/// Uses the credentials set in -`config.toml`.
fn get_irc_identify_messages (config: &Value)
-> Result<VecDeque<Message>, Box<dyn std::error::Error>> {
let mut queue = VecDeque::new();
queue.push_back(Message::from(
Command::CAP(
None,
CapSubCommand::END,
None,
None
)));
match config.get("password") {
Some(p) => queue.push_back(Message::from(
Command::PASS(
String::from(p.as_str().ok_or("Could not parse password.")?)
)
)),
None => ()
}
let nick: String;
match config.get("nickname") {
Some(n) => {
nick = String::from(n.as_str().ok_or("Could not parse nickname.")?);
queue.push_back(Message::from(Command::NICK(nick.clone())));
},
None => return Err("No nickname supplied!".into()),
}
match config.get("username") {
Some(u) => queue.push_back(Message::from(
Command::USER(
String::from(u.as_str().ok_or("Could not parse username.")?),
"0".to_owned(),
String::from(u.as_str().ok_or("Could not parse username.")?)
)
)),
None => queue.push_back(Message::from(
// nick.clone() is only used once because the value
// can be moved the second time
Command::USER(nick.clone(), "0".to_owned(), nick)
))
}
Ok(queue)
}
/// Appends a given VecDeque to include the messages
/// used to join the channels defined in `config.toml`.
fn get_irc_join_messages(config: &Value, queue: VecDeque<Message>)
-> Result<VecDeque<Message>, Box<dyn std::error::Error>> {
let mut queue = queue;
match config.get("channels") {
Some(c) => {
for channel in c.as_array().ok_or("Could not parse channels.")? {
queue.push_back(Message::from(
Command::JOIN(
String::from(channel.as_str().ok_or("Could not parse one of the channels")?),
None,
None
)
))
}
},
None => ()
}
Ok(queue)
}
fn handle_message(message: Message) -> (Option<Message>, Option<[String; 3]>) {
let sender = match message.clone().source_nickname() {
Some(s) => String::from(s),
None => String::from("anonymous")
};
match message.clone().command {
Command::PING(ref data, _) => {
return (
Some(Message::from(
Command::PONG(data.to_owned(), None)
)),
None
);
},
Command::PRIVMSG(ref rec, ref msg) => {
return (None, Some([rec.clone(), sender.clone(), msg.clone()]))
},
_ => println!("{}", message.clone().to_string())
}
return (None, None)
}
pub fn handle_server(config: Value, tx: Sender<[String; 4]>)
-> Result<(), Box<dyn std::error::Error>> {
let server_name = config.get("server").unwrap().as_str()
.ok_or("Could not get server adress from config")?;
let mut stream = super::connect::connect_irc(&config).unwrap();
let mut message_queue = get_irc_identify_messages(&config).unwrap();
while let Some(message) = message_queue.pop_front() {
stream.write(message).unwrap();
}
message_queue = get_irc_join_messages(&config, message_queue).unwrap();
// Wait for first ping and join channels after sending pong.
loop {
let message = stream.read()?;
match message.command {
Command::PING(ref data, _) => {
stream.write(Message::from(
Command::PONG(data.to_owned(), None)
)).unwrap();
while let Some(message) = message_queue.pop_front() {
stream.write(message).unwrap();
}
break;
}
_ => ()
}
}
// Handle all incoming messages after joining the channels.
loop {
let message = stream.read()?;
let (answer, data) = handle_message(message);
match answer {
Some(a) => stream.write(a).unwrap(),
None => ()
}
match data {
Some(d) => {
// There must be a better way to do this…
let d = [
server_name.to_owned(),
d[0].clone(),
d[1].clone(),
d[2].clone()
];
tx.send(d)?
},
None => (),
}
}
}