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

170 lines
5.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)
# Autor: Patrick Asmus
# Web: https://www.cleveradmin.de
# Repository: https://git.techniverse.net/scriptos/minecraft-ntfy-notify
# Version: 1.2
# Datum: 18.09.2025
# Modifikation: Spielername in Meldungen in Anführungszeichen gesetzt; Sanitizing beibehalten
#####################################################
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"
# Optional Debug
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; }
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"
}
# Strippt ANSI-Escape-Sequenzen, MC-Farbcodes (§x), CR und Steuerzeichen
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)"
# "There are 0 of a max of 20 players online"
# "There are 2 of a max of 20 players online: Alice, Bob"
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 =====
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 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 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
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}: 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