minecraft-ntfy-notify/mc-ntfy-notify.v1.sh

201 lines
6.2 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#!/bin/bash
# Beschreibung: Minecraft → ntfy Notifier (Join/Leave + optional Up/Down) mit Cron-safe Locking
# Autor: Patrick Asmus
# Web: https://www.cleveradmin.de
# Repository: https://git.techniverse.net/scriptos/minecraft-ntfy-notify
# Version: 1.5
# Datum: 18.09.2025
# Modifikation: Tags entfernt; Titel statisch (kein Join/Leave im Titel); DNS:Port im Body ergänzt
#####################################################
set -euo pipefail
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# .env laden: bevorzugt ENV_FILE, sonst ./.env
if [[ -n "${ENV_FILE:-}" && -f "${ENV_FILE}" ]]; then
set -a; . "${ENV_FILE}"; set +a
elif [[ -f "${SCRIPT_DIR}/.env" ]]; then
set -a; . "${SCRIPT_DIR}/.env"; set +a
fi
# ===== Konfiguration via ENV =====
MC_HOST="${MC_HOST:-127.0.0.1}"
RCON_PORT="${RCON_PORT:-25575}"
RCON_PASSWORD="${RCON_PASSWORD:-changeme}"
POLL_SECONDS="${POLL_SECONDS:-10}"
ANNOUNCE_SERVER_UPDOWN="${ANNOUNCE_SERVER_UPDOWN:-true}"
# ntfy
NTFY_SERVER="${NTFY_SERVER:-}"
NTFY_TOPIC="${NTFY_TOPIC:-}"
NTFY_TOKEN="${NTFY_TOKEN:-}"
NTFY_TITLE_PREFIX="${NTFY_TITLE_PREFIX:-Minecraft}"
NTFY_PRIORITY_JOIN="${NTFY_PRIORITY_JOIN:-3}"
NTFY_PRIORITY_LEAVE="${NTFY_PRIORITY_LEAVE:-3}"
NTFY_PRIORITY_UP="${NTFY_PRIORITY_UP:-4}"
NTFY_PRIORITY_DOWN="${NTFY_PRIORITY_DOWN:-5}"
NTFY_MARKDOWN="${NTFY_MARKDOWN:-false}"
# Optionaler Anzeigename (nur informativ)
SERVER_NAME="${SERVER_NAME:-}"
# Lock & State je Instanz (Host:Port:Topic)
LOCK_KEY="$(printf '%s' "${MC_HOST}_${RCON_PORT}_${NTFY_TOPIC}" | tr -c 'A-Za-z0-9._-' '_')"
STATE_DIR="${STATE_DIR:-${SCRIPT_DIR}/state.${LOCK_KEY}}"
STATE_PLAYERS="${STATE_DIR}/players.prev"
STATE_UP="${STATE_DIR}/server_up.prev"
RUN_DIR="${RUN_DIR:-${STATE_DIR}}"
LOCK_DIR="${RUN_DIR}/lock.${LOCK_KEY}"
PID_FILE="${LOCK_DIR}/pid"
RUN_MARK="${RUN_DIR}/running.${LOCK_KEY}"
DEBUG="${DEBUG:-false}"
# ===== Helpers =====
die() { echo "ERROR: $*" >&2; exit 1; }
need_bin() { command -v "$1" >/dev/null 2>&1 || die "Benötigtes Tool fehlt: $1"; }
dbg() { [[ "$DEBUG" == "true" ]] && echo "DBG: $*" >&2 || true; }
cleanup() {
rm -f "$RUN_MARK" "$PID_FILE" 2>/dev/null || true
rmdir "$LOCK_DIR" 2>/dev/null || true
}
acquire_lock() {
mkdir -p "$RUN_DIR"
if mkdir "$LOCK_DIR" 2>/dev/null; then
echo "$$" > "$PID_FILE"
trap cleanup EXIT INT TERM
else
if [[ -f "$PID_FILE" ]]; then
oldpid="$(cat "$PID_FILE" 2>/dev/null || true)"
if [[ -n "${oldpid:-}" ]] && kill -0 "$oldpid" 2>/dev/null; then
dbg "Bereits laufend (PID $oldpid), beende."
exit 0
fi
fi
rm -rf "$LOCK_DIR" 2>/dev/null || true
mkdir "$LOCK_DIR" 2>/dev/null || die "Konnte Lock nicht übernehmen: ${LOCK_DIR}"
echo "$$" > "$PID_FILE"
trap cleanup EXIT INT TERM
fi
}
ntfy_notify() {
# ntfy_notify "Titel" "Nachricht" PRIORITY
local title="$1" body="$2" priority="$3"
[[ -z "$NTFY_SERVER" || -z "$NTFY_TOPIC" ]] && { echo "ntfy Server/Topic fehlt skip"; return 1; }
local url="${NTFY_SERVER%/}/${NTFY_TOPIC}"
local args=(-sS -X POST "$url" -H "Title: ${title}" -H "Priority: ${priority}")
[[ "$NTFY_MARKDOWN" == "true" ]] && args+=(-H "Markdown: yes")
[[ -n "$NTFY_TOKEN" ]] && args+=(-H "Authorization: Bearer ${NTFY_TOKEN}")
curl "${args[@]}" --data-raw "$body" >/dev/null 2>&1 || return 1
return 0
}
ensure_state() {
mkdir -p "$STATE_DIR"
touch "$STATE_PLAYERS"
[[ -f "$STATE_UP" ]] || echo "unknown" >"$STATE_UP"
}
# ANSI- und MC-Farbcodes entfernen
sanitize() {
sed -E $'s/\x1B\\[[0-9;]*[A-Za-z]//g' \
| sed -E 's/§[0-9A-FK-ORa-fk-or]//g' \
| tr -d '\r' \
| sed -E 's/[\x00-\x1F\x7F]//g'
}
get_players() {
local out
if ! out=$(mcrcon -H "$MC_HOST" -P "$RCON_PORT" -p "$RCON_PASSWORD" "list" 2>/dev/null); then
return 1
fi
dbg "RAW: $out"
out="$(printf '%s' "$out" | sanitize)"
if ! grep -q "players online:" <<<"$out"; then
return 0
fi
local names="${out#*:}"
names="$(echo "$names" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [[ -z "$names" ]]; then
return 0
fi
printf '%s\n' "$names" \
| tr ',' '\n' \
| sed 's/^[[:space:]]*//;s/[[:space:]]*$//' \
| sed '/^$/d' \
| LC_ALL=C sort -u
return 0
}
# ===== Checks & Lock =====
need_bin curl
need_bin mcrcon
[[ -n "${NTFY_SERVER}" && -n "${NTFY_TOPIC}" ]] || die "NTFY_SERVER/NTFY_TOPIC nicht gesetzt"
ensure_state
acquire_lock
echo "Starte Polling ${MC_HOST}:${RCON_PORT} -> ntfy ${NTFY_SERVER}/${NTFY_TOPIC} (PID $$)"
prev_up="$(cat "$STATE_UP")"
# ===== Main Loop =====
while :; do
date -Iseconds > "$RUN_MARK"; echo "$$" >> "$RUN_MARK"
tmp_players="$(mktemp)"
if get_players >"$tmp_players"; then
server_up="true"
else
server_up="false"
: >"$tmp_players"
fi
if [[ "$ANNOUNCE_SERVER_UPDOWN" == "true" && "$prev_up" != "unknown" && "$server_up" != "$prev_up" ]]; then
if [[ "$server_up" == "true" ]]; then
ntfy_notify "${NTFY_TITLE_PREFIX}" \
"Server ist wieder erreichbar (${MC_HOST}:${RCON_PORT})." \
"$NTFY_PRIORITY_UP" || true
else
ntfy_notify "${NTFY_TITLE_PREFIX}" \
"Server ist nicht erreichbar (${MC_HOST}:${RCON_PORT})." \
"$NTFY_PRIORITY_DOWN" || true
fi
fi
if [[ "$server_up" == "true" ]]; then
LC_ALL=C sort -u "$STATE_PLAYERS" -o "$STATE_PLAYERS"
joined="$(comm -13 "$STATE_PLAYERS" "$tmp_players" || true)"
left="$(comm -23 "$STATE_PLAYERS" "$tmp_players" || true)"
if [[ -n "$joined" ]]; then
while IFS= read -r name; do
[[ -z "$name" ]] && continue
ntfy_notify "${NTFY_TITLE_PREFIX}" \
"Player \"${name}\" ist beigetreten. Server: ${MC_HOST}:${RCON_PORT}" \
"$NTFY_PRIORITY_JOIN" || true
done <<<"$joined"
fi
if [[ -n "$left" ]]; then
while IFS= read -r name; do
[[ -z "$name" ]] && continue
ntfy_notify "${NTFY_TITLE_PREFIX}" \
"Player \"${name}\" hat den Server verlassen. Server: ${MC_HOST}:${RCON_PORT}" \
"$NTFY_PRIORITY_LEAVE" || true
done <<<"$left"
fi
mv "$tmp_players" "$STATE_PLAYERS"
else
rm -f "$tmp_players"
fi
echo -n "$server_up" >"$STATE_UP"
prev_up="$server_up"
sleep "$POLL_SECONDS"
done