Skip to content

CLI-Referenz — Mercy SF aus einem anderen Programm steuern

English version: cli-api.md

mercy-cli hat einen nicht-interaktiven Modus: Argumente rein, ein JSON-Objekt raus. Gedacht für Dashboards, Überwachung und Skripte.

Diese Seite ist die Zusage. Die Kommandos und Feldnamen hier ändern sich nicht ohne Ankündigung. Alles andere an der CLI — das Menü, seine Formulierungen, das Aussehen der Ausgabe — darf sich jederzeit ändern. Bitte baue nichts darauf.

Ohne Argumente startet das interaktive Menü wie bisher.

Jede Antwort trägt "api". Heute ist das 1. Ein neues Feld ändert die Zahl nicht — wer nur die Felder liest, die er kennt, ist davon nicht betroffen. Sie ändert sich, wenn ein Feld verschwindet oder seine Bedeutung wechselt. So kannst du dich weigern, gegen eine Form zu laufen, für die du nicht gebaut bist.


Inhalt


Anmelden

Konten aus dem S&F-Single-Sign-on brauchen nichts weiter — --user genügt, und eine Anmeldung deckt alle Charaktere des Kontos ab, über Server hinweg.

Ein Konto, das zu genau einem Server gehört und nie Teil des SSO war, existiert für das SSO nicht. Solche Konten brauchen --server:

bash
mercy-cli --status --user <konto> --server https://f1.sfgame.net/

--server funktioniert mit jedem Kommando auf dieser Seite.


Das Passwort

Es gibt kein --password, und das bleibt so.

Kommandozeilenargumente sind für jeden anderen Prozess auf der Maschine lesbar — ps, /proc, der Task-Manager. Ein Dashboard, das die CLI für mehrere Leute aufruft, würde damit sämtliche Spielpasswörter an jeden weitergeben, der eine Shell auf dieser Kiste hat.

Zwei Wege stattdessen:

bash
MERCY_SF_PASSWORD='…' mercy-cli --status --user jemand
bash
printf '%s' "$passwort" | mercy-cli --status --user jemand --password-stdin

--password-stdin liest die erste Zeile der Standardeingabe. Das hält das Geheimnis auch aus der Umgebung heraus, was der sicherere der beiden Wege ist: die Umgebung eines Prozesses ist auf manchen Systemen ebenfalls auslesbar.


Ausgabe

Jedes Kommando außer --start gibt JSON aus und sonst nichts. --json ist damit bereits die Vorgabe und ändert nichts. Es existiert für Aufrufer, die es lieber hinschreiben als sich auf eine Vorgabe zu verlassen — und für --start, das sonst ein Log für Menschen schreibt.


Lesen

Lesende Kommandos verändern nichts. --character darf fehlen, wenn das Konto genau einen Charakter hat; bei mehreren ist es ein Fehler, weil Raten hier nichts kostet, aber auch nichts nützt.

Charaktere eines Kontos auflisten

bash
mercy-cli --characters --user <konto>
json
{
  "ok": true,
  "characters": [
    { "character": "Hero", "server": "https://s1.sfgame.net/", "level": 345 },
    { "character": "Alt",  "server": "https://s42.sfgame.net/", "level": 88 }
  ]
}

Eine Anmeldung deckt jeden Charakter des Kontos ab, serverübergreifend. level ist null, wenn der Zustand dieses Charakters nicht abrufbar war — der Eintrag bleibt trotzdem in der Liste, denn dass er existiert, ist der Sinn des Aufrufs.

Den Zustand eines Charakters lesen

bash
mercy-cli --status --user <konto> [--character <name>]
json
{
  "ok": true,
  "character": "Hero",
  "server": "https://s1.sfgame.net/",
  "level": 345,
  "class": "DemonHunter",
  "race": "DarkElf",
  "experience": { "current": 740300000, "next_level": 769800000 },
  "silver": 725000000,
  "mushrooms": 8,
  "honor": 12043,
  "rank": 1800,
  "action": "CityGuard",
  "attributes": {
    "strength":     { "base": 4077, "bonus": 1200 },
    "dexterity":    { "base": 1000, "bonus": 300 },
    "intelligence": { "base": 900,  "bonus": 250 },
    "constitution": { "base": 3000, "bonus": 800 },
    "luck":         { "base": 700,  "bonus": 150 }
  },
  "equipment": [
    {
      "slot": "Weapon",
      "model_id": 55,
      "color": 1,
      "class": "DemonHunter",
      "epic": true,
      "legendary": false,
      "armor": 0,
      "min_damage": 900,
      "max_damage": 1400,
      "upgrades": 3,
      "attributes": { "strength": 420, "constitution": 210 },
      "rune": { "type": "FireDamage", "value": 60 },
      "enchantment": "SwordOfVengeance",
      "gem": { "type": "Strength", "value": 300 }
    }
  ],
  "arena": {
    "fights_for_xp": 7,
    "next_free_fight": "2026-08-06T12:04:11+02:00",
    "opponents": [50099, 34316, 13668]
  },
  "tavern": {
    "thirst_for_adventure_sec": 3600,
    "beer_drunk": 2,
    "mushroom_skip_allowed": true,
    "quests": [
      { "length_sec": 600, "silver": 120000, "experience": 45000,
        "location": "Wolves", "item": true }
    ]
  },
  "guild": {
    "name": "Blvck Clover",
    "rank": 12,
    "honor": 90000,
    "members": [
      { "name": "Hero", "level": 345, "last_online": "2026-08-06T09:12:00+02:00" }
    ]
  },
  "scrapbook_items": 1102
}

Alles stammt aus einem Zustandsabruf. Deshalb gibt es keine Abschnitte anzufordern und nichts zu sparen, indem man weniger verlangt — ein Schalter pro Abschnitt wäre nur eine weitere Möglichkeit, den Aufruf falsch zu machen.

guild ist null, wenn der Charakter in keiner Gilde ist. scrapbook_items ist null, solange das Sammelalbum noch nie gelesen wurde. Ein leerer Ausrüstungsplatz wird weggelassen statt als null aufgeführt.

Zu den Gegenstandsnamen: das Protokoll überträgt eine Modell-ID und einen Platz, keinen Anzeigenamen — also bekommst du das. Es ist zugleich das, worüber die Grafiken adressiert werden, ein Dashboard kann also genau dasselbe Bild zeichnen wie die Anwendung. Einen Namen zu erfinden wäre eine Vermutung, die du nicht nachprüfen könntest.

Auf einem Takt wiederholen

bash
mercy-cli --status --user <konto> --character <name> --watch 60

Eine Anmeldung, danach alle 60 Sekunden ein Objekt, bis der Prozess beendet wird.

Nutze das, statt den Befehl in einer Schleife neu aufzurufen. Jeder neue Aufruf ist eine neue Anmeldung — die teuerste Art, eine Zahl zu beobachten, und aus Sicht des Spielservers die auffälligste.

Untergrenze sind 30 Sekunden. Ein fehlgeschlagener Durchgang wird ausgegeben und die Schleife läuft weiter: eine Überwachung, die beim ersten Schluckauf aufgibt, sieht genauso aus wie eine, bei der alles in Ordnung ist.

Anwendbar auf --status, --plan, --simulate, --history, --inventory, --scrapbook und --health.

Was als Nächstes gekämpft wird, und warum

bash
mercy-cli --plan --user <konto> [--character <name>]
json
{
  "ok": true,
  "outcome": "planned",
  "reason": "Next: Someone — 3 scrapbook items, 72% odds",
  "candidates": [
    {
      "name": "Someone",
      "level": 340,
      "source": "pool",
      "missing_items": 3,
      "rank_gain": 12,
      "favourite": false,
      "win_chance": 0.72,
      "item_score": 300.0,
      "xp_score": 60.0,
      "rank_score": 25.0,
      "base": 385.0,
      "score": 277.2
    }
  ]
}

Dieselbe Entscheidung, die die Anwendung zeichnet: Kandidaten werden nach Gegenständen, Tages-XP und Rangzuwachs bewertet und dann mit der Siegchance multipliziert. base ist die Summe der drei Anteile, score dieselbe Summe mal win_chance — deshalb kann ein starker Kandidat mit schlechten Aussichten hinter einem schwächeren mit guten landen.

outcome ist planned, waiting, blocked oder empty. reason sagt in einem Satz, welches davon und warum.

source sagt, woher der Kandidat kommt: die drei Arenagegner, der durchsuchte Spielerpool oder die Bestenliste um den eigenen Rang herum.

Das kostet keinen Kampf und keinen Pilz. Es durchsucht aber nach Kandidaten, ist also nicht kostenlos in Anfragen — bitte nicht auf einen engen Takt legen.

Einen Kampf durchrechnen

bash
mercy-cli --simulate --user <konto> [--character <name>] --against <spieler>
json
{ "ok": true, "against": "Someone", "against_level": 340, "win_chance": 0.63, "iterations": 1000 }

Reine Rechnerei. Eine Anfrage geht an den Server, um nachzusehen, wer der Spieler überhaupt ist; der Kampf selbst wird nie gesendet.

win_chance sind Siege geteilt durch entschiedene Kämpfe. Eine Simulation, die sich nicht auflösen lässt, meldet deshalb weniger iterations, als sie angesetzt hat — dann ist der Wert auf weniger Durchläufen gebaut und entsprechend gröber.

Was getragen wird

bash
mercy-cli --inventory --user <konto> [--character <name>]
json
{ "ok": true, "carried": 12, "backpack": [ { "slot_index": 0, "model_id": 55 } ] }

Eine einzige Liste, weil das Spiel eine einzige führt. Eine Aufteilung in „Tasche" und „Truhe" wäre eine Unterscheidung, die das Protokoll nicht macht.

Die Gegenstände haben dieselbe Form wie bei --status, zusätzlich slot_index.

Sammelalbum

bash
mercy-cli --scrapbook --user <konto> [--character <name>]
json
{ "ok": true, "unlocked": true, "owned": 1102, "monsters": 214 }

unlocked ist false, solange der Charakter zu niedrigstufig für ein Sammelalbum ist; owned ist dann null. Das ist kein Fehler, und ok bleibt true.

Aufgezeichnete Kämpfe

bash
mercy-cli --history --user <konto> [--character <name>] [--limit <n>]
json
{ "ok": true, "total": 4210, "returned": 50, "battles": [] }

Neueste zuerst.

Wichtig: diese Kämpfe wurden von der Installation aufgezeichnet, die den Bot laufen ließ — sie werden nicht vom Server geholt. Ein frisch gestarteter Prozess auf einer anderen Maschine hat also nichts anzuzeigen, und das ist kein Fehler.

total ist, was vorhanden ist. returned ist, was --limit übrig gelassen hat.


Handeln

Handelnde Kommandos verlangen --character ausdrücklich. Lesen und Auflisten sind harmlos, aber den Bot auf dem falschen Charakter zu starten spielt das Spiel — und das nimmt man nicht zurück.

Den Bot laufen lassen

bash
mercy-cli --start --user <konto> --character <name>
mercy-cli --start --user <konto> --all

Läuft, bis der Prozess beendet wird.

--all führt jeden Charakter des Kontos in diesem einen Prozess. Das ist eine Anmeldung für das Konto statt einer pro Charakter — billiger, und die Charaktere machen sich nicht gegenseitig die Sitzung ungültig.

Bei Ctrl-C (oder SIGTERM) darf der laufende Zug zu Ende gehen, bevor der Prozess aussteigt, statt mitten in einer Anfrage abgeschnitten zu werden.

Ereignisse statt Prosa

bash
mercy-cli --start --user <konto> --character <name> --events

Ein JSON-Objekt pro Zeile, damit eine Überwachung zeilenweise mitliest:

json
{"at":"2026-08-06T17:22:04+02:00","api":1,"event":"arena_win","character":"Hero"}
{"at":"2026-08-06T17:22:31+02:00","api":1,"event":"quest_done","character":"Hero"}

event ist eines von started, stopping, stopped, arena_win, arena_loss, dungeon_win, dungeon_loss, quest_done, scrapbook_win, scrapbook_loss, scrapbook_items (trägt zusätzlich count).

Diese Liste wird wachsen. Behandle ein unbekanntes Ereignis als eines zum Überspringen, nicht als Fehler. Genau deshalb sind die Namen ausgeschrieben und nicht das, was Rust zufällig ausgibt: so kann eine Umbenennung im Programm nicht stillschweigend ein Feld bei dir umbenennen.

Nutze das statt des Logs für Menschen. Jenes ist für einen Leser geschrieben und darf jederzeit umformuliert werden — es zu zerlegen heißt, dass dein Werkzeug bricht, sobald jemand einen Satz verbessert.

Einsammeln, was bereitliegt

bash
mercy-cli --claim --user <konto> --character <name>

Kalender, Tagesaufgaben, ausstehende Freischaltungen. --character ist Pflicht, aus demselben Grund wie bei --start.

Konfiguration lesen oder ändern

bash
mercy-cli --config --user <konto> [--character <name>]
mercy-cli --config --user <konto> --character <name> --set auto_arena=false
json
{ "ok": true, "settable": ["auto_arena"], "changed": [ { "key": "auto_arena", "from": true, "to": false } ] }

Beim Lesen kommt die gesamte Konfiguration unter config zurück.

Die Feldnamen innerhalb dieses Blocks sind nicht Teil der Zusage — nur die Schlüssel, die unter settable aufgeführt sind. Die Konfiguration hat weit über hundert Felder und wächst mit jeder Version; sie alle zuzusagen hieße, die Einstellungen nie wieder umbauen zu können, ohne fremde Dashboards zu brechen.

--set nimmt true/false, on/off, yes/no, 1/0 und darf mehrfach vorkommen. --character ist dabei immer Pflicht: ein Schalter gehört zu einem Charakter, und „der einzige" ist eine Vermutung, die zum Lesen taugt und zum Schreiben nicht.

Der Wert wird anschließend von der Platte zurückgelesen, und der Aufruf schlägt fehl, wenn er nicht angekommen ist. Eine Änderung zu melden, die nicht stattfand, ist das einzige Ergebnis, von dem sich ein Aufrufer nicht erholen kann.


Betrieb

Welcher Stand ist das

bash
mercy-cli --version
json
{ "ok": true, "version": "2.12.0", "api": 1 }

Das einzige Kommando, das weder Passwort noch Netz braucht. Eine Überwachung muss fragen können, womit sie redet, bevor sie Zugangsdaten hat, um zu reden.

Ist es erreichbar

bash
mercy-cli --health --user <konto> [--character <name>]
json
{ "ok": true, "character": "Hero", "level": 345, "round_trip_ms": 412 }

Anmelden, abrufen, melden, aussteigen. Bewusst klein gehalten: eine Erreichbarkeitsprüfung, die ein ganzes Charakterblatt zurückgibt, lädt dazu ein, es zu zerlegen — und dann ist die Prüfung selbst eine Schnittstelle geworden.


Abbruchcodes

Code Bedeutung
0 Erfolg, und für --help
1 Es lief und schlug fehl — der Grund steht in error, ok ist false
2 Der Aufruf war falsch: unbekannte Option, fehlender Wert, keine Aktion. Auf stderr

Der Unterschied zwischen 1 und 2 ist für eine Überwachung entscheidend: 2 scheitert jedes Mal gleich und braucht einen Menschen. 1 klappt womöglich beim nächsten Versuch. Ein Wiederholungsmechanismus, der beide gleich behandelt, läuft entweder ewig gegen einen Tippfehler oder gibt bei einem Netzhänger zu früh auf.


Fehler

Auch Fehlschläge sind JSON, damit ein einziger Parser beide Fälle abdeckt:

json
{ "ok": false, "error": "no character called 'Typo' on this account" }

ok ist in jeder Antwort vorhanden. Prüfe es vor allem anderen.

Häufige Ursachen:

Meldung Was zu tun ist
--user is required --user fehlt (außer bei --version)
--start needs --character (or --all) Ziel benennen, es wird nicht geraten
this account has N characters — name one with --character Mehrere vorhanden, einen wählen
no character called '…' on this account Tippfehler; --characters zeigt die gültigen Namen
no password: set MERCY_SF_PASSWORD or pass --password-stdin Passwortweg wählen
login failed: … Zugangsdaten oder Server prüfen; bei Nicht-SSO-Konten --server
'…' is not settable from here Der Schlüssel steht nicht auf der Positivliste; --config ohne --set zeigt settable
'…' did not save — it still reads back unchanged Die Änderung kam nicht an; Schreibrechte prüfen

Rezepte

Alle Konten einmal täglich einsammeln

bash
for konto in alpha beta gamma; do
  MERCY_SF_PASSWORD="$(pass mercy/$konto)" \
    mercy-cli --claim --user "$konto" --character Hero || echo "fehlgeschlagen: $konto"
done

Eine Kachel im Dashboard aktuell halten

bash
MERCY_SF_PASSWORD='…' mercy-cli --status --user jemand --character Hero --watch 60

Eine Anmeldung, ein Objekt pro Minute. Nicht per Cron jede Minute neu aufrufen.

Ereignisse in eine Datei mitschreiben

bash
MERCY_SF_PASSWORD='…' mercy-cli --start --user jemand --character Hero --events \
  >> ereignisse.ndjson

Vor dem Anmelden prüfen, womit man redet

bash
api=$(mercy-cli --version | jq -r .api)
[ "$api" = "1" ] || { echo "unbekannte Vertragsversion: $api"; exit 1; }

Als systemd-Dienst

ini
[Service]
Environment=MERCY_SF_PASSWORD=…
ExecStart=/usr/local/bin/mercy-cli --start --user jemand --all --events
Restart=on-failure
KillSignal=SIGTERM

SIGTERM lässt den laufenden Zug zu Ende gehen. Bitte kein SIGKILL.


Was es noch nicht gibt

Mehrere Charaktere aus einem Prozess starten und einzeln wieder stoppen (--all startet alle, stoppt aber auch alle zusammen), ein Steuerkanal für einen laufenden Prozess, und das Ändern von mehr als den Modulschaltern.

Wenn du eines davon brauchst, sag welches. Das Richtige zu bauen ist einfacher, als zu raten und es dann ungenutzt liegen zu sehen.

Mercy SF is not affiliated with Shakes & Fidget. Use at your own risk.