From 19369b1366790bd59bb0e9c6bbc989d76cb27f03 Mon Sep 17 00:00:00 2001 From: Thelie Date: Wed, 5 May 2021 22:20:16 +0200 Subject: [PATCH] Made the bot actually join channels after connecting. Also split up main a bit. Co-authored-by: Thelie Co-committed-by: Thelie --- irc-stream | 2 +- src/connect.rs | 64 ++++++++++++++++++++++ src/main.rs | 141 +++++++++++++++++++++++++++++-------------------- 3 files changed, 150 insertions(+), 57 deletions(-) create mode 100644 src/connect.rs diff --git a/irc-stream b/irc-stream index bf53e62..ef1e849 160000 --- a/irc-stream +++ b/irc-stream @@ -1 +1 @@ -Subproject commit bf53e62f1713dfe83198f75992cc0e889de398ff +Subproject commit ef1e849d0ea7e6472a3fad86ea859a00597d27ea diff --git a/src/connect.rs b/src/connect.rs new file mode 100644 index 0000000..9d8f12c --- /dev/null +++ b/src/connect.rs @@ -0,0 +1,64 @@ +/* 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 . + */ + +use std::{ + format, + net::TcpStream, +}; +use irc_stream::{IrcStream, IrcRead, IrcWrite}; +use toml::value::Value; +use native_tls::{TlsConnector,TlsStream}; + +// Weird workaround for multitrait dyn pointer +// https://github.com/rust-lang/rfcs/issues/2035 +// would be a charm... +pub trait IrcReadWrite: IrcRead + IrcWrite {} +impl IrcReadWrite for T {} + +fn get_tcp_stream (config: &Value) + -> Result> { + let address = format!("{}:{}", + config.get("server").unwrap().as_str() + .ok_or("Could not get server adress from config")?, + config.get("port").unwrap().as_str() + .ok_or("Could not get port from config")? + ); + println!("Connectiing to: {}", address); + Ok(TcpStream::connect(address)?) +} + +fn get_tls_stream (config: &Value) + -> Result, Box> { + let connector = TlsConnector::new().unwrap(); + let stream = get_tcp_stream(config)?; + Ok(connector.connect(config["server"] + .as_str() + .ok_or("Could not get server adress from config")?, + stream)? + ) +} + +pub fn connect_irc (config: &Value) + -> Result, Box> { + + if *config["tls"].as_bool().get_or_insert(true) == true { + return Ok(Box::new(IrcStream::new(get_tls_stream(&config)?))) + } else { + return Ok(Box::new(IrcStream::new(get_tcp_stream(&config)?))) + } +} + diff --git a/src/main.rs b/src/main.rs index a913402..ec283bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,12 +15,13 @@ * along with Mention2Mail. If not, see . */ +pub mod connect; + use std::{ format, env::args, path::Path, io::Read, - net::TcpStream, collections::VecDeque, }; use irc_proto::command::{ @@ -28,9 +29,11 @@ use irc_proto::command::{ Command, }; use irc_proto::message::Message; -use irc_stream::{IrcStream, IrcRead, IrcWrite}; +use irc_stream::{ + IrcRead, + IrcWrite, +}; use toml::value::Value; -use native_tls::{TlsConnector,TlsStream}; trait IrcReadWrite: IrcRead + IrcWrite {} impl IrcReadWrite for T {} @@ -65,29 +68,6 @@ fn get_config>(config_path: P) Ok(Value::from(config)) } -fn get_tcp_stream (config: &Value) - -> Result> { - let address = format!("{}:{}", - config.get("server").unwrap().as_str() - .ok_or("Could not get server adress from config")?, - config.get("port").unwrap().as_str() - .ok_or("Could not get port from config")? - ); - println!("Connectiing to: {}", address); - Ok(TcpStream::connect(address)?) -} - -fn get_tls_stream (config: &Value) - -> Result, Box> { - let connector = TlsConnector::new().unwrap(); - let stream = get_tcp_stream(config)?; - Ok(connector.connect(config["server"] - .as_str() - .ok_or("Could not get server adress from config")?, - stream)? - ) -} - fn get_irc_identify_messages (config: &Value) -> Result, Box> { let mut queue = VecDeque::new(); @@ -135,30 +115,93 @@ fn get_irc_identify_messages (config: &Value) Ok(queue) } +fn get_irc_join_messages(config: &Value, queue: VecDeque) + -> Result, Box> { + 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 { + 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) + )); + }, + Command::PRIVMSG(ref rec, ref msg) => println!( + "{} -> {}: {}", + rec, + msg, + sender + ), + _ => println!("{}", message.clone().to_string()) + } + return None +} + fn main() { let config_path = args().nth(1) .expect("no config given"); let config = get_config(config_path).expect("Could not get config"); - // Weird workaround for multitrait dyn pointer - // https://github.com/rust-lang/rfcs/issues/2035 - // would be a charm... - - let mut stream: Box; - - if config["tls"].as_bool().unwrap() == true { - stream = Box::new(IrcStream::new(get_tls_stream(&config).unwrap())); - } else { - stream = Box::new(IrcStream::new(get_tcp_stream(&config).unwrap())); - } + 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(); } - stream.flush().unwrap(); + message_queue = get_irc_join_messages(&config, message_queue).unwrap(); + + // Wait for first ping and join channels after sending pong. + // TODO this approach is not very DRY! + loop { + let message = match stream.read() { + Ok(m) => m, + Err(e) => Message::from( + Command::PRIVMSG( + String::from("Error"), + format!("{}", e) + ) + ) + }; + 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; + } + _ => match handle_message(message) { + Some(m) => stream.write(m).unwrap(), + None => () + } + } + } loop { let message = match stream.read() { @@ -170,23 +213,9 @@ fn main() { ) ) }; - let sender = match message.clone().source_nickname() { - Some(s) => String::from(s), - None => String::from("anonymous") - }; - match message.clone().command { - Command::PING(ref data, _) => { - stream.write(Message::from( - Command::PONG(data.to_owned(), None) - )).unwrap(); - }, - Command::PRIVMSG(ref rec, ref msg) => println!( - "{} -> {}: {}", - rec, - msg, - sender - ), - _ => println!("Other message: {}", message.clone().to_string()) + match handle_message(message) { + Some(m) => stream.write(m).unwrap(), + None => () } } }