diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ebe80ca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+
+target
+scripts
+Cargo.lock
\ No newline at end of file
diff --git a/ABOUT.md b/ABOUT.md
new file mode 100644
index 0000000..cebe1ca
--- /dev/null
+++ b/ABOUT.md
@@ -0,0 +1,12 @@
+
+# Rule 20 20 20.
+This is a utility software to make you pause what you are doing every X minutes, which are passed from command line.
+
+The app waits X minutes then warnings the user by playing Half Life game wav sounds to look away from the computer or screen for 20 seconds long. You can also keep your eyes closed during the activation of 20 20 20 rule.
+
+This is a great tool for keeping your eye healthy as a developer.
+
+Personally I've used this software for about 1 year and the state of my eyes improved dramatically.
+
+Read more about 20 20 20 rule:
+ https://www.healthline.com/health/eye-health/20-20-20-rule#definition
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..c0a963a
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "rule_202020"
+version = "0.0.1"
+edition = "2021"
+
+default-run = "rule_202020"
+
+[[bin]]
+name = "rule_202020"
+path = "src/bin/main.rs"
+
+
+[dependencies]
+core-dev = { path = "../core-dev", features = [
+"datetime", "audio", "core", "linuxapi", "image"] }
+notify-rust = { version = "4.5.8", features = ["images"] }
+xdg-utils = "0.4.0"
+ctrlc = { version = "3.2.1", features = ["termination"] }
+open = "2.1.1"
+chrono = "0.4.19"
+time = { version = "0.3.9", features = ["parsing"] }
+clap = { version = "3.1.8", features = ["derive"] }
+ansi_term = { version = "0.12.1", features = ["derive_serde_style", "serde"] }
+serde = { version = "1.0.136", features = ["derive"] }
+serde_json = "1.0.79"
+color-backtrace = "0.5.1"
+soloud = "1.0.2"
+image = "0.24.1"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e5d0f4d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Rule 20 20 20
+This is a utility software to make you pause what you are doing every X minutes, which are passed from command line.
+
+The app waits X minutes then warnings the user by playing Half Life game wav sounds to look away from the computer or screen for 20 seconds long. You can also keep your eyes closed during the activation of 20 20 20 rule.
+
+This is a great tool for keeping your eye healthy as a developer.
+
+Personally I've used this software for about 1 year and the state of my eyes improved dramatically.
+
+Read more about [`20 20 20 rule`](https://www.healthline.com/health/eye-health/20-20-20-rule#definition)
+
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..991be61
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,22 @@
+
+
+# TODO
+
+great todos for improvement
+
+- [x] load the audio files into the binary (binary bloat)
+- [x] load the notification icon from the binary (binary bloat 2)
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
+- [ ]
\ No newline at end of file
diff --git a/src/202020Rule.py b/src/202020Rule.py
new file mode 100755
index 0000000..796c3ce
--- /dev/null
+++ b/src/202020Rule.py
@@ -0,0 +1,384 @@
+#!/usr/bin/python3
+"""
+ 202020 order helpful application
+ for eye health stabilitty
+"""
+
+from collections import namedtuple
+from datetime import datetime
+
+
+TimeInterval = namedtuple("TimeIntervals", ["name", "seconds"])
+TimeIntervals = [
+ TimeInterval(name=_name, seconds=_value) for _name, _value in (
+ ("millennia", 60 * 60 * 24 * 365 * 1000),
+ ("century", 60 * 60 * 24 * 365 * 100),
+ ("decade", 60 * 60 * 24 * 365 * 10),
+ ("year", 60 * 60 * 24 * 365),
+ ("week", 60 * 60 * 24 * 7),
+ ("day", 60 * 60 * 24),
+ ("hour", 60 * 60),
+ ("minute", 60),
+ ("second", 1)
+ )
+]
+
+
+def seconds_to_time(seconds):
+ """
+ example of result:
+ Time(
+ millennials=0,
+ centuries=0,
+ decades=5,
+ years=1,
+ weeks=9,
+ days=2,
+ hours=0,
+ minutes=38,
+ seconds=5
+ )
+ you can select whatever you want from this named tuple
+ """
+ if type(seconds) not in [str, int, float]:
+ raise TypeError(f"seconds: {type(seconds)}; not int or str")
+
+ seconds = int(seconds)
+ intervals = ["millennials", "centuries", "decades", "years", "weeks", "days", "hours", "minutes", "seconds"]
+ TimeDict = dict(zip(intervals, [0] * len(intervals)))
+ # {'millennials': 0, 'centuries': 0, 'decades': 0, 'years': 0, 'days': 0, 'hours': 0, 'minutes': 0, 'seconds': 0}
+
+ for (_, _seconds), k in zip(TimeIntervals, intervals):
+ result = seconds // _seconds
+ TimeDict[k] = result
+ seconds -= result * _seconds
+
+ for i, inter in enumerate(intervals):
+ if TimeDict[inter] != 0:
+ values = list(TimeDict.values())[i:]
+ return namedtuple("Time", intervals[i:])(*values)
+
+ return namedtuple("Time", "seconds")(seconds)
+
+
+
+def get_current_time(__format="%H:%M:%S"):
+ return datetime.now().strftime(__format)
+
+def get_current_datetime(__format="%d.%m.%Y-%H:%M:%S"):
+ return datetime.now().strftime(__format)
+
+
+endc_effect = "\033[0m"
+bold_effect = "\033[1m"
+underlined_effect = "\033[4m"
+reversed_effect = "\u001b[7m"
+ansi_codes = {
+ 'purple': '\033[95m',
+ 'blue': '\033[94m',
+ 'green': '\033[92m',
+ 'yellow': '\033[93m',
+ 'red': '\033[91m',
+ 'endc': '\033[0m',
+ 'bold': '\033[1m',
+ 'underlined': '\033[4m',
+ 'white': "\u001b[37;1m",
+ "cyan": '\x1b[38;5;44m',
+ "darkcyan": '\033[36m',
+ "magenta": "\033[35m",
+ "black": "\033[30m",
+ "grey": "\x1b[38;5;246m",
+ "orange": "\x1b[38;5;208m"
+}
+
+def ConsoleColored(string, color, bold=0, underlined=0):
+ if type(string) != str:
+ string = str(string)
+
+
+ # incorrect color
+ if color not in ansi_codes and color != 'random':
+ raise TypeError
+
+ # bold == 0 and underlined == 0
+ if not bold and not underlined:
+ if color == "random":
+ from random import choice
+ return ansi_codes[choice(list(ansi_codes.keys()))] + string + endc_effect
+
+ return ansi_codes[color] + string + endc_effect
+
+ # bold == 0 and underlined == 1
+ elif not bold and underlined:
+ if color == "random":
+ from random import choice
+ return ansi_codes[choice(list(ansi_codes.keys()))] + \
+ ansi_codes["underlined"] + string + endc_effect
+
+ return ansi_codes[color] + ansi_codes["underlined"] + string + endc_effect
+
+ # bold == 1 and underlined == 0
+ elif bold and not underlined:
+ if color == "random":
+ from random import choice
+ return ansi_codes[choice(list(ansi_codes.keys()))] + \
+ ansi_codes["bold"] + string + endc_effect
+
+ return ansi_codes[color] + ansi_codes["bold"] + string + endc_effect
+
+ # bold == 1 and underlined == 1
+ if color == "random":
+ from random import choice
+ return ansi_codes[choice(list(ansi_codes.keys()))] + \
+ ansi_codes["bold"] + ansi_codes["underlined"] + string + endc_effect
+
+ return ansi_codes[color] + ansi_codes["bold"] + ansi_codes["underlined"] + string + endc_effect
+
+
+def yellow_bold(__string):
+ return ConsoleColored(__string, "yellow", bold=1)
+
+
+def red_bold(__string):
+ return ConsoleColored(__string, "red", bold=1)
+
+
+
+import json
+def read_json_from_file(__path: str):
+ """ reads .json from @path"""
+
+ if isinstance(__path, str):
+ with open(__path, "r+", encoding="utf-8") as _json:
+ return json.loads(_json.read())
+
+ elif isinstance(__path, Path):
+ return json.loads(__path.read_text())
+
+ else:
+ raise TypeError(
+ f"{__path} is not type str; type({__path})=={type(__path)}")
+
+
+# pypi
+import subprocess
+from time import sleep
+try:
+ from playsound import playsound
+except ImportError:
+ process = subprocess.Popen("pip3 install playsound", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ output, _ = process.communicate()
+ output = output.decode("utf-8")
+ print(output)
+ sleep(2)
+
+ try:
+ from playsound import playsound
+ except ImportError:
+ print("it was installed, but importing after install from code doesnt work")
+
+# pip3 install playsound
+# or
+# python3.7 -m pip install playsound
+
+# python
+import os
+import sys
+from pathlib import Path
+
+
+from string import Template
+
+notify_send_template = Template(
+ "notify-send '${title}' '${message}'"
+ " --icon=${icon_path}"
+)
+
+notify_send_without_icon_template = Template(
+ "notify-send '${title}' '${message}"
+)
+
+
+
+def linux_notification(title, message, icon_path=None):
+ if icon_path:
+ subprocess.call(notify_send_template.safe_substitute(
+ title=title,
+ message=message,
+ icon_path=icon_path
+ ), shell=True)
+ else:
+ subprocess.call(notify_send_without_icon_template.safe_substitute(
+ title=title,
+ message=message,
+ ), shell=True)
+
+
+__appname__ = "202020Rule"
+
+assets_folder = Path("assets")
+
+sounds_json = read_json_from_file(assets_folder / "sounds" / "sounds.json")
+
+rule_202020_icon_file = assets_folder / "icons" / "202020-order-icon.png"
+
+
+
+# project_logs_folder = Path("logs")
+# project_logs_folder.mkdir(exist_ok=1)
+#
+# # stream handler
+# stream_handler_FaceCamLog = logging__.get_stream_handler()
+# # file handler
+# file_handler_FaceCamLog = logging__.get_file_handler_with_datetime(
+# project_logs_folder.as_posix(),
+# "announcements"
+# )
+# # logger
+# logger_20_20_20 = logging__.Loggerr(
+# name="202020Rule.py",
+# file_handler=file_handler_FaceCamLog,
+# stream_handler=stream_handler_FaceCamLog
+# )
+# logger_20_20_20.info(f"application {__appname__} loaded.")
+
+
+def comma_sound():
+ playsound(sounds_json["comma"])
+
+def warning_sound():
+ playsound(sounds_json["warning"])
+
+def bizwarn_sound():
+ playsound(sounds_json["bizwarn"])
+
+def number_sound(number):
+ playsound(sounds_json[number])
+
+def order_sound():
+ playsound(sounds_json["order"])
+
+def rule_202020_sound():
+ number_sound("20")
+ number_sound("20")
+ number_sound("20")
+ order_sound()
+
+
+
+
+def countdown(start=20, color="yellow", __pause=.4):
+ if start > 20:
+ raise ValueError("this cannot be > 10")
+
+ for i in range(start, -1, -1):
+ if i > 9:
+ print("[ {} ]".format(ConsoleColored(str(i), color, bold=1)))
+ else:
+ print("[ {} ]".format(ConsoleColored(str(i), color, bold=1)))
+
+ playsound(sounds_json[str(i)])
+ sleep(__pause)
+
+
+def Rule202020(total_time_seconds):
+ for _seconds in range(total_time_seconds, -1, -1):
+ time_left = seconds_to_time(_seconds)
+ seconds = time_left.seconds
+
+ try:
+ minutes = time_left.minutes
+ if minutes < 10:
+ minutes = f"0{minutes}"
+
+ except AttributeError:
+ minutes = "00"
+
+ try:
+ hours = time_left.hours
+ if hours < 10:
+ hours = f"0{hours}"
+
+ except AttributeError:
+ hours = "00"
+
+
+ if seconds < 10:
+ seconds = f"0{seconds}"
+
+ time_left = yellow_bold(f"{hours}:{minutes}:{seconds}")
+ print(f"Time Left: [ {time_left} ]", end="\r")
+ sleep(1)
+
+ print(f"\n\n{red_bold('20 20 20 ORDER')} !!! ( {get_current_time()} )")
+
+ # warning before announcement
+ for _ in range(3):
+ warning_sound()
+ comma_sound()
+ bizwarn_sound()
+
+ rule_202020_sound()
+
+ linux_notification(
+ "20 20 20 ORDER",
+ "ITS TIME NOW!!",
+ rule_202020_icon_file.absolute().as_posix()
+ )
+ print(f"notification sent at {get_current_datetime()}")
+
+ countdown(20, "red", 1)
+ print("\n")
+
+
+
+
+# TODO: add key listener for reloading the 20 20 20 rule
+# control + r should be the one
+
+
+import platform
+
+if __name__ == '__main__':
+ os.system("clear")
+
+ print("========== 202020 Rule ==========\n")
+ print(f"python3-script: {__file__}")
+ print(f"script-folder: {os.getcwd()}\n")
+ print(f"python3 --version: {platform.python_version()}\n\n")
+
+
+ program_arguments = sys.argv
+ print(f"Args: {program_arguments}")
+
+ if len(program_arguments) >= 2:
+ total_time_seconds = 0
+
+ if "-s" in program_arguments:
+ seconds_arg_position = program_arguments.index("-s")
+ total_time_seconds += int(program_arguments[seconds_arg_position + 1])
+
+ if "-m" in program_arguments:
+ minutes_arg_position = program_arguments.index("-m")
+ total_time_seconds += int(program_arguments[minutes_arg_position + 1]) * 60
+
+ if "-h" in program_arguments:
+ hours_arg_position = program_arguments.index("-h")
+ total_time_seconds += int(program_arguments[hours_arg_position + 1]) * 3600
+
+ else:
+ total_time_seconds = 18 * 60
+
+
+ print()
+ while 1:
+ try:
+ Rule202020(total_time_seconds)
+ except KeyboardInterrupt:
+ print()
+ break
+
+
+ except BaseException as err:
+ print(err)
+ break
diff --git a/src/bin/main.rs b/src/bin/main.rs
new file mode 100644
index 0000000..7bc4dfb
--- /dev/null
+++ b/src/bin/main.rs
@@ -0,0 +1,418 @@
+#![allow(dead_code)]
+
+use std::io::Write;
+use core_dev::datetime::time_attributes::TimeAttributes;
+use core_dev::datetime::datetime::get_current_datetime;
+use core_dev::datetime::datetime::get_current_time;
+use core_dev::datetime::time::minutes_to_time_struct;
+use core_dev::datetime::time::sleep_by_secs;
+
+
+use clap::Parser;
+
+
+#[derive(Parser, Debug)]
+#[clap(
+ author = "Author: alexzanderr",
+ version = include_str!("../../version"),
+ about = "Rule 20 20 20 -> eye utility software to make you look away from screen for 20 seconds, every X minutes.",
+ long_about = include_str!("../../ABOUT.md")
+)]
+struct Args {
+ /// number of minutes as interval for 20 20 20 rule desktop notification
+ #[clap(short = 'm', long = "minutes")]
+ minutes: Option,
+
+ #[clap(short = 's', long = "seconds")]
+ seconds: Option,
+
+ #[clap(long = "message")]
+ message: Option,
+}
+
+use ansi_term::Color::{
+ Red,
+ Yellow,
+};
+
+use soloud::*;
+
+use std::collections::HashMap;
+
+
+pub struct MusicPlayer<'a> {
+ player: Soloud,
+ playing: bool,
+ songs: HashMap<&'a str, AudioFile<'a>>,
+ handle: Option,
+}
+
+impl<'a> MusicPlayer<'a> {
+ pub fn new() -> Result {
+ let mut sl = Soloud::default()?;
+ sl.set_global_volume(2.0);
+
+ Ok(Self {
+ player: sl,
+ playing: false,
+ songs: HashMap::new(),
+ handle: None,
+ })
+ }
+
+ pub fn load_file(
+ &mut self,
+ alias: &'a str,
+ path: &'a str,
+ ) -> Result<(), SoloudError> {
+ let wav = self.songs.entry(alias).or_insert(AudioFile::new(
+ alias,
+ path,
+ Wav::default(),
+ ));
+ wav.wav.load(path)?;
+ Ok(())
+ }
+
+ pub fn load_bytes(
+ &mut self,
+ alias: &'a str,
+ path: &'a str,
+ bytes: &[u8],
+ ) -> Result<(), SoloudError> {
+ let wav = self.songs.entry(alias).or_insert(AudioFile::new(
+ alias,
+ path,
+ Wav::default(),
+ ));
+ wav.wav.load_mem(bytes)?;
+ Ok(())
+ }
+
+ pub fn pause_playing(&mut self) {
+ if let Some(handle) = self.handle {
+ self.playing = false;
+ self.player.set_pause(handle, true);
+ }
+ }
+
+ pub fn continue_playing(&mut self) {
+ if let Some(handle) = self.handle {
+ self.playing = true;
+ self.player.set_pause(handle, false);
+ }
+ }
+
+ pub fn is_done_playing(&self) -> bool {
+ self.player.voice_count() == 0
+ }
+
+ pub fn is_playing(&self) -> bool {
+ self.player.voice_count() > 0
+ }
+
+ pub fn play_music(&mut self, alias: String) {
+ self.playing = true;
+ let wav = self.songs.get_mut(alias.as_str()).unwrap();
+ let handle = self.player.play(&wav.wav);
+ self.handle = Some(handle);
+ while self.is_playing() {
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ }
+ }
+
+ // ERROR
+ // cannot borrow mutable and immutable at the same time
+ // pub fn play_all(&mut self) {
+ // for (alias, audio_file) in self.songs.iter {
+ // self.play_music(alias);
+ // }
+ // }
+}
+
+struct AudioFile<'a> {
+ alias: &'a str,
+ path: &'a str,
+ wav: Wav,
+}
+
+impl<'a> AudioFile<'a> {
+ pub fn new(alias: &'a str, path: &'a str, wav: Wav) -> Self {
+ Self {
+ alias,
+ path,
+ wav,
+ }
+ }
+}
+
+
+fn yellow_bold(string: &str) -> String {
+ Yellow.bold().paint(string).to_string()
+}
+
+
+fn red_bold(string: &str) -> String {
+ Red.bold().paint(string).to_string()
+}
+
+
+use color_backtrace::install as _install_colored_backtrace;
+use core_dev::imagelib::ImageBufferType;
+
+fn desktop_notification(message: &str, image_buffer: &ImageBufferType) {
+ linux_notification_from_image_buffer(
+ "RULE 20 20 20",
+ message,
+ image_buffer,
+ 10,
+ )
+}
+
+
+// DEPRECATED
+// fn playsound(path: &str) -> bool {
+// let command = format!(
+// "cvlc --play-and-exit --no-repeat {path} > /dev/null 2>&1"
+// );
+// let mut p = std::process::Command::new("sh")
+// .arg("-c")
+// .arg(command)
+// .spawn()
+// .expect("failed to spawm command");
+// let status = p.wait().unwrap();
+// status.success()
+// }
+
+fn comma_sound(sounds_map: &mut MusicPlayer) {
+ sounds_map.play_music("comma".to_string());
+}
+
+fn warning_sound(sounds_map: &mut MusicPlayer) {
+ sounds_map.play_music("warning".to_string());
+}
+
+fn bizwarn_sound(sounds_map: &mut MusicPlayer) {
+ sounds_map.play_music("bizwarn".to_string());
+}
+
+fn number_sound<'a>(sounds_map: &mut MusicPlayer<'a>, number: String) {
+ sounds_map.play_music(number);
+}
+
+fn order_sound(sounds_map: &mut MusicPlayer) {
+ sounds_map.play_music("order".to_string());
+}
+
+fn rule_202020_sound(sounds_map: &mut MusicPlayer) {
+ number_sound(sounds_map, "20".to_owned());
+ number_sound(sounds_map, "20".to_owned());
+ number_sound(sounds_map, "20".to_owned());
+ order_sound(sounds_map);
+}
+
+fn countdown<'a>(
+ sounds_map: &mut MusicPlayer<'a>,
+ start: usize,
+ pause: f32,
+) {
+ if start > 20 {
+ panic!("start should be less than 20, cuz 20 20 20 rule boy.")
+ }
+ for i in (0..21).rev().into_iter() {
+ let i_str = i.to_string();
+ if i > 9 {
+ println!("[ {} ]", red_bold(&i_str));
+ } else {
+ println!("[ {} ]", red_bold(&i_str));
+ }
+ number_sound(sounds_map, i.to_string());
+ sleep_by_secs(pause);
+ }
+}
+
+static BIZWARN_SOUND: &[u8; 9839] =
+ include_bytes!("../../static/audio/bizwarn_minus_15.wav");
+static WARNING_SOUND: &[u8; 6267] =
+ include_bytes!("../../static/audio/warning_minus_15.wav");
+static COMMA_SOUND: &[u8; 2788] =
+ include_bytes!("../../static/audio/comma_minus_15.wav");
+static ORDER_SOUND: &[u8; 6200] =
+ include_bytes!("../../static/audio/order_minus_15.wav");
+static ZERO_SOUND: &[u8; 7197] =
+ include_bytes!("../../static/audio/numbers/zero_minus_15.wav");
+static ONE_SOUND: &[u8; 5269] =
+ include_bytes!("../../static/audio/numbers/one_minus_15.wav");
+static TWO_SOUND: &[u8; 5639] =
+ include_bytes!("../../static/audio/numbers/two_minus_15.wav");
+static THREE_SOUND: &[u8; 5160] =
+ include_bytes!("../../static/audio/numbers/three_minus_15.wav");
+static FOUR_SOUND: &[u8; 5247] =
+ include_bytes!("../../static/audio/numbers/four_minus_15.wav");
+static FIVE_SOUND: &[u8; 7692] =
+ include_bytes!("../../static/audio/numbers/five_minus_15.wav");
+static SIX_SOUND: &[u8; 6098] =
+ include_bytes!("../../static/audio/numbers/six_minus_15.wav");
+static SEVEN_SOUND: &[u8; 6701] =
+ include_bytes!("../../static/audio/numbers/seven_minus_15.wav");
+static EIGHT_SOUND: &[u8; 5353] =
+ include_bytes!("../../static/audio/numbers/eight_minus_15.wav");
+static NINE_SOUND: &[u8; 6632] =
+ include_bytes!("../../static/audio/numbers/nine_minus_15.wav");
+static TEN_SOUND: &[u8; 5599] =
+ include_bytes!("../../static/audio/numbers/ten_minus_15.wav");
+static ELEVEN_SOUND: &[u8; 7140] =
+ include_bytes!("../../static/audio/numbers/eleven_minus_15.wav");
+static TWELVE_SOUND: &[u8; 5834] =
+ include_bytes!("../../static/audio/numbers/twelve_minus_15.wav");
+static THIRTEEN_SOUND: &[u8; 8487] =
+ include_bytes!("../../static/audio/numbers/thirteen_minus_15.wav");
+static FOURTEEN_SOUND: &[u8; 8864] =
+ include_bytes!("../../static/audio/numbers/fourteen_minus_15.wav");
+static FIFTEEN_SOUND: &[u8; 8936] =
+ include_bytes!("../../static/audio/numbers/fifteen_minus_15.wav");
+static SIXTEEN_SOUND: &[u8; 9356] =
+ include_bytes!("../../static/audio/numbers/sixteen_minus_15.wav");
+static SEVENTEEN_SOUND: &[u8; 11078] =
+ include_bytes!("../../static/audio/numbers/seventeen_minus_15.wav");
+static EIGHTTEEN_SOUND: &[u8; 7163] =
+ include_bytes!("../../static/audio/numbers/eighteen_minus_15.wav");
+static NINETEEN_SOUND: &[u8; 8673] =
+ include_bytes!("../../static/audio/numbers/nineteen_minus_15.wav");
+static TWENTY_SOUND: &[u8; 6497] =
+ include_bytes!("../../static/audio/numbers/twenty_minus_15.wav");
+
+/// you dont need to specify total bytes, you can mention just a slice ...
+static NOTIFICATION_ICON: &[u8] =
+ include_bytes!("../../static/icons/rule-202020.png");
+
+
+use core_dev::core::set_keyboard_interrupt_handler;
+use core_dev::imagelib::create_image_buffer_from_bytes;
+use core_dev::linuxapi::linux_notification_from_image_buffer;
+
+fn main() -> Result<(), SoloudError> {
+ _install_colored_backtrace();
+
+ let rule_202020_icon_image_buffer =
+ create_image_buffer_from_bytes(NOTIFICATION_ICON);
+
+ set_keyboard_interrupt_handler(move || {
+ println!();
+ println!("received SIGKILL");
+ println!("application stopped");
+ println!("exited with code 1");
+ std::process::exit(1);
+ });
+
+ let mut sounds_map: HashMap<&str, &[u8]> = HashMap::new();
+ sounds_map.insert(
+ "bizwarn",
+ include_bytes!("../../static/audio/bizwarn_minus_15.wav"),
+ );
+ sounds_map.insert(
+ "warning",
+ include_bytes!("../../static/audio/warning_minus_15.wav"),
+ );
+ sounds_map.insert("comma", COMMA_SOUND);
+ sounds_map.insert("order", ORDER_SOUND);
+ sounds_map.insert("0", ZERO_SOUND);
+ sounds_map.insert("1", ONE_SOUND);
+ sounds_map.insert("2", TWO_SOUND);
+ sounds_map.insert("3", THREE_SOUND);
+ sounds_map.insert("4", FOUR_SOUND);
+ sounds_map.insert("5", FIVE_SOUND);
+ sounds_map.insert("6", SIX_SOUND);
+ sounds_map.insert("7", SEVEN_SOUND);
+ sounds_map.insert("8", EIGHT_SOUND);
+ sounds_map.insert("9", NINE_SOUND);
+ sounds_map.insert("10", TEN_SOUND);
+ sounds_map.insert("11", ELEVEN_SOUND);
+ sounds_map.insert("12", TWELVE_SOUND);
+ sounds_map.insert("13", THIRTEEN_SOUND);
+ sounds_map.insert("14", FOURTEEN_SOUND);
+ sounds_map.insert("15", FIFTEEN_SOUND);
+ sounds_map.insert("16", SIXTEEN_SOUND);
+ sounds_map.insert("17", SEVENTEEN_SOUND);
+ sounds_map.insert("18", EIGHTTEEN_SOUND);
+ sounds_map.insert("19", NINETEEN_SOUND);
+ sounds_map.insert("20", TWENTY_SOUND);
+
+
+ let mut music_player = MusicPlayer::new()?;
+ for (alias, bytes) in sounds_map.iter() {
+ music_player.load_bytes(alias, "asd", bytes)?;
+ }
+
+
+ let args = Args::parse();
+ println!("{:?}", args);
+ println!("{:?}", args.minutes);
+ println!("{:?}", args.seconds);
+ let seconds = if let Some(seconds) = args.seconds {
+ seconds as usize
+ } else {
+ 0
+ };
+
+ let minutes = if let Some(minutes) = args.minutes {
+ minutes as usize
+ } else {
+ 0
+ };
+
+ let mut default_message = String::from("ITS TIME NOW!!!");
+ if let Some(message) = args.message {
+ default_message = message;
+ }
+
+ let mut time_struct = minutes_to_time_struct(minutes);
+ time_struct.seconds += seconds;
+ time_struct.normalize();
+
+
+ loop {
+ let mut ta = time_struct.clone();
+ println!("{:?}", ta);
+ rule_202020(&mut ta, &mut music_player, &default_message, &rule_202020_icon_image_buffer);
+ }
+}
+
+
+fn rule_202020(
+ ta: &mut TimeAttributes,
+ music_player: &mut MusicPlayer,
+ message: &str,
+ image_buffer: &ImageBufferType
+) {
+ let clock = yellow_bold(&ta.format_as_clock_with_level(2));
+ print!("Time Left: [ {} ]\r", clock);
+ std::io::stdout().flush().expect("failed to flush stdout");
+ std::thread::sleep(std::time::Duration::from_millis(1000));
+
+ while !ta.all_zeros() {
+ ta.decrement_seconds(1);
+ let clock = yellow_bold(&ta.format_as_clock_with_level(2));
+ print!("\x1b[2KTime Left: [ {} ]\r", clock);
+ ta.normalize_decrement();
+ std::io::stdout().flush().expect("failed to flush stdout");
+
+ std::thread::sleep(std::time::Duration::from_millis(1000));
+ }
+ println!(
+ "\n\n{} !!! ( {} )",
+ red_bold("20 20 20 ORDER"),
+ get_current_time()
+ );
+
+ for _ in 0..3 {
+ warning_sound(music_player);
+ comma_sound(music_player);
+ }
+ bizwarn_sound(music_player);
+ rule_202020_sound(music_player);
+ desktop_notification(message, &image_buffer);
+ println!("notification sent at {}", get_current_datetime());
+ countdown(music_player, 20, 1.0);
+ println!()
+}
diff --git a/src/bin/notif.rs b/src/bin/notif.rs
new file mode 100644
index 0000000..bc9f825
--- /dev/null
+++ b/src/bin/notif.rs
@@ -0,0 +1,30 @@
+
+use core_dev::linuxapi::linux_notification_from_image_buffer;
+use core_dev::imagelib::create_image_buffer_from_bytes;
+use color_backtrace::install;
+
+
+
+fn main() {
+ install();
+
+ static NOTIFICATION_ICON: &[u8] = include_bytes!("../../static/icons/rule-202020.png");
+
+ let image_buffer = create_image_buffer_from_bytes(NOTIFICATION_ICON);
+
+ linux_notification_from_image_buffer(
+ "its working",
+ "i've sent a notification from image loaded into the binary file of the app",
+ &image_buffer,
+ 1);
+ linux_notification_from_image_buffer(
+ "its working",
+ "i've sent a notification from image loaded into the binary file of the app",
+ &image_buffer,
+ 1);
+ linux_notification_from_image_buffer(
+ "its working",
+ "i've sent a notification from image loaded into the binary file of the app",
+ &image_buffer,
+ 10);
+}
diff --git a/static/audio/bizwarn_minus_15.wav b/static/audio/bizwarn_minus_15.wav
new file mode 100644
index 0000000..aed9c65
Binary files /dev/null and b/static/audio/bizwarn_minus_15.wav differ
diff --git a/static/audio/comma_minus_15.wav b/static/audio/comma_minus_15.wav
new file mode 100644
index 0000000..3e577c2
Binary files /dev/null and b/static/audio/comma_minus_15.wav differ
diff --git a/static/audio/numbers/eight_minus_15.wav b/static/audio/numbers/eight_minus_15.wav
new file mode 100644
index 0000000..4376ea7
Binary files /dev/null and b/static/audio/numbers/eight_minus_15.wav differ
diff --git a/static/audio/numbers/eighteen_minus_15.wav b/static/audio/numbers/eighteen_minus_15.wav
new file mode 100644
index 0000000..5c2af24
Binary files /dev/null and b/static/audio/numbers/eighteen_minus_15.wav differ
diff --git a/static/audio/numbers/eleven_minus_15.wav b/static/audio/numbers/eleven_minus_15.wav
new file mode 100644
index 0000000..eac042c
Binary files /dev/null and b/static/audio/numbers/eleven_minus_15.wav differ
diff --git a/static/audio/numbers/fifteen_minus_15.wav b/static/audio/numbers/fifteen_minus_15.wav
new file mode 100644
index 0000000..49677f4
Binary files /dev/null and b/static/audio/numbers/fifteen_minus_15.wav differ
diff --git a/static/audio/numbers/five_minus_15.wav b/static/audio/numbers/five_minus_15.wav
new file mode 100644
index 0000000..6fae4bb
Binary files /dev/null and b/static/audio/numbers/five_minus_15.wav differ
diff --git a/static/audio/numbers/four_minus_15.wav b/static/audio/numbers/four_minus_15.wav
new file mode 100644
index 0000000..6a4810c
Binary files /dev/null and b/static/audio/numbers/four_minus_15.wav differ
diff --git a/static/audio/numbers/fourteen_minus_15.wav b/static/audio/numbers/fourteen_minus_15.wav
new file mode 100644
index 0000000..c836741
Binary files /dev/null and b/static/audio/numbers/fourteen_minus_15.wav differ
diff --git a/static/audio/numbers/nine_minus_15.wav b/static/audio/numbers/nine_minus_15.wav
new file mode 100644
index 0000000..c464395
Binary files /dev/null and b/static/audio/numbers/nine_minus_15.wav differ
diff --git a/static/audio/numbers/nineteen_minus_15.wav b/static/audio/numbers/nineteen_minus_15.wav
new file mode 100644
index 0000000..3dfe5fa
Binary files /dev/null and b/static/audio/numbers/nineteen_minus_15.wav differ
diff --git a/static/audio/numbers/one_minus_15.wav b/static/audio/numbers/one_minus_15.wav
new file mode 100644
index 0000000..d6603b6
Binary files /dev/null and b/static/audio/numbers/one_minus_15.wav differ
diff --git a/static/audio/numbers/seven_minus_15.wav b/static/audio/numbers/seven_minus_15.wav
new file mode 100644
index 0000000..09afdda
Binary files /dev/null and b/static/audio/numbers/seven_minus_15.wav differ
diff --git a/static/audio/numbers/seventeen_minus_15.wav b/static/audio/numbers/seventeen_minus_15.wav
new file mode 100644
index 0000000..11d43de
Binary files /dev/null and b/static/audio/numbers/seventeen_minus_15.wav differ
diff --git a/static/audio/numbers/six_minus_15.wav b/static/audio/numbers/six_minus_15.wav
new file mode 100644
index 0000000..4ca6bce
Binary files /dev/null and b/static/audio/numbers/six_minus_15.wav differ
diff --git a/static/audio/numbers/sixteen_minus_15.wav b/static/audio/numbers/sixteen_minus_15.wav
new file mode 100644
index 0000000..bd0637f
Binary files /dev/null and b/static/audio/numbers/sixteen_minus_15.wav differ
diff --git a/static/audio/numbers/ten_minus_15.wav b/static/audio/numbers/ten_minus_15.wav
new file mode 100644
index 0000000..e7887bf
Binary files /dev/null and b/static/audio/numbers/ten_minus_15.wav differ
diff --git a/static/audio/numbers/thirteen_minus_15.wav b/static/audio/numbers/thirteen_minus_15.wav
new file mode 100644
index 0000000..35e6c83
Binary files /dev/null and b/static/audio/numbers/thirteen_minus_15.wav differ
diff --git a/static/audio/numbers/three_minus_15.wav b/static/audio/numbers/three_minus_15.wav
new file mode 100644
index 0000000..bb22767
Binary files /dev/null and b/static/audio/numbers/three_minus_15.wav differ
diff --git a/static/audio/numbers/twelve_minus_15.wav b/static/audio/numbers/twelve_minus_15.wav
new file mode 100644
index 0000000..9374f0b
Binary files /dev/null and b/static/audio/numbers/twelve_minus_15.wav differ
diff --git a/static/audio/numbers/twenty_minus_15.wav b/static/audio/numbers/twenty_minus_15.wav
new file mode 100644
index 0000000..da9495c
Binary files /dev/null and b/static/audio/numbers/twenty_minus_15.wav differ
diff --git a/static/audio/numbers/two_minus_15.wav b/static/audio/numbers/two_minus_15.wav
new file mode 100644
index 0000000..00f0ee2
Binary files /dev/null and b/static/audio/numbers/two_minus_15.wav differ
diff --git a/static/audio/numbers/zero_minus_15.wav b/static/audio/numbers/zero_minus_15.wav
new file mode 100644
index 0000000..33c23f2
Binary files /dev/null and b/static/audio/numbers/zero_minus_15.wav differ
diff --git a/static/audio/order_minus_15.wav b/static/audio/order_minus_15.wav
new file mode 100644
index 0000000..aedd182
Binary files /dev/null and b/static/audio/order_minus_15.wav differ
diff --git a/static/audio/warning_minus_15.wav b/static/audio/warning_minus_15.wav
new file mode 100644
index 0000000..0167104
Binary files /dev/null and b/static/audio/warning_minus_15.wav differ
diff --git a/static/icons/rule-202020.png b/static/icons/rule-202020.png
new file mode 100644
index 0000000..e15a7fa
Binary files /dev/null and b/static/icons/rule-202020.png differ
diff --git a/static/img/logo/rule-202020-logo.png b/static/img/logo/rule-202020-logo.png
new file mode 100644
index 0000000..d7fed50
Binary files /dev/null and b/static/img/logo/rule-202020-logo.png differ
diff --git a/version b/version
new file mode 100644
index 0000000..8a9ecc2
--- /dev/null
+++ b/version
@@ -0,0 +1 @@
+0.0.1
\ No newline at end of file