title: "Opis CLI" description: "Sterowanie Mercy SF z innego programu: każde polecenie, jego wyjście JSON, kody wyjścia i rozpisane przykłady."
Opis CLI, sterowanie Mercy SF z innego programu
mercy-cli ma tryb nieinteraktywny: argumenty do środka, jeden obiekt JSON na zewnątrz. Jest przeznaczony dla paneli, nadzorców i skryptów.
Ta strona jest obietnicą. Polecenia i nazwy pól poniżej nie zmienią się bez uprzedzenia. Cała reszta CLI, menu, jego sformułowania, układ wyjścia, może się zmienić w każdej chwili, więc proszę, nie buduj na tym.
Uruchomione bez argumentów, startuje interaktywne menu dokładnie jak wcześniej.
Każda odpowiedź niesie "api". Dziś to 1. Dodanie pola tego nie zmienia, wywołujący, który czyta tylko znane sobie pola, pozostaje nietknięty. Zmienia się, gdy pole zostaje usunięte albo zmienia się jego znaczenie, więc możesz odmówić działania wobec kształtu, pod który nie byłeś budowany.
Logowanie
Konta założone przez pojedyncze logowanie S&F nie potrzebują niczego więcej. Konto należące do jednego serwera, które nigdy nie było częścią pojedynczego logowania, z jego punktu widzenia nie istnieje i wymaga --server:
mercy-cli --status --user <account> --server https://f1.sfgame.net/--server działa z każdym poleceniem poniżej.
Wyjście
Każde polecenie oprócz --start wypisuje JSON i nic więcej, więc --json i tak jest wartością domyślną, a podanie go nic nie zmienia. Istnieje dla wywołujących, którzy wolą to powiedzieć, niż polegać na domyślnym zachowaniu, oraz dla --start, które w przeciwnym razie pisze dziennik dla człowieka.
Odczyt
Lista postaci na koncie
mercy-cli --characters --user <account>{
"ok": true,
"characters": [
{ "character": "Hero", "server": "https://s1.sfgame.net/", "level": 345 },
{ "character": "Alt", "server": "https://s42.sfgame.net/", "level": 88 }
]
}Jedno logowanie obejmuje każdą postać na koncie, na wszystkich serwerach. level jest null, jeśli stanu tej postaci nie udało się pobrać; wpis i tak jest wypisany, bo świadomość, że postać istnieje, jest sensem tego wywołania.
Odczyt stanu jednej postaci
mercy-cli --status --user <account> --character <name>--character można pominąć, gdy konto ma dokładnie jedną.
{
"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
}Wszystko pochodzi z jednego pobrania stanu, więc nie ma sekcji do zamawiania ani niczego do zaoszczędzenia przez proszenie o mniej.
guild jest null, gdy postać do żadnej nie należy. scrapbook_items jest null, dopóki album nie zostanie odczytany choć raz. Puste miejsce na ekwipunek jest pomijane, a nie wypisywane jako null.
O nazwach przedmiotów: protokół niesie identyfikator modelu i slot, a nie nazwę wyświetlaną, więc to właśnie dostajesz. To także sposób, w jaki adresowana jest grafika, więc panel może narysować dokładnie ten obrazek, który rysuje aplikacja na pulpicie. Wymyślanie tu nazwy byłoby zgadywanką, której nie dałoby się sprawdzić.
Powtarzanie odczytu w odstępach
mercy-cli --status --user <account> --character <name> --watch 60Jedno logowanie, a potem jeden obiekt co 60 sekund, dopóki proces nie zostanie zatrzymany. Używaj tego zamiast uruchamiać polecenie w pętli: każde uruchomienie to świeże logowanie, a to najdroższy sposób obserwowania liczby i najbardziej rzucający się w oczy od strony serwera. Dolna granica to 30 sekund. Nieudana runda jest wypisywana i pętla leci dalej; obserwator, który poddaje się przy pierwszej czkawce, wygląda dokładnie tak samo jak taki, u którego nic się nie dzieje.
Z kim będzie walczył następnie i dlaczego
mercy-cli --plan --user <account> --character <name>{
"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
}
]
}Ta sama decyzja, którą rysuje aplikacja: kandydaci oceniani po przedmiotach, dziennym PD i miejscu w rankingu, a potem mnożeni przez szanse. outcome to planned, waiting, blocked albo empty, a reason mówi jednym zdaniem który.
To nie wydaje żadnej walki ani grzyba. Przeczesuje jednak w poszukiwaniu kandydatów, więc nie jest darmowe w zapytaniach; nie wsadzaj tego w ciasną pętlę.
Próba walki
mercy-cli --simulate --user <account> --character <name> --against <player>{ "ok": true, "against": "Someone", "against_level": 340, "win_chance": 0.63, "iterations": 1000 }Arytmetyka. Jedno zapytanie idzie do serwera, żeby ustalić, kto to jest; sama walka nigdy nie jest wysyłana. win_chance to wygrane wobec walk rozstrzygniętych, więc symulacja, która nie potrafi rozstrzygnąć, raportuje z mniejszej liczby, niż przeprowadziła.
Co jest noszone
mercy-cli --inventory --user <account> --character <name>{ "ok": true, "carried": 12, "backpack": [ { "slot_index": 0, "model_id": 55 } ] }Jedna lista, bo gra trzyma jedną. Przedmioty mają ten sam kształt co w --status, plus slot_index.
Album
mercy-cli --scrapbook --user <account> --character <name>{ "ok": true, "unlocked": true, "owned": 1102, "monsters": 214 }unlocked jest false poniżej poziomu, który daje album, a owned jest tam null. To nie jest błąd.
Zapisane walki
mercy-cli --history --user <account> --character <name> --limit 50{ "ok": true, "total": 4210, "returned": 50, "battles": [] }Najnowsze na początku. Zapisuje je ta instalacja, która prowadziła bota, nie są pobierane z serwera. Proces, który dopiero wystartował, nie ma nic do pokazania i nie jest to usterka. total to to, co istnieje, returned to to, co zostawił --limit.
Działanie
Uruchomienie bota
mercy-cli --start --user <account> --character <name>Chodzi, dopóki proces nie zostanie zatrzymany, zapisując przy tym dziennik. --character jest tu wymagane: wypisywanie listy i czytanie stanu są nieszkodliwe, ale uruchomienie bota gra w grę, a wybranie złej postaci nie jest czymś, co da się cofnąć.
--all uruchamia zamiast tego każdą postać konta w tym jednym procesie. To jedno logowanie na konto zamiast jednego na postać, co jest tańsze i powstrzymuje postaci przed wzajemnym unieważnianiem sesji.
Przy Ctrl-C (albo SIGTERM) bieżący ruch może się dokończyć, zanim proces wyjdzie, zamiast być ucięty w środku zapytania.
Zdarzenia zamiast prozy
mercy-cli --start --user <account> --character <name> --eventsJeden obiekt JSON na linię, więc nadzorca czyta linia po linii:
{"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 to jedno z started, stopping, stopped, arena_win, arena_loss, dungeon_win, dungeon_loss, quest_done, scrapbook_win, scrapbook_loss, scrapbook_items (które niesie count).
Ta lista będzie rosła. Traktuj nierozpoznane zdarzenie jako takie do zignorowania, a nie jako błąd; to cały powód, dla którego nazwy są wypisane, a nie są tym, co akurat wypluł Rust.
Używaj tego zamiast czytać dziennik dla ludzi. Tamten dziennik pisany jest dla człowieka i może zostać przeredagowany w każdej chwili; parsowanie go znaczy, że twoje narzędzie psuje się, gdy ktoś poprawi zdanie.
Odbiór tego, co czeka
mercy-cli --claim --user <account> --character <name>Kalendarz, zadania dzienne, oczekujące odblokowania. --character jest wymagane, z tego samego powodu co przy --start.
Odczyt lub zmiana konfiguracji
mercy-cli --config --user <account> --character <name>
mercy-cli --config --user <account> --character <name> --set auto_arena=false{ "ok": true, "settable": ["auto_arena"], "changed": [ { "key": "auto_arena", "from": true, "to": false } ] }Odczyt zwraca całą konfigurację pod config. Nazwy pól wewnątrz tego bloku nie są częścią tej obietnicy, są nią wyłącznie klucze wypisane w settable. Konfiguracja ma znacznie ponad sto pól i rośnie z każdym wydaniem; obiecanie ich wszystkich znaczyłoby, że nigdy więcej nie da się przebudować ustawień.
--set przyjmuje true/false, on/off, yes/no, 1/0, zawsze wymaga --character (przełącznik należy do jednej postaci, a „ta jedyna” to zgadywanka, po której czytać można, a pisać nie), i można je powtarzać. Wartość jest potem odczytywana z dysku, a wywołanie kończy się niepowodzeniem, jeśli nie wylądowała, bo zgłoszenie zmiany, która się nie wydarzyła, to jedyny wynik, po którym nie da się pozbierać.
Eksploatacja
Co to za kompilacja
mercy-cli --version{ "ok": true, "version": "2.12.0", "api": 1 }Jedyne polecenie, które nie potrzebuje ani hasła, ani sieci. Nadzorca musi móc zapytać, z czym rozmawia, zanim będzie miał dane do tej rozmowy.
Czy jest osiągalne
mercy-cli --health --user <account> --character <name>{ "ok": true, "character": "Hero", "level": 345, "round_trip_ms": 412 }Zaloguj, pobierz, zgłoś, wyjdź. Celowo maleńkie: kontrola zdrowia zwracająca pełną kartę postaci zachęca do jej parsowania, a wtedy sama kontrola staje się kolejnym interfejsem.
Hasło
Nie ma flagi --password i jest to celowe. Argumenty wiersza poleceń są czytelne dla każdego innego procesu na maszynie, ps, /proc, Menedżer zadań, więc panel uruchamiający to dla kilku osób oddawałby wszystkie ich hasła do gry każdemu, kto ma powłokę na tej maszynie.
Zamiast tego dwie drogi:
MERCY_SF_PASSWORD='…' mercy-cli --status --user someoneprintf '%s' "$password" | mercy-cli --status --user someone --password-stdin--password-stdin czyta pierwszą linię standardowego wejścia, co trzyma sekret także poza środowiskiem.
Kody wyjścia
| Kod | Znaczenie |
|---|---|
0 | Sukces, a także dla --help |
1 | Ruszyło i zawiodło, powód jest w error, a ok to false |
2 | Wywołanie było błędne: nieznana opcja, brak wartości, brak akcji. Na stderr |
Różnica między 1 a 2 ma znaczenie dla nadzorcy: 2 zawiedzie za każdym razem tak samo i potrzebuje człowieka, 1 może całkiem dobrze zadziałać przy kolejnej próbie.
Błędy
Niepowodzenia to też JSON, więc jeden parser obsługuje jedno i drugie:
{ "ok": false, "error": "no character called 'Typo' on this account" }ok obecne jest w każdej odpowiedzi. Sprawdź je przed wszystkim innym.
Częste przyczyny:
| Komunikat | Co zrobić |
|---|---|
--user is required | Brakuje --user (każda akcja poza --version) |
--start needs --character (or --all) | Nazwij cel; nie zostanie zgadnięty |
this account has N characters — name one with --character | Jest ich kilka, wskaż jedną |
no character called '…' on this account | Literówka; --characters wypisuje poprawne nazwy |
no password: set MERCY_SF_PASSWORD or pass --password-stdin | Wybierz jedną z dwóch dróg podania hasła |
login failed: … | Sprawdź dane logowania albo serwer; konta spoza pojedynczego logowania wymagają --server |
'…' is not settable from here | Nie ma tego na liście dozwolonych; --config bez --set wypisuje settable |
'…' did not save — it still reads back unchanged | Zmiana nie wylądowała; sprawdź uprawnienia do zapisu |
Przepisy
Odbieranie na każdym koncie raz dziennie
for account in alpha beta gamma; do
MERCY_SF_PASSWORD="$(pass mercy/$account)" \
mercy-cli --claim --user "$account" --character Hero || echo "failed: $account"
doneUtrzymywanie kafelka panelu na bieżąco
MERCY_SF_PASSWORD='…' mercy-cli --status --user someone --character Hero --watch 60Jedno logowanie, jeden obiekt na minutę. Nie uruchamiaj tego zamiast tego z crona co minutę; to za każdym razem świeże logowanie.
Zapis zdarzeń do pliku
MERCY_SF_PASSWORD='…' mercy-cli --start --user someone --character Hero --events \
>> events.ndjsonSprawdzenie, z czym rozmawiasz, przed zalogowaniem
api=$(mercy-cli --version | jq -r .api)
[ "$api" = "1" ] || { echo "unknown contract version: $api"; exit 1; }Jako usługa systemd
[Service]
Environment=MERCY_SF_PASSWORD=…
ExecStart=/usr/local/bin/mercy-cli --start --user someone --all --events
Restart=on-failure
KillSignal=SIGTERMSIGTERM pozwala dokończyć bieżący ruch. Proszę, nie rób mu SIGKILL.
Czego to jeszcze nie robi
Uruchamiania kilku postaci i zatrzymywania ich pojedynczo (--all uruchamia wszystkie i zatrzymuje wszystkie naraz), kanału sterującego do działającego procesu oraz zmieniania czegokolwiek poza przełącznikami modułów.
Jeśli potrzebujesz którejś z tych rzeczy, powiedz której; łatwiej zbudować właściwą rzecz niż zgadywać i patrzeć, jak leży nieużywana.