- Serverlabel/Tags (SERVER_NAME/SERVER_TAG) in Titel & ntfy-Tags
- State-Verzeichnis per Instanz (Host:Port:Topic) - Optionales ENV_FILE zum Laden einer spezifischen .env
This commit is contained in:
parent
95ac12bbe2
commit
5f4b02d912
6
.env
6
.env
@ -7,11 +7,15 @@ export RCON_PASSWORD=""
|
|||||||
export POLL_SECONDS="10"
|
export POLL_SECONDS="10"
|
||||||
export ANNOUNCE_SERVER_UPDOWN="true"
|
export ANNOUNCE_SERVER_UPDOWN="true"
|
||||||
|
|
||||||
|
# Anzeigename/Tag
|
||||||
|
SERVER_NAME="[GER] Blockventure | ⌁25573"
|
||||||
|
SERVER_TAG="blockventure-25573"
|
||||||
|
|
||||||
# ntfy
|
# ntfy
|
||||||
export NTFY_SERVER="https://ntfy.pushservice.techniverse.net"
|
export NTFY_SERVER="https://ntfy.pushservice.techniverse.net"
|
||||||
export NTFY_TOPIC="mc-events"
|
export NTFY_TOPIC="mc-events"
|
||||||
export NTFY_TOKEN=""
|
export NTFY_TOKEN=""
|
||||||
export NTFY_TITLE_PREFIX="Minecraft"
|
export NTFY_TITLE_PREFIX="🟩 Minecraft"
|
||||||
export NTFY_TAGS_BASE="minecraft"
|
export NTFY_TAGS_BASE="minecraft"
|
||||||
export NTFY_MARKDOWN="false"
|
export NTFY_MARKDOWN="false"
|
||||||
|
|
||||||
|
@ -4,20 +4,23 @@
|
|||||||
# Autor: Patrick Asmus
|
# Autor: Patrick Asmus
|
||||||
# Web: https://www.cleveradmin.de
|
# Web: https://www.cleveradmin.de
|
||||||
# Repository: https://git.techniverse.net/scriptos/minecraft-ntfy-notify
|
# Repository: https://git.techniverse.net/scriptos/minecraft-ntfy-notify
|
||||||
# Version: 1.3
|
# Version: 1.4
|
||||||
# Datum: 18.09.2025
|
# Datum: 18.09.2025
|
||||||
# Modifikation: Cron-sicheres Locking (Lockdir+PID), Running-Mark, Cleanup via trap
|
# Modifikation:
|
||||||
|
# - Serverlabel/Tags (SERVER_NAME/SERVER_TAG) in Titel & ntfy-Tags
|
||||||
|
# - State-Verzeichnis per Instanz (Host:Port:Topic)
|
||||||
|
# - Optionales ENV_FILE zum Laden einer spezifischen .env
|
||||||
#####################################################
|
#####################################################
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Robustes PATH für Cron
|
|
||||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
if [[ -f "${SCRIPT_DIR}/.env" ]]; then
|
|
||||||
set -a
|
# --- .env laden: zuerst ENV_FILE (falls gesetzt), sonst ./.env ---
|
||||||
. "${SCRIPT_DIR}/.env"
|
if [[ -n "${ENV_FILE:-}" && -f "${ENV_FILE}" ]]; then
|
||||||
set +a
|
set -a; . "${ENV_FILE}"; set +a
|
||||||
|
elif [[ -f "${SCRIPT_DIR}/.env" ]]; then
|
||||||
|
set -a; . "${SCRIPT_DIR}/.env"; set +a
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ===== Konfiguration via ENV =====
|
# ===== Konfiguration via ENV =====
|
||||||
@ -40,14 +43,20 @@ NTFY_PRIORITY_UP="${NTFY_PRIORITY_UP:-4}"
|
|||||||
NTFY_PRIORITY_DOWN="${NTFY_PRIORITY_DOWN:-5}"
|
NTFY_PRIORITY_DOWN="${NTFY_PRIORITY_DOWN:-5}"
|
||||||
NTFY_MARKDOWN="${NTFY_MARKDOWN:-false}"
|
NTFY_MARKDOWN="${NTFY_MARKDOWN:-false}"
|
||||||
|
|
||||||
# State (standardmäßig im Script-Ordner, root-frei)
|
# Server-Label/Tag
|
||||||
STATE_DIR="${STATE_DIR:-${SCRIPT_DIR}/state}"
|
SERVER_NAME="${SERVER_NAME:-}" # z. B. "[GER] Blockventure | ⌁25573"
|
||||||
|
SERVER_LABEL="${SERVER_NAME:-${MC_HOST}:${RCON_PORT}}"
|
||||||
|
# Maschinenfreundlicher Tag, wenn nicht gesetzt: aus Name oder Host:Port ableiten
|
||||||
|
SERVER_TAG="${SERVER_TAG:-$(printf '%s' "${SERVER_NAME:-${MC_HOST}-${RCON_PORT}}" \
|
||||||
|
| tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g;s/^-+|-+$//g')}"
|
||||||
|
|
||||||
|
# ==== Lock & State (pro Instanz eindeutig nach Host:Port:Topic) ====
|
||||||
|
LOCK_KEY="$(printf '%s' "${MC_HOST}_${RCON_PORT}_${NTFY_TOPIC}" | tr -c 'A-Za-z0-9._-' '_')"
|
||||||
|
# eigenes State-Verzeichnis je Instanz (damit mehrere Server parallel sauber laufen)
|
||||||
|
STATE_DIR="${STATE_DIR:-${SCRIPT_DIR}/state.${LOCK_KEY}}"
|
||||||
STATE_PLAYERS="${STATE_DIR}/players.prev"
|
STATE_PLAYERS="${STATE_DIR}/players.prev"
|
||||||
STATE_UP="${STATE_DIR}/server_up.prev"
|
STATE_UP="${STATE_DIR}/server_up.prev"
|
||||||
|
|
||||||
# Lock & Running-Mark (pro MC_HOST:RCON_PORT:NTFY_TOPIC eindeutig)
|
|
||||||
# -> erlaubt mehrere parallele Instanzen für verschiedene Server/Topics
|
|
||||||
LOCK_KEY="$(printf '%s' "${MC_HOST}_${RCON_PORT}_${NTFY_TOPIC}" | tr -c 'A-Za-z0-9._-' '_')"
|
|
||||||
RUN_DIR="${RUN_DIR:-${STATE_DIR}}"
|
RUN_DIR="${RUN_DIR:-${STATE_DIR}}"
|
||||||
LOCK_DIR="${RUN_DIR}/lock.${LOCK_KEY}"
|
LOCK_DIR="${RUN_DIR}/lock.${LOCK_KEY}"
|
||||||
PID_FILE="${LOCK_DIR}/pid"
|
PID_FILE="${LOCK_DIR}/pid"
|
||||||
@ -62,41 +71,28 @@ need_bin() { command -v "$1" >/dev/null 2>&1 || die "Benötigtes Tool fehlt: $1"
|
|||||||
dbg() { [[ "$DEBUG" == "true" ]] && echo "DBG: $*" >&2 || true; }
|
dbg() { [[ "$DEBUG" == "true" ]] && echo "DBG: $*" >&2 || true; }
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
# Lauf-Mark und Lock sauber entfernen
|
rm -f "$RUN_MARK" "$PID_FILE" 2>/dev/null || true
|
||||||
rm -f "$RUN_MARK" 2>/dev/null || true
|
|
||||||
rm -f "$PID_FILE" 2>/dev/null || true
|
|
||||||
rmdir "$LOCK_DIR" 2>/dev/null || true
|
rmdir "$LOCK_DIR" 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
acquire_lock() {
|
acquire_lock() {
|
||||||
mkdir -p "$RUN_DIR"
|
mkdir -p "$RUN_DIR"
|
||||||
# Atomarer Lock-Versuch
|
|
||||||
if mkdir "$LOCK_DIR" 2>/dev/null; then
|
if mkdir "$LOCK_DIR" 2>/dev/null; then
|
||||||
echo "$$" > "$PID_FILE"
|
echo "$$" > "$PID_FILE"
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
return 0
|
else
|
||||||
fi
|
if [[ -f "$PID_FILE" ]]; then
|
||||||
|
oldpid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
||||||
# Lock existiert -> prüfen, ob Prozess noch lebt
|
if [[ -n "${oldpid:-}" ]] && kill -0 "$oldpid" 2>/dev/null; then
|
||||||
if [[ -f "$PID_FILE" ]]; then
|
dbg "Bereits laufend (PID $oldpid), beende."
|
||||||
oldpid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
exit 0
|
||||||
if [[ -n "${oldpid:-}" ]] && kill -0 "$oldpid" 2>/dev/null; then
|
fi
|
||||||
# Schon aktiv → leise beenden (Cron-safe)
|
|
||||||
dbg "Bereits laufend (PID $oldpid), beende."
|
|
||||||
exit 0
|
|
||||||
fi
|
fi
|
||||||
fi
|
rm -rf "$LOCK_DIR" 2>/dev/null || true
|
||||||
|
mkdir "$LOCK_DIR" 2>/dev/null || die "Konnte Lock nicht übernehmen: ${LOCK_DIR}"
|
||||||
# Stale Lock entfernen und erneut versuchen
|
|
||||||
rm -rf "$LOCK_DIR" 2>/dev/null || true
|
|
||||||
if mkdir "$LOCK_DIR" 2>/dev/null; then
|
|
||||||
echo "$$" > "$PID_FILE"
|
echo "$$" > "$PID_FILE"
|
||||||
trap cleanup EXIT INT TERM
|
trap cleanup EXIT INT TERM
|
||||||
return 0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Falls wir hier landen, ist wirklich etwas schief
|
|
||||||
die "Konnte Lock nicht übernehmen: ${LOCK_DIR}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ntfy_notify() {
|
ntfy_notify() {
|
||||||
@ -131,23 +127,19 @@ get_players() {
|
|||||||
fi
|
fi
|
||||||
dbg "RAW: $out"
|
dbg "RAW: $out"
|
||||||
out="$(printf '%s' "$out" | sanitize)"
|
out="$(printf '%s' "$out" | sanitize)"
|
||||||
|
|
||||||
if ! grep -q "players online:" <<<"$out"; then
|
if ! grep -q "players online:" <<<"$out"; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local names="${out#*:}"
|
local names="${out#*:}"
|
||||||
names="$(echo "$names" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
names="$(echo "$names" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
|
||||||
if [[ -z "$names" ]]; then
|
if [[ -z "$names" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '%s\n' "$names" \
|
printf '%s\n' "$names" \
|
||||||
| tr ',' '\n' \
|
| tr ',' '\n' \
|
||||||
| sed 's/^[[:space:]]*//;s/[[:space:]]*$//' \
|
| sed 's/^[[:space:]]*//;s/[[:space:]]*$//' \
|
||||||
| sed '/^$/d' \
|
| sed '/^$/d' \
|
||||||
| LC_ALL=C sort -u
|
| LC_ALL=C sort -u
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,14 +150,12 @@ need_bin mcrcon
|
|||||||
ensure_state
|
ensure_state
|
||||||
acquire_lock
|
acquire_lock
|
||||||
|
|
||||||
echo "Starte Polling ${MC_HOST}:${RCON_PORT} → ntfy ${NTFY_SERVER}/${NTFY_TOPIC} (PID $$)"
|
echo "Starte Polling ${MC_HOST}:${RCON_PORT} → ntfy ${NTFY_SERVER}/${NTFY_TOPIC} (PID $$) [${SERVER_LABEL}]"
|
||||||
prev_up="$(cat "$STATE_UP")"
|
prev_up="$(cat "$STATE_UP")"
|
||||||
|
|
||||||
# ===== Main Loop =====
|
# ===== Main Loop =====
|
||||||
while :; do
|
while :; do
|
||||||
# Running-Mark aktualisieren (für Monitoring/„läuft“-Check)
|
date -Iseconds > "$RUN_MARK"; echo "$$" >> "$RUN_MARK"
|
||||||
date -Iseconds > "$RUN_MARK"
|
|
||||||
echo "$$" >> "$RUN_MARK"
|
|
||||||
|
|
||||||
tmp_players="$(mktemp)"
|
tmp_players="$(mktemp)"
|
||||||
if get_players >"$tmp_players"; then
|
if get_players >"$tmp_players"; then
|
||||||
@ -177,36 +167,35 @@ while :; do
|
|||||||
|
|
||||||
if [[ "$ANNOUNCE_SERVER_UPDOWN" == "true" && "$prev_up" != "unknown" && "$server_up" != "$prev_up" ]]; then
|
if [[ "$ANNOUNCE_SERVER_UPDOWN" == "true" && "$prev_up" != "unknown" && "$server_up" != "$prev_up" ]]; then
|
||||||
if [[ "$server_up" == "true" ]]; then
|
if [[ "$server_up" == "true" ]]; then
|
||||||
ntfy_notify "${NTFY_TITLE_PREFIX}: Server up" \
|
ntfy_notify "${NTFY_TITLE_PREFIX}: Server up — ${SERVER_LABEL}" \
|
||||||
"Server ist wieder erreichbar (${MC_HOST}:${RCON_PORT})." \
|
"Server ist wieder erreichbar (${MC_HOST}:${RCON_PORT})." \
|
||||||
"${NTFY_TAGS_BASE},up" "$NTFY_PRIORITY_UP" || true
|
"${NTFY_TAGS_BASE},${SERVER_TAG},up" "$NTFY_PRIORITY_UP" || true
|
||||||
else
|
else
|
||||||
ntfy_notify "${NTFY_TITLE_PREFIX}: Server down" \
|
ntfy_notify "${NTFY_TITLE_PREFIX}: Server down — ${SERVER_LABEL}" \
|
||||||
"Server ist nicht erreichbar (${MC_HOST}:${RCON_PORT})." \
|
"Server ist nicht erreichbar (${MC_HOST}:${RCON_PORT})." \
|
||||||
"${NTFY_TAGS_BASE},down" "$NTFY_PRIORITY_DOWN" || true
|
"${NTFY_TAGS_BASE},${SERVER_TAG},down" "$NTFY_PRIORITY_DOWN" || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$server_up" == "true" ]]; then
|
if [[ "$server_up" == "true" ]]; then
|
||||||
LC_ALL=C sort -u "$STATE_PLAYERS" -o "$STATE_PLAYERS"
|
LC_ALL=C sort -u "$STATE_PLAYERS" -o "$STATE_PLAYERS"
|
||||||
|
|
||||||
joined="$(comm -13 "$STATE_PLAYERS" "$tmp_players" || true)"
|
joined="$(comm -13 "$STATE_PLAYERS" "$tmp_players" || true)"
|
||||||
left="$(comm -23 "$STATE_PLAYERS" "$tmp_players" || true)"
|
left="$(comm -23 "$STATE_PLAYERS" "$tmp_players" || true)"
|
||||||
|
|
||||||
if [[ -n "$joined" ]]; then
|
if [[ -n "$joined" ]]; then
|
||||||
while IFS= read -r name; do
|
while IFS= read -r name; do
|
||||||
[[ -z "$name" ]] && continue
|
[[ -z "$name" ]] && continue
|
||||||
ntfy_notify "${NTFY_TITLE_PREFIX}: Join" \
|
ntfy_notify "${NTFY_TITLE_PREFIX}: Join — ${SERVER_LABEL}" \
|
||||||
"Player \"${name}\" ist beigetreten." \
|
"Player \"${name}\" ist beigetreten." \
|
||||||
"${NTFY_TAGS_BASE},join" "$NTFY_PRIORITY_JOIN" || true
|
"${NTFY_TAGS_BASE},${SERVER_TAG},join" "$NTFY_PRIORITY_JOIN" || true
|
||||||
done <<<"$joined"
|
done <<<"$joined"
|
||||||
fi
|
fi
|
||||||
if [[ -n "$left" ]]; then
|
if [[ -n "$left" ]]; then
|
||||||
while IFS= read -r name; do
|
while IFS= read -r name; do
|
||||||
[[ -z "$name" ]] && continue
|
[[ -z "$name" ]] && continue
|
||||||
ntfy_notify "${NTFY_TITLE_PREFIX}: Leave" \
|
ntfy_notify "${NTFY_TITLE_PREFIX}: Leave — ${SERVER_LABEL}" \
|
||||||
"Player \"${name}\" hat den Server verlassen." \
|
"Player \"${name}\" hat den Server verlassen." \
|
||||||
"${NTFY_TAGS_BASE},leave" "$NTFY_PRIORITY_LEAVE" || true
|
"${NTFY_TAGS_BASE},${SERVER_TAG},leave" "$NTFY_PRIORITY_LEAVE" || true
|
||||||
done <<<"$left"
|
done <<<"$left"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user