Skip to content

title: "CLI-Referenz" description: "Mercy SF aus einem anderen Programm steuern: jeder Befehl, seine JSON-Ausgabe, Rückgabewerte und durchgerechnete Beispiele."

CLI-Referenz, Mercy SF aus einem anderen Programm steuern

mercy-cli hat einen nicht-interaktiven Modus: Argumente hinein, ein JSON-Objekt heraus. Gedacht ist er für Dashboards, Supervisoren und Skripte.

Diese Seite ist das Versprechen. Die Befehle und Feldnamen unten ändern sich nicht ohne Ankündigung. Alles andere an der CLI, das Menü, seine Formulierungen, das Layout seiner Ausgabe, darf sich jederzeit ändern, bau also bitte nicht darauf.

Ohne Argumente aufgerufen startet das interaktive Menü genau wie bisher.

Jede Antwort trägt "api". Heute ist das 1. Ein Feld hinzuzufügen ändert das nicht, ein Aufrufer, der nur die Felder liest, die er kennt, bleibt unberührt. Es ändert sich, wenn ein Feld entfernt wird oder seine Bedeutung sich ändert, du kannst dich also weigern, gegen eine Form zu laufen, für die du nicht gebaut wurdest.


Anmelden

Konten, die über die S&F-Einmalanmeldung angelegt wurden, brauchen nichts zusätzlich. Ein Konto, das zu einem Server gehört und nie Teil der Einmalanmeldung war, existiert für sie nicht und braucht --server:

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

--server funktioniert mit jedem Befehl weiter unten.

Ausgabe

Jeder Befehl außer --start gibt JSON aus und sonst nichts, --json ist also ohnehin der Standard und es zu übergeben ändert nichts. Es gibt das Flag für Aufrufer, die es lieber ausdrücklich sagen, als sich auf einen Standard zu verlassen, und für --start, das sonst ein menschenlesbares Protokoll schreibt.


Lesen

Die Charaktere eines Kontos auflisten

bash
mercy-cli --characters --user <account>
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, über Server hinweg. level ist null, wenn der Zustand dieses Charakters nicht geholt werden konnte, der Eintrag wird trotzdem aufgeführt, denn zu wissen, dass er existiert, ist der Sinn des Aufrufs.

Den Zustand eines Charakters lesen

bash
mercy-cli --status --user <account> --character <name>

--character darf entfallen, wenn das Konto genau einen hat.

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 einzigen Zustandsabruf, es gibt also keine Abschnitte anzufordern und nichts zu sparen, indem man weniger verlangt.

guild ist null, wenn der Charakter in keiner ist. scrapbook_items ist null, bis das Sammelalbum mindestens einmal gelesen wurde. Ein leerer Ausrüstungsplatz wird weggelassen statt als null aufgeführt.

Zu den Gegenstandsnamen: Das Protokoll trägt eine Modell-ID und einen Platz, keinen Anzeigenamen, und genau das bekommst du. Es ist auch das, worüber die Grafik adressiert wird, ein Dashboard kann also exakt das Bild zeichnen, das die Desktop-App zeichnet. Hier einen Namen zu erfinden wäre eine Vermutung, die du nicht prüfen könntest.

Ein Lesen in einem Intervall wiederholen

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

Eine Anmeldung, dann alle 60 Sekunden ein Objekt, bis der Prozess gestoppt wird. Nutze das, statt den Befehl in einer Schleife neu zu starten: Jeder Lauf ist eine frische Anmeldung, und das ist die teuerste Art, eine Zahl zu beobachten, und von der Serverseite aus die auffälligste. Die Untergrenze liegt bei 30 Sekunden. Eine fehlgeschlagene Runde wird ausgegeben und die Schleife läuft weiter, ein Beobachter, der beim ersten Schluckauf aufgibt, sieht genau aus wie einer, bei dem nichts los ist.

Wogegen er als Nächstes kämpfen wird, und warum

bash
mercy-cli --plan --user <account> --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 App zeichnet: Kandidaten bewertet nach Gegenständen, Tageserfahrung und Rang, dann multipliziert mit den Chancen. outcome ist planned, waiting, blocked oder empty, und reason sagt in einem Satz, welches.

Das gibt keinen Kampf und keinen Pilz aus. Es durchsucht aber nach Kandidaten, ist also in Anfragen nicht kostenlos, setz es nicht in eine enge Schleife.

Einen Kampf proben

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

Rechnerei. Eine Abfrage geht an den Server, um herauszufinden, wer das ist; der Kampf selbst wird nie geschickt. win_chance ist Siege geteilt durch entschiedene Kämpfe, eine Simulation, die nicht auflösen kann, berichtet also über weniger, als sie gelaufen ist.

Was getragen wird

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

Eine Liste, weil das Spiel eine führt. Gegenstände haben dieselbe Form wie in --status, dazu slot_index.

Sammelalbum

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

unlocked ist false unterhalb der Stufe, die ein Sammelalbum gewährt, und owned ist dort null. Das ist kein Fehler.

Aufgezeichnete Kämpfe

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

Neueste zuerst. Diese werden von der Installation aufgezeichnet, die den Bot gefahren hat, und nicht vom Server geholt, ein Prozess, der gerade erst gestartet ist, hat also nichts zu zeigen, und das ist kein Fehler. total ist, was existiert, returned ist, was --limit übrig gelassen hat.


Handeln

Den Bot laufen lassen

bash
mercy-cli --start --user <account> --character <name>

Läuft, bis der Prozess gestoppt wird, und protokolliert dabei. --character ist hier erforderlich: Auflisten und Zustand lesen sind harmlos, aber den Bot zu starten spielt das Spiel, und den falschen Charakter zu wählen ist nichts, was du rückgängig machen kannst.

--all lässt stattdessen jeden Charakter des Kontos in diesem einen Prozess laufen. Das ist eine Anmeldung fürs Konto statt einer je Charakter, was günstiger ist und die Charaktere davon abhält, sich gegenseitig die Sitzung ungültig zu machen.

Bei Strg-C (oder SIGTERM) darf der aktuelle Zug zu Ende laufen, bevor der Prozess sich beendet, statt mitten in einer Anfrage abgeschnitten zu werden.

Ereignisse statt Prosa

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

Ein JSON-Objekt pro Zeile, ein Supervisor liest also Zeile für Zeile:

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 (das ein count mitführt).

Diese Liste wird wachsen. Behandle ein Ereignis, das du nicht kennst, als eines zum Ignorieren und nicht als Fehler, das ist der ganze Grund, warum die Namen ausgeschrieben sind, statt das zu sein, was Rust gerade ausgegeben hat.

Nutze das, statt das menschenlesbare Protokoll zu lesen. Jenes Protokoll ist für einen Menschen geschrieben und darf jederzeit umformuliert werden; es zu parsen heißt, dass dein Werkzeug kaputtgeht, wenn jemand einen Satz verbessert.

Einsammeln, was wartet

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

Kalender, Tagesaufgaben, anstehende Freischaltungen. --character ist erforderlich, aus demselben Grund wie bei --start.

Die Konfiguration lesen oder ändern

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

Lesen gibt die ganze Konfiguration unter config zurück. Die Feldnamen in diesem Block sind nicht Teil dieses Versprechens, nur die unter settable aufgeführten Schlüssel sind es. Die Konfiguration hat weit über hundert Felder und wächst mit jeder Version; sie alle zu versprechen hieße, die Einstellungen nie wieder umbauen zu können.

--set nimmt true/false, on/off, yes/no, 1/0, verlangt immer --character (ein Schalter gehört zu einem Charakter, und „der einzige“ ist eine Vermutung, mit der zu lesen in Ordnung und mit der zu schreiben falsch ist), und lässt sich wiederholen. Der Wert wird danach von der Platte zurückgelesen, und der Aufruf schlägt fehl, wenn er nicht angekommen ist, denn eine Änderung zu melden, die nicht stattgefunden hat, ist das eine Ergebnis, von dem du dich nicht erholen kannst.


Betrieb

Welcher Build ist das

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

Der einzige Befehl, der weder ein Passwort noch ein Netz braucht. Ein Supervisor muss fragen können, womit er spricht, bevor er Zugangsdaten hat, um damit zu sprechen.

Ist es erreichbar

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

Anmelden, holen, melden, beenden. Bewusst klein: Eine Erreichbarkeitsprüfung, die ein volles Charakterblatt zurückgibt, lädt dazu ein, es zu parsen, und dann wird die Prüfung selbst zu einer Schnittstelle.


Das Passwort

Es gibt kein --password-Flag, mit Absicht. Kommandozeilenargumente sind für jeden anderen Prozess auf dem Rechner lesbar, ps, /proc, Task-Manager, ein Dashboard, das das für mehrere Leute laufen lässt, würde also all ihre Spielpasswörter an jeden weiterreichen, der eine Shell auf dieser Kiste hat.

Stattdessen zwei Wege:

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

--password-stdin liest die erste Zeile der Standardeingabe, was das Geheimnis auch aus der Umgebung heraushält.


Rückgabewerte

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

Der Unterschied zwischen 1 und 2 zählt für einen Supervisor: 2 schlägt jedes Mal gleich fehl und braucht einen Menschen, 1 kann beim nächsten Versuch durchaus klappen.


Fehler

Fehlschläge sind ebenfalls JSON, ein Parser bedient also beides:

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

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

Häufige Ursachen:

MeldungWas zu tun ist
--user is required--user fehlt (jede Aktion außer --version)
--start needs --character (or --all)Nenn das Ziel; es wird nicht erraten
this account has N characters — name one with --characterEs gibt mehrere, wähl einen aus
no character called '…' on this accountTippfehler; --characters listet die gültigen Namen
no password: set MERCY_SF_PASSWORD or pass --password-stdinWähl einen der beiden Passwortwege
login failed: …Prüf die Zugangsdaten oder den Server; Konten ohne Einmalanmeldung brauchen --server
'…' is not settable from hereNicht auf der Erlaubnisliste; --config ohne --set gibt settable aus
'…' did not save — it still reads back unchangedDie Änderung kam nicht an; prüf die Schreibrechte

Rezepte

Einmal am Tag auf jedem Konto einsammeln

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

Eine Dashboard-Kachel aktuell halten

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

Eine Anmeldung, ein Objekt pro Minute. Lass das nicht stattdessen jede Minute aus cron neu laufen, das ist jedes Mal eine frische Anmeldung.

Ereignisse in eine Datei schreiben

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

Vor dem Anmelden prüfen, womit du sprichst

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

Als systemd-Dienst

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

SIGTERM lässt den aktuellen Zug zu Ende laufen. Bitte kein SIGKILL.


Was das noch nicht tut

Mehrere Charaktere zu starten und sie einzeln zu stoppen (--all startet alle und stoppt alle zusammen), ein Steuerkanal in einen laufenden Prozess hinein, und irgendetwas jenseits der Modulschalter zu ändern.

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

Mercy SF steht in keiner Verbindung zu Shakes & Fidget oder Playa Games. Nutzung auf eigenes Risiko.