2025-09-15 05:25:23 -04:00
NOTIFY_V2_VERSION = "v0.6"
2025-05-25 12:26:13 -04:00
#
# If migrating from an older notify template, remove your existing notify.sh file.
2025-05-29 16:43:34 -04:00
# Leave (or place) this file in the "notify_templates" subdirectory within the same directory as the main dockcheck.sh script.
2025-06-24 09:16:48 -04:00
# If you instead wish make your own modifications, make a copy in the same directory as the main dockcheck.sh script and rename to notify.sh.
2025-05-25 12:26:13 -04:00
# Enable and configure all required notification variables in your dockcheck.config file, e.g.:
# NOTIFY_CHANNELS=apprise gotify slack
# SLACK_TOKEN=xoxb-some-token-value
# GOTIFY_TOKEN=some.token
2025-06-24 09:16:48 -04:00
# Number of seconds to snooze identical update notifications based on local image name
# or dockcheck.sh/notify.sh template file updates.
# Actual snooze will be 60 seconds less to avoid the chance of missed notifications due to minor scheduling or script run time issues.
snooze = " ${ SNOOZE_SECONDS :- } "
SnoozeFile = " ${ ScriptWorkDir } /snooze.list "
2025-09-15 05:25:23 -04:00
[ [ ! -f " ${ SnoozeFile } " ] ] && touch " ${ SnoozeFile } "
2025-06-24 09:16:48 -04:00
2025-05-25 12:26:13 -04:00
enabled_notify_channels = ( ${ NOTIFY_CHANNELS :- } )
2025-09-15 05:25:23 -04:00
# Global output string variable for modification by functions
UpdToString = ""
FormattedOutput = ""
get_channel_template( ) {
local UpperChannel = " ${ 1 ^^ } "
local TemplateVar = " ${ UpperChannel } _TEMPLATE "
if [ [ -n " ${ !TemplateVar :- } " ] ] ; then
printf " ${ !TemplateVar } "
else
printf " $1 "
fi
}
declare -A unique_templates
for channel in " ${ enabled_notify_channels [@] } " ; do
template = $( get_channel_template " ${ channel } " )
unique_templates[ " ${ template } " ] = 1
done
enabled_notify_templates = ( " ${ !unique_templates[@] } " )
2025-05-29 16:43:34 -04:00
FromHost = $( cat /etc/hostname)
2025-05-25 12:26:13 -04:00
2025-06-24 09:16:48 -04:00
CurrentEpochTime = $( date +"%Y-%m-%dT%H:%M:%S" )
CurrentEpochSeconds = $( date +%s)
NotifyError = false
2025-09-15 05:25:23 -04:00
for template in " ${ enabled_notify_templates [@] } " ; do
source_if_exists_or_fail " ${ ScriptWorkDir } /notify_ ${ template } .sh " || \
source_if_exists_or_fail " ${ ScriptWorkDir } /notify_templates/notify_ ${ template } .sh " || \
printf " The notification channel template ${ template } is enabled, but notify_ ${ template } .sh was not found. Check the ${ ScriptWorkDir } directory or the notify_templates subdirectory.\n "
done
skip_snooze( ) {
local UpperChannel = " ${ 1 ^^ } "
local SkipSnoozeVar = " ${ UpperChannel } _SKIPSNOOZE "
if [ [ " ${ !SkipSnoozeVar :- } " = = "true" ] ] ; then
printf "true"
else
printf "false"
fi
}
allow_empty( ) {
local UpperChannel = " ${ 1 ^^ } "
local AllowEmptyVar = " ${ UpperChannel } _ALLOWEMPTY "
if [ [ " ${ !AllowEmptyVar :- } " = = "true" ] ] ; then
printf "true"
else
printf "false"
fi
}
containers_only( ) {
local UpperChannel = " ${ 1 ^^ } "
local ContainersOnlyVar = " ${ UpperChannel } _CONTAINERSONLY "
if [ [ " ${ !ContainersOnlyVar :- } " = = "true" ] ] ; then
printf "true"
else
printf "false"
fi
}
output_format( ) {
local UpperChannel = " ${ 1 ^^ } "
local OutputFormatVar = " ${ UpperChannel } _OUTPUT "
if [ [ -z " ${ !OutputFormatVar :- } " ] ] ; then
printf "text"
else
printf " ${ !OutputFormatVar :- } "
fi
}
2025-05-25 12:26:13 -04:00
remove_channel( ) {
local temp_array = ( )
for channel in " ${ enabled_notify_channels [@] } " ; do
2025-09-15 05:25:23 -04:00
local channel_template = $( get_channel_template " ${ channel } " )
[ [ " ${ channel_template } " != " $1 " ] ] && temp_array += ( " ${ channel } " )
2025-05-25 12:26:13 -04:00
done
enabled_notify_channels = ( " ${ temp_array [@] } " )
}
2025-09-15 05:25:23 -04:00
is_snoozed( ) {
if [ [ -n " ${ snooze } " ] ] && [ [ -f " ${ SnoozeFile } " ] ] ; then
local found = $( grep -w " $1 " " ${ SnoozeFile } " || printf "" )
2025-06-24 09:16:48 -04:00
if [ [ -n " ${ found } " ] ] ; then
read -a arr <<< " ${ found } "
CheckEpochSeconds = $(( $( date -d " ${ arr [1] } " +%s 2>/dev/null) + ${ snooze } - 60 )) || CheckEpochSeconds = $(( $( date -f "%Y-%m-%d" -j " ${ arr [1] } " +%s) + ${ snooze } - 60 ))
2025-09-15 05:25:23 -04:00
if [ [ " ${ CurrentEpochSeconds } " -le " ${ CheckEpochSeconds } " ] ] ; then
printf "true"
else
printf "false"
2025-06-24 09:16:48 -04:00
fi
else
2025-09-15 05:25:23 -04:00
printf "false"
fi
else
printf "false"
fi
}
unsnoozed_count( ) {
unset Unsnoozed
Unsnoozed = ( )
for element in " $@ "
do
read -a item <<< " ${ element } "
if [ [ $( is_snoozed " ${ item [0] } " ) = = "false" ] ] ; then
Unsnoozed += ( " ${ element } " )
2025-06-24 09:16:48 -04:00
fi
done
2025-09-15 05:25:23 -04:00
printf " ${# Unsnoozed [@] } "
2025-06-24 09:16:48 -04:00
}
2025-05-25 12:26:13 -04:00
2025-06-24 09:16:48 -04:00
update_snooze( ) {
for arg in " $@ "
do
read -a entry <<< " ${ arg } "
found = $( grep -w " ${ entry [0] } " " ${ SnoozeFile } " || printf "" )
if [ [ -n " ${ found } " ] ] ; then
sed -e " s/ ${ entry [0] } .*/ ${ entry [0] } ${ CurrentEpochTime } / " " ${ SnoozeFile } " > " ${ SnoozeFile } .new "
mv " ${ SnoozeFile } .new " " ${ SnoozeFile } "
else
printf " ${ entry [0] } ${ CurrentEpochTime } \n " >> " ${ SnoozeFile } "
fi
2025-05-25 12:26:13 -04:00
done
}
2025-06-24 09:16:48 -04:00
cleanup_snooze( ) {
unset NotifyEntries
NotifyEntries = ( )
switch = ""
for arg in " $@ "
do
read -a entry <<< " ${ arg } "
NotifyEntries += ( " ${ entry [0] } " )
done
if [ [ ! " ${ NotifyEntries [@] } " = = *".sh" * ] ] ; then
switch = "-v"
fi
while read -r entry datestamp; do
if [ [ ! " ${ NotifyEntries [@] } " = = *" $entry " * ] ] ; then
sed -e " / ${ entry } /d " " ${ SnoozeFile } " > " ${ SnoozeFile } .new "
mv " ${ SnoozeFile } .new " " ${ SnoozeFile } "
fi
done <<< " $( grep ${ switch } '\.sh ' ${ SnoozeFile } ) "
}
2025-09-15 05:25:23 -04:00
format_output( ) {
local UpdateType = " $1 "
local OutputFormat = " $2 "
local FormattedTextTemplate = " $3 "
local tempcsv = ""
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
if [ [ ! " ${ UpdateType } " = = "dockcheck_update" ] ] ; then
tempcsv = " ${ UpdToString // -> /, } "
tempcsv = " ${ tempcsv //.sh /.sh, } "
else
tempcsv = " ${ UpdToString } "
fi
if [ [ " ${ OutputFormat } " = = "csv" ] ] ; then
if [ [ -z " ${ UpdToString } " ] ] ; then
FormattedOutput = "None"
else
FormattedOutput = " ${ tempcsv } "
fi
elif [ [ " ${ OutputFormat } " = = "json" ] ] ; then
if [ [ -z " ${ UpdToString } " ] ] ; then
FormattedOutput = '{"updates": []}'
else
if [ [ " ${ UpdateType } " = = "container_update" ] ] ; then
# container updates case
FormattedOutput = $( jq --compact-output --null-input --arg updates " ${ tempcsv } " '($updates | split("\\n")) | map(split(",")) | {"updates": map({"container_name": .[0], "release_notes": .[1]})} | del(..|nulls)' )
elif [ [ " ${ UpdateType } " = = "notify_update" ] ] ; then
# script updates case
FormattedOutput = $( jq --compact-output --null-input --arg updates " ${ tempcsv } " '($updates | split("\\n")) | map(split(",")) | {"updates": map({"script_name": .[0], "installed_version": .[1], "latest_version": .[2]})}' )
elif [ [ " ${ UpdateType } " = = "dockcheck_update" ] ] ; then
# dockcheck update case
FormattedOutput = $( jq --compact-output --null-input --arg updates " ${ tempcsv } " '($updates | split("\\n")) | map(split(",")) | {"updates": map({"script_name": .[0], "installed_version": .[1], "latest_version": .[2], "release_notes": (.[3:] | join(","))})}' )
else
FormattedOutput = "Invalid input"
fi
fi
2025-06-24 09:16:48 -04:00
else
2025-09-15 05:25:23 -04:00
if [ [ -z " ${ UpdToString } " ] ] ; then
FormattedOutput = "None"
else
if [ [ " ${ UpdateType } " = = "container_update" ] ] ; then
FormattedOutput = " ${ FormattedTextTemplate /<insert_text_cu>/ ${ UpdToString } } "
elif [ [ " ${ UpdateType } " = = "notify_update" ] ] ; then
FormattedOutput = " ${ FormattedTextTemplate /<insert_text_nu>/ ${ UpdToString } } "
elif [ [ " ${ UpdateType } " = = "dockcheck_update" ] ] ; then
FormattedOutput = " ${ FormattedTextTemplate /<insert_text_iv>/ $4 } "
FormattedOutput = " ${ FormattedOutput /<insert_text_lv>/ $5 } "
FormattedOutput = " ${ FormattedOutput /<insert_text_rn>/ $6 } "
else
FormattedOutput = "Invalid input"
fi
fi
2025-06-24 09:16:48 -04:00
fi
2025-09-15 05:25:23 -04:00
}
skip_notification( ) {
# Skip notification logic. Default to false. Handle all cases, and only those cases, where notifications should be skipped.
local SkipNotification = "false"
local Channel = " $1 "
local UnsnoozedCount = " $2 "
local NotificationType = " $3 "
if [ [ $( containers_only " ${ Channel } " ) = = "true" ] ] && [ [ " ${ NotificationType } " != "container" ] ] ; then
# Do not send notifications through channels only configured for container update notifications
SkipNotification = "true"
else
# Handle empty update cases separately
if [ [ -z " ${ UpdToString } " ] ] ; then
if [ [ $( allow_empty " ${ Channel } " ) = = "false" ] ] ; then
# Do not send notifications if there are none and allow_empty is false
SkipNotification = "true"
fi
else
if [ [ $( skip_snooze " ${ Channel } " ) = = "false" ] ] && [ [ ${ UnsnoozedCount } -eq 0 ] ] ; then
# Do not send notifications if there are any, they are all snoozed, and skip_snooze is false
SkipNotification = "true"
fi
fi
fi
printf " ${ SkipNotification } "
}
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
send_notification( ) {
[ [ -s " $ScriptWorkDir " /urls.list ] ] && releasenotes || Updates = ( " $@ " )
[ [ -n " ${ snooze } " ] ] && cleanup_snooze " ${ Updates [@] } "
UnsnoozedContainers = $( unsnoozed_count " ${ Updates [@] } " )
2025-06-24 09:16:48 -04:00
NotifyError = false
2025-09-15 05:25:23 -04:00
Notified = "false"
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
# To be added in the MessageBody if "-d X" was used
# Trailing space is left intentionally for clean output
[ [ -n " $DaysOld " ] ] && msgdaysold = " with images ${ DaysOld } + days old " || msgdaysold = ""
MessageTitle = " $FromHost - updates ${ msgdaysold } available. "
2025-05-25 12:26:13 -04:00
2025-09-15 05:25:23 -04:00
UpdToString = $( printf '%s\\n' " ${ Updates [@] } " )
UpdToString = " ${ UpdToString %, } "
UpdToString = ${ UpdToString % \\ n }
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
for channel in " ${ enabled_notify_channels [@] } " ; do
local SkipNotification = $( skip_notification " ${ channel } " " ${ UnsnoozedContainers } " "container" )
if [ [ " ${ SkipNotification } " = = "false" ] ] ; then
local template = $( get_channel_template " ${ channel } " )
# Formats UpdToString variable per channel settings
format_output "container_update" " $( output_format " ${ channel } " ) " " 🐋 Containers on $FromHost with updates available:\n<insert_text_cu>\n "
2025-06-24 09:16:48 -04:00
# Setting the MessageBody variable here.
2025-09-15 05:25:23 -04:00
printf -v MessageBody " ${ FormattedOutput } "
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
printf " \nSending ${ channel } notification "
exec_if_exists_or_fail trigger_${ template } _notification " ${ channel } " || \
printf " \nAttempted to send notification to channel ${ channel } , but the function was not found. Make sure notify_ ${ template } .sh is available in the ${ ScriptWorkDir } directory or notify_templates subdirectory. "
Notified = "true"
fi
done
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
if [ [ " ${ Notified } " = = "true" ] ] ; then
[ [ -n " ${ snooze } " ] ] && [ [ -n " ${ UpdToString } " ] ] && [ [ " ${ NotifyError } " = = "false" ] ] && update_snooze " ${ Updates [@] } "
printf "\n"
2025-06-24 09:16:48 -04:00
fi
2025-06-27 03:10:31 -04:00
return 0
2025-06-24 09:16:48 -04:00
}
### Set DISABLE_DOCKCHECK_NOTIFICATION=false in dockcheck.config
### to not send notifications when dockcheck itself has updates.
dockcheck_notification( ) {
if [ [ ! " ${ DISABLE_DOCKCHECK_NOTIFICATION :- } " = = "true" ] ] ; then
2025-09-15 05:25:23 -04:00
UnsnoozedDockcheck = $( unsnoozed_count "dockcheck\.sh" )
2025-06-24 09:16:48 -04:00
NotifyError = false
2025-09-15 05:25:23 -04:00
Notified = false
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
MessageTitle = " $FromHost - New version of dockcheck available. "
UpdToString = " dockcheck.sh, $1 , $2 ,\" $3 \" "
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
for channel in " ${ enabled_notify_channels [@] } " ; do
local SkipNotification = $( skip_notification " ${ channel } " " ${ UnsnoozedDockcheck } " "dockcheck" )
if [ [ " ${ SkipNotification } " = = "false" ] ] ; then
local template = $( get_channel_template " ${ channel } " )
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
# Formats UpdToString variable per channel settings
format_output "dockcheck_update" " $( output_format " ${ channel } " ) " "Installed version: <insert_text_iv>\nLatest version: <insert_text_lv>\n\nChangenotes: <insert_text_rn>\n" " $1 " " $2 " " $3 "
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
# Setting the MessageBody variable here.
printf -v MessageBody " ${ FormattedOutput } "
printf " \nSending dockcheck update notification - ${ channel } "
exec_if_exists_or_fail trigger_${ template } _notification " ${ channel } " || \
printf " \nAttempted to send notification to channel ${ channel } , but the function was not found. Make sure notify_ ${ template } .sh is available in the ${ ScriptWorkDir } directory or notify_templates subdirectory. "
Notified = "true"
fi
done
if [ [ " ${ Notified } " = = "true" ] ] ; then
2025-08-11 15:17:01 -04:00
[ [ -n " ${ snooze } " ] ] && [ [ " ${ NotifyError } " = = "false" ] ] && update_snooze "dockcheck.sh"
2025-09-15 05:25:23 -04:00
printf "\n"
2025-06-24 09:16:48 -04:00
fi
2025-05-25 12:26:13 -04:00
fi
2025-06-27 03:10:31 -04:00
return 0
2025-05-25 12:26:13 -04:00
}
2025-06-27 03:10:31 -04:00
### Set DISABLE_NOTIFY_NOTIFICATION=false in dockcheck.config
2025-05-25 12:26:13 -04:00
### to not send notifications when notify scripts themselves have updates.
notify_update_notification( ) {
2025-06-27 03:10:31 -04:00
if [ [ ! " ${ DISABLE_NOTIFY_NOTIFICATION :- } " = = "true" ] ] ; then
2025-06-24 09:16:48 -04:00
NotifyError = false
2025-06-27 03:10:31 -04:00
NotifyUpdates = ( )
2025-09-15 05:25:23 -04:00
Notified = false
2025-05-25 12:26:13 -04:00
2025-09-15 05:25:23 -04:00
UpdateChannels = ( " ${ enabled_notify_templates [@] } " "v2" )
2025-06-24 09:16:48 -04:00
for NotifyScript in " ${ UpdateChannels [@] } " ; do
2025-09-15 05:25:23 -04:00
UpperChannel = " ${ NotifyScript ^^ } "
2025-06-24 09:16:48 -04:00
VersionVar = " NOTIFY_ ${ UpperChannel } _VERSION "
2025-05-29 16:43:34 -04:00
if [ [ -n " ${ !VersionVar :- } " ] ] ; then
2025-06-24 09:16:48 -04:00
RawNotifyUrl = " https://raw.githubusercontent.com/mag37/dockcheck/main/notify_templates/notify_ ${ NotifyScript } .sh "
LatestNotifySnippet = " $( curl ${ CurlArgs } -r 0-150 " $RawNotifyUrl " || printf "undefined" ) "
2025-09-15 05:25:23 -04:00
if [ [ ! " ${ LatestNotifySnippet } " = = "undefined" ] ] ; then
LatestNotifyRelease = " $( echo " $LatestNotifySnippet " | sed -n " / ${ VersionVar } /s/ ${ VersionVar } =//p " | tr -d '"' ) "
2025-06-24 09:16:48 -04:00
if [ [ " ${ !VersionVar } " != " ${ LatestNotifyRelease } " ] ] ; then
2025-09-15 05:25:23 -04:00
NotifyUpdates += ( " ${ NotifyScript } .sh ${ !VersionVar } -> ${ LatestNotifyRelease } " )
2025-05-25 12:26:13 -04:00
fi
fi
fi
done
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
UpdatesPlusDockcheck = ( " ${ NotifyUpdates [@] } " )
UpdatesPlusDockcheck += ( "dockcheck.sh" )
[ [ -n " ${ snooze } " ] ] && cleanup_snooze " ${ UpdatesPlusDockcheck [@] } "
UnsnoozedTemplates = $( unsnoozed_count " ${ NotifyUpdates [@] } " )
MessageTitle = " $FromHost - New version of notify templates available. "
UpdToString = $( printf '%s\\n' " ${ NotifyUpdates [@] } " )
UpdToString = " ${ UpdToString %, } "
UpdToString = ${ UpdToString % \\ n }
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
for channel in " ${ enabled_notify_channels [@] } " ; do
local SkipNotification = $( skip_notification " ${ channel } " " ${ UnsnoozedTemplates } " "notify" )
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
if [ [ " ${ SkipNotification } " = = "false" ] ] ; then
local template = $( get_channel_template " ${ channel } " )
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
# Formats UpdToString variable per channel settings
format_output "notify_update" " $( output_format " ${ channel } " ) " " Notify templates on $FromHost with updates available:\n<insert_text_nu>\n "
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
# Setting the MessageBody variable here.
printf -v MessageBody " ${ FormattedOutput } "
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
printf " \nSending notify template update notification - ${ channel } "
exec_if_exists_or_fail trigger_${ template } _notification " ${ channel } " || \
printf " \nAttempted to send notification to channel ${ channel } , but the function was not found. Make sure notify_ ${ template } .sh is available in the ${ ScriptWorkDir } directory or notify_templates subdirectory. "
Notified = "true"
2025-06-24 09:16:48 -04:00
fi
2025-09-15 05:25:23 -04:00
done
2025-06-24 09:16:48 -04:00
2025-09-15 05:25:23 -04:00
if [ [ " ${ Notified } " = = "true" ] ] ; then
[ [ -n " ${ snooze } " ] ] && [ [ -n " ${ UpdToString } " ] ] && [ [ " ${ NotifyError } " = = "false" ] ] && update_snooze " ${ NotifyUpdates [@] } "
printf "\n"
fi
2025-05-25 12:26:13 -04:00
fi
2025-06-27 03:10:31 -04:00
return 0
2025-05-25 12:26:13 -04:00
}