dotfiles/.local/bin/sqs

216 lines
6.7 KiB
Bash
Executable File

#!/bin/bash
if ((BASH_VERSINFO[0] < 4)); then
echo "This script needs at least bash 4."
missing_deps=1
fi
for dep in curl jq; do
if ! command -v $dep &>/dev/null; then
echo "$dep is required but not found."
missing_deps=1
fi
done
[[ $missing_deps ]] &&
exit 1
if [[ -z $SYNCTHING_API_KEY ]]; then
: "${SYNCTHING_CONFIG_FILE:="$HOME/.config/syncthing/config.xml"}"
apikey_regex='^\s+<apikey>([^<]+)</apikey>$'
apikey_line="$(grep -E "$apikey_regex" "$SYNCTHING_CONFIG_FILE")"
[[ $apikey_line =~ $apikey_regex ]] &&
SYNCTHING_API_KEY=${BASH_REMATCH[1]}
fi
if [[ -z $SYNCTHING_API_KEY ]]; then
echo "No API key in env. Set one of the variables SYNCTHING_API_KEY or SYNCTHING_CONFIG_FILE and try again..."
exit 1
fi
: "${SYNCTHING_ADDRESS:="localhost:8384"}"
COLOR_PURPLE='\e[95m'
COLOR_GRAY='\e[90m'
COLOR_GREEN='\e[92m'
COLOR_BLUE='\e[34m'
COLOR_RED='\e[31m'
COLOR_RESET='\e[0m'
RECENT_CHANGES_LIMIT=5
LOG_ENTRIES_LIMIT=15
LOG_MAX_AGE=300 # seconds
declare -A api_cache=()
function get_api_response() { # $0 api_name
if [[ -z ${api_cache["$1"]} ]]; then
[[ $DEBUG ]] && echo -e "${COLOR_GRAY}CALLING API: $1${COLOR_RESET}" >&2
api_cache["$1"]="$(curl --silent --insecure -L -H "X-API-Key: $SYNCTHING_API_KEY" "http://$SYNCTHING_ADDRESS/rest/$1")"
fi
RESULT="${api_cache["$1"]}"
# using stdout and piping directly to jq would jump over the cache... not sure why :/
[[ $RESULT == "CSRF Error" ]] && return 1
return 0
}
function jq_arg() {
echo "$1" | jq -r "$2"
}
function call_jq() { # $0 api_name jq_commands
get_api_response "$1" &&
RESULT="$(jq_arg "$RESULT" "$2")"
}
function format_time() {
echo "${COLOR_GRAY}$(echo "$1" | cut -d'.' -f1)${COLOR_RESET}"
}
function get_messages() { # $0 api_name jq_commands message_color_control_code max_age_in_seconds
call_jq "$1" "$2"
RESULT="$(jq_arg "$RESULT" '.when + " " + .message' | tail -n "$LOG_ENTRIES_LIMIT")"
local message_color="$3"
local max_age="${4:-0}"
local min_timestamp="$(($(date +%s) - max_age))"
local result=
local formatted_line=
while IFS= read -r line; do
[[ -z $line ]] && continue
when="$(echo "$line" | cut -d' ' -f1)"
message="$(echo "$line" | cut -d' ' -f2-)"
timestamp="$(date --date="$when" +%s)"
formatted_line="$(format_time "$when") ${message_color}$message${COLOR_RESET}"$'\n'
[[ $max_age -gt 0 ]] && [[ $timestamp -lt $min_timestamp ]] &&
continue
result+="$formatted_line"
done <<< "$RESULT"
[[ -z $result ]] &&
result="$formatted_line" # take the last log line in any case
RESULT="${result%$'\n'}"
}
[[ $1 == -v ]] && VERBOSE=true
if ! call_jq "system/status" '.myID'; then
echo "Error from Syncthing API: $RESULT"
echo "You should probably check and change the variables SYNCTHING_API_KEY or SYNCTHING_CONFIG_FILE."
exit 1
elif [[ -z $RESULT ]]; then
echo "Empty response from Syncthing API."
echo "You should probably check and change the variables SYNCTHING_ADDRESS, SYNCTHING_API_KEY or SYNCTHING_CONFIG_FILE."
exit 1
fi
local_device_id="$RESULT"
call_jq "system/config" '.devices | map(select(.deviceID == "'"$local_device_id"'"))[] | .name'
local_device_name="$RESULT"
echo -n "Local device: $local_device_name"
[[ $VERBOSE ]] && echo -en " ${COLOR_GRAY}($local_device_id)${COLOR_RESET}"
echo $'\n'
echo "Devices:"
call_jq "system/config" '.devices[] | .deviceID'
for device_id in $RESULT; do
[[ $device_id == "$local_device_id" ]] && continue
call_jq "system/config" '.devices | map(select(.deviceID == "'"$device_id"'"))[]'
device_config="$RESULT"
device_name="$(jq_arg "$device_config" '.name')"
echo -n "$device_name: "
call_jq "system/connections" '.connections["'"$device_id"'"]'
device_status="$RESULT"
status="${COLOR_PURPLE}disconnected${COLOR_RESET}"
if [ "$(jq_arg "$device_status" '.paused')" == "true" ]; then
status="${COLOR_GRAY}paused${COLOR_RESET}"
elif [ "$(jq_arg "$device_status" '.connected')" == "true" ]; then
status="${COLOR_GREEN}$(jq_arg "$device_status" '.type')${COLOR_RESET}"
fi
echo -en "$status"
[[ $VERBOSE ]] && echo -en " ${COLOR_GRAY}($device_id)${COLOR_RESET}"
echo
done
echo -e "\nFolders:"
call_jq "system/config" '.folders[] | .id'
for folder_id in $RESULT; do
call_jq "system/config" '.folders | map(select(.id == "'"$folder_id"'"))[]'
folder_config="$RESULT"
folder_label="$(jq_arg "$folder_config" '.label')"
if [ "$folder_label" ]; then
[[ $VERBOSE ]] && folder_label+=" ${COLOR_GRAY}($folder_id)${COLOR_RESET}"
else
folder_label="$folder_id"
fi
echo -en "$folder_label: "
folder_status=
need_bytes=0
folder_paused="$(jq_arg "$folder_config" '.paused')"
[[ $folder_paused == true ]] && folder_status="paused"
if [[ -z $folder_status ]]; then
call_jq "db/status?folder=$folder_id" '.state'
folder_status="$RESULT"
call_jq "db/status?folder=$folder_id" '.needBytes'
need_bytes="$RESULT"
need_bytes_formatted=
[[ $need_bytes -gt 0 ]] &&
need_bytes_formatted="$(numfmt --to=iec-i --suffix=B "$need_bytes")"
fi
case "$folder_status" in
paused)
folder_status="${COLOR_GRAY}$folder_status${COLOR_RESET}"
;;
idle)
[[ $need_bytes -eq 0 ]] &&
folder_status="${COLOR_GREEN}up to date${COLOR_RESET}" ||
folder_status="${COLOR_RED}out of sync${COLOR_RESET}"
;;
scanning|syncing)
folder_status="${COLOR_BLUE}$folder_status${COLOR_RESET}"
;;
esac
[[ $need_bytes -gt 0 ]] && folder_status+=" ($need_bytes_formatted)"
echo -e "$folder_status"
done
echo -e "\nRecent changes:"
call_jq "events/disk?limit=$RECENT_CHANGES_LIMIT&timeout=1" '.[] | .id'
for event_id in $RESULT; do
call_jq "events/disk?limit=$RECENT_CHANGES_LIMIT&timeout=1" '. | map(select(.id == '"$event_id"'))[]'
event="$RESULT"
when="$(jq_arg "$event" '.time')"
path="$(jq_arg "$event" '.data.path')"
folder_id="$(jq_arg "$event" '.data.folderID')"
call_jq "system/config" '.folders | map(select(.id == "'"$folder_id"'"))[].label'
folder_label="${RESULT:-$folder_id}"
action="$(jq_arg "$event" '.data.action')"
action_color='?'
case "$action" in
added) action_color=${COLOR_GREEN}+ ;;
deleted) action_color=${COLOR_RED}- ;;
modified) action_color=${COLOR_BLUE}\# ;;
esac
device_id_prefix="$(jq_arg "$event" '.data.modifiedBy')"
call_jq "system/config" '.devices | map(select(.deviceID | startswith("'"$device_id_prefix"'")))[].name'
device_name="${RESULT:-$device_id_prefix}"
echo -e "$(format_time "$when") ${COLOR_GRAY}$device_name${COLOR_RESET} $folder_label ${action_color}$path${COLOR_RESET}"
done
if [[ $VERBOSE ]]; then
get_messages "system/log" '.messages[]?' '' "$LOG_MAX_AGE"
echo -e "\nLast log entries:"
echo -e "$RESULT"
fi
get_messages "system/error" '.errors[]?' "$COLOR_RED"
if [[ $RESULT ]]; then
echo -e "\nERRORS:"
echo -e "$RESULT"
fi
if [[ $DEBUG ]]; then
echo -e "\ncached responses:"
for k in "${!api_cache[@]}"; do
echo "$k"
done
fi