service
This commit is contained in:
@@ -7,3 +7,4 @@ edition = "2024"
|
||||
clap = { version = "4.5.47", features = ["derive"] }
|
||||
rand = "0.9.2"
|
||||
rodio = "0.21.1"
|
||||
windows-service = "0.8.0"
|
||||
|
||||
149
src/main.rs
149
src/main.rs
@@ -1,32 +1,69 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
use clap::Parser;
|
||||
use rand::prelude::*;
|
||||
use rodio::Decoder;
|
||||
use std::ffi::OsString;
|
||||
use std::{io::Cursor, path::Path, time::Duration};
|
||||
use windows_service::{
|
||||
Result, define_windows_service,
|
||||
service::{ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceType},
|
||||
service::{ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus},
|
||||
service_control_handler::{self, ServiceControlHandlerResult},
|
||||
service_dispatcher,
|
||||
service_manager::{ServiceManager, ServiceManagerAccess},
|
||||
};
|
||||
|
||||
define_windows_service!(ffi_service_main, my_service_main);
|
||||
|
||||
const SERVICE_NAME: &str = "interruptsfx";
|
||||
|
||||
fn my_service_main(arguments: Vec<OsString>) {
|
||||
if let Err(e) = run_service(arguments) {
|
||||
eprintln!("Service failed: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
dynamic_sounds: bool,
|
||||
#[arg(long, default_value_t = 60 * 4)]
|
||||
#[arg(long, default_value_t = 0.1)]
|
||||
volume: f32,
|
||||
#[arg(long, default_value_t = 60 * 30)]
|
||||
min_time_interval_seconds: u64,
|
||||
#[arg(long, default_value_t = 60 * 15)]
|
||||
#[arg(long, default_value_t = 60 * 60 * 3)]
|
||||
max_time_interval_seconds: u64,
|
||||
#[arg(long, default_value_t = String::from("./custom_sounds"))]
|
||||
#[arg(long, default_value_t = String::from(r"C:\Users\Pleb\AppData\Local\Larian Studios\Baldur's Gate 3\Sounds"))]
|
||||
custom_sounds_directory: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
play_audio(&args);
|
||||
if std::env::args().any(|arg| arg == "install") {
|
||||
install_service();
|
||||
return;
|
||||
}
|
||||
|
||||
if std::env::args().any(|arg| arg == "uninstall") {
|
||||
uninstall_service();
|
||||
return;
|
||||
}
|
||||
if let Err(e) = service_dispatcher::start(SERVICE_NAME, ffi_service_main) {
|
||||
eprintln!("Failed to start service: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn play_audio(args: &Args) {
|
||||
use rand::prelude::*;
|
||||
let mut rng = rand::rng();
|
||||
struct AudioData {
|
||||
data: Vec<Cursor<Vec<u8>>>,
|
||||
rng: ThreadRng,
|
||||
sink: rodio::Sink,
|
||||
}
|
||||
|
||||
fn setup_audio(args: &Args) -> AudioData {
|
||||
let rng = rand::rng();
|
||||
|
||||
let stream_handle =
|
||||
rodio::OutputStreamBuilder::open_default_stream().expect("open default audio stream");
|
||||
let sink = rodio::Sink::connect_new(&stream_handle.mixer());
|
||||
let sink = rodio::Sink::connect_new(stream_handle.mixer());
|
||||
sink.set_volume(args.volume);
|
||||
let mut data = vec![
|
||||
Cursor::new(
|
||||
include_bytes!("../sounds/35-roblox-death-sound-variations-in-60-seconds-pt.mp3")
|
||||
@@ -69,7 +106,7 @@ fn play_audio(args: &Args) {
|
||||
Cursor::new(include_bytes!("../sounds/whistle_RjClohy.mp3").to_vec()),
|
||||
];
|
||||
|
||||
if args.dynamic_sounds {
|
||||
if args.custom_sounds_directory.as_str() != "none" {
|
||||
let path = Path::new(&args.custom_sounds_directory);
|
||||
for item in path.read_dir().unwrap() {
|
||||
let item = item.unwrap();
|
||||
@@ -82,13 +119,91 @@ fn play_audio(args: &Args) {
|
||||
}
|
||||
}
|
||||
|
||||
AudioData { data, rng, sink }
|
||||
}
|
||||
|
||||
fn play_audio(args: &Args, audio_data: &mut AudioData) {
|
||||
loop {
|
||||
let i = rng.random_range(0..data.len());
|
||||
let source = Decoder::try_from(data[i].clone()).unwrap();
|
||||
sink.append(source);
|
||||
sink.sleep_until_end();
|
||||
std::thread::sleep(Duration::from_secs(rng.random_range(
|
||||
std::thread::sleep(Duration::from_secs(audio_data.rng.random_range(
|
||||
args.min_time_interval_seconds..args.max_time_interval_seconds,
|
||||
)));
|
||||
let i = audio_data.rng.random_range(0..audio_data.data.len());
|
||||
let source = Decoder::try_from(audio_data.data[i].clone()).unwrap();
|
||||
audio_data.sink.append(source);
|
||||
audio_data.sink.sleep_until_end();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn install_service() {
|
||||
println!("starting to install");
|
||||
let manager =
|
||||
ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CREATE_SERVICE).unwrap();
|
||||
let service_info = ServiceInfo {
|
||||
name: SERVICE_NAME.into(),
|
||||
display_name: "interruptsfx".into(),
|
||||
service_type: ServiceType::OWN_PROCESS,
|
||||
start_type: ServiceStartType::AutoStart,
|
||||
error_control: ServiceErrorControl::Normal,
|
||||
executable_path: std::env::current_exe().unwrap(),
|
||||
launch_arguments: vec![],
|
||||
dependencies: vec![],
|
||||
account_name: None, // Local system account
|
||||
account_password: None,
|
||||
};
|
||||
manager
|
||||
.create_service(&service_info, ServiceAccess::START)
|
||||
.unwrap();
|
||||
println!("Service installed successfully");
|
||||
}
|
||||
pub fn uninstall_service() {
|
||||
println!("starting to uninstall");
|
||||
let manager =
|
||||
ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT).unwrap();
|
||||
let service = manager
|
||||
.open_service(SERVICE_NAME, ServiceAccess::DELETE)
|
||||
.unwrap();
|
||||
service.delete().unwrap();
|
||||
println!("Service uninstalled successfully");
|
||||
}
|
||||
|
||||
fn run_service(args: Vec<OsString>) -> Result<()> {
|
||||
let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel();
|
||||
let status_handle =
|
||||
service_control_handler::register(
|
||||
SERVICE_NAME,
|
||||
move |control_event| match control_event {
|
||||
ServiceControl::Stop => {
|
||||
shutdown_tx.send(()).unwrap();
|
||||
ServiceControlHandlerResult::NoError
|
||||
}
|
||||
_ => ServiceControlHandlerResult::NotImplemented,
|
||||
},
|
||||
)?;
|
||||
status_handle.set_service_status(ServiceStatus {
|
||||
service_type: ServiceType::OWN_PROCESS,
|
||||
current_state: ServiceState::Running,
|
||||
controls_accepted: ServiceControlAccept::STOP,
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
checkpoint: 0,
|
||||
wait_hint: Duration::from_secs(10),
|
||||
process_id: None,
|
||||
})?;
|
||||
let args = Args::parse_from(args);
|
||||
let mut audio_data = setup_audio(&args);
|
||||
loop {
|
||||
play_audio(&args, &mut audio_data);
|
||||
if shutdown_rx.try_recv().is_ok() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
status_handle.set_service_status(ServiceStatus {
|
||||
service_type: ServiceType::OWN_PROCESS,
|
||||
current_state: ServiceState::Stopped,
|
||||
controls_accepted: ServiceControlAccept::empty(),
|
||||
exit_code: ServiceExitCode::Win32(0),
|
||||
checkpoint: 0,
|
||||
wait_hint: Duration::from_secs(0),
|
||||
process_id: None,
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user