diff --git a/.gitignore b/.gitignore index ea8c4bf..07c12f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +result diff --git a/Cargo.lock b/Cargo.lock index b18b513..f16cd47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.21" @@ -251,6 +260,19 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link 0.2.1", +] + [[package]] name = "clap" version = "4.5.57" @@ -306,6 +328,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -500,11 +528,36 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" name = "hydration-notifier" version = "0.1.0" dependencies = [ + "chrono", "clap", "notify-rust", "rand", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "id-arena" version = "2.3.0" @@ -616,6 +669,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "objc2" version = "0.6.3" diff --git a/Cargo.toml b/Cargo.toml index b7f6309..1f1f420 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +chrono = "0.4.45" clap = { version = "4.5.53", features = ["derive"] } notify-rust = { version = "4.12.0", features = ["serde", "zbus"]} rand = { version = "0.9.2", features = ["thread_rng"]} diff --git a/README.md b/README.md index 9858838..a871c08 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,12 @@ A simple service that reminds you to drink water at regular intervals via notifi services.hydration-notifier = { enable = true; + # Optional settings interval = 1800 ; # 30 min duration = 10; + dailyTarget = 2000; + dayStart = 21600; # 6h + dayEnd = 75600; # 21h }; } ``` diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..396f074 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1785858998, + "narHash": "sha256-fKCq5jphd6l/Ms6gc3dptkw/TcKLYub9lQE5g6rbbkc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "04607e1165ac22c5fde6dcc54c9e0b3c0487c555", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-26.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix index a1a82da..1116e48 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; - outputs = { nixpkgs, ... }: + outputs = { nixpkgs, self, ... }: let system = "x86_64-linux"; @@ -11,7 +11,10 @@ package = pkgs.rustPlatform.buildRustPackage { pname = "hydration-notifier"; - version = "0.1.0"; + version = + if self ? rev + then builtins.substring 0 7 self.rev + else "dirty"; src = ./.; diff --git a/module.nix b/module.nix index e0720c1..3c28a7e 100644 --- a/module.nix +++ b/module.nix @@ -11,7 +11,7 @@ interval = lib.mkOption { type = lib.types.int; - default = 1800; + default = 60 * 30; description = "Notification interval in seconds."; }; @@ -20,6 +20,25 @@ default = 10; description = "Duration of the notification."; }; + + dailyTarget = lib.mkOption { + type = lib.types.int; + default = 2000; + description = "Daily target in milliliters."; + }; + + dayStart = lib.mkOption { + type = lib.types.int; + default = 6 * 60 * 60; + description = "Day start in seconds."; + }; + + dayEnd = lib.mkOption { + type = lib.types.int; + default = 21 * 60 * 60; + description = "Day end in seconds."; + }; + }; config = lib.mkIf cfg.enable { @@ -34,7 +53,7 @@ ]; serviceConfig = { - ExecStart = "${package}/bin/hydration-notifier -i ${toString cfg.interval} -d ${toString cfg.duration}"; + ExecStart = "${package}/bin/hydration-notifier -i ${toString cfg.interval} -d ${toString cfg.duration} --daily-target ${toString cfg.dailyTarget} --day-start ${toString cfg.dayStart} --day-end ${toString cfg.dayEnd}"; Restart = "always"; RestartSec = 5; }; diff --git a/src/main.rs b/src/main.rs index 1b1f2a3..b86c320 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ use std::{thread, time::Duration}; +use chrono::{Local, Timelike}; use clap::Parser; use notify_rust::Notification; use rand::Rng; -const MESSAGES: [&str; 20] = [ +const STATIC_MESSAGES: [&str; 20] = [ "Don't forget to hydrate!", "Time for a sip!", "Water break!", @@ -27,6 +28,19 @@ const MESSAGES: [&str; 20] = [ "Even your code runs smoother with water.", ]; +const DYNAMIC_MESSAGES: [&str; 10] = [ + "Plot twist: you should already be at %s L.", + "Your water bar should read %s L.", + "%s L unlocked? If not, time to sip.", + "Achievement target: %s L consumed.", + "Your hydration XP target: %s L.", + "Your future self is waiting for %s L.", + "Your kidneys vote for %s L.", + "%s L is calling your name.", + "Still below %s L? Let's fix that.", + "The water boss requires %s L.", +]; + #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { @@ -35,6 +49,15 @@ struct Args { #[arg(short, long, default_value_t = 10)] duration_seconds: u64, + + #[arg(long = "daily-target", default_value_t = 2000)] + daily_target_mililiter: u32, + + #[arg(long = "day-start", default_value_t = 6 * 60 * 60)] + day_start_seconds: u32, + + #[arg(long = "day-end", default_value_t = 21 * 60 * 60)] + day_end_seconds: u32, } fn main() { @@ -42,11 +65,26 @@ fn main() { let mut rng = rand::rng(); loop { + let message = if rng.random_bool(0.5) { + let message = DYNAMIC_MESSAGES[rng.random_range(0..DYNAMIC_MESSAGES.len())]; + + let seconds_passed = Local::now().num_seconds_from_midnight(); + let passed_percentage = (seconds_passed - args.day_start_seconds) as f64 / (args.day_end_seconds - args.day_start_seconds) as f64; + + if (0.0..=1.0).contains(&passed_percentage) { + &message.replace("%s", &format!("{:.1}", args.daily_target_mililiter as f64 * passed_percentage / 1000.0)) + } else { + STATIC_MESSAGES[rng.random_range(0..STATIC_MESSAGES.len())] + } + } else { + STATIC_MESSAGES[rng.random_range(0..STATIC_MESSAGES.len())] + }; + if let Err(e) = Notification::new() .appname("Hydration Notifier") .summary("Drink Water") .timeout(Duration::from_secs(args.duration_seconds)) - .body(MESSAGES[rng.random_range(0..MESSAGES.len())]) + .body(message) .show() { eprintln!("Failed to show notification: {}", e);