Справочник CLI, управление Mercy SF из другой программы
У mercy-cli есть неинтерактивный режим: аргументы на вход, один объект JSON на выход. Он предназначен для панелей, супервизоров и скриптов.
Эта страница и есть обещание. Команды и имена полей ниже не изменятся без предупреждения. Всё остальное в CLI, меню, его формулировки, вёрстка вывода, вправе меняться в любой момент, поэтому, пожалуйста, не стройте на этом.
Запущенный без аргументов, он открывает интерактивное меню ровно как прежде.
Каждый ответ несёт "api". Сегодня это 1. Добавление поля этого не меняет, вызывающая сторона, читающая только знакомые ей поля, остаётся невредимой. Оно меняется, когда поле убирают или его смысл меняется, так что вы можете отказаться работать против формы, под которую вас не строили.
Вход
Аккаунтам, созданным через единую авторизацию S&F, ничего дополнительно не нужно. Аккаунт, принадлежащий одному серверу и никогда не входивший в единую авторизацию, для неё не существует и требует --server:
mercy-cli --status --user <account> --server https://f1.sfgame.net/--server работает с любой командой ниже.
Вывод
Все команды кроме --start печатают JSON и больше ничего, так что --json и так поведение по умолчанию, и его передача ничего не меняет. Он существует для тех, кто предпочитает сказать это прямо, а не полагаться на умолчание, и для --start, который иначе пишет журнал для человека.
Чтение
Перечислить персонажей аккаунта
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 }
]
}Один вход покрывает всех персонажей аккаунта, на всех серверах. level равен null, если состояние этого персонажа получить не удалось, запись всё равно выводится, потому что знать о её существовании и есть смысл вызова.
Прочитать состояние одного персонажа
mercy-cli --status --user <account> --character <name>--character можно опустить, когда на аккаунте ровно один.
{
"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
}Всё берётся из одного запроса состояния, так что запрашивать разделы не нужно и сэкономить, попросив меньше, не получится.
guild равен null, когда персонаж ни в одной гильдии не состоит. scrapbook_items равен null, пока альбом не прочитан хотя бы раз. Пустая ячейка снаряжения опускается, а не выводится как null.
О названиях предметов: протокол несёт идентификатор модели и слот, а не отображаемое имя, и именно это вы получаете. По нему же адресуется и графика, так что панель может нарисовать ровно ту же картинку, что и настольное приложение. Придумать здесь название значило бы дать догадку, которую вы не сможете проверить.
Повторять чтение с интервалом
mercy-cli --status --user <account> --character <name> --watch 60Один вход, затем по объекту каждые 60 секунд, пока процесс не остановят. Пользуйтесь этим, а не перезапуском команды в цикле: каждый запуск это новый вход, а это самый дорогой способ следить за одним числом и самый заметный со стороны сервера. Нижняя граница 30 секунд. Неудачный круг печатается, и цикл идёт дальше: наблюдатель, сдающийся на первой икоте, выглядит ровно так же, как тот, у которого всё в порядке.
С кем он будет драться дальше и почему
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
}
]
}То же решение, которое рисует приложение: кандидаты оценены по предметам, дневному опыту и месту в рейтинге, затем умножены на шансы. outcome это planned, waiting, blocked или empty, а reason одной фразой говорит какое.
Это не тратит ни боя, ни гриба. Зато оно обходит сервер в поисках кандидатов, то есть в запросах не бесплатно, не ставьте его в плотный цикл.
Прогнать бой на бумаге
mercy-cli --simulate --user <account> --character <name> --against <player>{ "ok": true, "against": "Someone", "against_level": 340, "win_chance": 0.63, "iterations": 1000 }Арифметика. На сервер уходит один запрос, чтобы узнать, кто это; сам бой не отправляется никогда. win_chance это победы к решённым боям, поэтому симуляция, которая не смогла свести исход, отчитывается по меньшему числу, чем прогнала.
Что носится
mercy-cli --inventory --user <account> --character <name>{ "ok": true, "carried": 12, "backpack": [ { "slot_index": 0, "model_id": 55 } ] }Один список, потому что игра ведёт один. Предметы имеют ту же форму, что и в --status, плюс slot_index.
Альбом
mercy-cli --scrapbook --user <account> --character <name>{ "ok": true, "unlocked": true, "owned": 1102, "monsters": 214 }unlocked равен false ниже того уровня, который даёт альбом, а owned там null. Это не ошибка.
Записанные бои
mercy-cli --history --user <account> --character <name> --limit 50{ "ok": true, "total": 4210, "returned": 50, "battles": [] }Новейшие первыми. Их записывает та установка, которая гоняла бота, а не подтягивает сервер, поэтому только что запущенному процессу нечего показать, и это не неисправность. total это то, что существует, returned это то, что оставил --limit.
Действие
Запустить бота
mercy-cli --start --user <account> --character <name>Работает, пока процесс не остановят, попутно ведя журнал. --character здесь обязателен: перечислять и читать состояние безобидно, а запуск бота играет в игру, и выбрать не того персонажа это не то, что можно отменить.
--all вместо этого запускает всех персонажей аккаунта в одном процессе. Это один вход на аккаунт вместо одного на персонажа, что дешевле и не даёт персонажам обнулять друг другу сессию.
По Ctrl-C (или SIGTERM) текущему ходу дают завершиться, прежде чем процесс выйдет, вместо того чтобы обрубать его посреди запроса.
События вместо прозы
mercy-cli --start --user <account> --character <name> --eventsПо одному объекту 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 это одно из started, stopping, stopped, arena_win, arena_loss, dungeon_win, dungeon_loss, quest_done, scrapbook_win, scrapbook_loss, scrapbook_items (несёт count).
Этот список будет расти. Считайте незнакомое событие тем, которое нужно пропустить, а не ошибкой; ровно для этого имена и выписаны, а не взяты из того, что случилось напечатать Rust.
Пользуйтесь этим, а не чтением журнала для человека. Тот журнал написан для человека и вправе быть переформулирован в любой момент; разбирать его значит, что ваш инструмент сломается, когда кто-то улучшит формулировку.
Забрать то, что ждёт
mercy-cli --claim --user <account> --character <name>Календарь, дневные поручения, ожидающие открытия. --character обязателен, по той же причине, что и у --start.
Прочитать или изменить конфигурацию
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 } ] }Чтение возвращает всю конфигурацию под config. Имена полей внутри этого блока не входят в это обещание, входят только ключи, перечисленные в settable. В конфигурации сильно больше сотни полей, и она растёт с каждым выпуском; обещать их все значило бы никогда больше не иметь возможности перестроить настройки.
--set принимает true/false, on/off, yes/no, 1/0, всегда требует --character (переключатель принадлежит одному персонажу, а «тот единственный» это догадка, по которой читать можно, а писать нельзя), и его можно повторять. Значение затем перечитывается с диска, и вызов завершается неудачей, если оно не легло, потому что сообщить об изменении, которого не произошло, это единственный исход, от которого не оправиться.
Эксплуатация
Что это за сборка
mercy-cli --version{ "ok": true, "version": "2.12.0", "api": 1 }Единственная команда, которой не нужны ни пароль, ни сеть. Супервизор должен уметь спросить, с чем он разговаривает, ещё до того, как у него появятся данные для разговора.
Доступно ли оно
mercy-cli --health --user <account> --character <name>{ "ok": true, "character": "Hero", "level": 345, "round_trip_ms": 412 }Войти, получить, сообщить, выйти. Намеренно маленькая: проверка доступности, возвращающая полный лист персонажа, приглашает его разбирать, и тогда сама проверка становится ещё одним интерфейсом.
Пароль
Флага --password нет, и это намеренно. Аргументы командной строки читаются любым другим процессом на машине, ps, /proc, диспетчер задач, так что панель, запускающая это за нескольких людей, раздавала бы все их игровые пароли каждому, у кого есть оболочка на этой машине.
Вместо этого два пути:
MERCY_SF_PASSWORD='…' mercy-cli --status --user someoneprintf '%s' "$password" | mercy-cli --status --user someone --password-stdin--password-stdin читает первую строку стандартного ввода, что держит секрет ещё и вне окружения.
Коды возврата
| Код | Значение |
|---|---|
0 | Успех, а также для --help |
1 | Отработало и не вышло, причина в error, а ok равен false |
2 | Вызов был неверным: неизвестная опция, пропущенное значение, нет действия. В stderr |
Разница между 1 и 2 важна супервизору: 2 будет проваливаться одинаково каждый раз и требует человека, 1 вполне может сработать со следующей попытки.
Ошибки
Неудачи тоже JSON, так что один разборщик справляется с обоими:
{ "ok": false, "error": "no character called 'Typo' on this account" }ok присутствует в каждом ответе. Проверяйте его раньше всего остального.
Частые причины:
| Сообщение | Что делать |
|---|---|
--user is required | Пропущен --user (любое действие кроме --version) |
--start needs --character (or --all) | Назовите цель; угадывать её не станут |
this account has N characters — name one with --character | Их несколько, выберите одного |
no character called '…' on this account | Опечатка; --characters выводит допустимые имена |
no password: set MERCY_SF_PASSWORD or pass --password-stdin | Выберите один из двух путей для пароля |
login failed: … | Проверьте учётные данные или сервер; аккаунтам вне единой авторизации нужен --server |
'…' is not settable from here | Не в списке разрешённых; --config без --set печатает settable |
'…' did not save — it still reads back unchanged | Изменение не легло; проверьте права на запись |
Рецепты
Забирать на каждом аккаунте раз в день
for account in alpha beta gamma; do
MERCY_SF_PASSWORD="$(pass mercy/$account)" \
mercy-cli --claim --user "$account" --character Hero || echo "failed: $account"
doneДержать плитку панели свежей
MERCY_SF_PASSWORD='…' mercy-cli --status --user someone --character Hero --watch 60Один вход, один объект в минуту. Не запускайте это вместо того из cron каждую минуту, там каждый раз новый вход.
Писать события в файл
MERCY_SF_PASSWORD='…' mercy-cli --start --user someone --character Hero --events \
>> events.ndjsonПроверить, с чем вы говорите, до входа
api=$(mercy-cli --version | jq -r .api)
[ "$api" = "1" ] || { echo "unknown contract version: $api"; exit 1; }Как служба systemd
[Service]
Environment=MERCY_SF_PASSWORD=…
ExecStart=/usr/local/bin/mercy-cli --start --user someone --all --events
Restart=on-failure
KillSignal=SIGTERMSIGTERM даёт текущему ходу завершиться. Пожалуйста, не шлите ему SIGKILL.
Чего это пока не умеет
Запускать нескольких персонажей и останавливать их по отдельности (--all запускает всех и останавливает всех вместе), канал управления внутрь работающего процесса, и менять что-либо помимо переключателей модулей.
Если вам нужно что-то из этого, скажите что именно: построить нужное проще, чем угадать и оставить это лежать без дела.