From dfd00f9832b7c8658583957fa1fd9d60f57d9d2e Mon Sep 17 00:00:00 2001 From: devaine Date: Sun, 8 Dec 2024 12:37:25 -0600 Subject: [PATCH 01/10] Adding Scripts --- .gitignore | 1 + ssh_and_gpg.py | 224 +++++++++++++++++++++++++++++++++++++++++++++++++ sunset.sh | 14 ++++ wayshot2.sh | 41 +++++++++ 4 files changed, 280 insertions(+) create mode 100644 .gitignore create mode 100644 ssh_and_gpg.py create mode 100755 sunset.sh create mode 100755 wayshot2.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06c5b69 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +serverssh diff --git a/ssh_and_gpg.py b/ssh_and_gpg.py new file mode 100644 index 0000000..d9e832a --- /dev/null +++ b/ssh_and_gpg.py @@ -0,0 +1,224 @@ +# Made by Devaine +# mostly because bash was pissing me off when it comes to recursion +# also because i am lazy and i wanted to automate things + +import time +import os + + +class GPG(): + def export(id_num): + print("\n\nHere's your exported key for ID: " + id_num) + os.system("gpg --armor --export " + id_num) + + def view(): + id_output = os.popen("gpg --list-keys --with-colons | \ + awk -F: '/^pub:/ { print $5 }'").read() + id_split = id_output.split("\n") + + info_output = os.popen("gpg --list-keys --with-colons | \ + awk -F: '/^uid:/ { print $10 }'").read() + info_split = info_output.split("\n") + + print("\n\n----------------------") + for i in range(len(info_split) - 1): + print("ID #" + str(i) + ": " + id_split[i] + + "\nInfo: " + info_split[i]) + + print("\n") + + def prompt(): + question = input("ID # you want to export: ") + + while question == "": + question = input("ID # you want to export: ") + + try: + response = int(question) + except ValueError: + print("Not a valid ID number!") + time.sleep(1) + prompt() + + if response >= len(info_output) - 1 or response < 0: + print("Not a valid ID number! test") + time.sleep(1) + prompt() + else: + GPG.export(id_split[response]) + + prompt() + + def view_prompt(): + prompt = input("Do you want to see the entire GPG Key? (Y/N): ") + while prompt == "": + prompt = input("Do you want to see the entire GPG Key? (Y/N): ") + + ans = prompt.upper() + + if "Y" in ans: + GPG.view() + + elif "N" in ans: + print("Exiting...") + exit(0) + else: + print("Incorrect Reponse!") + print("Retrying...") + time.sleep(1) + GPG.view_prompt() + + def gpg_keygen(): + os.system("gpg --full-generate-key") + + def __init__(): + GPG.gpg_keygen() + GPG.view_prompt() + + +class SSH(): + def keygen(): + def fileDestination(): + default_destination = os.path.expanduser("~") + "/.ssh" + print("------------") + print("Default: " + default_destination) + prompt = input("Path for Key (Press Enter for Default): ") + + if prompt == "": + prompt = default_destination + + if os.path.exists(prompt) is True: + os.system("ssh-keygen -f " + prompt + "/" + name + " -t ed25519") + else: + print("Path " + prompt + " doesn't exist, try again.") + time.sleep(1) + fileDestination() + + def nameConfirmation(confirm): + while confirm == "": + confirm = input("Are you sure this is the name you want? (Y/N): ") + ans = confirm.upper() + + if "Y" in ans: + fileDestination() + + elif "N" in ans: + print("Retrying...") + SSH.keygen() + + else: + print("Incorrect Reponse!") + print("Retrying...") + time.sleep(1) + nameConfirmation() + # Function starts here actually. + name = input("What is the name of your key: ") + + while name == "": + name = input("What is the name of your key: ") + + confirm = input("Are you sure this is the name you want (" + name + ")? (Y/N): ") + nameConfirmation(confirm) + + def gpg_prompt(): + prompt = input("Do you want to create a GPG Key? (Y/N): ") + while prompt == "": + prompt = input("Do you want to create a GPG Key? (Y/N): ") + ans = prompt.upper() + if "Y" in ans: + print("Starting...") + GPG.gpg_keygen() + elif "N" in ans: + GPG.view_prompt() + + else: + print("Incorrect Reponse!") + time.sleep(1) + SSH.gpg_prompt() + + def public_key_view(): + def choose_file(): + default_destination = os.path.expanduser("~") + "/.ssh" + print("------------") + print("Default Path: " + default_destination) + path = input("Enter Key Path (Press Enter for Default): ") + + if path == "": + path = default_destination + + if os.path.exists(path) is True: + avail_options = os.popen("ls " + path + "| grep .pub").read() + else: + print("Path " + path + " doesn't exist, try again.") + time.sleep(1) + choose_file() + + options_split = avail_options.split("\n") + + print("There are " + str(len(options_split) - 1) + + " public keys available to read...") + + for i in range(len(options_split) - 1): + print("Option #" + str(i) + ": " + options_split[i][:-4]) + + def prompt(): + question = input("Choose an option (by number): ") + + while question == "": + question = input("Choose an option (by number): ") + + try: + response = int(question) + except ValueError: + print("Not a valid number!") + time.sleep(1) + prompt() + + if response >= len(options_split) - 1 or response < 0: + print("Not a valid ID number! test") + time.sleep(1) + prompt() + else: + print("Here's the public key from Option #" + str(i) + + " (" + options_split[response] + "):") + os.system("cat " + path + "/" + options_split[response]) + + prompt() + + prompt = input("Do you want to view your SSH public key? (Y/N): ") + while prompt == "": + prompt = input("Do you want to view your SSH public key? (Y/N): ") + ans = prompt.upper() + if "Y" in ans: + print("Starting...") + choose_file() + + elif "N" in ans: + SSH.gpg_prompt() + + else: + print("Incorrect Reponse!") + time.sleep(1) + SSH.public_key_view() + + def start(): + ssh_prompt = input("Do you want to create a SSH Key? (Y/N): ") + while ssh_prompt == "": + ssh_prompt = input("Do you want to create a SSH Key? (Y/N): ") + ans = ssh_prompt.upper() + + if "Y" in ans: + print("Starting...") + SSH.keygen() + + elif "N" in ans: + SSH.public_key_view() + + else: + print("Incorrect Reponse!") + time.sleep(1) + SSH.start() + + +if __name__ == "__main__": + SSH.start() diff --git a/sunset.sh b/sunset.sh new file mode 100755 index 0000000..a7d016b --- /dev/null +++ b/sunset.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Build and Install 'redshift' for wayland: https://github.com/minus7/redshift +# You can also use gammastep + +#cd $HOME/.local/bin +# killing gammastep if it already exists +if [[ $(pgrep gammastep) =~ ^[0-9]+$ ]] + then + kill $(pgrep gammastep) + + else + gammastep -PO 3200 +fi + diff --git a/wayshot2.sh b/wayshot2.sh new file mode 100755 index 0000000..4fcb854 --- /dev/null +++ b/wayshot2.sh @@ -0,0 +1,41 @@ +#!/bin/bash + + +## Variables +declare -r time=$(date --iso-8601=seconds) + + +while(( $# > 0)); do + case $1 in + -s) + REGION=yes + shift + ;; + -c) + CURSOR=yes + shift + ;; + *) + if [ -z "$FILENAME" ]; then + FILENAME="$1/$time.png" + shift + else + echo "wrong format" + exit 1 + fi + ;; +esac +done + +OPTS=() +if [ -n "$REGION" ]; then + OPTS+=("-g $(slurp)") + + if [ -n "$CURSOR" ]; then + OPTS+=("-c") + fi +fi + +grim "${OPTS[@]}" "$FILENAME" +# https://github.com/bugaevc/wl-clipboard/issues/198 lifesaver +wl-copy --type image/png < $FILENAME From 413b470d994ae0ac6daafc4e473c967d7ee0fca5 Mon Sep 17 00:00:00 2001 From: devaine Date: Sun, 8 Dec 2024 12:40:35 -0600 Subject: [PATCH 02/10] forgot to add gitignore to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 06c5b69..c4ba3e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ serverssh +.gitignore From 9b374bb70db835439aa9952d7b98bb3345c404eb Mon Sep 17 00:00:00 2001 From: devaine Date: Sat, 26 Apr 2025 10:22:34 -0500 Subject: [PATCH 03/10] feat: added notifications & daemons + simplified naming notifications are for `dunst` a notification handler for my setup I added volume, charging, battery, time, and brightness notifications --- .gitignore | 2 - daemons/battery-daemon | 5 ++ notifs/battery | 73 +++++++++++++++++++++++ notifs/battery-status | 11 ++++ notifs/brightness | 24 ++++++++ notifs/charge-status | 28 +++++++++ notifs/time | 4 ++ notifs/volume | 122 ++++++++++++++++++++++++++++++++++++++ wayshot2.sh => screenshot | 24 ++++---- ssh_and_gpg.py => ssgpg | 1 + sunset.sh => sunset | 2 +- 11 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 daemons/battery-daemon create mode 100755 notifs/battery create mode 100755 notifs/battery-status create mode 100755 notifs/brightness create mode 100755 notifs/charge-status create mode 100755 notifs/time create mode 100755 notifs/volume rename wayshot2.sh => screenshot (70%) rename ssh_and_gpg.py => ssgpg (99%) rename sunset.sh => sunset (92%) diff --git a/.gitignore b/.gitignore index c4ba3e5..e69de29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +0,0 @@ -serverssh -.gitignore diff --git a/daemons/battery-daemon b/daemons/battery-daemon new file mode 100644 index 0000000..ea05e40 --- /dev/null +++ b/daemons/battery-daemon @@ -0,0 +1,5 @@ +#!/bin/bash +while true; do + ~/.scripts/notifs/battery + sleep 1 +done diff --git a/notifs/battery b/notifs/battery new file mode 100755 index 0000000..6fb6c55 --- /dev/null +++ b/notifs/battery @@ -0,0 +1,73 @@ +#!/bin/bash + +# Constants (access to display) +export DISPLAY=:0 +export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus" + +# Find out which battery is the laptop's battery +BATTERY=$(acpi | grep -vwE "(unavailable)" | grep -o '[0-9]' | head -n 1) + +# Daemon Constants +WARNING_PERCENT=35 +FULL_PERCENT=99 +BAT_PERCENT=$(acpi -b | grep "Battery $BATTERY" | head -n 1 | grep -P -o "[0-9]+(?=%)") +DISCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep -c "Discharging") +NOTCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep "until" | grep -c "Not Charging") +## Conditioning Files +LOW_BAT_FILE=/tmp/battery_low +FULL_BAT_FILE=/tmp/battery_full + +# Functions +battery-full() { + notify-send "Battery Full" "Battery is fully charged!" -i "battery-full-charged" -r 9991 -t 5000 + touch $FULL_BAT_FILE +} + +battery-low() { + notify-send "Low Battery" "${BAT_PERCENT}% of battery remaining." -u critical -i "battery-caution" -r 9991 + touch $LOW_BAT_FILE +} + +battery-normal() { + if [ "$1" == "Normal" ]; then + rm $FULL_BAT_FILE + + elif [ "$1" == "Charging" ]; then + rm $LOW_BAT_FILE + fi +} + +# IF the batttery is discharging AND there is a FULL_BAT_FILE exists AND battery is less than 99% +if [ "$BAT_PERCENT" -lt $FULL_PERCENT ] && [ "$DISCHARGING_COUNT" -eq 1 ] && [ -f $FULL_BAT_FILE ]; then + #echo "hit normal + full file exists + less than full %" + battery-normal "Normal" + +# If charging, AND less than full percentage AND LOW_BAT_FILE exists: +elif [ "$BAT_PERCENT" -lt $FULL_PERCENT ] && [ "$DISCHARGING_COUNT" -eq 0 ] && [ -f "$EMPTY_BAT_FILE" ]; then + #echo "hit normal + warning file exists + more than warning %" + battery-normal "Charging" +fi + +# IF the battery is charging AND is full (+ hasn't show any notif yet): +if [ "$BAT_PERCENT" -ge $FULL_PERCENT ] && [ ! -f $FULL_BAT_FILE ] && [ "$DISCHARGING_COUNT" = 0 ]; then + #echo "hit full + charging" + battery-full + +# Another condition, battery could be full, yet it won't be charging: +elif [ "$BAT_PERCENT" -ge $FULL_PERCENT ] && [ "$NOTCHARGING_COUNT" = 1 ] && [ ! -f $LOW_BAT_FILE ]; then + #echo "hit full + not charging" + battery-full + +# IF the battery is low and it's discharging (+ hasn't shown any notif yet): +elif [ "$BAT_PERCENT" -le $WARNING_PERCENT ] && [ "$DISCHARGING_COUNT" -eq 1 ] && [ ! -f $LOW_BAT_FILE ]; then + #echo "hit low + discharging" + battery-low + +# If LOW_BAT_FILE Exists, Spam Until It's Charging +elif [ "$BAT_PERCENT" -le $WARNING_PERCENT ] && [ "$DISCHARGING_COUNT" -eq 1 ] && [ -f $LOW_BAT_FILE ]; then + notify-send "Low Battery" "${BAT_PERCENT}% of battery remaining." -u critical -i "battery-caution" -r 9991 +fi + +# Debug commands +echo "$BAT_PERCENT is the battery percent" +echo "$DISCHARGING_COUNT -- 1 = discharging, 0 = charging" diff --git a/notifs/battery-status b/notifs/battery-status new file mode 100755 index 0000000..5222e75 --- /dev/null +++ b/notifs/battery-status @@ -0,0 +1,11 @@ +#!/bin/bash +BATTERY=$(acpi | grep -vwE "(unavailable)" | grep -o '[0-9]' | head -n 1) +BAT_PERCENT=$(acpi -b | grep "Battery $BATTERY" | grep -P -o "[0-9]+(?=%)") +DISCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep "until" | grep -c "Discharging") +#NOTCHARGING_COUNT=$(acpi -b | head -n 1 | grep -c "Not Charging") + +if [[ $DISCHARGING_COUNT -eq 0 ]]; then + dunstify "Battery Status" "Battery is currently charging at ${BAT_PERCENT}%" -i "battery-level-50-charging-symbolic" -r 9991 -t 5000 +else + dunstify "Battery Status" "Battery is currently at ${BAT_PERCENT}%" -i "battery-level-50-symbolic" -r 9991 -t 5000 +fi diff --git a/notifs/brightness b/notifs/brightness new file mode 100755 index 0000000..cec2e0b --- /dev/null +++ b/notifs/brightness @@ -0,0 +1,24 @@ +#!/bin/bash + +CURRENT_BRIGHTNESS=$(xbacklight -get) + +bright_inc() { + dunstify "Increasing Brightness" "Current Brightness: ${CURRENT_BRIGHTNESS}%" -i "display-brightness-medium-symbolic" -t 5000 -r 9991 +} + +bright_dec() { + dunstify "Decreasing Brightness" "Current Brightness: ${CURRENT_BRIGHTNESS}%" -i "display-brightness-low-symbolic" -t 5000 -r 9991 + +} + +while test $# -gt 0; do + case "$1" in + inc) + bright_inc && exit 0 + ;; + + dec) + bright_dec && exit 0 + ;; + esac +done diff --git a/notifs/charge-status b/notifs/charge-status new file mode 100755 index 0000000..c8c447e --- /dev/null +++ b/notifs/charge-status @@ -0,0 +1,28 @@ +#!/bin/bash + +export DISPLAY=:0 +export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus" + +# Find out which battery is the laptop's battery +BATTERY=$(acpi | grep -vwE "(unavailable)" | grep -o '[0-9]' | head -n 1) + +# 1 = Charging +# 0 = Discharging +BAT_CHARGING=$1 + +# Filtering one more time to find the percentage of the laptop's battery level +BAT_LEVEL=$(acpi -b | grep "Battery $BATTERY" | grep -P -o '[0-9]+(?=%)') + +CHARGE_FILE=/tmp/laptop-charging +DISCHARGE_FILE=/tmp/laptop-discharging + +# Notification handling +if [ "$BAT_CHARGING" -eq 1 ] && [ ! -f $CHARGE_FILE ]; then + rm $DISCHARGE_FILE + touch $CHARGE_FILE + /usr/bin/notify-send "Charging" "Charging battery at ${BAT_LEVEL}%" -u low -i "battery-level-50-charging-symbolic" -t 5000 -r 9991 +elif [ "$BAT_CHARGING" -eq 0 ] && [ ! -f $DISCHARGE_FILE ]; then + rm $CHARGE_FILE + touch $DISCHARGE_FILE + /usr/bin/notify-send "Discharging" "${BAT_LEVEL}% remaining" -u low -i "battery-level-70-symbolic" -t 5000 -r 9991 +fi diff --git a/notifs/time b/notifs/time new file mode 100755 index 0000000..f8471a5 --- /dev/null +++ b/notifs/time @@ -0,0 +1,4 @@ +#!/bin/bash +CURRENT_TIME=$(date) + +notify-send "Date / Time Info:" "${CURRENT_TIME}" -t 5000 -r 9991 diff --git a/notifs/volume b/notifs/volume new file mode 100755 index 0000000..9202784 --- /dev/null +++ b/notifs/volume @@ -0,0 +1,122 @@ +#!/bin/bash + +CURRENT_VOL=$(wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{ print $2 }') +VOL_PRESENT=$(wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{ print $2 }' | cut -c 3,4) + +CURRENT_MIC_VOL=$(wpctl get-volume @DEFAULT_AUDIO_SOURCE@ | awk '{ print $2 }') +MIC_PRESENT=$(wpctl get-volume @DEFAULT_AUDIO_SOURCE@ | awk '{ print $2 }' | cut -c 3,4) + +CURRENT_MUTE=$(wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{ print $3 }' | tr -d '[]') +CURRENT_MIC_MUTE=$(wpctl get-volume @DEFAULT_AUDIO_SOURCE@ | awk '{ print $3 }' | tr -d '[]') + +vol_inc() { + if [[ "$CURRENT_VOL" == "0.0"* ]]; then + dunstify "Increasing Volume" "Current Volume: $(echo "$VOL_PRESENT" | cut -c 2)%" -i "audio-volume-high" -t 5000 -r 9991 + elif [[ "$CURRENT_VOL" == "1.00" ]]; then + dunstify "Increasing Volume" "Current Volume: 100%" -i "audio-volume-high" -t 5000 -r 9991 + else + dunstify "Increasing Volume" "Current Volume: ${VOL_PRESENT}%" -i "audio-volume-high" -t 5000 -r 9991 + fi + +} + +vol_dec() { + if [[ "$CURRENT_VOL" == "0.0"* ]]; then + dunstify "Decreasing Volume" "Current Volume: $(echo "$VOL_PRESENT" | cut -c 2)%" -i "audio-volume-medium" -t 5000 -r 9991 + elif [[ "$CURRENT_VOL" == "1.00" ]]; then + dunstify "Decreasing Volume" "Current Volume: 100%" -i "audio-volume-medium" -t 5000 -r 9991 + else + dunstify "Decreasing Volume" "Current Volume: ${VOL_PRESENT}%" -i "audio-volume-medium" -t 5000 -r 9991 + fi +} + +vol_mute_toggle() { + # If "MUTED" doesn't exist, then send notification and toggle mute, otherwise (if "MUTED" DOES exist, send notif and unmute + if [ "$CURRENT_MUTE" == "MUTED" ]; then + if [[ "$CURRENT_VOL" == "0.0"* ]]; then + dunstify "Currently Muting" "Current Volume: $(echo "$VOL_PRESENT" | cut -c 2)%" -i "audio-volume-muted" -t 5000 -r 9991 + elif [[ "$CURRENT_VOL" == "1.00" ]]; then + dunstify "Currently Muting" "Current Volume: 100%" -i "audio-volume-muted" -t 5000 -r 9991 + else + dunstify "Currently Muting" "Current Volume: ${VOL_PRESENT}%" -i "audio-volume-muted" -t 5000 -r 9991 + fi + else + if [[ "$CURRENT_VOL" == "0.0"* ]]; then + dunstify "Currently Unmuting" "Current Volume: $(echo "$VOL_PRESENT" | cut -c 2)%" -i "audio-volume-medium" -t 5000 -r 9991 + elif [[ "$CURRENT_VOL" == "1.00" ]]; then + dunstify "Currently Unmuting" "Current Volume: 100%" -i "audio-volume-medium" -t 5000 -r 9991 + else + dunstify "Currently Unmuting" "Current Volume: ${VOL_PRESENT}%" -i "audio-volume-medium" -t 5000 -r 9991 + fi + fi +} + +micInc() { + if [[ "$CURRENT_MIC_VOL" == "0.0"* ]]; then + dunstify "Increasing Microphone Volume" "Current Volume: $(echo "$MIC_PRESENT" | cut -c 2)%" -i "audio-input-microphone-high" -t 5000 -r 9991 + elif [[ "$CURRENT_MIC_VOL" == "1.00" ]]; then + dunstify "Increasing Microphone Volume" "Current Volume: 100%" -i "audio-input-microphone-high" -t 5000 -r 9991 + else + dunstify "Increasing Microphone Volume" "Current Volume: ${MIC_PRESENT}%" -i "audio-input-microphone-high" -t 5000 -r 9991 + fi +} + +micDec() { + if [[ "$CURRENT_MIC_VOL" == "0.0"* ]]; then + dunstify "Decreasing Microphone Volume" "Current Volume: $(echo "$MIC_PRESENT" | cut -c 2)%" -i "audio-input-microphone-low" -t 5000 -r 9991 + elif [[ "$CURRENT_MIC_VOL" == "1.00" ]]; then + dunstify "Decreasing Microphone Volume" "Current Volume: 100%" -i "audio-input-microphone-low" -t 5000 -r 9991 + else + dunstify "Decreasing Microphone Volume" "Current Volume: ${MIC_PRESENT}%" -i "audio-input-microphone-low" -t 5000 -r 9991 + fi +} + +mic_mute_toggle() { + # If "MUTED" doesn't exist, then send notification and toggle mute, otherwise (if "MUTED" DOES exist, send notif and unmute + if [ "$CURRENT_MIC_MUTE" == "MUTED" ]; then + if [[ "$CURRENT_MIC_VOL" == "0.0"* ]]; then + dunstify "Currently Muting Microphone" "Current Volume: $(echo "$VOL_PRESENT" | cut -c 2)%" -i "audio-input-microphone-muted" -t 5000 -r 9991 + elif [[ "$CURRENT_MIC_VOL" == "1.00" ]]; then + dunstify "Currently Muting Microphone" "Current Volume: 100%" -i "audio-input-microphone-muted" -t 5000 -r 9991 + else + dunstify "Currently Muting Microphone" "Current Volume: ${VOL_PRESENT}%" -i "audio-input-microphone-muted" -t 5000 -r 9991 + fi + else + if [[ "$CURRENT_MIC_VOL" == "0.0"* ]]; then + dunstify "Currently Unmuting Microphone" "Current Volume: $(echo "$VOL_PRESENT" | cut -c 2)%" -i "audio-input-microphone-high" -t 5000 -r 9991 + elif [[ "$CURRENT_MIC_VOL" == "1.00" ]]; then + dunstify "Currently Unmuting Microphone" "Current Volume: 100%" -i "audio-input-microphone-high" -t 5000 -r 9991 + else + dunstify "Currently Unmuting Microphone" "Current Volume: ${VOL_PRESENT}%" -i "audio-input-microphone-high" -t 5000 -r 9991 + fi + fi +} + +while test $# -gt 0; do + case "$1" in + inc) + vol_inc && exit 0 + ;; + + dec) + vol_dec && exit 0 + ;; + + mute-toggle) + vol_mute_toggle && exit 0 + ;; + + micInc) + micInc && exit 0 + ;; + + micDec) + micDec && exit 0 + ;; + + micMute) + mic_mute_toggle && exit 0 + ;; + + esac +done diff --git a/wayshot2.sh b/screenshot similarity index 70% rename from wayshot2.sh rename to screenshot index 4fcb854..c03da55 100755 --- a/wayshot2.sh +++ b/screenshot @@ -1,30 +1,30 @@ #!/bin/bash - ## Variables declare -r time=$(date --iso-8601=seconds) - -while(( $# > 0)); do +while (($# > 0)); do case $1 in - -s) + -s) REGION=yes shift ;; - -c) + + -c) CURSOR=yes shift ;; - *) + + *) if [ -z "$FILENAME" ]; then - FILENAME="$1/$time.png" - shift + FILENAME="$1/$time.png" + shift else - echo "wrong format" - exit 1 + echo "wrong format" + exit 1 fi ;; -esac + esac done OPTS=() @@ -38,4 +38,4 @@ fi grim "${OPTS[@]}" "$FILENAME" # https://github.com/bugaevc/wl-clipboard/issues/198 lifesaver -wl-copy --type image/png < $FILENAME +wl-copy --type image/png <"$FILENAME" diff --git a/ssh_and_gpg.py b/ssgpg similarity index 99% rename from ssh_and_gpg.py rename to ssgpg index d9e832a..637470a 100644 --- a/ssh_and_gpg.py +++ b/ssgpg @@ -1,3 +1,4 @@ +#!/bin/python # Made by Devaine # mostly because bash was pissing me off when it comes to recursion # also because i am lazy and i wanted to automate things diff --git a/sunset.sh b/sunset similarity index 92% rename from sunset.sh rename to sunset index a7d016b..8868890 100755 --- a/sunset.sh +++ b/sunset @@ -9,6 +9,6 @@ if [[ $(pgrep gammastep) =~ ^[0-9]+$ ]] kill $(pgrep gammastep) else - gammastep -PO 3200 + gammastep -PO 2900 fi From db605c6c984d9e3b140a60658ecc9d321a169342 Mon Sep 17 00:00:00 2001 From: devaine Date: Sat, 26 Apr 2025 13:32:10 -0500 Subject: [PATCH 04/10] fix: changing FULL_BATTERY value + add condition + var add mild fixes + additions --- notifs/battery | 4 +++- notifs/battery-status | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/notifs/battery b/notifs/battery index 6fb6c55..6038978 100755 --- a/notifs/battery +++ b/notifs/battery @@ -9,7 +9,7 @@ BATTERY=$(acpi | grep -vwE "(unavailable)" | grep -o '[0-9]' | head -n 1) # Daemon Constants WARNING_PERCENT=35 -FULL_PERCENT=99 +FULL_PERCENT=98 BAT_PERCENT=$(acpi -b | grep "Battery $BATTERY" | head -n 1 | grep -P -o "[0-9]+(?=%)") DISCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep -c "Discharging") NOTCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep "until" | grep -c "Not Charging") @@ -21,11 +21,13 @@ FULL_BAT_FILE=/tmp/battery_full battery-full() { notify-send "Battery Full" "Battery is fully charged!" -i "battery-full-charged" -r 9991 -t 5000 touch $FULL_BAT_FILE + rm -rf $LOW_BAT_FILE } battery-low() { notify-send "Low Battery" "${BAT_PERCENT}% of battery remaining." -u critical -i "battery-caution" -r 9991 touch $LOW_BAT_FILE + rm -rf $FULL_BAT_FILE } battery-normal() { diff --git a/notifs/battery-status b/notifs/battery-status index 5222e75..2acd9a1 100755 --- a/notifs/battery-status +++ b/notifs/battery-status @@ -1,10 +1,10 @@ #!/bin/bash BATTERY=$(acpi | grep -vwE "(unavailable)" | grep -o '[0-9]' | head -n 1) BAT_PERCENT=$(acpi -b | grep "Battery $BATTERY" | grep -P -o "[0-9]+(?=%)") -DISCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep "until" | grep -c "Discharging") -#NOTCHARGING_COUNT=$(acpi -b | head -n 1 | grep -c "Not Charging") +DISCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep -E "until|remaining" | grep -c "Discharging") +NOTCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep -c "Not Charging") -if [[ $DISCHARGING_COUNT -eq 0 ]]; then +if [[ $DISCHARGING_COUNT -eq 0 ]] || [[ $NOTCHARGING_COUNT -eq 1 ]]; then dunstify "Battery Status" "Battery is currently charging at ${BAT_PERCENT}%" -i "battery-level-50-charging-symbolic" -r 9991 -t 5000 else dunstify "Battery Status" "Battery is currently at ${BAT_PERCENT}%" -i "battery-level-50-symbolic" -r 9991 -t 5000 From f5532e89ca8da754280ebd77fac40a7c5a5323bc Mon Sep 17 00:00:00 2001 From: devaine Date: Sun, 27 Apr 2025 23:15:07 -0500 Subject: [PATCH 05/10] mild tweaking, just added "discharing" LOL --- notifs/battery | 4 ++-- notifs/battery-status | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/notifs/battery b/notifs/battery index 6038978..cebbcca 100755 --- a/notifs/battery +++ b/notifs/battery @@ -71,5 +71,5 @@ elif [ "$BAT_PERCENT" -le $WARNING_PERCENT ] && [ "$DISCHARGING_COUNT" -eq 1 ] & fi # Debug commands -echo "$BAT_PERCENT is the battery percent" -echo "$DISCHARGING_COUNT -- 1 = discharging, 0 = charging" +#echo "$BAT_PERCENT is the battery percent" +#echo "$DISCHARGING_COUNT -- 1 = discharging, 0 = charging" diff --git a/notifs/battery-status b/notifs/battery-status index 2acd9a1..c894814 100755 --- a/notifs/battery-status +++ b/notifs/battery-status @@ -7,5 +7,5 @@ NOTCHARGING_COUNT=$(acpi -b | grep "Battery $BATTERY" | grep -c "Not Charging") if [[ $DISCHARGING_COUNT -eq 0 ]] || [[ $NOTCHARGING_COUNT -eq 1 ]]; then dunstify "Battery Status" "Battery is currently charging at ${BAT_PERCENT}%" -i "battery-level-50-charging-symbolic" -r 9991 -t 5000 else - dunstify "Battery Status" "Battery is currently at ${BAT_PERCENT}%" -i "battery-level-50-symbolic" -r 9991 -t 5000 + dunstify "Battery Status" "Battery is currently discharging at ${BAT_PERCENT}%" -i "battery-level-50-symbolic" -r 9991 -t 5000 fi From 16690836d6af7a7903c2b09d5f46c277394ffd4d Mon Sep 17 00:00:00 2001 From: devaine Date: Tue, 29 Jul 2025 15:05:52 -0500 Subject: [PATCH 06/10] refactor: refactored battery bash scripts into a C++ daemon --- {notifs => archived/battery-scripts}/battery | 4 +- archived/battery-scripts/battery-daemon | 5 + .../battery-scripts}/battery-status | 0 .../battery-scripts}/charge-status | 0 daemons/battery-daemon | Bin 68 -> 46424 bytes daemons/battery-daemon.cpp | 173 ++++++++++++++++++ notifs/brightness | 9 + notifs/info | 15 ++ notifs/volume | 8 + notifs/window | 3 + 10 files changed, 215 insertions(+), 2 deletions(-) rename {notifs => archived/battery-scripts}/battery (96%) create mode 100644 archived/battery-scripts/battery-daemon rename {notifs => archived/battery-scripts}/battery-status (100%) rename {notifs => archived/battery-scripts}/charge-status (100%) mode change 100644 => 100755 daemons/battery-daemon create mode 100644 daemons/battery-daemon.cpp create mode 100755 notifs/info create mode 100755 notifs/window diff --git a/notifs/battery b/archived/battery-scripts/battery similarity index 96% rename from notifs/battery rename to archived/battery-scripts/battery index cebbcca..98398e8 100755 --- a/notifs/battery +++ b/archived/battery-scripts/battery @@ -25,7 +25,7 @@ battery-full() { } battery-low() { - notify-send "Low Battery" "${BAT_PERCENT}% of battery remaining." -u critical -i "battery-caution" -r 9991 + notify-send "Low Battery" "${BAT_PERCENT}% of battery remaining." -u critical -i "battery-caution" -r 9991 -t 5000 touch $LOW_BAT_FILE rm -rf $FULL_BAT_FILE } @@ -67,7 +67,7 @@ elif [ "$BAT_PERCENT" -le $WARNING_PERCENT ] && [ "$DISCHARGING_COUNT" -eq 1 ] & # If LOW_BAT_FILE Exists, Spam Until It's Charging elif [ "$BAT_PERCENT" -le $WARNING_PERCENT ] && [ "$DISCHARGING_COUNT" -eq 1 ] && [ -f $LOW_BAT_FILE ]; then - notify-send "Low Battery" "${BAT_PERCENT}% of battery remaining." -u critical -i "battery-caution" -r 9991 + notify-send "Low Battery" "${BAT_PERCENT}% of battery remaining." -u critical -i "battery-caution" -r 9991 -t 5000 fi # Debug commands diff --git a/archived/battery-scripts/battery-daemon b/archived/battery-scripts/battery-daemon new file mode 100644 index 0000000..ea05e40 --- /dev/null +++ b/archived/battery-scripts/battery-daemon @@ -0,0 +1,5 @@ +#!/bin/bash +while true; do + ~/.scripts/notifs/battery + sleep 1 +done diff --git a/notifs/battery-status b/archived/battery-scripts/battery-status similarity index 100% rename from notifs/battery-status rename to archived/battery-scripts/battery-status diff --git a/notifs/charge-status b/archived/battery-scripts/charge-status similarity index 100% rename from notifs/charge-status rename to archived/battery-scripts/charge-status diff --git a/daemons/battery-daemon b/daemons/battery-daemon old mode 100644 new mode 100755 index ea05e4057260dd3576e9392d8b597bd816e916ed..3ef384b6e77654bc16e11d20a576bcf9f3f75d8e GIT binary patch literal 46424 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 literal 68 zcmY#Z)KALH(@#n)&fqG~$jnJqC@CsUwN^;U=i;o>*DFpg$}A`;*3Zi?$xJH-Dk~{T WEvn?=EY3+yEl@DzO3BYl +#include +#include +#include +#include // pid_t data type +#include +#include // For fork(); + +using namespace std; + +// If another instance of this program is running... +const char *lockFilePath = "/tmp/bat-daemon-run"; + +// Checks if the file exists. (Another instance is running) +bool isRunning() { + ifstream lockFile(lockFilePath); + return lockFile.good(); +} + +// Creates file w/ PID +void createLockFile() { + ofstream lockFile(lockFilePath); + lockFile << getpid(); +} + +// Removes file. +void removeLockFile() { remove(lockFilePath); } + +void send_notifs_warn(const int &percent) { + // Formulate command + string command = + "notify-send 'Low Battery' '" + to_string(percent) + + "% of battery remaining.' -u critical -i 'battery-caution' -t 5000"; + + // Execute command as a C-String (same contents, but compatible with C++ code) + // "Returns a pointer to an array that contains the contents of the variable" + system(command.c_str()); +} + +void send_notifs_charge(const int &percent, const int status) { + string command; + + switch (status) { + case 1: + command = "notify-send 'Charging' 'Charging battery at " + + to_string(percent) + + "%' -u low -i 'battery-level-50-charging-symbolic' -t 5000"; + break; + + case 0: + command = "notify-send 'Discharging' '" + to_string(percent) + + "% remaining' -u low -i 'battery-level-70-symbolic' -t 5000"; + break; + } + + system(command.c_str()); +} + +void send_notifs_full() { + string command = "notify-send 'Battery Full' 'Battery is fully charged!' -i " + "'battery-full-charged' -t 5000"; + system(command.c_str()); +} + +// Turn program into a daemon. +void daemonize() { + pid_t pid = fork(); + + // if forking fails, exit. + if (pid < 0) { + exit(EXIT_FAILURE); + } + + if (pid > 0) { + exit(EXIT_SUCCESS); + } + + // New session: if fails, exit. + // - New process becomes the leader + if (setsid() < 0) { + exit(EXIT_FAILURE); + } + + // Change directory to root + chdir("/"); + + // Redirects all streams to /dev/null + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); +} + +// Checks battery info. (Charging, Discharging, etc.) +void battery() { + int OLD_BAT_PERCENT = 100; // Maxing out to prevent bugs + int CHARGE = 0; // 1 = Charging, 0 = Discharging + int BAT_FULL = 0; // 1 = Full, 0 = Not Full + while (true) { + int BAT_WARN = 36; + // Read and Grab File Info: + + // ifstream (input file stream) class: operates on files (I/O) + ifstream BAT_STATUS_FILE("/sys/class/power_supply/BAT0/status"); + string BAT_STATUS; + + ifstream BAT_PERCENT_FILE("/sys/class/power_supply/BAT0/capacity"); + int BAT_PERCENT; + + // If it can't extract info, exit. + if (!(BAT_PERCENT_FILE >> BAT_PERCENT) || + !(BAT_STATUS_FILE >> BAT_STATUS)) { + exit(EXIT_FAILURE); + } + + // Timing: + // timeval = Time Value accurate from microseconds to years. + struct timeval tv; + + // Set intervals: 0sec + 100 milsec. + tv.tv_sec = 0; + tv.tv_usec = 100000; + + if (BAT_PERCENT <= BAT_WARN) { + if ((BAT_PERCENT < OLD_BAT_PERCENT) && (BAT_STATUS == "Discharging")) { + CHARGE = 0; + OLD_BAT_PERCENT = BAT_PERCENT; + send_notifs_warn(BAT_PERCENT); + } + } + + if (BAT_STATUS == "Charging") { + if ((BAT_PERCENT < 99) && (CHARGE == 0)) { + CHARGE = 1; + OLD_BAT_PERCENT = BAT_PERCENT; + BAT_FULL = 0; + send_notifs_charge(BAT_PERCENT, CHARGE); + } + + if (BAT_PERCENT >= 99 && BAT_FULL == 0) { + BAT_FULL = 1; + CHARGE = 1; + send_notifs_full(); + } + } + + if (BAT_STATUS == "Discharging" && CHARGE == 1) { + CHARGE = 0; + send_notifs_charge(BAT_PERCENT, CHARGE); + + if (BAT_PERCENT < 99) { + BAT_FULL = 0; + } + } + + select(0, NULL, NULL, NULL, &tv); + } +} + +// Initializer +int main() { + if (isRunning()) { + cout << "Another process already exists!" << endl; + return 1; + } + + createLockFile(); + + daemonize(); + battery(); + + removeLockFile(); + return 0; +} diff --git a/notifs/brightness b/notifs/brightness index cec2e0b..86c26f5 100755 --- a/notifs/brightness +++ b/notifs/brightness @@ -11,6 +11,10 @@ bright_dec() { } +keybind-func() { + echo "$CURRENT_BRIGHTNESS" +} + while test $# -gt 0; do case "$1" in inc) @@ -20,5 +24,10 @@ while test $# -gt 0; do dec) bright_dec && exit 0 ;; + + keybind) + keybind-func && exit 0 + ;; + esac done diff --git a/notifs/info b/notifs/info new file mode 100755 index 0000000..6376149 --- /dev/null +++ b/notifs/info @@ -0,0 +1,15 @@ +#!/bin/bash + +# Enter scripts directory +cd ~/.scripts/notifs || exit + +# Constants +CURRENT_VOL=$(./volume keybind | grep -o '^[^ ]*') +CURRENT_MIC_VOL=$(./volume keybind | awk '{ print $2 }') +BRIGHTNESS=$(./brightness keybind) + +# Variables for display, basically multiplies floats to integers +DISPLAY_VOL=$(awk '{ print $1 * $2 }' <<<"${CURRENT_VOL} 100") +DISPLAY_MIC_VOL=$(awk '{ print $1 * $2 }' <<<"${CURRENT_MIC_VOL} 100") + +dunstify "All other info:" "Brightness: $BRIGHTNESS%\nCurrent Volume: $DISPLAY_VOL%\nCurrent Mic. Volume: $DISPLAY_MIC_VOL%" diff --git a/notifs/volume b/notifs/volume index 9202784..2db2103 100755 --- a/notifs/volume +++ b/notifs/volume @@ -92,6 +92,10 @@ mic_mute_toggle() { fi } +keybind-func() { + echo "$CURRENT_VOL" "$CURRENT_MIC_VOL" +} + while test $# -gt 0; do case "$1" in inc) @@ -118,5 +122,9 @@ while test $# -gt 0; do mic_mute_toggle && exit 0 ;; + keybind) + keybind-func && exit 0 + ;; + esac done diff --git a/notifs/window b/notifs/window new file mode 100755 index 0000000..823ea72 --- /dev/null +++ b/notifs/window @@ -0,0 +1,3 @@ +#!/bin/bash +dunstify "Switching Window" "Current Window: $1" -t 1000 -r 5000 +swaymsg workspace number "$1" From a7967a86d7d662d04a4e262b9bcc81759f3588e4 Mon Sep 17 00:00:00 2001 From: devaine Date: Mon, 4 Aug 2025 13:00:42 -0500 Subject: [PATCH 07/10] test: no longer daemon + added 0% alert every 5 sec, bat status is back --- daemons/battery-daemon | Bin 46424 -> 62568 bytes daemons/battery-daemon.cpp | 112 ++++++++++-------- .../battery-scripts => notifs}/battery-status | 0 3 files changed, 64 insertions(+), 48 deletions(-) rename {archived/battery-scripts => notifs}/battery-status (100%) diff --git a/daemons/battery-daemon b/daemons/battery-daemon index 3ef384b6e77654bc16e11d20a576bcf9f3f75d8e..26dc28f28a655a803372906188f2b1675b064cb9 100755 GIT binary patch literal 62568 zcmeHwdtg+>_5aOF0mZ}zijUR>Q9&i_<{j|C>*59i#v~{rbXj&cWOb8WH=6(!EHz+B zV>I=vmbO}>wU+wRpZxs#Kk(E+O|$0f#KX`uN?#J7`uX|f&zH-90@v{Q{ zwIxNf0^XSce`E88nH$PVW|kD?HwE(xw9}DqCO)J$QN~oXXq*k=CTm;9Yg;nMT|C`> zoK2sbmyx?H&$dSM?mFe@VBWYb0`Dai?k3fVKEs zg3qP+ti*?|)%bh|9~VB8e(ibUpCzX^7u|H;*1^lF`mflzcxaDv4Cn`yOsA0ee&gLInTWE{58Et9$8WTar-In|M}yh&x)?PefqbqKV{LW1GLR|Hslv zq35UGf8XMvpZTx4Y5UVRZvM@zJu`o~c+0Y1XZ-yiXI0!YbH>B3cCEbe*<0F<-mz)j za(CXR3y;V>vV6(V-`<+BY5$5fw|92j9{ADhlFFyv&;QE*P38Of>!*L?Te9c_G%4kE z-qD(NOcLq}^bbfQuBHje`TelhWO8nUc_hOhnL#Z@Z{xQhcc7ZCkqyz3{S%$8UE80_*p6H*_VR<*c5s$Ng?ONBa+vrCPn@Cr6_m$ zQOV`pmxBM96!@odlgpV5{i6vZuG3PqYkvy)Po%(qIWc*;vyM!Te=mhS^rn!%0OKWD zzrUKo-Y!p}e~8y?BebAt9VyB^EQNlmF+n7&56uI~LUI;)C1M@( zzl$W{yU0=FBEL=7Q7mUSIdNR;CD81!H{##PjNfMbJoGQ&Z{v2&W&HV=)Cm6~O)9uf zl0XY!91(ueWfH;75R)L`=d&EeKN0oM0Ijq%Ok0&i$zD;s3Bc`&se(SWXA)=Nq=?=NZ3&?e;;gr?T6A*8c{U zuk`jX*K<4ThhinVGFZ=BxLhtqyNB(n>Q2cAHgTQG^|b2e^uuJktoCpLu%Y=^5@Z-tD1mGv-W(c6=(=OMOdHSTt@oUhoPD_M?<>v;tSU@&)8`o8MY9bDYr`{cQMZ#`>q^ZKA*SPVemjrx` z>mv1fi{@Hc6DhE}T#@>4aJ@^SUA}NQ7_M640d?8fg%^7QK6hBiPN!~;V%Jiar{3qe zlB=UPG|0NtUV_?`pr$^zx5QNwaYuZ5dQ~h(5$>iY|GGxKdPz+Yl%GyRg_Kap*XY$z z!G`pNl%gtLcf_q%vAVf|LJiV3WQ%olroFk*e^s;36^eu_meshF5=xl*6JL{Eg5Ub$J?{>Tpo6xI=D_KeACzyR9{@hG2^iZ8a3; z4>#%6{`4wU8uo<(ZjVn|2vy1(sA`dw3kI6>G{r7(NaG#5={pKYOTk8rzh+OQ0*%!5 zmReU$8LYLyUKb3ncZa<$KRUx531U>b*QM8Rhn=j(hhl>1cShLP7svY z=&%nG#i!xJghhJ&O;DXn0K9>RJXS-RiY;*)J|(>hEi6dee#x#z3@Y;C<*d~pCXtb|iryG8 z{QbY+ELs-~dIdAYVBV4w&sw+FF*%NJG>F~ z_eX>uPQ&cW3?~c;u5}wWxI(^gQ?St;fMa&G*hznw8>PVJQo?JGgft!_#5GVBRa=6WGCoO zSP0CXViV9@b#8wEj>KPMci}%dgEYF(2rz`wC{#EI2e~lqDG);{{b>Q2sg{lF({!Cl zMtAx>RO+gMI}dF%M=N8Fv?)gPU>-uKTAl8Mz?}gTDN_D2*c@>M>s(=X<2s+Kt|0;| zxti=F4WlkZ4Vy5+&C~JN#ZWsNnng%1!u51JFonMvdxhX`YLwT4?lDf&4K&Ic+#4_f zUF}QTZ-{8k-xLZq88Xx;42~A2#7E&lv5ykfq~SM;;!RE?cNy#<;BWB5!KJNt8alKN zR6a5t?}rEv6`?6duCmw(j@4Ty6H^ljCk=+=61$qBOZK!JiPpMm?Jm8h)K#5&oFbyG zDR$M;>J8%=GRoDE4+Pg?W&iIj%`N_sW-yL^mN%~VH+o&wzNY2|pVsJZ3^oOPzL3@w z@%kGPXkaCZ1-YmGN--&Eo_eo8tob(hBU)Y97exH5HTW86ZKpMD#P&r4GKS$%G{g=e zzn8?oWkC*2aJ=Qw;D+R|PiBfB8ViH&+2D57VWAqpc+;-*2LdG5(-0CMv{7rq^43Fw zz}^@XJsa@{8#Ty98J@Gxro+M#N;VjG>pUouQVzmut$3tWZycJ0E-iunsxtiXLy zeqljEvcRr^GH-r`%aLE0NO71b`Q-_;!UUNKqym=jD#$OA1l&2|w}tWBg8X9a6J_GG zG#mdiaq3Fn^e>xn**Hx<9O*298z;WU<9mX31imHbk=P&0M4Dt|;K>HcjdR*j{^8UX z|K#^*d{&yuk(D5uwz`OO9X3I+U8mXbsXAjLcKVLezQ-G*8932h#`I3m=MVoFJ8u)U zA22Q?(no5ya{3PsT#23IBeY*~`YJ)6pzY%H##bJI{Bhb-oSrM_IogYyj?B0TTaMY< zTbvFHdY1Ngr0L24t(j?*KF;sQ4(E97bH?5Lqg%1lhs|^xa!{Kd66wRVBRJiBk{>(0 zncDG4)1}VU)jnqxZ#>TB{Yiz_7++<<@8I+b3%>CRDSwRxzk~7h7W}h}@3P>vW+}&Z zr-Z6LDm~qT9~Y8*s$R6sNzby7F6i3H@(GWQG_LMDi5dJ(u@TK;v3C~ANg0hC?6Mi=H z3eY7JuoT!-{2JFK?+K5iBjd{JlDCA%ma%b7?vl5Jr!v%K>ymebC!MNmdY8N-Jblz< z?~-?fgMk`XS(m&e`~(9FJ(%zqP{!pn;c+BjTvaAK28?m7FyYmi0TI`j@HlcXu6h%m zhoqo|On4qz0>8n8KUQGm*A^50I1_%m2|vk%?=<0$H{o}f@F$q?T_*fw6Mm-&f1(M$ z%Y;{FGeq8P!mBe*!grhS-y#HG`?}-}^_M!EAxv);m+;4y7Y&|f}*`D(}o)vO#>yBi`dY!G$j(bLk zh!wv8bnNsO@tu3>T-;H*o@fL8FwzsIeGH{3 zq#g)~^aM&%C_S)7q%$c^A@o3%NPpFcG=j)@U>E84DNRe!0h>s_OKA$7 z2l7PvO-fV9JfMm6tCXftd0^^SdE2yk}f{FFfXaAXop z^9(SNX^xNe=bpNS_?}@1{;LFk@f8GSeB*2zaz6dR1DfiYwC zGx7SO9&@$2c$(x5tOM_I$f$9C|G;Dn1+TL``*e!#oo#y5(d}%Xct16$V;gCZo-736 zC1+bt^hs!((as!N4-?W4WZN^H3J=T=z6Pex*}gpLb)J2kkQ^x+bU53dF)^(LlhewT z6KzM4B()Gyqt{TPef9A3&x~H(Z%BPyT{3;H?kr@FUX8D|y-aO;on-frSjkSw4WbmC zDl646va-vGWn^XlBHGXsy$r!sWZ1 z9TTU|q1Nc#+={x%v_10wbtPbWZc@FyR8U@n9!DXb1!%Y zR|}JoO}`dnyfaB3tVG*M2brQTU}2=Vp6KO5H5MbxiJrsUuK`9+rG(LAG#a)G<9T8O z>SgVv_HBf1&qD!S(v03eQs}lDD(`~6x~biAoO}TP3BE1Pj!C~0+E}VO^9k9RqKdn7 z1y>hU_da>Qh3;QFiUjpU^MukM1^3qzJwZE>!3L?FZlVW3AIy@7E<#*R8Ae*{IV2P0 z*90VO3Gp+NbAR)to(T^yRpsk%!TPvWKwuftz=&WNK&4E$$jyf93HR9Ly0wcT^NA1COM{> z{AOajCJ)GaQIlWbo_wiNTn`x;YMV6-MaI#aC`*DpY#MAfb^j{Rl|2Z#O9*kgiDR~v zV@4cdls*Z#Bv!Ql3)!nTpiZo96_K=UTu zT1u_tc8EG{!f=4U-`bAM?c1o%t;1`-H^21>p{V9^sWFl)sck}+aqYF=LX40}S!Jb3 z*@G2^uxqBZJ|T47e6q|&!s241Zp_ewCrS2lA${|a=CUw3{S$phy1EdRHKwSoeL~K5 zl!hBfydHOA1{HT|61@iY(6+4;wALr+J2P=+>?k3PO!}1oIaNiN8UKU|Qfcjl5^C>8yUK_^$-W`9*=Lv;Ojt%46xQo#28tpOblllt*>ey+$5OXJr+OJU7Wa zb10AMtQO>H-$w0#P~TeaEHT_F+PWVjOWZ?WPtw;@^cBteSPT$y6QsIqNHE4=Pjo33 zs7UsbL{Rq+#bN_~kuZnkF9Xe!hT(;dP0V>8SdvT#f286dcr$9DLiOoMRu&yO#nfDI zenL6ga{_LvSn(90go$M3&bGbIwwI$GNk3Q8CrbL)uxO?)NTC0N2ptpOj?+&AJ$eF+ z9!_gDRy1#|A&wC%ngb-JBTY?+)prO&iPpKb>_#_z=s(p&!7hXr|Dm>ex{Q;!r8WY*xA-Rt=gATR@72Nmq&jPi*?GCD0ZassF|voM~VEfhX-KugIJl9mV2Tv z|HGWaW+}$r3fm@eMBGJu78Spw4reQ zK%4$`?jy?wkDSJ-7o6>zkq0f?8|{G~ClRtDZJXb2YwqjO4^SThoBKdF zioi|#@}b-Z_O_91AoWk%x)FGyh`w_D?WP*6xhK5|RFt)0u;3 zAnlx^4>KZba1s#0+d98!9&Xd$Zq*ND58iJcCDd&_s0bogY2N^H^s$|w;=&*hYf=!E z6Kx)}b}x1x;+rtT*o5hzPVI^Q7}knTCiTHaG`9B>A*gi-X1jbizQvLgU3MTAdp^WGB577lQE>Ja$!VdjIvbnZ5qOYN0s172` z*9Fm1tw+(?=aXyXapW{?i_DzHEo>P2Vx9dEOVdss3N;vbWnkK8uBh0tTD=d|c?0?S zv5nHMW32Z?!;tN4ds-4OC@`?ziGG)mg0&|)i$qIqy-(bT36=8R1~$T=ThyjdPV~d~ zmA6gWyL}{@Ykd~FO^x60ACOyRPmBYy?cr?tzJ)e#&8?s=-)*vPD$X;wO zv6njAw%NexY`bMTf!+2pd%45za5(ok3LJ%wB1bVe?2@BQa5zdFrH(R3xpPl}y}(gW zP*4btxst;vI0}jiiVI2#O2JW9P+n*+#LnEdD#=04(Yd>@ps=v8sIb_%r?8~3w6LtO z92{~*dP}|FD6$thiVBKQ)1soH;-Zows%c2d*dRH|ipq=a#i(gBen$foHINS`}grU&hK^@n&&*|?z zb=%;@&Znccr=vMn_Ilg)&FIbOZrgSW$nDz%Q+Fa$o4~dUjt*x=`z<8Y*}jde0>i0o zU+yDYb8JX#&ukY3QpuR>6obqeXKKBl1Z1{8L4|p8f8Yj?iSaEL$|_KWCIj6rzEP-> z)=`y_SRQfZK60}tpP9Dj4IU@Ow07H#ieQ>28#fP&zP}q@$L#Omti{; z6Kd>K(LhE7fIdQ3yg)tKuJ3N`*T(l^H`F>d+Oe4;#7vcbrAMf0t($wb=3Hk-&iiK)OHcH{_k>i4=!rVT4#)Cdu^U3o zr0@_{Ax}#MUP&(G%VIUnEJLLZJJHRYVOF_mC7IjOe%Zz+CGL z1>YXMAJGsj1Xd}+Sk+~J1DbZiZ-8%+t?G$>Az6FDO2QTEM}`DxtU5(RWXv7p%6g(x zq;Zqoi)x=vOA#13miZ`BSh(oP@qdG#7>+d`8-4j*xCZQXWLsC#%`XmSQ0FHMgpZKV zn5yk)Bl?fScNrA)bAP@jD{AfL$He&vb+hmQax_&z&Q8ebdQN|o%pUhW(GK*o2q$>^ zX=}F%v=<2$Y2IKJ?fDHA$2q@;eHdxpiu%9EV(7So0vQs%msr~MF1kNnm=pE6DEDxY z#HgmpK`35^1%h`N>ZW~>TVziGhi+)cr(EBedkgQv;D&gb5SFSk=s9B?sR&A>OkuYG zThaEcN;h7$f&CMBdZO!< zMYKL!Wq2p@E0YbY6+vcC^h;qcWB~n?(MFpn<KN)n9q&JXB_XGAfg=c>` zdNvl~*3BsCF4RnC6U7RV`Y>W@G!%=LA+;Yh4~uxLSXwuKYB_=X{;4RgW6}-*a!#~i z!$+OK)xLv4y!lhhHq;cbAj3MDAqrrez@3H+Xw`5#!fiSJmdg=XhRW^62;60!ar@{L z7^VRg9FN_Nr?7{iMR$>iwrA!&PKz#(ckwvs;@p9|aOs+Ap@J zmcp5nRN->xkvPHF6D2Nv>wA&fC#fWwFKa*@)GOM4t5|LZ%U~7|#5d94I~#nJZRFDX z%qyXVqOLvB1At^ZMC8NWoamWEY<;#2wlQzDF?V4Az_SZy-V@!1(yS{f9Sh_ zGH7$>(NaQAy)ara+TKQwJ*R&`od#4-bWx0q0keWw#eKo6xYKBZgA`!a)qnpv<=SmO zx=Q+#6DSu)9m8DaG+4Qc_Gs))M^(`z%;`MC-YMg@ex!ThMEi4Fw@}|xwK@{!=03?t zx_|&Yg+Oc_D`>h9=?xS=h@b$C!>AP2HC{Lv$#8*eE(;JExmgy)l@X|)0VSrrgqa=< zlNJ*9AFRX-Q7rimq0_IZmgUcglkEt+6ZPHPan9D^T=Nl%DULAP2wK&M;zwE>)G=i0 z@p$Y3brYqE;*Pi#%AGz%8)wigWi7&vrZJUE8&dU^wkTQ@rWRo*1lx97VNp6B!!SxR zB&$vpff}hB_9~}N!*->H^kHGceW*7ah7m4}XpuoN*BOBpo=V82N{CI6p6Hcmf?8m= z9$2HC7{<@e5o5vdR69{UvCu~iL_Mj#RL`jMYzo8M;I^pp=(Ls#V1<<0E%$z;RV_89 z-`#BHOGNNWQ7Sn$VMx_L4UU%xj9fH$H=G!X6x73nF-qV1q}YN-IE%%t@TX$Wt(Bth z9=#U^LBqi;I)`E?j`!XWB`1#7rEi)?EA{L!hTIUe5=8-`SI@;}j2JD10tqUuzeuwbS}z|TZcuP(PsuIKV)@4fY_uG&3c~Dv@vAa`}xhEOs1A+R7kD>P%)8NM~Uzx zCi9ma9Upm4)xqZKh@Bs364pUI$8*Vv*!h&P)GkRuAT1;07`DfpGoBGn0&cd0KIC3S z9mdH9zZ67kKB4shX%(78=*v;V`%)vrIDohVhtH$|QnnFs_L0#DWWs*( zsY1~dBRcpV;4F@>ZV-Z%>nWA!jx$o z_C#ai(GxU<1B{=D2#+J~a=o`{xojv!qi_`+O95%Ph-$njnc+p`2=bC@yV4?wkw_}W!V>`(7~=#n2Wz zvGhK^gmvt9|A^c;YeF0QjcSUMAyvzSIYVgh*K+Fq7hI2`UaCcERIrcfqRCh8`NM{U z@18+Qqgwo2&}i{61Qr@8_<&nf|7S!bN)I_diXXTVb__biaEV*wJpWIp@#pW>Uij%8 zp^Zex^ts#zKs^%o%BMmxMt`t(B=;4smNCa!TK56AVX%EOfzGC|gRBi6iCmB{H%~OE zb@T91EyoBHnmd!0f^?ikd0BWD>s)CbuQ z{DJI_ctO|unyO_&+DAsWvwUyU1wg@8jw7x1OVcrnzR@gN1}%`gk+2kpwPh;tGTag! z_*dFYk-_~5v>r11MreRzXpVa6{o#g|Vd`SSftIO`qTf+RIt3dV%qEPAWs+~AICsP3 z==vTM-3xw<)t=~Qavh6iQ46Uf$k5P@1~)vR*e4PGzL(vE*vE$Au*+&Sjl-eW1j{q` zOdo{`a*u(ELnD^rLq!S)^&_|%lbrhc0k^GYO$U2V9LY1Hws(^^VUWBv8$uQ@6O>Cr@)!%1{m~ImtKyxOQB7ErO zR1hVFn$)~tCpSh7G53pDuMsB&#xxt_LjfE4F)8dN7(geaC9KTcP?SkWlMPE_NBfi< zoc+irRFBP9bo^w-(k}Iui~TCLfJWzqQ0Un$?n;eX&jbI<5SVLJ>?pB(jRPN8{SqSX3a)^W4r>tyr0qiiyCY{J12q>hZ4q3sxJM(7 zBzKICLVWUa^i{GsvA4Dc9B9|lZcvBV4NZWP9=x!U*xRwaBZy?;%_O#OY1`8kZ4lHG zeH~H;Pqpy$5)U&xE$0I25>@>zqOs~I9yVkXHWXiH2+2Rkwip^SB&V&jkzb&HKxkU5 zi$=l7&wCzQJ{WgmyE6Lq?@5vRP)heJOnE*DAvcU!5AwI`u`QcpT7)K&zQH_v3oh}j z4!8_iNo^z9NQ7c8!={|63%UJ0QQE8%`Znx?%mu1}zeHR>ozo%0e4xaHa1wlpun9V$ zz=QS7OwVUXJ&?9lx7CoVY2DndMb40Yi>?uhLL5xJFE)(2*-M=vnWI|dJSDY(q>7Ub zbauuIFdt|`%os4}y`ms#MNp8iqIa>or!r5*{tp+2$_qY6sQP2nSv|{zUYs? zGknoVdk}sREfm?mo4}c8SdOq%G*xo_3SoZSiAXLspa)Zv=w-iZkHX@=o)9DoCe>_( zCMLRJIX#M=JcAaBP!MUdL*LIm`7UJO5eGW96I`0$q9ue#-cRb2a|z}8JLkGje>)wc1OPVA3ralbkeZn_Mdyu|~?%}6|Z-QkCt16*=n=A{X4NJo?ZOoHbMFq*4_u&?DdNIDp z#}ex>4-_h9D#gA%l3}jr}*CD>9zyQ@Mz-Jx?~xSlhxD z6Dq$ujtm8(#Z3c6~}Y3H`PJKW?!O-GWY2w?-N6h)t9$dMtnrHg{IQ*WwGz zEVj?2pT{5xGn+OxtPKYIo_PNyl(f*_q*zgyXphl|QA#MaClM?fk|3RJ&$g&J5P*1< z^f%e+fZ1phMfdA0Ug zc%5gYxk+%2%BNm7t|{%NvVmrQn2Z`%OVIBf)oPpY7IEM7xGkO|xf4zJV2n4}_#)G% zH<+5bOPi&IwOL+Y%dAG|%lLgJ`EFLcrWBn&TMile(FTlQTU|KVU?XN5IspA-Yr*?S zy;?$!+43#@79_p0R=n-mg6DVYTd?3(1dNZS*%+XBN3d=B`g*^op2|M`>gE}?^KFrO zf78tK>3zJ@XIQ9G=gGd~?|`RQA=8_gUH*npfPOcm434HHc%?5~Q4=YSG=~DdipAo$ zLUeKAMGz-v&DnxLk~aC@+Y^gDg!^ev#A3Grehb0sn!`2iMZoQV8=s2Bb^+c9*b8{} z)3Mkv;AMMbF&mx(xawIf6aZ^}8;gYi*JH)n3HT2z=ijDa^~G51Ymy6i3ih(61D*>w z8*mlirGT3Nn*eVEycO_qz)P^-^ETjiz^?(H0XzlgrP(hbKj1RJO@MCzb^)$=ITq^% zYys>Cyate7ka!oM4R9Ec&aaQ_jm37OerEvE^CO*r!+`e!PR5fsF9DVTuEI|C8o*Y- zt$>ZM#bS2@?f~2eScy}jC}03*XL)$6>_I>~VD%fZ*b2a_05Mvh*8$oA z&wVErs|8#P7yn}FaC(z23Q7I3%CMs4d4dAF2Egt zdjNL>z6ba=;8%Y|e*m73M?_CK3XiJ+o(uS&fU5wX1>6K!g@;1B0Pg|p2AqJ?w)X&Q z0kd&97Qq?9bikYGECKL;1Jc`SPTG%l1I`891y~2z3;0{WA;4*PraTWDg9`!efOi8{ z0S0hB9Rd6y;LU)K06qkG0nPwl1Kb4oDc~o7lkwIY@B64X;9kHrfY%SAp8@v+-VONe z1F_h%fMtO10agQMW2<;G;55K)z=eSQfL_240Jj3J{Q&s^p9FjsaKeYk4;UUo{$ufb zvw+h8_X0Wr{|p!a90t4|kp6n_KEQ_ndjY=z90IJveq7#hs3)Ku@IL{o0Dlh{0*nH7 z0+wL!Zx>(yuov)$fJ1=y0OsKY>2W|i;Ol@@fJ1;G!2bs91RRgUlU;zP0rmnG01g4J z0?a!e;}OsfxD&7n@L9kR;D>;nfJbA0c^6<2U@zb@z#+g!z&spJZU?jjqDtb*xVl=) z*pQcT@{xy+>%@^L;prtmTd+TI25~Gr9K9yr?OFsl{Sk~~3jL1CTXfXq#kohUAGbxj z;G{Wc6;7k&sK~nta6C3GpmscXcvRkXnF}T!wiE?0eG}l<&&6W2v^D4};`G~q{t|Qy zvp9WGoc=h_?|}}ph|??M^tXY2eH8g$gZ>B5Pci4eG@kzy=;3A1PczfM6Q`dG`m3OK zo9UUq5ey`A73g8Kzrajyi09t~`c}~AnCUHX`fZ@!0s1^MeN&wNIOyG=FE-ONb)$Sj zXIx!XDSGzspKUu`(@s|XYXfZ>J})4D8(?-1x>xm4=2k<74ShUoALbzN#ObXDoorwS z=rFA~{o;5X*MNQl=y3UQ`usS33+OGNQ(X-Ct~k95^s7O?#7xhW1(1%rLEi;>vOXl4 zbvZpnB?fKSy?w_2tmZx1^U^K#bWuyx7EaldSy+C^X&uOf!Kgv1C`xu;X5MJ zkT(RrDUZiu?^6AnOnfLevoBtMlJge$=;_bw)h0faOZ6%P{T;*>EmW^d6X{ugSufHx zJvUGb+etO{R)D?;bU%0vU1oYkMJWFU&^tjt$xL4!ujdZXe+c?CGo9^%aQ&#~4WM`6 zK6@`FG}T|!UTL114YYCSuTQ~uCh`5;#J4tHRyK6;1$YJ_(~y(3UnrMkR6@pH#8Wd! z#&=9IWOIbCqbKS2f#)Ic7&0;+5QR{E>37jr{SGk@>Gj(t85drk)ta4k9BG#H(g``W zFUDev66DM?Cg}A0rrB#u^^ooHlJacC2RT!FW3lySIn+lcKe!b< zE#UdFnJ4omQP_0gUPM_RfPNI|j)ErD=8BH2>$6*P@HTv--o233i&!z0T@8W$0_aoB zWv!C!qIT!OQN0LyD!aCW{v_z%Hs`-Ap1%t8$3b_R>6r_R`h`G$5A7>v5;&pfpJpJH-m6>!Em+>j+FM(cVrjrcQ+&USP^jF|X zrN=VRKOKd>0`w0+r=5SJzb=W_aRcb@k3!!8`u#>@dvv z=bH0>H=du?Ale_2x8DZR8t9Ppa~AHiH(?>6Y|iA=i7fw9;c9ut)M$W7rz~>^oV+7 zO^T1}o#0vcI{YX24P9Iwujf9d(;wa&^jb-u22>u}dOq?8aG%|a&Nr2lX_^c2U@+H$ z?+mjXcf1@s=-Wr3SAl*5=q_{qOzC(@7a`DpKMK7Q^dqrmP35;TUnz;hMu6Z&dhTn@dP!1kwDOl+{4`?Ds_L%*lI5&nRzm$@lc;l1AR8=rDi&GN>doo7lK|5dMaP(1-)_<`c}{vfu2g=cY}U0=nKr{ zUlXtYv!F*nPh~UjF+JIQK=scCVJ-5D-%?h3%nBNQY8vQ|V4q}_x%^DIP!jD2{bSH8 z6X+L;3KHE5`nR#aB7WoDkiXWDzZLX4?5E5y=jYhU2HcD2!w~2+CbMk_T$BtRm&7v! zo-lZ5zRA{1Jf=D40Lt-$CsjWkiEx+pdy>(qjB`N$4(NB`-lzkOtsU{cr~(h|4J|hF znAT(=&=132k$B^=YDbpG&_yTc%dj_;%Kz*Fy%zK{&GJqDs~7akK~L5O>Wd-JmybfH zJ)J7hmzm{fRv3Lrdp*~HUSg(SWokd@H-nyPO&;j$oC)HT*WqK<84uM`diu^om)?5nuRCD`R$NSd~`emS>W~Q6=M5;hv z1$wIf34wkI=r(hH(>&hE^i=tGfllpD);}bY-YkdD#3HzWikv3xjttx=L}kuZ+U<rggS0#B~8C2 zZl=?3b<>4yR(aVaz}B+3Ub|94asGnavfE`^>_g!eCobX=`#QK&zspT~UUVt?XWz)9 z(O#T$(}f)kanUnubm90`T-%v|V;^x%#l8?-VqXt%vPfwf9+^tGSgaPkL!#-(pDrg8 z)bmg{-V@ge47boB5-x?`E;h*G^&VzLj+bmk`TxyFy+Dg5e#7bQQzT=I)2d!WoZf4b z^e;KB(S@Q7= z&tO=>a1p~x7}hatVR#+GTN&QN@DYa3F?^ljeukejJgk7rXLts~5{8QyUcyl6N&UXN z`aO4rzi`2V*|zD}m78Ice+gj2+n|f>r3K>PUvZ6uWMiy8QO`)_)z66!p&4|qSm`oV$?YQ4&%4iNW7{(osk?PQ3>BqAw_a#%#&!v z^AY3cE|7R}euCR?p}|LMCmX*Pulxi(uSIemU^%DBY+4oLCs2iPbx5H7P{`qg3G8D2 zPPsj5_>Y$u|E^y$i}MKFX24-kxmNje7=Ks2t5z6n<+ek&ZmVTUo zO<}_Sg#}!~a!vz25A`qO`ZHQP3wYIc3P;e#a*FCCR+alO$5?0{;t^bM8DTN1RvT_FWvT*wEg|miE32{-ZS@8IWgH;4JJ20mGP>BY&^UK=Z3JTrnkhhxGayov|Jxe@Re zGJYr9r>Z|aUqt+uSnBg<#^2zU6%pr4xXpz@694OLKV+A5RSLYkH-E1KK3V;*PJ!=8 zfqy0i{(a!7KK-j?sr$I_d`y6Z*DdzEiShs2CmEG~?g9Q7@c-up5>L-F()GU>e?L2_ zcO_bTn&r%LNyJr*r@twna;LK1#4`Y(UB>vkSkIhI`zhlmvp)>7oWC+Y!gAEOn2CUe z{JWTa=@=&Axfsh)v9I`bq=%`1`{KS0EC?yZoR8B7?5fNhF)ycXj5dphIufv0jm zXTNtAJx{~p4vwYRw-vU1c^|#+C1*5yg^;hN(T`ALw z|8&HCRL}VqIUdG8Sta>@EP?iY;FHz!UgqCdFB#8d{!fTM^C(TluZ-5dWd3WaC1wf} zreH`XlYc(&G*1P%UCKYu-&9cjpIa>joXv8+!+5KH-VpL9YQLk0>~Ya^#B_bY{L}n0 zy-Wh__&h29W0v2>_!W$|&PQ8-r+QXd^nVlcuVOvp;8R?5o=){YYpqO+XGCy&;?WY{ z!}F1{EBYm4;!(T0zApjVE>@T-I2#i{AKeb zABKatihxg6?uE=hf!ljB^V8pcko}zXm>8{r51xwn7THp5;tOfJE|t$m7VycrW9N zxL=T6T$>ngo!_4TK3Tc5Fks=zMqD1?MZa7sNs4EP@q1W5%0Faa91;I>Y#>mVxE3+~ zhYMs{`S~@B|6qy4cS)e#!}w!a{&vRy9(eNqYb^f1A9#xMt#R)^Sk5A^6d<1A!)=j` z_)peCEQiHw<-k+@zxy4DIgbg~GyY!Izv{=w7;p8rF~+NXFH}QZ3#TO4|8n4|+^5*i z#ouxu$F+?A#wYVDeZIhW>$)$>_${j>ze@t`tf^A|zBLkY8RI>S@2Zvv)xUQ$zJ&Xo zyFuH}_;=YpS*_Yxr%CzNdWL~dW^db=-?}dNZ3_N3nE%=NvYvCe{$HlxFPbLnb2j%E zsx7W-7;p6_yBYuLJjvHBf%X^0&-3ZojQ{rOQvMp&v+CcAfhW6~dnYq8SPgtKJ>1B0 ztmCMQ@z(X-JB;7W_PL(rPdkI;Pt+bRk_b#&;wlq(PMClfc(S)=SwG6p-vm6(N6ymg?l>;@elFuI;7R@&Jl`q14KV&Ow`5$v{0|E`oG^jsng0e3 z5R{(j0a7Y=U5R9-zeA(z1XMVgo=btJ`h3mhDmg*M?_47DFOooeg7HgPKQJ?KWzCRs zcCMCbxIu9(Vf=e+C#t<07;p7&PXeE;o?kHkc2=;B+f{UyZ0}`-vb+bl-1!2}2@_ZW zJhiuFo^64dbnLekd0s#AF+=#no>(f#_eCCdX< zo(A3J3FtL;R|CDwDA*ja6o}l`dKUyDdi4@dO#y2{Nk7;YFK`H5)#5ECk&;L~UbW(7 zE$XuH6)RkY`9=8!1yZAqVwbDYx8CIr2&DwWP}@Q&gKB4YIUzy3e5siu}z6a-g!E~+U^Q`u0S@q!wcCm8TTT0_+mPeX&OT#2VX9Bd4hd7Hz+-YNokos77x zs0=uC`sjMm)xL00bXrXWZ=XqN%g}rZW5C$4yJ{USUB9TN%vH6drkL78mh5t^^F>_s zzCZ}CvZ-uv=s*^EeRb~UK*Z(6o38t_mjrdwd z*4y$bp#_eL%C#^9e4ub);AZbv;P(n`Ho6;wO#z=TWbqzwO%8N$z`qtg(dBMx@`WR= zI=4Rn3-rV3;J>*F`Gt7lnsBcF(kk(8HhH05sAQmpsz(j1xCk!J>u+>5H~GBW7k282 zW*_xM5?e~u4Nx9b2fJfF%ak!&!NyJ4@07DMnDrL5CrP z7gphwSrK~mRRuJw>n*h|3^QooUKb3ncZa<$KRO*Rg2VLVUdK9?gJO&=8O`x7YH5L` zkgIi=ZH4@|F%)QU9ef`cwzr_d0sWZ;iT7ZkV}x~c57Cf580Uc*PvuDIu~O>WT>~Fh z1cSgl?{?K;AO-MJuLQ$KR|I^CQ#Yn(PlI)MP<_kU#RL%&p!Sv+L2r!cNo*u>+FXd> zBVib+nRBeBR8E#8UVWxls|g~}y^@1od~izE^-_qd$K0$}3w_bRPBxc2>|P(w?LL~F zGpRl%dk$Roy^fc??aL`OF zr(Em20~cFO!+Ws~e~5{MW)r)dP73VGtI1kW02>U$4Vg_6qs7}4bk(~Xy#XK8o=#j; zfLI~yqc`ZeAjTDJ@rBpp9iF}l2y-Qbwj9{P4>lkiSVktyQ(iDkg9R0+y<}u3n=34% z&vdFec7LodD{)=e3@^=fL(Rr&38k(Y%mJZ|7(W!;3B&e>oAheGxlz*I(+njXBSMM) zVeN>q2FJVb()_TmE+26PVhTi!O+j+Ok&UhviXJdhtuZCm1k!d#3mFT{AtU_qSbLy| zW;PkE(JX`&nd!Z&6@J~*;^zq3QEm*F;5xiEHu-oV05{q6NOI;2Ayej2NaAt097NBTyqhEX@b&&%z>Y{ydDPrB$_;2{jiYhOB51 zQ5RgUTn+wDTLjida%ypun&%2%LnyM5CyQh=PMI;?U@0W{(&+HA2KNRRc_S5~C+jV; zJ5xL)B2z=9a!F4?z?hBAT<%yiiya4kXQ|#G7K7{ zk3y@^MnkKPBD5wQL;QaAw3|VNdR56Lm<8F$mrJ*TQ8KHZ%(m`OY>lkOW zFpuNJ3Ozt!w^*c=AS{Jk^q^$q6}*mNLcxy%!4ECPRqVr|l%v?F6<(9u>}#qB7>j)$vdzVfW@>SO21^=4}~zyDqJ*5N(!=g{TV)pJZl? zn1J*U8L{t?s1{xu(dLQtg%UxV6Ny2d>t98PV(O9Fv+Qe)dE6%K3&o?xWPWEXvxKpg zHs%UZ_(^+m0atwTi*Kro@`7SoX!ynJiz^n>E*h>Ds1_#M==E)I@#~CNG=(tXNK;M2 zNtRLV{0;sHoSbxUV{Kq5!cAYe#RqF`!gh=&+2-ho?Z@~kSZxWKcTBLUkUWs4>3)Qt zJNRy-Z9=(CL4p#t#N;;R!HY7l+r%~1h34J=$sFB!xgJr*id|@kA@I${RT(d`lS^K;%18WD}qg~i=Zi-yzpL-VdkJG zS&@t!kID8`#3sXN%a&sEvSsXDE?1m1$9b-FdFrneo3qATk4OrOhX_^$wM!RZA-&Kg zm;8n3NO9IOrsYUAskKn}b{D*9gRe1CLEH85_t#_7oBE~jF zs0pV+*aL9k$3TpA*O<+cf)b2IF^m0sQgDWMMRTJkLMB1gPGiPWCf}m@Hu#fFHXO7x z_>)C8GC(ICJ3PS#{7Q=W?U9Pb3GR!doMmvy#x9tl?YF&Ohug2Di;jF~MLTUBHYOG_Z#NS9~t2*uGq-m_;ITRlp zXV6%kr`1p96XO;QkwVDt9ceG=S)u4$PrcV~8TMp>*6^^X0Vf3J{qTc}>Bvo*=K*N5 zCSnSBTLn5V>h2frMGS!t4YyeQR3im6v<;> zbBkf&V--x3XW~)hSS64G)DjV^U_=8r4)!6MZAm;~H?P;@r@SK!1zs5r+K-i;d^p5% zri>>e8nIl46^}eh8WGdcu}zYJfw5?thfwUpdWpy$UBp39Axbmk|7GHGu0;aLlwO~+~r^7)&@xDd1Yzj$Dx zXk9Sq9pS>$FlH?7Sz+b=rt0QK`sGO3l3<`oPj;^QFCSWx<`{QZ6}6c4ePQ1^KepJz zSYro*jlNoX5KVI}Z(Q%kDR(tGv;nriU;&&rfU9u$Ug#U{44~R21+vHHobTdafVglyn7S*LjKjaa6UPNfw&8{wPX( z;3%Eg8}>KGXIJCTN~LY|m?BgpG%hNO51F;x&PwkE}{qk0-X z9d}J<@Tn;^%Sm z#o{ic3C4PNQ@xh&-Pj1B5=O$3f^GLE{BcS`!UY3qoFuw9dZDh$iu~Nir>2e{V;q;l*&ID_jFy34;88MtM~CKsNTOv zymVRX?*We9b*J+0wGkJ7SI;JZwpRkQ#in^+Mf;Zq}&Tr%V3eHsx<4k!L_!CQh^_~s|!{wa8l22jpu;f?o%TaKmVq|Ew z|6g)`qngyAbTOQx6fd!wjiCbfm{dxDF5R{{rqPeAvv4_iL*6Y+CE9(h7cr zyt$_Q>iwGvDn77?OQjY3C$Q8eRlfTBb_MHYHsf36Q-}%3ti~qc)O!aN%;Sz#<3r_F zc6==Io6GMegT$rabeS~1Rel90Tk@;-e=11t6Et5cpMp~?`PF+e6ttN$<6h-c=rfU@ za7usb{RZlNDt5)l&}K=W!{uA~RDP@fP|YzzHFnIRSKKL6!~DVn`g?rp166MnE0@2cGr_T%q)&6g@y;f}^lvi$Zz$x&>{U`cBK7Y$0us{jB1 literal 46424 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 diff --git a/daemons/battery-daemon.cpp b/daemons/battery-daemon.cpp index c2f6500..4dd83f4 100644 --- a/daemons/battery-daemon.cpp +++ b/daemons/battery-daemon.cpp @@ -1,13 +1,21 @@ +#include +#include +#include #include #include #include #include #include // pid_t data type #include +#include #include // For fork(); using namespace std; +// atomic type => ensure that variable can be safely accessed and modified. +// // 1 = Suddenly at 0%, 0 = Not at 0% +atomic BAT_EMPTY(0); + // If another instance of this program is running... const char *lockFilePath = "/tmp/bat-daemon-run"; @@ -26,6 +34,17 @@ void createLockFile() { // Removes file. void removeLockFile() { remove(lockFilePath); } +void send_notifs_empty() { + string command = "notify-send 'Low Battery' 'ALERT! 0% Battery Remaining.' " + "-u critical -i 'battery-caution' -t 5000"; + + // If battery is empty... + while (BAT_EMPTY.load() == 1) { + system(command.c_str()); + this_thread::sleep_for(chrono::seconds(5)); + } +} + void send_notifs_warn(const int &percent) { // Formulate command string command = @@ -62,41 +81,14 @@ void send_notifs_full() { system(command.c_str()); } -// Turn program into a daemon. -void daemonize() { - pid_t pid = fork(); - - // if forking fails, exit. - if (pid < 0) { - exit(EXIT_FAILURE); - } - - if (pid > 0) { - exit(EXIT_SUCCESS); - } - - // New session: if fails, exit. - // - New process becomes the leader - if (setsid() < 0) { - exit(EXIT_FAILURE); - } - - // Change directory to root - chdir("/"); - - // Redirects all streams to /dev/null - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); -} - // Checks battery info. (Charging, Discharging, etc.) void battery() { int OLD_BAT_PERCENT = 100; // Maxing out to prevent bugs int CHARGE = 0; // 1 = Charging, 0 = Discharging int BAT_FULL = 0; // 1 = Full, 0 = Not Full + while (true) { - int BAT_WARN = 36; + int BAT_WARN = 35; // Read and Grab File Info: // ifstream (input file stream) class: operates on files (I/O) @@ -120,35 +112,45 @@ void battery() { tv.tv_sec = 0; tv.tv_usec = 100000; - if (BAT_PERCENT <= BAT_WARN) { - if ((BAT_PERCENT < OLD_BAT_PERCENT) && (BAT_STATUS == "Discharging")) { - CHARGE = 0; - OLD_BAT_PERCENT = BAT_PERCENT; - send_notifs_warn(BAT_PERCENT); - } - } - + // If the actual battery is charging, update all values if (BAT_STATUS == "Charging") { - if ((BAT_PERCENT < 99) && (CHARGE == 0)) { + if (BAT_PERCENT < 99 && CHARGE == 0) { CHARGE = 1; OLD_BAT_PERCENT = BAT_PERCENT; BAT_FULL = 0; send_notifs_charge(BAT_PERCENT, CHARGE); - } - if (BAT_PERCENT >= 99 && BAT_FULL == 0) { + } else if (BAT_PERCENT >= 99 && BAT_FULL == 0) { BAT_FULL = 1; CHARGE = 1; send_notifs_full(); + + } else if (BAT_PERCENT > OLD_BAT_PERCENT) { + OLD_BAT_PERCENT = BAT_PERCENT; + } + + if (BAT_EMPTY == 1) { + BAT_EMPTY.store(0); } } - if (BAT_STATUS == "Discharging" && CHARGE == 1) { - CHARGE = 0; - send_notifs_charge(BAT_PERCENT, CHARGE); + // If the actual battery is discharging, update all values + if (BAT_STATUS == "Discharging") { + if (CHARGE == 1) { + CHARGE = 0; + send_notifs_charge(BAT_PERCENT, CHARGE); - if (BAT_PERCENT < 99) { + } else if (BAT_PERCENT < 99 && BAT_FULL == 1) { BAT_FULL = 0; + + } else if (BAT_PERCENT <= BAT_WARN && BAT_PERCENT < OLD_BAT_PERCENT) { + OLD_BAT_PERCENT = BAT_PERCENT; + send_notifs_warn(BAT_PERCENT); + } + + // Change value of BAT_EMPTY (alert notification) + if (BAT_PERCENT == 0 && BAT_EMPTY == 0) { + BAT_EMPTY.store(1); } } @@ -158,16 +160,30 @@ void battery() { // Initializer int main() { + // Change directory to root + chdir("/"); + + // Redirects all streams to /dev/null + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + if (isRunning()) { - cout << "Another process already exists!" << endl; - return 1; + ifstream lockFile(lockFilePath); + int pid; + + lockFile >> pid; + + removeLockFile(); + + kill(pid, 1); } createLockFile(); - daemonize(); battery(); - removeLockFile(); + thread background(send_notifs_empty); + return 0; } diff --git a/archived/battery-scripts/battery-status b/notifs/battery-status similarity index 100% rename from archived/battery-scripts/battery-status rename to notifs/battery-status From 016b95104745287730a74ed842623bffcda19431 Mon Sep 17 00:00:00 2001 From: devaine Date: Thu, 7 Aug 2025 11:45:47 -0500 Subject: [PATCH 08/10] fix + feat: added a new alert system for when battery is close to 0% --- daemons/battery-daemon | Bin 62568 -> 62400 bytes daemons/battery-daemon.cpp | 44 ++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/daemons/battery-daemon b/daemons/battery-daemon index 26dc28f28a655a803372906188f2b1675b064cb9..4d35cb060110addeecdb3584055048d32a2268f7 100755 GIT binary patch delta 16853 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%0A2+i1bX8uApeU_XD?s^Fcp{rd<=LBSP2}ADZ3L` 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 delta 16730 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 #include #include // pid_t data type +#include #include #include #include // For fork(); @@ -35,13 +36,29 @@ void createLockFile() { void removeLockFile() { remove(lockFilePath); } void send_notifs_empty() { - string command = "notify-send 'Low Battery' 'ALERT! 0% Battery Remaining.' " - "-u critical -i 'battery-caution' -t 5000"; - // If battery is empty... - while (BAT_EMPTY.load() == 1) { - system(command.c_str()); - this_thread::sleep_for(chrono::seconds(5)); + while (true) { + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 100000; + + ifstream BAT_PERCENT_FILE("/sys/class/power_supply/BAT0/capacity"); + int BAT_PERCENT; + + BAT_PERCENT_FILE >> BAT_PERCENT; + + string command = + "notify-send 'Low Battery' 'ALERT! " + to_string(BAT_PERCENT) + + "% Battery Remaining.' -u critical -i 'battery-caution' -t 5000"; + + // If battery is empty... + if (BAT_EMPTY.load() == 1) { + system(command.c_str()); + this_thread::sleep_for(chrono::seconds(5)); + } else { + select(0, NULL, NULL, NULL, &tv); + } } } @@ -129,7 +146,7 @@ void battery() { OLD_BAT_PERCENT = BAT_PERCENT; } - if (BAT_EMPTY == 1) { + if (BAT_EMPTY.load() == 1) { BAT_EMPTY.store(0); } } @@ -149,7 +166,7 @@ void battery() { } // Change value of BAT_EMPTY (alert notification) - if (BAT_PERCENT == 0 && BAT_EMPTY == 0) { + if (BAT_PERCENT < 10 && BAT_EMPTY.load() == 0) { BAT_EMPTY.store(1); } } @@ -164,9 +181,9 @@ int main() { chdir("/"); // Redirects all streams to /dev/null - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); + // freopen("/dev/null", "r", stdin); + // freopen("/dev/null", "w", stdout); + // freopen("/dev/null", "w", stderr); if (isRunning()) { ifstream lockFile(lockFilePath); @@ -181,9 +198,10 @@ int main() { createLockFile(); - battery(); + thread bg(send_notifs_empty); + bg.detach(); - thread background(send_notifs_empty); + battery(); return 0; } From 2a7bd2c25029646e0bcfc5eaa0881ba00b643741 Mon Sep 17 00:00:00 2001 From: devaine Date: Sun, 10 Aug 2025 17:20:10 -0500 Subject: [PATCH 09/10] feat(server): added a new server script for desec, updates all dns' --- .gitignore | 1 + server-scripts/desec/desec.py | 101 ++++++++++++++++++++++++++++++++++ server-scripts/desec/dev.bash | 11 ++++ server-scripts/desec/run.bash | 7 +++ 4 files changed, 120 insertions(+) create mode 100755 server-scripts/desec/desec.py create mode 100755 server-scripts/desec/dev.bash create mode 100755 server-scripts/desec/run.bash diff --git a/.gitignore b/.gitignore index e69de29..4c49bd7 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/server-scripts/desec/desec.py b/server-scripts/desec/desec.py new file mode 100755 index 0000000..78f5fa6 --- /dev/null +++ b/server-scripts/desec/desec.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +import subprocess +import json +import os +import asyncio +from dotenv import load_dotenv + +load_dotenv() # Loads up .env file + +current_domain = os.getenv("CURRENT_DOMAIN") +desec_token = os.getenv("DESEC_TOKEN") +excluded_subdomains = ["mail._domainkey.mail", "mail", "_dmarc.mail"] +timeout = 1800 # In seconds, 600 = 10min, 900 = 15m, 1800 = 30min + +# Credits: +# https://desec.readthedocs.io/en/latest/dns/rrsets.html#modifying-an-rrset +# for documentation + + +async def modifyRecords(newIP): + args = "curl https://desec.io/api/v1/domains/" + current_domain + \ + "/rrsets/ --header 'Authorization: Token " + desec_token + "'" + + data_binary = subprocess.run( + args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) + data = data_binary.stdout.decode("utf-8") + + # convert to python object from str + json_data = json.loads(data) + + for i in range(len(excluded_subdomains)): + excluded_subdomains[i] = excluded_subdomains[i] + \ + "." + current_domain + "." + + for entry in json_data: + # Only allow "A" record subdomains at the moment + if entry["name"] not in excluded_subdomains and entry["type"] == "A": + if (entry["records"][0] != newIP): + subname = str(entry["subname"]) # Subdomain Name + + # Basically runs a PATCH method for the api to change + # the ip record of all "A" record subdomains to the new + # public ip address + + change_record_arg = \ + "curl -X PATCH https://desec.io/api/v1/domains/" + \ + current_domain + "/rrsets/" + subname + \ + "/A/" + " --header 'Authorization: Token " \ + + desec_token + "'" + " --header " + \ + "'Content-Type: application/json' " + \ + "--data @- <<< '{\"records\": [\"" + newIP + "\"] }'" + + # print(change_record_arg) + subprocess.run(change_record_arg, shell=True) + await asyncio.sleep(5) + + print("done with changing records!") + + +def getCurrentIP(): + print("getting current ip...") + + # Get the current Public IP to a "public_ip" file + subprocess.run( + ["curl", "ifconfig.me", "-o", "public_ip"], stderr=subprocess.DEVNULL) + + +async def newIPCheck(): + print("checking for new ips...") + presentIPFile = open("public_ip", "r") + + newIP_curl = subprocess.run(["curl", "ifconfig.me"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL) + + newIP = newIP_curl.stdout.decode("utf-8") + + if (presentIPFile.readline() == newIP): + await asyncio.sleep(timeout) + await newIPCheck() + + else: + print("uh oh! public ip updated!") + await modifyRecords(newIP) + getCurrentIP() # update current ip + await newIPCheck() + + +def main(): + if not os.path.exists("public_ip"): + getCurrentIP() + elif not os.path.exists(".env"): + print("no visible .env file for token!") + exit(1) + + asyncio.run(newIPCheck()) + + +if __name__ == "__main__": + main() diff --git a/server-scripts/desec/dev.bash b/server-scripts/desec/dev.bash new file mode 100755 index 0000000..be6b92f --- /dev/null +++ b/server-scripts/desec/dev.bash @@ -0,0 +1,11 @@ +#!/bin/bash + +# If virtual environment does exist.. +if [ ! -d .venv ]; then + python3 -m venv .venv + source .venv/bin/activate + pip install -U python-dotenv + pip install --upgrade pip # Update pip to the latest version +fi + +echo 'Make sure to run ". .venv/bin/activate" to enter the development environment' diff --git a/server-scripts/desec/run.bash b/server-scripts/desec/run.bash new file mode 100755 index 0000000..4c833f9 --- /dev/null +++ b/server-scripts/desec/run.bash @@ -0,0 +1,7 @@ +#!/bin/bash + +cd /home/user/scripts/desec + +. .venv/bin/activate + +./desec.py From 5b03adaaa9100f17daf1e3cb09ae48c256734759 Mon Sep 17 00:00:00 2001 From: devaine Date: Mon, 20 Oct 2025 19:02:42 -0500 Subject: [PATCH 10/10] updated scripts --- .gitignore | 1 + server-scripts/desec/desec.py | 13 +++++++++++++ server-scripts/desec/dev.bash | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4c49bd7..3afbece 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +server-scripts/desec/public_ip diff --git a/server-scripts/desec/desec.py b/server-scripts/desec/desec.py index 78f5fa6..ed34e37 100755 --- a/server-scripts/desec/desec.py +++ b/server-scripts/desec/desec.py @@ -4,7 +4,9 @@ import subprocess import json import os import asyncio +from time import sleep from dotenv import load_dotenv +import urllib3 load_dotenv() # Loads up .env file @@ -86,8 +88,19 @@ async def newIPCheck(): getCurrentIP() # update current ip await newIPCheck() +def waitForConnection(): + while True: + try: + response = urllib3.request("GET", "https://ifconfig.me") + return + except urllib3.exceptions.MaxRetryError: + print("Failed connection!") + sleep(1) + pass def main(): + waitForConnection() + if not os.path.exists("public_ip"): getCurrentIP() elif not os.path.exists(".env"): diff --git a/server-scripts/desec/dev.bash b/server-scripts/desec/dev.bash index be6b92f..24a745d 100755 --- a/server-scripts/desec/dev.bash +++ b/server-scripts/desec/dev.bash @@ -4,8 +4,8 @@ if [ ! -d .venv ]; then python3 -m venv .venv source .venv/bin/activate - pip install -U python-dotenv - pip install --upgrade pip # Update pip to the latest version + pip install -U python-dotenv urllib3 + pip install -U pip # Update pip to the latest version fi echo 'Make sure to run ". .venv/bin/activate" to enter the development environment'