Numerous new commands, documentation changews, started with ssl
This commit is contained in:
parent
162d5a42d0
commit
92ee775225
102
Cargo.lock
generated
102
Cargo.lock
generated
|
@ -2,15 +2,111 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "circe"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"openssl",
|
||||
"openssl-sys",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.0.2+3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14a760a11390b1a5daf72074d4f6ff1a6e772534ae191f999f57e9ee8146d1fb"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6517987b3f8226b5da3661dad65ff7f300cc59fb5ea8333ca191fc65fde3edf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
|
@ -71,3 +167,9 @@ name = "unicode-xid"
|
|||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
|
|
@ -11,6 +11,8 @@ exclude = ["src/main.rs", "config.toml"]
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
toml = "0.5.8"
|
||||
serde_derive = "1.0.130"
|
||||
serde = "1.0.130"
|
||||
toml = "0.5"
|
||||
serde_derive = "1.0"
|
||||
serde = "1.0"
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
openssl-sys = "0.9"
|
||||
|
|
431
src/lib.rs
431
src/lib.rs
|
@ -17,11 +17,11 @@
|
|||
//! }
|
||||
//! # break;
|
||||
//! }
|
||||
//!
|
||||
//! # Ok(())
|
||||
//! }
|
||||
|
||||
#![warn(missing_docs)]
|
||||
use openssl::ssl::{SslConnector, SslMethod};
|
||||
use std::borrow::Cow;
|
||||
use std::fs::File;
|
||||
use std::io::{Error, Read, Write};
|
||||
|
@ -33,7 +33,8 @@ use serde_derive::Deserialize;
|
|||
/// An IRC client
|
||||
pub struct Client {
|
||||
config: Config,
|
||||
stream: TcpStream,
|
||||
stream: Option<TcpStream>,
|
||||
sslstream: Option<openssl::ssl::SslStream<TcpStream>>,
|
||||
}
|
||||
|
||||
/// Config for the IRC client
|
||||
|
@ -44,6 +45,7 @@ pub struct Config {
|
|||
mode: Option<String>,
|
||||
nickname: Option<String>,
|
||||
port: u16,
|
||||
ssl: bool,
|
||||
username: String,
|
||||
}
|
||||
|
||||
|
@ -55,27 +57,78 @@ pub enum CapMode {
|
|||
}
|
||||
|
||||
/// IRC commands
|
||||
/// Not reccomended to use, use the helper functions instead
|
||||
#[derive(Debug)]
|
||||
pub enum Command {
|
||||
// TODO:
|
||||
// SERVICE <nickname> <reserved> <distribution> <type> <reserved> <info>
|
||||
// SQUIT <server> <comment>
|
||||
//
|
||||
/// Gets information about the admin of the IRC server.
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.admin("192.168.178.100")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
ADMIN(
|
||||
/// Target
|
||||
String,
|
||||
),
|
||||
/// Sets the user status to AWAY
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.away("AFK")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
AWAY(
|
||||
/// Message
|
||||
String,
|
||||
),
|
||||
#[doc(hidden)]
|
||||
CAP(CapMode),
|
||||
/// Invite user to channel
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.invite("liblirc", "#circe")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
INVITE(
|
||||
/// User
|
||||
String,
|
||||
/// Channel
|
||||
String,
|
||||
),
|
||||
/// Joins a channel
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::JOIN("#main".to_string()))?;
|
||||
/// client.join("#main")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
JOIN(
|
||||
/// Channel
|
||||
String,
|
||||
),
|
||||
/// Lists all channels and their topics
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.list(None, None)?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
LIST(
|
||||
/// Channel
|
||||
Option<String>,
|
||||
// Server
|
||||
Option<String>,
|
||||
),
|
||||
/// Sets the mode of the user
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::MODE("#main".to_string(), Some("+B".to_string())))?;
|
||||
/// client.mode("test", Some("+B"))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
/// If the MODE is not given (e.g. None), then the client will send "MODE target"
|
||||
|
@ -85,18 +138,49 @@ pub enum Command {
|
|||
/// Mode
|
||||
Option<String>,
|
||||
),
|
||||
/// List all nicknames visiable to the Client
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.names("#main,#circe", None)?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
NAMES(
|
||||
/// Channel
|
||||
String,
|
||||
/// Server to foreward request to
|
||||
Option<String>,
|
||||
),
|
||||
#[doc(hidden)]
|
||||
NICK(String),
|
||||
/// Attempts to identify as a channel operator
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.oper("circe", "foo")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
OPER(
|
||||
/// Username
|
||||
String,
|
||||
/// Password
|
||||
String,
|
||||
),
|
||||
/// Everything that is not a command
|
||||
OTHER(String),
|
||||
/// Leaves channels
|
||||
/// Leave a channel
|
||||
/// ```no_run
|
||||
/// # use circe::*
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_part(Command::PART("#main".to_string()))?;
|
||||
/// client.part("#main")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
PART(String),
|
||||
PART(
|
||||
/// Target
|
||||
String,
|
||||
),
|
||||
#[doc(hidden)]
|
||||
PASS(String),
|
||||
#[doc(hidden)]
|
||||
PING(String),
|
||||
#[doc(hidden)]
|
||||
|
@ -105,7 +189,7 @@ pub enum Command {
|
|||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::PRIVMSG("#main".to_string(), "This is an example message".to_string()))?;
|
||||
/// client.privmsg("This is an example message")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
PRIVMSG(
|
||||
|
@ -118,10 +202,26 @@ pub enum Command {
|
|||
/// ```no_run
|
||||
/// # use circe::*
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::QUIT("Leaving...".to_string()))?;
|
||||
/// client.quit(Some("Leaving..."))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
QUIT(String),
|
||||
QUIT(
|
||||
/// Leave message
|
||||
String,
|
||||
),
|
||||
/// Sets or gets the topic of a channel
|
||||
/// ```no_run
|
||||
/// # use circe::*
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.topic("#main", Some("main channel"))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
TOPIC(
|
||||
/// Channel
|
||||
String,
|
||||
/// Topic
|
||||
Option<String>,
|
||||
),
|
||||
#[doc(hidden)]
|
||||
USER(String, String, String, String),
|
||||
}
|
||||
|
@ -135,9 +235,9 @@ impl Command {
|
|||
return Self::PONG(command);
|
||||
} else if new.contains("PRIVMSG") {
|
||||
let parts: Vec<&str> = new.split_whitespace().collect();
|
||||
|
||||
let target = parts[2];
|
||||
let mut builder = String::new();
|
||||
|
||||
for part in parts[3..].to_vec() {
|
||||
builder.push_str(&format!("{} ", part));
|
||||
}
|
||||
|
@ -150,7 +250,7 @@ impl Command {
|
|||
}
|
||||
|
||||
impl Client {
|
||||
/// Creates a new client with a given config
|
||||
/// Creates a new client with a given [`Config`].
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let config = Config::from_toml("config.toml")?;
|
||||
|
@ -158,21 +258,36 @@ impl Client {
|
|||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// Errors if the client could not connect to the given host.
|
||||
/// Returns error if the client could not connect to the host.
|
||||
pub fn new(config: Config) -> Result<Self, Error> {
|
||||
let stream = TcpStream::connect(format!("{}:{}", config.host, config.port))?;
|
||||
Ok(Self { stream, config })
|
||||
let sslstream: openssl::ssl::SslStream<TcpStream>;
|
||||
|
||||
if config.ssl {
|
||||
let connector = SslConnector::builder(SslMethod::tls())?.build();
|
||||
sslstream = connector.connect(config.host.as_str(), stream).unwrap();
|
||||
return Ok(Self {
|
||||
config,
|
||||
stream: None,
|
||||
sslstream: Some(sslstream),
|
||||
});
|
||||
} else {
|
||||
return Ok(Self {
|
||||
config,
|
||||
stream: Some(stream),
|
||||
sslstream: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Identify user and join the specified channels
|
||||
/// Identify user and joins the in the [`Config`] specified channels.
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.identify()?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// Errors if the client could not write to the stream.
|
||||
/// Returns error if the client could not write to the stream.
|
||||
pub fn identify(&mut self) -> Result<(), Error> {
|
||||
self.write_command(Command::CAP(CapMode::END))?;
|
||||
self.write_command(Command::USER(
|
||||
|
@ -214,15 +329,22 @@ impl Client {
|
|||
fn read_string(&mut self) -> Option<String> {
|
||||
let mut buffer = [0u8; 512];
|
||||
|
||||
match self.stream.read(&mut buffer) {
|
||||
Ok(_) => {}
|
||||
Err(_) => return None,
|
||||
};
|
||||
if self.config.ssl {
|
||||
match self.sslstream.unwrap().read(&mut buffer) {
|
||||
Ok(_) => {}
|
||||
Err(_) => return None,
|
||||
};
|
||||
} else {
|
||||
match self.stream.as_ref().unwrap().read(&mut buffer) {
|
||||
Ok(_) => {}
|
||||
Err(_) => return None,
|
||||
};
|
||||
}
|
||||
|
||||
Some(String::from_utf8_lossy(&buffer).into())
|
||||
}
|
||||
|
||||
/// Read data coming from the IRC as a [`Command`]
|
||||
/// Read data coming from the IRC as a [`Command`].
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # fn main() -> Result<(), std::io::Error> {
|
||||
|
@ -236,8 +358,7 @@ impl Client {
|
|||
/// # Ok::<(), std::io::Error>(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Errors if there are no new messages. This should not be taken as an actual Error, because nothing went wrong.
|
||||
/// Returns error if there are no new messages. This should not be taken as an actual error, because nothing went wrong.
|
||||
pub fn read(&mut self) -> Result<Command, ()> {
|
||||
if let Some(string) = self.read_string() {
|
||||
return Ok(Command::from_str(&string));
|
||||
|
@ -249,26 +370,40 @@ impl Client {
|
|||
fn write(&mut self, data: &str) -> Result<(), Error> {
|
||||
let formatted = {
|
||||
let new = format!("{}\r\n", data);
|
||||
|
||||
Cow::Owned(new) as Cow<str>
|
||||
};
|
||||
self.stream.write(formatted.as_bytes())?;
|
||||
if self.config.ssl {
|
||||
self.sslstream
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.write(formatted.as_bytes())?;
|
||||
} else {
|
||||
self.stream.as_ref().unwrap().write(formatted.as_bytes())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a [`Command`] to the IRC
|
||||
/// Send a [`Command`] to the IRC.<br>
|
||||
/// Not reccomended to use, use the helper functions instead.
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::PRIVMSG("#main".to_string(), "Hello".to_string()))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// Errors if the stream could not write.
|
||||
/// Returns error if the client could not write to the stream.
|
||||
pub fn write_command(&mut self, command: Command) -> Result<(), Error> {
|
||||
use Command::*;
|
||||
let computed = match command {
|
||||
ADMIN(target) => {
|
||||
let formatted = format!("ADMIN {}", target);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
AWAY(message) => {
|
||||
let formatted = format!("AWAY {}", message);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
CAP(mode) => {
|
||||
use CapMode::*;
|
||||
Cow::Borrowed(match mode {
|
||||
|
@ -276,10 +411,34 @@ impl Client {
|
|||
END => "CAP END",
|
||||
}) as Cow<str>
|
||||
}
|
||||
INVITE(username, channel) => {
|
||||
let formatted = format!("INVITE {} {}", username, channel);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
JOIN(channel) => {
|
||||
let formatted = format!("JOIN {}", channel);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
LIST(channel, server) => {
|
||||
let mut formatted = "LIST".to_string();
|
||||
if let Some(channel) = channel {
|
||||
formatted.push_str(format!(" {}", channel).as_str());
|
||||
}
|
||||
if let Some(server) = server {
|
||||
formatted.push_str(format!(" {}", server).as_str());
|
||||
}
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
NAMES(channel, server) => {
|
||||
let formatted = {
|
||||
if let Some(server) = server {
|
||||
format!("NAMES {} {}", channel, server)
|
||||
} else {
|
||||
format!("NAMES {}", channel)
|
||||
}
|
||||
};
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
NICK(nickname) => {
|
||||
let formatted = format!("NICK {}", nickname);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
|
@ -292,7 +451,10 @@ impl Client {
|
|||
format!("MODE {}", target)
|
||||
}
|
||||
};
|
||||
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
OPER(nick, password) => {
|
||||
let formatted = format!("OPER {} {}", nick, password);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
OTHER(_) => {
|
||||
|
@ -305,6 +467,10 @@ impl Client {
|
|||
let formatted = format!("PART {}", target);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
PASS(password) => {
|
||||
let formatted = format!("PASS {}", password);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
PING(target) => {
|
||||
let formatted = format!("PING {}", target);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
|
@ -318,7 +484,17 @@ impl Client {
|
|||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
QUIT(message) => {
|
||||
let formatted = format!("QUIT {}", message);
|
||||
let formatted = format!("QUIT :{}", message);
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
TOPIC(channel, topic) => {
|
||||
let formatted = {
|
||||
if let Some(topic) = topic {
|
||||
format!("TOPIC {} :{}", channel, topic)
|
||||
} else {
|
||||
format!("TOPIC {}", channel)
|
||||
}
|
||||
};
|
||||
Cow::Owned(formatted) as Cow<str>
|
||||
}
|
||||
USER(username, s1, s2, realname) => {
|
||||
|
@ -331,70 +507,121 @@ impl Client {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
// Helper commands
|
||||
|
||||
/// Helper function for requesting information about the ADMIN of an IRC server
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.admin("192.168.178.100")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn admin(&mut self, target: &str) -> Result<(), Error> {
|
||||
self.write_command(Command::ADMIN(target.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for setting the users status to AWAY
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.away("AFK")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn away(&mut self, message: &str) -> Result<(), Error> {
|
||||
self.write_command(Command::AWAY(message.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for sending PRIVMSGs.
|
||||
/// This makes
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.send_privmsg("#main", "Hello")?;
|
||||
/// client.privmsg("#main", "Hello")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// equivalent to
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::PRIVMSG("#main".to_string(), "Hello".to_string()))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn send_privmsg(&mut self, channel: &str, message: &str) -> Result<(), Error> {
|
||||
pub fn privmsg(&mut self, channel: &str, message: &str) -> Result<(), Error> {
|
||||
self.write_command(Command::PRIVMSG(channel.to_string(), message.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function to INVITE people to a channels
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.invite("liblirc", "#circe")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn invite(&mut self, username: &str, channel: &str) -> Result<(), Error> {
|
||||
self.write_command(Command::INVITE(username.to_string(), channel.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for sending JOINs.
|
||||
/// This makes
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.send_join("#main")?;
|
||||
/// client.join("#main")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// equivalent to
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::JOIN("#main".to_string()))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn send_join(&mut self, channel: &str) -> Result<(), Error> {
|
||||
pub fn join(&mut self, channel: &str) -> Result<(), Error> {
|
||||
self.write_command(Command::JOIN(channel.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for LISTing channels and modes
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.list(None, None)?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn list(&mut self, channel: Option<&str>, server: Option<&str>) -> Result<(), Error> {
|
||||
let channel_config = {
|
||||
if let Some(channel) = channel {
|
||||
Some(channel.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let server_config = {
|
||||
if let Some(server) = server {
|
||||
Some(server.to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
self.write_command(Command::LIST(channel_config, server_config))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for getting all nicknames in a channel
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.names("#main,#circe", None)?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn names(&mut self, channel: &str, server: Option<&str>) -> Result<(), Error> {
|
||||
if let Some(server) = server {
|
||||
self.write_command(Command::NAMES(
|
||||
channel.to_string(),
|
||||
Some(server.to_string()),
|
||||
))?;
|
||||
} else {
|
||||
self.write_command(Command::NAMES(channel.to_string(), None))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for sending MODEs.
|
||||
/// This makes
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.send_mode("test", Some("+B"))?;
|
||||
/// client.mode("test", Some("+B"))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// equivalent to
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_command(Command::MODE("test".to_string(), Some("+B".to_string())))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn send_mode(&mut self, target: &str, mode: Option<&str>) -> Result<(), Error> {
|
||||
pub fn mode(&mut self, target: &str, mode: Option<&str>) -> Result<(), Error> {
|
||||
if let Some(mode) = mode {
|
||||
self.write_command(Command::MODE(target.to_string(), Some(mode.to_string())))?;
|
||||
} else {
|
||||
|
@ -404,54 +631,54 @@ impl Client {
|
|||
}
|
||||
|
||||
/// Helper function for leaving channels.
|
||||
/// This makes
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.send_part("#main")?;
|
||||
/// client.part("#main")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// equivalent to
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use circe::*
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_part(Command::PART("#main".to_string()))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn send_part(&mut self, target: &str) -> Result<(), Error> {
|
||||
pub fn part(&mut self, target: &str) -> Result<(), Error> {
|
||||
self.write_command(Command::PART(target.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for leaving the IRC server and shutting down the TCP stream afterwards.
|
||||
/// This makes
|
||||
/// Helper function for setting or getting the topic of a channel
|
||||
/// ```no_run
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.send_quit(None)?;
|
||||
/// client.topic("#main", Some("main channel"))?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// equivalent to
|
||||
///
|
||||
pub fn topic(&mut self, channel: &str, topic: Option<&str>) -> Result<(), Error> {
|
||||
if let Some(topic) = topic {
|
||||
self.write_command(Command::TOPIC(channel.to_string(), Some(topic.to_string())))?;
|
||||
} else {
|
||||
self.write_command(Command::TOPIC(channel.to_string(), None))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper function for leaving the IRC server and shutting down the TCP stream afterwards.
|
||||
/// ```no_run
|
||||
/// # use circe::*
|
||||
/// # use circe::*;
|
||||
/// # let mut client = Client::new(Config::from_toml("config.toml")?)?;
|
||||
/// client.write_part(Command::QUIT("Leaving...".to_string()))?;
|
||||
/// client.stream.shutdown(std::net::Shutdown::Both)?;
|
||||
/// client.quit(None)?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
pub fn send_quit(&mut self, message: Option<&str>) -> Result<(), Error> {
|
||||
pub fn quit(&mut self, message: Option<&str>) -> Result<(), Error> {
|
||||
if let Some(message) = message {
|
||||
self.write_command(Command::QUIT(message.to_string()))?;
|
||||
} else {
|
||||
self.write_command(Command::QUIT(
|
||||
format!("circe {} (https://crates.io/crates/circe)", env!("CARGO_PKG_VERSION")),
|
||||
))?;
|
||||
self.write_command(Command::QUIT(format!(
|
||||
"circe {} (https://crates.io/crates/circe)",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
)))?;
|
||||
}
|
||||
if self.config.ssl {
|
||||
self.sslstream.as_ref().unwrap().shutdown().unwrap();
|
||||
} else {
|
||||
self.stream.unwrap().shutdown(std::net::Shutdown::Both)?;
|
||||
}
|
||||
self.stream.shutdown(std::net::Shutdown::Both)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -466,7 +693,7 @@ impl Config {
|
|||
/// vec!["#main", "#circe"],
|
||||
/// "192.168.178.100",
|
||||
/// Some("+B"),
|
||||
/// Some("IRSC"),
|
||||
/// Some("circe"),
|
||||
/// 6667,
|
||||
/// "circe",
|
||||
/// );
|
||||
|
@ -477,21 +704,22 @@ impl Config {
|
|||
mode: Option<&'static str>,
|
||||
nickname: Option<&'static str>,
|
||||
port: u16,
|
||||
ssl: bool,
|
||||
username: &str,
|
||||
) -> Self {
|
||||
// Conversion from &'static str to String
|
||||
let channels_config = channels.iter().map(|channel| channel.to_string()).collect();
|
||||
|
||||
let mode_config: Option<String>;
|
||||
if mode.is_some() {
|
||||
mode_config = Some(mode.unwrap().to_string());
|
||||
if let Some(mode) = mode {
|
||||
mode_config = Some(mode.to_string());
|
||||
} else {
|
||||
mode_config = None;
|
||||
}
|
||||
|
||||
let nickname_config: Option<String>;
|
||||
if nickname.is_some() {
|
||||
nickname_config = Some(nickname.unwrap().to_string());
|
||||
if let Some(nickname) = nickname {
|
||||
nickname_config = Some(nickname.to_string());
|
||||
} else {
|
||||
nickname_config = Some(username.to_string());
|
||||
}
|
||||
|
@ -502,6 +730,7 @@ impl Config {
|
|||
mode: mode_config,
|
||||
nickname: nickname_config,
|
||||
port,
|
||||
ssl,
|
||||
username: username.into(),
|
||||
}
|
||||
}
|
||||
|
@ -512,7 +741,6 @@ impl Config {
|
|||
/// let config = Config::from_toml("config.toml")?;
|
||||
/// # Ok::<(), std::io::Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// ```toml
|
||||
/// channels = ["#main", "#main2"]
|
||||
/// host = "192.168.178.100"
|
||||
|
@ -521,7 +749,6 @@ impl Config {
|
|||
/// port = 6667
|
||||
/// username = "circe"
|
||||
/// ```
|
||||
///
|
||||
/// Returns an Error if the file cannot be opened or if the TOML is invalid
|
||||
pub fn from_toml<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
|
||||
let mut file = File::open(&path)?;
|
||||
|
|
|
@ -29,12 +29,12 @@ fn main() -> Result<(), std::io::Error> {
|
|||
}
|
||||
if let Command::PRIVMSG(channel, message) = command {
|
||||
if message.contains("!quit") {
|
||||
client.send_quit(None)?;
|
||||
client.quit(None)?;
|
||||
break;
|
||||
}
|
||||
|
||||
if message.contains("!close") {
|
||||
client.send_part("#main")?;
|
||||
client.part("#main")?;
|
||||
}
|
||||
println!("PRIVMSG received: {} {}", channel, message);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue