diff --git a/home/.local/bin/scripts/gh-notification b/home/.local/bin/scripts/gh-notification new file mode 100755 index 0000000..87a1d0a --- /dev/null +++ b/home/.local/bin/scripts/gh-notification @@ -0,0 +1,257 @@ +#!/bin/sh + +# Parse arguments +# ------------------------------------------------------------------------------------ +ALL=0 +VERBOSE=0 +VERY_VERBOSE=0 +VERY_VERY_VERBOSE=0 +TEMP_SHOW=0 +DRY_RUN=0 +NO_CACHE=0 +NO_DISPLAY=0 +URGENCY="normal" +RESET=0 +while [ "$1" ]; do + case "$1" in + -h | --help) + cat << EOF +gh-notification is a tool that scrapes unread github notifications +It uses github-cli with meiji163/gh-notify addon to obtain the unread notifications +these are then parsed and sent as desktop notifications with notify-send + +Options: +-a | --all: Also process already read notifications +-t | --temp-files: Show names of used temporary files for each notification +-v | --verbose: Shows info about what's happening. +-vv | --very-verbose: Implies --verbose, shows some more info about what's happening +-vvv | --very-very-verbose: Implies --very-verbose and --temp-files, shows even more details, usually just for debugging +-d | --dry-run: Run without sending any notificatinos, when ran with -r, this will also prevent any actual cache file removal +-nc | --no-cache: Ignore the cache and send all found notifications, even if they were already sent before. +-nd | --no-display: When the script is ran from headless mode (such as by crontab), this will still attempt to set the DISPLAY and send the desktop notification +-r | --reset: Resets notification cache (storing which notifications were already sent), skips notification sending, WARNING: removes the whole cache, regardless of '--all') +-u | --urgency [urgency-level]: pass over notify-send urgency attribute (low, normal, critical) +EOF + exit 0 + ;; + -a | --all) + ALL=1 + ;; + -t | --temp-files) + TEMP_SHOW=1 + ;; + -v | --verbose) + VERBOSE=1 + ;; + -vv | --very-verbose) + VERBOSE=1 + VERY_VERBOSE=1 + ;; + -vvv | --very-very-verbose) + VERBOSE=1 + TEMP_SHOW=1 + VERY_VERBOSE=1 + VERY_VERY_VERBOSE=1 + ;; + -d | --dry-run) + DRY_RUN=1 + ;; + -nc | --no-cache) + NO_CACHE=1 + ;; + -nd | --no-display) + NO_DISPLAY=1 + ;; + -u | --urgency) + URGENCY="$2" + shift + ;; + -r | --reset) + RESET=1 + ;; + * ) + echo "Unknown argument '$1', use -h or --help for help" + exit 1 + ;; + esac + shift +done + + +# Perform cache resetting, if requested +# ------------------------------------------------------------------------------------ +if [ $RESET -eq 1 ]; then + if [ $NO_CACHE -eq 1 ]; then + echo "Can't ignore cache when resetting the cache..." + exit 1 + fi + out="$(find /tmp -maxdepth 1 -name 'gh-notification-*' 2>/dev/null)" + total="$(printf "$out\n" | wc -l)" + # Since we always end with a newline (to count the last entry as a line), we always get + # at least 1 as a total here, even if $out is empty. If we didn't use the \n, we'd always + # get 0, even if there was a single line, since it wasn't ended with a newline. To figure + # out whether there really is a line or not when we get a total of 1, we run character + # amount check as well + [ $total -eq 1 ] && [ "$(printf "$out" | wc -c)" -eq 0 ] && total=0 + + if [ $total -gt 0 ]; then + # Since the loop is running in a pipe, it can't modify variables, but we need to know + # which files have failed to be removed, so to get that information, we store it in a + # teporary file + fail_files_file="$(mktemp)" + + printf "$out\n" | while read -r file_name; do + # If desired, let user know about the found notification cache file + if [ $VERY_VERBOSE -eq 1 ] || [ $TEMP_SHOW -eq 1 ]; then + contents="$(cat "$file_name")" + title="$(printf "$contents" | awk -F '~@~' '{ print $1 }')" + + echo "Found cache tempfile: '$file_name' - $title" + if [ $VERY_VERY_VERBOSE -eq 1 ]; then + description="$(printf "$contents" | awk -F '~@~' '{ print $2 }')" + echo "Notification description: $description" + fi + fi + + if [ $DRY_RUN -ne 1 ]; then + # In case `rm` fails, keep track of which files it failed on + if ! rm "$file_name" 2>/dev/null; then + printf "$file_name\n" >> "$fail_files_file" + fi + else + [ $VERY_VERY_VERBOSE -eq 1 ] && echo "Tempfile removal skipped (dry-run)" + fi + + # Add a new-line separator on very very verbose to group prints from each iteration + [ $VERY_VERY_VERBOSE -eq 1 ] && echo "" + done + + # Recover failed files from the temporary file + failed_files="$(cat "$fail_files_file")" + failed="$(printf "$fail_files_file" | wc -l)" + rm "$fail_files_file" + + if [ $VERBOSE -eq 1 ]; then + echo "Notification cache was reset." + removed_count="$(($total-$failed))" + if [ $DRY_RUN -eq 1 ]; then + echo "Removed $removed_count files (dry-run: no files were actually removed)" + else + echo "Removed $removed_count files" + fi + fi + + # If some cache files were'nt removed successfully, inform the user about it + # regardless of verbosity, this shouldn't go silent, even though it may be fine + if [ $failed -gt 0 ]; then + echo "WARNING: Failed to remove $failed files." + echo "You probably don't have permission to remove these." + echo "Perhaps these were made by someone else? If so, you can ignore this warning." + if [ $VERBOSE -eq 0 ]; then + echo "Run with --verbose to show exactly which files weren't removed." + else + echo "These are:" + echo "$fail_files" + fi + fi + else + [ $VERBOSE -eq 1 ] && echo "No cache files found, nothing to reset" + fi + exit 0 +fi + + +# Helper functins +# ------------------------------------------------------------------------------------ +# This runs notify-send, and if NO_DISPLAY is set and we're running in headless +# mode, this will still try to send the notification by manually setting DISPLAY +send_notify() { + if [ $NO_DISPLAY -eq 1 ]; then + XDG_RUNTIME_DIR="/run/user/$(id -u)" \ + DISPLAY=:0 \ + notify-send --urgency="$URGENCY" "$1" "$2" + else + notify-send --urgency="$URGENCY" "$1" "$2" + fi +} + + +# Obtain notifications and show them, if they weren't showed (aren't in cache) already +# ------------------------------------------------------------------------------------ +# Request unread notifications with gh-notify extension for github-cli +[ $ALL -eq 1 ] && out="$(gh notify -s -a)" || out="$(gh notify -s)" +total="$(printf "$out\n" | wc -l)" +# Since we always end with a newline (to count the last entry as a line), we always get +# at least 1 as a total here, even if $out is empty. If we didn't use the \n, we'd always +# get 0, even if there was a single line, since it wasn't ended with a newline. To figure +# out whether there really is a line or not when we get a total of 1, we run character +# amount check as well +[ $total -eq 1 ] && [ "$(printf "$out" | wc -c)" -eq 0 ] && total=0 + +# Only run if we actually found some notifications +if [ $total -gt 0 ]; then + # Since the loop is running in a pipe, it can't modify variables, but we need to know + # how many notifications were sent, so to ge that information, we store it in a + # temporary file + sent_count_file="$(mktemp)" + printf "0" > "$sent_count_file" + + # Go through each notification, one by one + printf "$out\n" | while read -r line; do + # Parse out the name and the description from the output + name="$(echo "$line" | awk '{print $3 " (" $4 ")"}' | sed 's/\x1b\[[0-9;]*m//g')" + description="$(echo "$line" | awk '{for (i=5; i "$tmpname" + fi + # Keep track of how many notifications were sent (didn't have a cache file) + sent="$(cat "$sent_count_file")" + sent="$(($sent+1))" + printf "$sent" > "$sent_count_file" + else + [ $VERY_VERBOSE -eq 1 ] && echo "Skipping (cached) - notification already sent" + fi + + # Add a new-line separator on very verbose to group prints from each iteration + [ $VERY_VERBOSE -eq 1 ] && echo "" + done + + # Recover amount of sent notifications from the temporary file + sent="$(cat "$sent_count_file")" + rm "$sent_count_file" + + if [ $VERBOSE -eq 1 ]; then + unsent="$(($total-$sent))" + if [ $sent -eq $total ]; then + echo "Found and sent $total new notifications" + elif [ $unsent -eq $total ]; then + echo "Found $total notifications, all of which were already sent (no new notifications to send)" + else + echo "Found $total notifications, of which $sent were new and sent ($unsent were skipped - cached/already sent)" + fi + fi +else + [ $VERBOSE -eq 1 ] && echo "No new notifications" +fi