Skip to content

Commit be5c7e9

Browse files
committed
add structured notification.
1 parent 8ddf0b8 commit be5c7e9

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}"
@@ -1398,7 +1504,7 @@ doRun() {
13981504
# Shutdown the server when we are terminated
13991505
shutdown_server(){
14001506
restartserver=0
1401-
notify "${notifyMsgShuttingDown:-Shutting down}"
1507+
notify "${notifyMsgShuttingDown:-Shutting down}" 'info'
14021508
rm -f "$arkserverroot/$arkautorestartfile"
14031509
if [ "$serverpid" -ne 0 ]; then
14041510
kill -INT -$serverpid >/dev/null 2>&1
@@ -1413,8 +1519,8 @@ doRun() {
14131519
14141520
# Auto-restart loop
14151521
while [ $restartserver -ne 0 ]; do
1522+
notify "${notifyMsgStarting:-Starting}" 'info'
14161523
echo -n "$(timestamp): Running"
1417-
notify "${notifyMsgStarting:-Starting}"
14181524
printf " %q" "$arkserverroot/$arkserverexec" "$arkserveropts" "${arkextraopts[@]}"
14191525
echo
14201526
# Put the server process into the background so we can monitor it
@@ -1444,8 +1550,7 @@ doRun() {
14441550
# Check if the server has fully started
14451551
if ! isTheServerUp; then
14461552
# Enable auto-restart if the server is up
1447-
echo "$(timestamp): server is up"
1448-
notify "${notifyMsgServerUp:-Server is up}"
1553+
notify "${notifyMsgServerUp:-Server is up}" 'info'
14491554
if [ "$restartserver" -eq 0 ]; then
14501555
touch "$arkserverroot/$arkautorestartfile"
14511556
restartserver=1
@@ -1458,9 +1563,7 @@ doRun() {
14581563
14591564
if (( serverdowntries > 12 )); then
14601565
# Server has not been listening for 60 seconds, so restart it.
1461-
echo "$(timestamp): The server has stopped listening"
1462-
echo "$(timestamp): Restarting server"
1463-
notify "${notifyMsgStoppedListening:-Server has stopped listening - restarting}"
1566+
notify "${notifyMsgStoppedListening:-Server has stopped listening - restarting}" 'err'
14641567
for (( i = 0; i < 5; i++ )); do
14651568
if ! kill -0 "-$serverpid"; then
14661569
break
@@ -1484,9 +1587,7 @@ doRun() {
14841587
break
14851588
elif [ "$pid" == "$(pgrep --pgroup $serverpid)" ]; then
14861589
restartserver=1
1487-
echo "$(timestamp): Server has double-forked and cannot be effectively monitored"
1488-
echo "$(timestamp): Restarting server"
1489-
notify "${notifyMsgDoubleForked:-Server has daemonized itself - restarting}"
1590+
notify "${notifyMsgDoubleForked:-Server has daemonized itself - restarting}" 'err'
14901591
for (( i = 0; i < 5; i++ )); do
14911592
if ! kill -0 "-$serverpid"; then
14921593
break
@@ -1521,8 +1622,7 @@ doRun() {
15211622
fi
15221623
15231624
if [ "$restartserver" -ne 0 ]; then
1524-
notify "${notifyMsgServerTerminated:-Server exited - restarting}"
1525-
echo "$(timestamp): restarting server"
1625+
notify "${notifyMsgServerTerminated:-Server exited - restarting}" 'warn'
15261626
fi
15271627
done
15281628
}
@@ -2002,7 +2102,7 @@ doWarn(){
20022102
msg="Shutdown cancelled by operator ($1)"
20032103
fi
20042104
doBroadcastWithEcho "${msg}"
2005-
notify "${msg}"
2105+
notify "${msg}" 'warn'
20062106
exit 1
20072107
}
20082108
@@ -2042,19 +2142,18 @@ doWarn(){
20422142
numplayers=$(numPlayersConnected)
20432143
echo "There are ${numplayers} players connected"
20442144
if [[ "${numplayers}" == "-1" ]]; then
2045-
echo "Server is not running. Shutting down immediately"
2046-
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}"
2145+
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}" 'warn'
20472146
return 0
20482147
elif (( (numplayers + 0) == 0 )); then
20492148
doBroadcastWithEcho "Nobody is connected. Shutting down immediately"
2050-
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}"
2149+
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}" 'info'
20512150
rm -f "${arkserverroot}/${arkwarnlockfile}"
20522151
return 0
20532152
fi
20542153
fi
20552154
if isUpdateCancelRequested; then
20562155
doBroadcastWithEcho "Restart cancelled by player request"
2057-
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}"
2156+
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}" 'warn'
20582157
return 1
20592158
fi
20602159
wait $sleeppid
@@ -2089,19 +2188,18 @@ doWarn(){
20892188
numplayers=$(numPlayersConnected)
20902189
echo "There are ${numplayers} players connected"
20912190
if [[ "${numplayers}" == "-1" ]]; then
2092-
echo "Server is not running. Shutting down immediately"
2093-
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}"
2191+
notify "${notifyMsgServerNotRunning:-Server is not running. Shutting down immediately}" 'warn'
20942192
return 0
20952193
elif (( (numplayers + 0) == 0 )); then
20962194
doBroadcastWithEcho "Nobody is connected. Shutting down immediately"
2097-
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}"
2195+
notify "${notifyMsgNobodyConnected:-Nobody is connected. Shutting down immediately}" 'info'
20982196
rm -f "${arkserverroot}/${arkwarnlockfile}"
20992197
return 0
21002198
fi
21012199
fi
21022200
if isUpdateCancelRequested; then
21032201
doBroadcastWithEcho "Restart cancelled by player request"
2104-
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}"
2202+
notify "${notifyMsgRestartCancelled:-Restart cancelled by player request}" 'warn'
21052203
return 1
21062204
fi
21072205
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)