diff --git a/crons/backup-log.sh b/crons/backup-log.sh index c7c3408..1e8c8ae 100755 --- a/crons/backup-log.sh +++ b/crons/backup-log.sh @@ -1,16 +1,33 @@ #!/bin/bash -LOCATION=`dirname "$0"` +backupDir="$1" +backupType="$2" -source "${LOCATION}/../config.sh" +# load in defaults from config +scriptRoot="$(dirname "$0")/.." +configFile="$scriptRoot/config.sh" +if [[ -f "$configFile" ]]; then + source "$configFile" -cd "${BACKUP_DIRECTORY}" + if [[ -z "$backupDir" ]]; then + backupDir="$BACKUP_DIRECTORY" + fi + if [[ -z "$backupType" ]]; then + backupType="$PRUNE_TYPE" + fi +fi -backups=`find * -type f | sort | xargs du -h` -echo "${backups}" -${LOCATION}/../discord.sh block "${backups}" +if [[ "$backupType" = "" ]]; then + backups=$(find "$backupDir" -maxdepth 1 -type d | grep -v "^$backupDir$" | sort | xargs du -h --summarize) + hashes="" +else + backups=$(find "$backupDir" -type f -name "$backupType" | sort | xargs du -h) + hashes=$(find "$backupDir" -type f -name "$backupType" | sort | xargs md5sum --tag) +fi -hashes=`find * -type f | sort | xargs md5sum --tag` -echo "${hashes}" -${LOCATION}/../discord.sh block "${hashes}" +echo "$backups" +if [[ -n "$hashes" ]]; then + echo "$hashes" +fi +"$scriptRoot/discord.sh" -c "mint" -t "Backup Status" "$backups" "$hashes" diff --git a/crons/storage-alert.sh b/crons/storage-alert.sh index c7204aa..8e5106e 100755 --- a/crons/storage-alert.sh +++ b/crons/storage-alert.sh @@ -1,23 +1,35 @@ #!/bin/bash -LOCATION=`dirname "$0"` +locations="$@" -source "${LOCATION}/../config.sh" +# load in defaults from config +scriptRoot="$(dirname "$0")/.." +configFile="$scriptRoot/config.sh" +if [[ -f "$configFile" ]]; then + source "$configFile" -COUNT=1 -for index in "${!STORAGE_ALERT[@]}"; do - storage=`${LOCATION}/../storage.sh $index ${STORAGE_ALERT[$index]}` - alert=$? - - if [[ "0" != "${alert}" ]]; then - echo "${storage} is below threshold ${STORAGE_ALERT[$index]}G" - ${LOCATION}/../discord-if-distinct.sh block "storage-${COUNT}" "${storage} is below threshold ${STORAGE_ALERT[$index]}G" - else - echo "${storage} is above threshold ${STORAGE_ALERT[$index]}G" - - ${LOCATION}/../discord-if-distinct.sh block "storage-${COUNT}" "${storage} is above threshold ${STORAGE_ALERT[$index]}G" + if [[ -z "$locations" ]]; then + locations=${STORAGE_MONITOR[@]} fi +fi - let "COUNT++" +warn=false +messages=() +for index in "${!STORAGE_ALERT[@]}"; do + storage=$("$scriptRoot/storage.sh" "$index" "${STORAGE_ALERT[$index]}") + alert="$?" + + if [[ "0" != "$alert" ]]; then + warn=true + messages+=("$storage is below threshold ${STORAGE_ALERT[$index]}G") + else + messages+=("$storage is above threshold ${STORAGE_ALERT[$index]}G") + fi done +if [[ "$warn" = true ]]; then + purple="purple" +fi + +printf "%s\n" "${messages[@]}" +printf "%s\n" "${messages[@]}" | "$scriptRoot/discord.sh" -c "${purple-blue}" -d "Storage Alert" diff --git a/crons/storage-log.sh b/crons/storage-log.sh index d08de18..c603c96 100755 --- a/crons/storage-log.sh +++ b/crons/storage-log.sh @@ -1,11 +1,17 @@ #!/bin/bash -LOCATION=`dirname "$0"` +locations="$@" -source "${LOCATION}/../config.sh" +# load in defaults from config +scriptRoot="$(dirname "$0")/.." +configFile="$scriptRoot/config.sh" +if [[ -f "$configFile" ]]; then + source "$configFile" -storage=`${LOCATION}/../df-custom.sh ${STORAGE_MONITOR[@]}` + if [[ -z "$locations" ]]; then + locations=${STORAGE_MONITOR[@]} + fi +fi -echo "${storage}" -${LOCATION}/../discord.sh block "${storage}" +"$scriptRoot/df-custom.sh" $locations | "$scriptRoot/discord.sh" -c "blue" -t "Storage Status" diff --git a/crons/thermal-alert.sh b/crons/thermal-alert.sh index 8953c5a..39a97c6 100755 --- a/crons/thermal-alert.sh +++ b/crons/thermal-alert.sh @@ -21,12 +21,12 @@ alert="$?" if [[ "0" -ne "$alert" ]]; then # if currently in an alert status alertLevelF=`echo "scale=1 ; $alertLevel * 9 / 5 + 32" | bc -l` - "$scriptRoot/discord.sh" "$thermal, above threshold $alertLevel C ($alertLevelF F)" + "$scriptRoot/discord.sh" -c "red" -t "Thermal Alert" "$thermal" $'\n' "above threshold $alertLevel C ($alertLevelF F)" touch "$HOME/.thermal.alert" elif [[ -f "$HOME/.thermal.alert" ]]; then # if previously in an alert status alertLevelF=`echo "scale=1 ; $alertLevel * 9 / 5 + 32" | bc -l` - "$scriptRoot/discord.sh" "$thermal, previously above threshold $alertLevel C ($alertLevelF F)" + "$scriptRoot/discord.sh" -c "orange" -t "Thermal Alert" "$thermal" $'\n' "previously above threshold $alertLevel C ($alertLevelF F)" rm "$HOME/.thermal.alert" fi diff --git a/crons/thermal-log.sh b/crons/thermal-log.sh index 7c6c4dc..fd2e509 100755 --- a/crons/thermal-log.sh +++ b/crons/thermal-log.sh @@ -2,7 +2,5 @@ scriptRoot="$(dirname "$0")/.." -thermal=$("$scriptRoot/thermal.sh") - -"$scriptRoot/discord.sh" "$thermal" +"$scriptRoot/thermal.sh" | "$scriptRoot/discord.sh" -c "yellow" -t "Thermal Status" diff --git a/discord-if-distinct.sh b/discord-if-distinct.sh index f28ca9f..d66197e 100755 --- a/discord-if-distinct.sh +++ b/discord-if-distinct.sh @@ -86,7 +86,7 @@ if [ "${FULL_MESSAGE}" != "${OLD_MSG}" ]; then curl -s -H "Content-Type: application/json" -X POST -d "{\"username\": \"${DISCORD_SERVER_NAME}\", \"content\": ${MESSAGE}}" $HOOK_URL | jq -r '.id,.message' if [ -n "${EXTRA}" ]; then - "${LOCATION}/discord.sh" "${HOOK}" "${EXTRA}" + "${LOCATION}/discord-old.sh" "${HOOK}" "${EXTRA}" fi fi diff --git a/discord-old.sh b/discord-old.sh new file mode 100755 index 0000000..1f43e10 --- /dev/null +++ b/discord-old.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +LOCATION=`dirname "$0"` + +source "${LOCATION}/config.sh" + +if [[ $# -gt 1 ]]; then + HOOK_ARG="$1" + # sed removes spaces before each line + MESSAGE=`echo "${@:2}" | sed 's/^ *//' | sed 's/ *$//'` +else + # sed removes spaces before each line + MESSAGE=`echo "${@}" | sed 's/^ *//' | sed 's/ *$//'` + + if [[ "$MESSAGE" == "general" ]] || [[ "$MESSAGE" == "block" ]] || [[ "$MESSAGE" == "restake" ]] || [[ "$MESSAGE" == "storage" ]]; then + HOOK_ARG="$MESSAGE" + MESSAGE='' + fi +fi + +if [[ -z "$MESSAGE" ]]; then + echo 'no message provided' + exit 0 +fi + +# if message is too long, split it, and after curl, recursively call with remainder +# 2k max, but we must account for escaping text in json, so limit to 90% capacity +EXTRA="${MESSAGE:1800}" + +if [ -n "${EXTRA}" ]; then + MESSAGE="${MESSAGE:0:1800}" + + # as an added elegance, when spliting into pieces, find the last \n newline, + # and break there instead, but only if the new smaller chunk is of sufficient size + chunk1=`echo "$MESSAGE" | head -n -1` + chunks=`echo "$MESSAGE" | tail -n 1` + if (( "${#chunk1}" >= 1000 )); then + MESSAGE="$chunk1" + EXTRA="${chunks}${EXTRA}" + fi +fi + + +# sed: rewrite json string, because "\u001b[m" needs to "\u001b[0m" instead +# linux is fine with the missing zero, but discord needs it +MESSAGE=`echo "${MESSAGE}" | jq -aRs . | sed 's/\\\\u001b\[m/\\\\u001b[0m/g'` +HOOK="general" +HOOK_URL=$DISCORD_GENERAL_HOOK + +case $HOOK_ARG in + block) + # force block format without color + if [[ "$MESSAGE" != *"\\u001b"* ]]; then + MESSAGE="\"\`\`\`${MESSAGE:1:-1}\n\`\`\`\"" + # just here to resolve formatting issue in vim "`" + fi + ;; + restake) + HOOK="restake" + HOOK_URL=$DISCORD_RESTAKE_HOOK + ;; + storage) + HOOK="storage" + HOOK_URL=$DISCORD_STORAGE_HOOK + ;; +esac + +# if color detected, update message from "" to "```ansi ```", so it gets displayed correctly +if [[ "$MESSAGE" == *"\\u001b"* ]]; then + MESSAGE="\"\`\`\`ansi\n${MESSAGE:1:-1}\n\`\`\`\"" + # just here to resolve formatting issue in vim "`" +fi + +curl -s -H "Content-Type: application/json" -X POST -d "{\"username\": \"${DISCORD_SERVER_NAME}\", \"content\": ${MESSAGE}}" $HOOK_URL | jq -r '.id,.message' + +if [ -n "${EXTRA}" ]; then + "${LOCATION}/discord-old.sh" "${HOOK}" "${EXTRA}" +fi + diff --git a/discord.sh b/discord.sh index d61c9b4..40b77f6 100755 --- a/discord.sh +++ b/discord.sh @@ -1,79 +1,170 @@ #!/bin/bash -LOCATION=`dirname "$0"` +# discord colors are standard 6-digit hex, but converted to a base-10 integer +# colors taken from https://xkcd.com/color/rgb/ +# and built into a palette based on https://sashamaps.net/docs/resources/20-colors/ +# +# DARK: maroon(#650021), brown(#653700), olive(#6e750e), teal(#029386), navy(#01153e), black(#000000), +# BRIGHT: red(#e50000), orange(#f97306), yellow(#ffff14), lime(#aaff32), green(#15b01a), +# cyan(#00ffff), blue(#0343df), purple(#7e1e9c), magenta(#c20078), grey(#929591), +# LIGHT: pink(#ff81c0), apricot(#ffb16d), beige(#e6daa6), mint(#9ffeb0), lavender(#c79fef), white(#ffffff). -source "${LOCATION}/config.sh" +declare -A colors +colors=( + ["maroon"]=6619169 ["brown"]=6633216 ["olive"]=7238926 ["teal"]=168838 ["navy"]=70974 ["black"]=0 + ["red"]=15007744 ["orange"]=16347910 ["yellow"]=16776980 ["lime"]=11206450 ["green"]=1421338 + ["cyan"]=65535 ["blue"]=213983 ["purple"]=8265372 ["magenta"]=12714104 ["grey"]=9606545 + ["pink"]=16744896 ["apricot"]=16757101 ["beige"]=15129254 ["mint"]=10485424 ["lavender"]=13082607 ["white"]=16777215 +) -if [[ $# -gt 1 ]]; then - HOOK_ARG="$1" - # sed removes spaces before each line - MESSAGE=`echo "${@:2}" | sed 's/^ *//' | sed 's/ *$//'` -else - # sed removes spaces before each line - MESSAGE=`echo "${@}" | sed 's/^ *//' | sed 's/ *$//'` +print_usage() { + echo "Usage: $0 [-h hook-url], [-c color], [-d [distinct-name] | -t title] message..." +} - if [[ "$MESSAGE" == "general" ]] || [[ "$MESSAGE" == "block" ]] || [[ "$MESSAGE" == "restake" ]] || [[ "$MESSAGE" == "storage" ]]; then - HOOK_ARG="$MESSAGE" - MESSAGE='' +# handle all arguments provided +while getopts 'h:c:d:t:' flag; do + case "$flag" in + h) + if [[ "$OPTARG" == "http"*"?wait=true" ]]; then + hookUrl="$OPTARG" + else + echo "Invalid hook-url provided, should be something like:" + echo "https://discord.com/api/webhooks/***?wait=true" + print_usage + exit 1 + fi + ;; + c) + if [[ -v "colors[$OPTARG]" ]]; then + color="$OPTARG" + else + echo "Invalid color provided, colors available:" + echo "maroon, brown, olive, teal, navy, black," + echo "red, orange, yellow, lime, green," + echo " cyan, blue, purple, magenta, grey," + echo "pink, apricot, beige, mint, lavender, white." + print_usage + exit 1 + fi + ;; + d) + # '-d' without value is allowed, but generates a warning + # in which case we'll use name of parent command with arguments + # whatever the distinct key, we clean it up, since it will be used as a filename + if [[ -z "$OPTARG" ]]; then + distinct=$(ps -o args= $PPID | sed 's/[\/\|]/_/g') + echo "Warning: doing distinct by parent, with key '$distinct'" >&2 + else + distinct=$(echo "$OPTARG" | sed 's/[\/\|]/_/g') + fi + ;; + t) + # title, for supplying a title, without activing the "distinct" feature + title="$OPTARG" + ;; + *) + echo "unknown option provided '$flag'" + print_usage + exit 1 + ;; + esac +done +shift "$((OPTIND-1))" + +# load in defaults from config +scriptRoot=$(dirname "$0") +configFile="$scriptRoot/config.sh" +if [[ -f "$configFile" ]]; then + source "$configFile" + + botName="$DISCORD_SERVER_NAME" + if [[ -z "$hookUrl" ]]; then + hookUrl="$DISCORD_GENERAL_HOOK" fi fi -if [[ -z "$MESSAGE" ]]; then - echo 'no message provided' - exit 0 +# stop if we have no hook to send message to +if [[ -z "$hookUrl" ]]; then + echo "Discord Hook URL must either be in config file, or supplied via '-h' argument." + exit 1 fi -# if message is too long, split it, and after curl, recursively call with remainder -# 2k max, but we must account for escaping text in json, so limit to 90% capacity -EXTRA="${MESSAGE:1800}" +# get full message, read from arguments +message=$(echo "$@" | sed 's/^ *//' | sed 's/ *$//') -if [ -n "${EXTRA}" ]; then - MESSAGE="${MESSAGE:0:1800}" +# if we have no message, read from stdin instead +if [[ -z "$message" ]]; then + message=$(cat - | sed 's/^ *//' | sed 's/ *$//') +fi + +# sed removes blank lines from the end of the message +fullMsg=$(echo "$message" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}') + +# if message is too long, split it, and after curl, recursively call with remainder +# 4k max, but we must account for escaping text in json, so limit to 90% capacity +extra="${message:3600}" + +if [ -n "$extra" ]; then + message="${message:0:3600}" # as an added elegance, when spliting into pieces, find the last \n newline, # and break there instead, but only if the new smaller chunk is of sufficient size - chunk1=`echo "$MESSAGE" | head -n -1` - chunks=`echo "$MESSAGE" | tail -n 1` + chunk1=$(echo "$message" | head -n -1) + chunks=$(echo "$message" | tail -n 1) if (( "${#chunk1}" >= 1000 )); then - MESSAGE="$chunk1" - EXTRA="${chunks}${EXTRA}" + message="$chunk1" + extra="$chunks$extra" fi fi - # sed: rewrite json string, because "\u001b[m" needs to "\u001b[0m" instead # linux is fine with the missing zero, but discord needs it -MESSAGE=`echo "${MESSAGE}" | jq -aRs . | sed 's/\\\\u001b\[m/\\\\u001b[0m/g'` -HOOK="general" -HOOK_URL=$DISCORD_GENERAL_HOOK +message=$(echo "$message" | jq -aRs . | sed 's/\\u001b\[m/\\u001b[0m/g') -case $HOOK_ARG in - block) - # force block format without color - if [[ "$MESSAGE" != *"\\u001b"* ]]; then - MESSAGE="\"\`\`\`${MESSAGE:1:-1}\n\`\`\`\"" - # just here to resolve formatting issue in vim "`" - fi - ;; - restake) - HOOK="restake" - HOOK_URL=$DISCORD_RESTAKE_HOOK - ;; - storage) - HOOK="storage" - HOOK_URL=$DISCORD_STORAGE_HOOK - ;; -esac +message="\"\`\`\`ansi\n${message:1:-1}\n\`\`\`\"" -# if color detected, update message from "" to "```ansi ```", so it gets displayed correctly -if [[ "$MESSAGE" == *"\\u001b"* ]]; then - MESSAGE="\"\`\`\`ansi\n${MESSAGE:1:-1}\n\`\`\`\"" - # just here to resolve formatting issue in vim "`" +# handle distinct stuff +oldMsg="" +if [[ -n "$distinct" ]]; then + if [[ -f "$scriptRoot/distinct/$distinct.msg" ]]; then + oldMsg=$(cat "$scriptRoot/distinct/$distinct.msg") + fi + + if [[ "$fullMsg" = "$oldMsg" ]]; then + echo "duplicate message" + exit 0 + fi + + echo "$fullMsg" > "$scriptRoot/distinct/$distinct.msg" fi -curl -s -H "Content-Type: application/json" -X POST -d "{\"username\": \"${DISCORD_SERVER_NAME}\", \"content\": ${MESSAGE}}" $HOOK_URL | jq -r '.id,.message' - -if [ -n "${EXTRA}" ]; then - "${LOCATION}/discord.sh" "${HOOK}" "${EXTRA}" +# build content json +content="{" +if [[ -n "$botName" ]]; then + content="$content\"username\": $(echo "$botName" | jq -aRs .), " fi +content="$content\"embeds\":[{\"description\": $message" +if [[ -n "$color" ]]; then + content="$content, \"color\": ${colors[$color]}" +fi +if [[ -n "$distinct" ]] || [[ -n "$title" ]]; then + content="$content, \"title\": $(echo "${distinct-$title}" | jq -aRs .)" +fi +content="$content}]}" +# actually send the message to discord +curl -s -H "Content-Type: application/json" -X POST -d "$content" "$hookUrl" | jq -r '.id,.message' + +# if the message was too long, send along the rest as a separate message +if [ -n "$extra" ]; then + script=$(basename "$0") + args=("-h" "$hookUrl") + # when we call again, we convert distinct into a title + if [[ -n "$distinct" ]] || [[ -n "$title" ]]; then + args+=("-t" "${distinct-$title}") + fi + if [[ -n "$color" ]]; then + args+=("-c" "$color") + fi + "$scriptRoot/$script" ${args[@]} "$extra" +fi