Schlagwort-Archiv: Proxmox

Mastodon-Backups

Vor über 10 Jahren habe ich mal darüber berichtet, wie ich diverse Web-Dienste bei mir lokal sichere. Davon ist fast nichts mehr, wie es war: Viele Dienste wurden umgebaut, manche haben sich verabschiedet, und generell läuft das alle nicht mehr auf einem Raspberry Pi, sondern als virtuelle Maschine auf meinem Proxmox-Server (das wäre mal ein Thema für sich…).

Nicht geändert hat sich mein Wunsch, Daten bei mir lokal gesichert zu haben; das gilt auch für meinen Mastodon-Account (@dasaweb). Ich habe meine Mastodon-Aktivität zum Jahresanfang wieder intensiviert, und es gefällt mir. Mein Account wird zwar auf der Instanz https://wue.social/ von Ralf gehostet, und Ralf kann man auf jeden Fall vertrauen was die Datensicherheit angeht. Aber ihr wisst ja, wie das ist mit dem Vertrauen und der Kontrolle. Außerdem braucht man ja auch was zum basteln, und wenn sich so ein Gedanke erst mal vestgesetzt hat…

Nach etwas Recherche bin ich bei Mastodon-Archive hängen geblieben und habe das erst mal per pip installiert:

sudo apt update
pip install mastodon-archive

Damit habe ich mir dann ein Backup-Script gebaut, welches Beiträge, Follow-Listen, Medien etc. zieht und die Beiträge noch in verschiedenen Formaten speichert:

#!/usr/bin/env bash
export PATH="/usr/local/bin:/usr/bin:/bin:/home/daniel/.local/bin"
set -Eeuo pipefail

# Einfaches Backup-Script basiernd auf "mastodon-archive" https://github.com/kensanata/mastodon-archive
# 
# - optional: quiet-run fuer Crontab via -q/--quiet
# - nur Fehler gehen auf STDERR raus
# - Token/Secret-Dateien bleiben im ZIP enthalten

QUIET=0

usage() {
  cat <<'EOT'
Usage: backup-mastodon.sh [OPTION]

Optionen:
  -q, --quiet     Minimaler Output (fuer Crontab)
  -v, --verbose   Voller Output (Default)
  -h, --help      Diese Hilfe anzeigen
EOT
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    -q|--quiet)
      QUIET=1
      ;;
    -v|--verbose)
      QUIET=0
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "❌ Fehler: Unbekannte Option: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
  shift
done

log() {
  if (( QUIET == 0 )); then
    echo "$@"
  fi
}


ACCOUNT="dasaweb@wue.social"
BACKUP_BASE_DIR="$HOME/backups/mastodon"
TOOTS_PER_PAGE="2000"

USER_PART="${ACCOUNT%@*}"
INSTANCE_PART="${ACCOUNT#*@}"
STAMP="$(date +%Y-%m-%d_%H%M%S)"
WORKDIR="$BACKUP_BASE_DIR/workingdir"
ZIPFILE="$BACKUP_BASE_DIR/mastodon-${STAMP}.zip"

mkdir -p "$WORKDIR"
cd "$WORKDIR"

if ! command -v mastodon-archive >/dev/null 2>&1; then
  echo "❌ Fehler: mastodon-archive ist nicht im PATH." >&2
  exit 1
fi

if ! command -v zip >/dev/null 2>&1; then
  echo "❌ Fehler: zip ist nicht installiert." >&2
  exit 1
fi

ma() {
  if (( QUIET == 1 )); then
    mastodon-archive --quiet "$@"
  else
    mastodon-archive "$@"
  fi
}

log "📦 Starte Mastodon-Backup fuer $ACCOUNT"

log "🗂️ Aktualisiere Archiv"
ma archive \
  --with-mentions \
  --with-followers \
  --with-following \
  --with-mutes \
  --with-blocks \
  "$ACCOUNT"

log "🖼️ Lade Medien"
# Medien von eigenen Toots:
ma media --suppress-errors "$ACCOUNT"
# Medien von Toots, die ich gebookmarkt habe:
ma media --collection bookmarks --suppress-errors "$ACCOUNT"
# Medien von Toots, die ich geliked habe:
# ma media --collection favourites --suppress-errors "$ACCOUNT"

log "📝 Erzeuge Text-Exporte"
ma text "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.statuses.txt"
ma text --reverse "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.statuses.reverse.txt"
ma text --collection favourites "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.favourites.txt"
ma text --collection favourites --reverse "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.favourites.reverse.txt"
ma text --collection bookmarks "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.bookmarks.txt"
ma text --collection bookmarks --reverse "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.bookmarks.reverse.txt"
ma text --collection mentions "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.mentions.txt"
ma text --collection mentions --reverse "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.mentions.reverse.txt"

log "🌐 Erzeuge HTML"
ma html --toots-per-page "$TOOTS_PER_PAGE" "$ACCOUNT"
ma html --collection favourites --toots-per-page "$TOOTS_PER_PAGE" "$ACCOUNT"

log "📊 Erzeuge Report"
{
  echo "# report ($(date --iso-8601=seconds))"
  ma report --all --top -1 "$ACCOUNT"
} > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.report.txt"

log "👥 Versuche Zusatzlisten"
for cmd in allowlist followers following mutuals; do
  if ma "$cmd" "$ACCOUNT" > "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.${cmd}.txt" 2>/dev/null; then
    :
  else
    rm -f "$WORKDIR/${INSTANCE_PART}.user.${USER_PART}.${cmd}.txt"
  fi
done

cat > "$WORKDIR/README-backup.txt" <<EOT
📦 Mastodon-Backup
Account: $ACCOUNT
Erstellt: $(date --iso-8601=seconds)
Quelle: mastodon-archive

Enthalten:
- JSON-Archiv(e)
- heruntergeladene Medien für statuses/favourites/bookmarks
- Text-Exporte (normal + reverse)
- HTML-Exporte
- Report
- ggf. Zusatzlisten
- Token-/Secret-Dateien
EOT

log "🗜️ Packe ZIP: $ZIPFILE"
(
  cd "$BACKUP_BASE_DIR"
  zip -qr "$ZIPFILE" "$WORKDIR"
)

log "✅ Fertig: $ZIPFILE"

Man sieht dem Script an, dass ich mir dabei habe helfen lassen, denn so schön wäre ein selbstgebautes Script (bei mir) nicht geworden.

Beim ersten Lauf des Scripts muss man sich autorisieren. Man bekomme eine URL gezeigt, die man im Browser aufruft und damit Credentials erhält, die auch nur Leserechte haben:

Die Credentials werden dann lokal gespeichert. Ein Durchlauf des Scripts sieht in etwas so aus:

daniel@netzbackup:~/skripte$ ./backup-mastodon.sh

📦 Starte Mastodon-Backup fuer dasaweb@wue.social
🗂️ Aktualisiere Archiv
Registering app
This app needs access to your Mastodon account.
Visit the following URL and authorize the app:
https://wue.social/oauth/authorize?client_id=.............................................................................................................&response_type=code&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&scope=read&force_login=False&state=None&lang=None
Then paste the access token here:
_0...................................ciI
Get user info
Get all statuses (this may take a while)
Get favourites (this may take a while)
Get bookmarks (this may take a while)
Get notifications and look for mentions (this may take a while)
Get followers (this may take a while)
Get following (this may take a while)
Get mutes (this may take a while)
Get blocks (this may take a while)
Skipping notes
Saving 307 statuses, 436 favourites, 1 bookmarks, 143 mentions, 88 followers, 182 following, 0 mutes, 0 blocks, 0 notes
🖼️ Lade Medien
123 urls in your backup (39 are previews)
123 to download
Downloading |################################| 123/123
3 urls in your backup (0 are previews)
0 to download

📝 Erzeuge Text-Exporte
🌐 Erzeuge HTML
Loading existing archive: wue.social.user.dasaweb.json
Writing wue.social.user.dasaweb.statuses.0.html
Loading existing archive: wue.social.user.dasaweb.json
Writing wue.social.user.dasaweb.favourites.0.html
📊 Erzeuge Report
👥 Versuche Zusatzlisten
🗜️ Packe ZIP: /home/daniel/backup/Mastodon/mastodon-2026-04-02_001551.zip
✅ Fertig: /home/daniel/backup/Mastodon/mastodon-2026-04-02_001551.zip

Jetzt kann das Script in die Crontab gepackt werden:

0 1 * * 2,6 ~/skripte/backup-mastodon.sh --quiet

Jeder Run aktualisiert inkrementell das Arbeitsverzeichnis und zippt das Ergebnis. Fertig.