Skip to content

Commit e676e50

Browse files
authored
Merge pull request #1299 from MusclePr/feature/rich-notifications
Add structured notification.
2 parents 2a59da1 + be5c7e9 commit e676e50

3 files changed

Lines changed: 155 additions & 39 deletions

File tree

tools/arkmanager

Lines changed: 137 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ bnumber=""
298298
GREEN="\\033[1;32m"
299299
RED="\\033[1;31m"
300300
YELLOW="\\e[0;33m"
301+
CYAN="\\e[0;36m"
301302
NORMAL="\\033[0;39m"
302303

303304
# Set TERM to "dumb" if TERM is not set
@@ -661,14 +662,110 @@ doBroadcastWithEcho(){
661662
doBroadcast "$1"
662663
}
663664

665+
#
666+
# Apply notification template
667+
#
668+
function notifyApplyTemplate(){
669+
local msg="$1"
670+
#shellcheck disable=SC2154
671+
for v in "${!notifyvar_@}"; do
672+
vn="${v#notifyvar_}"
673+
msg="${msg//\{${vn}\}/${!v}}"
674+
done
675+
echo "$msg"
676+
}
677+
678+
#
679+
# Notifiy console
680+
#
681+
function notifyConsole(){
682+
local msg="${1//\\/\\\\}"
683+
local type=""
684+
case "$2" in
685+
info*) type="${CYAN}[INFO]${NORMAL} ";;
686+
warn*) type="${YELLOW}[WARN]${NORMAL} ";;
687+
err*) type="${RED}[ERROR]${NORMAL} ";;
688+
esac
689+
# shellcheck disable=SC2034
690+
local notifyvar_type="$type"
691+
# shellcheck disable=SC2034
692+
local notifyvar_msg="$msg"
693+
msg="$(notifyApplyTemplate "${consoleNotifyTemplate:-"{local_date} {type}({instance}) {msg}"}")"
694+
echo -e "${msg}${NORMAL}"
695+
}
696+
697+
#
698+
# Hex color codes
699+
#
700+
declare -A HexColorCodes=(
701+
['black']='000000'
702+
['red']='ff0000'
703+
['green']='00ff00'
704+
['yellow']='ffff00'
705+
['blue']='0000ff'
706+
['purple']='800080'
707+
['cyan']='00ffff'
708+
['white']='ffffff'
709+
)
710+
711+
#
712+
# Convert hex color to Discord color integer
713+
#
714+
function hexToDiscordColor(){
715+
local hex="${1}"
716+
local r g b
717+
r=$(( 0x${hex:0:2} ))
718+
g=$(( 0x${hex:2:2} ))
719+
b=$(( 0x${hex:4:2} ))
720+
echo $(( (r * 65536) + (g * 256) + b ))
721+
}
722+
723+
#
724+
# Escape string for JSON
725+
#
726+
function jsonEscape(){
727+
local msg=${1//\\/\\\\} # backslash to escape
728+
msg=${msg//\"/\\\"} # double quote to escape
729+
msg=${msg//$'\r'/\\r} # carriage return to \r
730+
msg=${msg//$'\n'/\\n} # newline to \n
731+
msg=${msg//$'\t'/\\t} # tab to \t
732+
printf '%s' "$msg"
733+
}
734+
664735
#
665736
# Discord Webhook notifier
666737
#
667738
function notifyDiscord(){
668739
if [ -n "$discordWebhookURL" ]; then
669-
local msg="$(echo -n "${msg}" | tr '\n' '\001' | sed 's/\001/\\n/g;s/["\\]/\\\0/g')"
670-
local json="{\"content\":\"${msg}\"}"
671-
curl -s -H "Content-Type: application/json" -d "${json}" "$discordWebhookURL" >/dev/null
740+
if [[ "${discordNotifyAdminCmd}" != "true" ]] && [[ "$1" =~ ^AdminCmd: ]]; then
741+
return
742+
fi
743+
local msg="$(jsonEscape "$1")"
744+
local type="$2"
745+
local color=""
746+
local template="${discordNotifyPlainTemplate:-"{\"content\": \"[{serverMap}] {msg}\"}"}"
747+
case "$type" in
748+
info*) template="${discordNotifyInfoTemplate:-"{\"embeds\": [{\"title\": \"📢 INFO - {msg}\", \"color\": 3447003, \"fields\": [{\"name\": \"{serverMap}\", \"value\": \"\"}]}]}"}";;
749+
warn*) template="${discordNotifyWarnTemplate:-"{\"embeds\": [{\"title\": \"⚠️ WARN - {msg}\", \"color\": 15105570, \"fields\": [{\"name\": \"{serverMap}\", \"value\": \"\"}]}]}"}";;
750+
err*) template="${discordNotifyErrorTemplate:-"{\"embeds\": [{\"title\": \"❗ ERROR - {msg}\", \"color\": 15158332, \"fields\": [{\"name\": \"{serverMap}\", \"value\": \"\"}]}]}"}";;
751+
*)
752+
if [[ "$type" =~ ^#([0-9A-Fa-f]{6})$ ]]; then
753+
color=$(hexToDiscordColor "${BASH_REMATCH[1]}")
754+
elif [ -n "$type" ]; then
755+
color=$(hexToDiscordColor "${HexColorCodes[$type]:-808080}")
756+
fi
757+
if [ -n "$color" ]; then
758+
template="${discordNotifyColorTemplate:-"{\"embeds\": [{\"color\": {color}, \"fields\": [{\"name\": \"{serverMap}\", \"value\": \"{msg}\"}]}]}"}"
759+
fi
760+
;;
761+
esac
762+
# shellcheck disable=SC2034
763+
local notifyvar_msg="$msg"
764+
# shellcheck disable=SC2034
765+
local notifyvar_color="$color"
766+
local json="$(notifyApplyTemplate "${template}")"
767+
local result=$(curl -sS -H "Content-Type: application/json" -d "${json}" "$discordWebhookURL" 2>/dev/null)
768+
echo "${result}" | grep -q '"message"' && echo -e "Discord notification failed: ${result}\n----\n${json}\n----" >&2
672769
fi
673770
}
674771

@@ -684,21 +781,30 @@ function notifyOverride(){
684781
#
685782
function notify(){
686783
if [[ -n "$1" && "$1" != "-" ]]; then
687-
local msg
688-
local notifymsg="$1"
689-
msg="${notifyTemplate:-Message from instance \`{instance\}\` on server \`{server\}\`: {msg\}}"
690-
msg="${msg//\{msg\}/${notifymsg}}"
691-
msg="${msg//\{instance\}/${instance}}"
692-
msg="${msg//\{server\}/${HOSTNAME}}"
693-
694-
#shellcheck disable=SC2154
695-
for v in "${!notifyvar_@}"; do
696-
vn="${v#notifyvar_}"
697-
msg="${msg//\{${vn}\}/${!v}}"
698-
done
699-
700-
notifyDiscord "$msg"
701-
notifyOverride "$msg"
784+
local msg="$1"
785+
local type="$2" # info, warn, err, ...
786+
787+
# Prepare notification variables
788+
# shellcheck disable=SC2034
789+
local notifyvar_instance="$instance"
790+
# shellcheck disable=SC2034
791+
local notifyvar_serverMap="$serverMap"
792+
# shellcheck disable=SC2034
793+
local notifyvar_server="$HOSTNAME"
794+
# shellcheck disable=SC2034
795+
local notifyvar_local_date="$(timestamp)"
796+
797+
# Console notification
798+
notifyConsole "$msg" "$type"
799+
800+
# Discord notification
801+
notifyDiscord "$msg" "$type"
802+
803+
# Custom notification
804+
# shellcheck disable=SC2034
805+
local notifyvar_msg="$msg"
806+
msg="$(notifyApplyTemplate "${notifyTemplate:-"Message from instance \`{instance}\` on server \`{server}\`: {msg}"}")"
807+
notifyOverride "$msg" "$type"
702808

703809
if [ -n "${notifyCommand}" ]; then
704810
eval " ${notifyCommand}"
@@ -1422,7 +1528,7 @@ doRun() {
14221528
# Shutdown the server when we are terminated
14231529
shutdown_server(){
14241530
restartserver=0
1425-
notify "${notifyMsgShuttingDown:-Shutting down}"
1531+
notify "${notifyMsgShuttingDown:-Shutting down}" 'info'
14261532
rm -f "$arkserverroot/$arkautorestartfile"
14271533
if [ "$serverpid" -ne 0 ]; then
14281534
kill -INT -$serverpid >/dev/null 2>&1
@@ -1437,8 +1543,8 @@ doRun() {
14371543
14381544
# Auto-restart loop
14391545
while [ $restartserver -ne 0 ]; do
1546+
notify "${notifyMsgStarting:-Starting}" 'info'
14401547
echo -n "$(timestamp): Running"
1441-
notify "${notifyMsgStarting:-Starting}"
14421548
printf " %q" "$arkserverroot/$arkserverexec" "$arkserveropts" "${arkextraopts[@]}"
14431549
echo
14441550
# Put the server process into the background so we can monitor it
@@ -1468,8 +1574,7 @@ doRun() {
14681574
# Check if the server has fully started
14691575
if ! isTheServerUp; then
14701576
# Enable auto-restart if the server is up
1471-
echo "$(timestamp): server is up"
1472-
notify "${notifyMsgServerUp:-Server is up}"
1577+
notify "${notifyMsgServerUp:-Server is up}" 'info'
14731578
if [ "$restartserver" -eq 0 ]; then
14741579
touch "$arkserverroot/$arkautorestartfile"
14751580
restartserver=1
@@ -1482,9 +1587,7 @@ doRun() {
14821587
14831588
if (( serverdowntries > 12 )); then
14841589
# Server has not been listening for 60 seconds, so restart it.
1485-
echo "$(timestamp): The server has stopped listening"
1486-
echo "$(timestamp): Restarting server"
1487-
notify "${notifyMsgStoppedListening:-Server has stopped listening - restarting}"
1590+
notify "${notifyMsgStoppedListening:-Server has stopped listening - restarting}" 'err'
14881591
for (( i = 0; i < 5; i++ )); do
14891592
if ! kill -0 "-$serverpid"; then
14901593
break
@@ -1508,9 +1611,7 @@ doRun() {
15081611
break
15091612
elif [ "$pid" == "$(pgrep --pgroup $serverpid)" ]; then
15101613
restartserver=1
1511-
echo "$(timestamp): Server has double-forked and cannot be effectively monitored"
1512-
echo "$(timestamp): Restarting server"
1513-
notify "${notifyMsgDoubleForked:-Server has daemonized itself - restarting}"
1614+
notify "${notifyMsgDoubleForked:-Server has daemonized itself - restarting}" 'err'
15141615
for (( i = 0; i < 5; i++ )); do
15151616
if ! kill -0 "-$serverpid"; then
15161617
break
@@ -1545,8 +1646,7 @@ doRun() {
15451646
fi
15461647
15471648
if [ "$restartserver" -ne 0 ]; then
1548-
notify "${notifyMsgServerTerminated:-Server exited - restarting}"
1549-
echo "$(timestamp): restarting server"
1649+
notify "${notifyMsgServerTerminated:-Server exited - restarting}" 'warn'
15501650
fi
15511651
done
15521652
}
@@ -2026,7 +2126,7 @@ doWarn(){
20262126
msg="Shutdown cancelled by operator ($1)"
20272127
fi
20282128
doBroadcastWithEcho "${msg}"
2029-
notify "${msg}"
2129+
notify "${msg}" 'warn'
20302130
exit 1
20312131
}
20322132
@@ -2066,19 +2166,18 @@ doWarn(){
20662166
numplayers=$(numPlayersConnected)
20672167
echo "There are ${numplayers} players connected"
20682168
if [[ "${numplayers}" == "-1" ]]; then
2069-
echo "Server is not running. Shutting down immediately"
2070-
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}"
2169+
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}" 'warn'
20712170
return 0
20722171
elif (( (numplayers + 0) == 0 )); then
20732172
doBroadcastWithEcho "Nobody is connected. Shutting down immediately"
2074-
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}"
2173+
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}" 'info'
20752174
rm -f "${arkserverroot}/${arkwarnlockfile}"
20762175
return 0
20772176
fi
20782177
fi
20792178
if isUpdateCancelRequested; then
20802179
doBroadcastWithEcho "Restart cancelled by player request"
2081-
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}"
2180+
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}" 'warn'
20822181
return 1
20832182
fi
20842183
wait $sleeppid
@@ -2113,19 +2212,18 @@ doWarn(){
21132212
numplayers=$(numPlayersConnected)
21142213
echo "There are ${numplayers} players connected"
21152214
if [[ "${numplayers}" == "-1" ]]; then
2116-
echo "Server is not running. Shutting down immediately"
2117-
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}"
2215+
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}" 'warn'
21182216
return 0
21192217
elif (( (numplayers + 0) == 0 )); then
21202218
doBroadcastWithEcho "Nobody is connected. Shutting down immediately"
2121-
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}"
2219+
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}" 'info'
21222220
rm -f "${arkserverroot}/${arkwarnlockfile}"
21232221
return 0
21242222
fi
21252223
fi
21262224
if isUpdateCancelRequested; then
21272225
doBroadcastWithEcho "Restart cancelled by player request"
2128-
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}"
2226+
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}" 'warn'
21292227
return 1
21302228
fi
21312229
fi

tools/arkmanager.cfg

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ msgWarnCancelled="Restart cancelled by player request"
5454
# notifyTemplate="Message from instance {instance} on server {server}: {msg}"
5555
# noNotifyWarning=true
5656
# notifyCommand='echo "$msg" | mailx -s "Message from instance ${instance} on server ${HOSTNAME}" "email@domain.com"'
57+
# consoleNotifyTemplate="{local_date} {type}({instance}) {msg}" # Template for console notifications
58+
# discordNotifyInfoTemplate='{"embeds": [{"title": "📢 INFO - {msg}", "color": 3447003, "fields": [{"name": "{serverMap}", "value": ""}]}]}'
59+
# discordNotifyWarnTemplate='{"embeds": [{"title": "⚠️ WARN - {msg}", "color": 15105570, "fields": [{"name": "{serverMap}", "value": ""}]}]}'
60+
# discordNotifyErrorTemplate='{"embeds": [{"title": "❗ ERROR - {msg}", "color": 15158332, "fields": [{"name": "{serverMap}", "value": ""}]}]}'
61+
# discordNotifyColorTemplate='{"embeds": [{"color": {color}, "fields": [{"name": "{serverMap}", "value": "{msg}"}]}]}'
62+
# discordNotifyPlainTemplate='{"content": "[{serverMap}] {msg}"}'
63+
# discordNotifyAdminCmd="false" # Set to true to enable discord notifications for AdminCmd messages
5764

5865
# Restart cancel chat command
5966
#chatCommandRestartCancel="/cancelupdate"

tools/arkmanager.cfg.shellcheck

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,17 @@ noNotifyWarn=true
235235
# ${msg} is expanded to message
236236
notifyCommand='echo "$msg" | mailx -s "Message from instance ${instance} on server ${HOSTNAME}" "email@domain.com"'
237237

238+
# Template for structured notifications
239+
consoleNotifyTemplate="{local_date} {type}({instance}) {msg}" # Template for console notifications
240+
discordNotifyInfoTemplate='{"embeds": [{"title": "📢 INFO - {msg}", "color": 3447003, "fields": [{"name": "{serverMap}", "value": ""}]}]}'
241+
discordNotifyWarnTemplate='{"embeds": [{"title": "⚠️ WARN - {msg}", "color": 15105570, "fields": [{"name": "{serverMap}", "value": ""}]}]}'
242+
discordNotifyErrorTemplate='{"embeds": [{"title": "❗ ERROR - {msg}", "color": 15158332, "fields": [{"name": "{serverMap}", "value": ""}]}]}'
243+
discordNotifyColorTemplate='{"embeds": [{"color": {color}, "fields": [{"name": "{serverMap}", "value": "{msg}"}]}]}'
244+
discordNotifyPlainTemplate='{"content": "[{serverMap}] {msg}"}'
245+
246+
# Set to true to enable AdminCmd notifications
247+
discordNotifyAdminCmd="false"
248+
238249
# Chat message players can use to cancel update / restart
239250
chatCommandRestartCancel="/cancelupdate"
240251

0 commit comments

Comments
 (0)