Add script for getting github notifications on desktop

This commit is contained in:
ItsDrike 2022-02-14 23:15:51 +01:00
parent eb784ab056
commit 0993faedf7
No known key found for this signature in database
GPG key ID: B014E761034AF742

View file

@ -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<NF; i++) printf $i " "; print $NF}' | sed 's/\x1b\[[0-9;]*m//g')"
[ $VERY_VERBOSE -eq 1 ] && echo "Found notification $name"
[ $VERY_VERY_VERBOSE -eq 1 ] && echo "Description: $description"
# Create hash from the name and description and use it to construct
# a path to a temporary file
hashsum="$(sum <<< "$name$description" | cut -f 1 -d ' ')"
tmpname="/tmp/gh-notification-$hashsum"
[ $TEMP_SHOW -eq 1 ] && echo "Tempfile: $tmpname"
# If the temporary file is already present, this notification was already
# send and we don't want to re-send it
# Only sent the notification if it wasn't already cached (doesn't have temp file)
# this avoids resending the same notifications
if [ ! -e "$tmpname" ] || [ $NO_CACHE -eq 1 ]; then
if [ $DRY_RUN -eq 1 ]; then
[ $VERY_VERBOSE -eq 1 ] && echo "Sending notification (dry-run, no actual notification was sent)"
else
[ $VERY_VERBOSE -eq 1 ] && echo "Sending notification"
send_notify "$name" "$description"
# Create the tempfile so that in the next run, we won't resend this notification again
# NOTE: We're storing the name and description into this file to make it easier
# to figure out what notification the tempfile belongs to, with ~@~ separator
printf "$name~@~$description" > "$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