#!/usr/bin/env bash #!/bin/bash # Beschreibung: Minecraft → ntfy Notifier (Join/Leave + optional Up/Down) # Autor: Patrick Asmus # Web: https://www.cleveradmin.de # Repository: https://git.techniverse.net/scriptos/minecraft-ntfy-notify # Version: 1.0 # Datum: 18.09.2025 # Modifikation: Initial ##################################################### set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" if [[ -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_TAGS_BASE="${NTFY_TAGS_BASE:-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}" # State (standardmäßig im Script-Ordner, root-frei) STATE_DIR="${STATE_DIR:-${SCRIPT_DIR}/state}" STATE_PLAYERS="${STATE_DIR}/players.prev" STATE_UP="${STATE_DIR}/server_up.prev" # ===== Helpers ===== die() { echo "ERROR: $*" >&2; exit 1; } need_bin() { command -v "$1" >/dev/null 2>&1 || die "Benötigtes Tool fehlt: $1"; } ntfy_notify() { local title="$1" body="$2" tags="$3" priority="$4" [[ -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}" -H "Tags: ${tags}") [[ "$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" } get_players() { local out if ! out=$(mcrcon -H "$MC_HOST" -P "$RCON_PORT" -p "$RCON_PASSWORD" "list" 2>/dev/null); then return 1 fi if grep -q "players online:" <<<"$out"; then local names="${out#*:}" names="$(echo "$names" | sed 's/^ *//;s/ *$//')" if [[ -n "$names" ]]; then tr ',' '\n' <<<"$names" | sed 's/^ *//;s/ *$//' | sed '/^$/d' | sort -u return 0 fi fi return 0 } # ===== Checks ===== need_bin curl need_bin mcrcon [[ -n "${NTFY_SERVER}" && -n "${NTFY_TOPIC}" ]] || die "NTFY_SERVER/NTFY_TOPIC nicht gesetzt" ensure_state echo "Starte Polling ${MC_HOST}:${RCON_PORT} → ntfy ${NTFY_SERVER}/${NTFY_TOPIC}" prev_up="$(cat "$STATE_UP")" while :; do tmp_players="$(mktemp)" if players=$(get_players); then server_up="true" printf "%s\n" $players 2>/dev/null | sed '/^$/d' | sort -u >"$tmp_players" 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 up" \ "Server ist wieder erreichbar (${MC_HOST}:${RCON_PORT})." \ "${NTFY_TAGS_BASE},up" "$NTFY_PRIORITY_UP" || true else ntfy_notify "${NTFY_TITLE_PREFIX}: Server down" \ "Server ist nicht erreichbar (${MC_HOST}:${RCON_PORT})." \ "${NTFY_TAGS_BASE},down" "$NTFY_PRIORITY_DOWN" || true fi fi if [[ "$server_up" == "true" ]]; then 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}: Join" \ "Player ${name} ist beigetreten." \ "${NTFY_TAGS_BASE},join" "$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}: Leave" \ "Player ${name} hat den Server verlassen." \ "${NTFY_TAGS_BASE},leave" "$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