tmc-cli - The Modding Community

Открытый код

tmc-cli, клиент командной строки для нашего публичного API контента. Он создаёт, редактирует и удаляет ваши ассеты, моды, серверы, статьи, сообщества, коллекции и группы, и, поскольку именно ради этого API и используют, он загружает файлы и выпускает релизы одной командой.

Python 3.11+Без зависимостейЛицензия MIT

Установка из клона. tmc-cli ещё нет на PyPI, поэтому pip ставит его из публичного репозитория. Три шага описаны ниже.

$ tmc auth whoamiprofile    defaultsite       https://moddingcommunity.comauthMode   tokentoken      tmc_ab…9fcanRead    truecanWrite   truecanDelete  false

Один пакет на стандартной библиотеке: собирать и разрешать зависимости не нужно. На PyPI его нет, поэтому он ставится из клона. Каждая команда, флаг и код выхода описаны в документации.

Установка

Три шага, без индекса пакетов

tmc-cli нет на PyPI. Он ставится из клона публичного репозитория, требует Python 3.11 или новее и не тянет других пакетов.

  1. 1

    Клонируйте

    Получите исходный код. Ничего не собирается и не компилируется.

    git clone https://github.com/modcommunity/tmc-cli.git && cd tmc-cli
  2. 2

    Установите

    pip добавит команду tmc в ваш PATH. Дополнение необязательно: если установлен cryptography, подпись работает быстрее. Пакет называется tmc-cli, но вводится команда tmc.

    pip install .
  3. 3

    Войдите

    Создайте ключ в разделе «Аккаунт, ключи API» и войдите. Или читайте публичные записи без ключа через --anon.

    tmc auth login --token tmc_xxxxxxxxxxxx
Команды

Один инструмент, шесть областей API

Названия команд это ровно то, что набирается. Полный список покажет tmc --help.

Контент

List, get, create, update и delete для каждого типа контента.

assetmodservercommunityarticlecollectiongroup

Вокруг записи

Теги, медиа, ссылки, связи, комментарии и отзывы, каждое управляется без повторной отправки записи.

tagtagsmedialinksrelcommentreview

Файлы и релизы

Загрузка файлов, создание релиза и прикрепление файлов одной командой.

filerelease

Ваш аккаунт

Избранное, друзья, группы, установки и подписки.

authaccountfavoritefriendspartyinstallssubscriptions

Сайт

Состояние сетевого монитора, статистика, фильтры, каталог, жалобы и play.

defconstatsfiltercatalogreportplay

Сам инструмент

Схема, по которой идёт проверка, автодополнение для вашей оболочки и raw для любого запроса.

schemacontractcompletionopenraw

Релизы

Релизы Одной Командой

Выпустить релиз через «сырое» API, это четыре запроса в определённом порядке: загрузить файлы, собрать их идентификаторы, прочитать уже существующий набор релизов и записать новый не задев остальные. release publish, это и есть вся эта последовательность.

publish.sh
# Upload the files, attach them, write the release: one command
tmc release publish --mod 5 \
  --version 1.2.0 \
  --title "Bug fixes" \
  --content-file CHANGELOG.md \
  --file 'dist/*.zip' --file dist/checksums.txt

# Everything on the item, without re-sending the item
tmc release list  --mod 5
tmc release files --mod 5 --version 1.2.0

Повторный Запуск Обновляет

Та же --version во второй раз обновляет этот релиз и добавляет новые файлы в его набор. Остальные ваши релизы не трогаются, а скрытый релиз остаётся скрытым.

Шаблоны, Пакетами

Передайте --file 'dist/*.zip' и сколько угодно файлов; ограничение в двадцать загрузок на запрос обрабатывается за вас.

Большие Файлы Передаются Потоком

Загрузка в 1 ГБ читается с диска кусками, а не целиком в память. Всё, что превышает лимит размера для вашего ключа, отмечается до отправки.
Что Он Умеет

Что Умеет CLI

Одна команда на каждое действие, которое вы выполнили бы в браузере, и ещё несколько, которые не хочется делать вручную: массовые правки, пайплайны и релизы из скриптов.

Вообще Никаких Зависимостей

Python 3.11+ и стандартная библиотека. Положите его на сборочную машину или игровой сервер без индекса пакетов, и он работает. Для подписи используется cryptography, если она случайно установлена, и встроенная реализация RFC 8032, если нет, подписи в обоих случаях одинаковы.

Оба Режима Аутентификации

Ключи Bearer tmc_ либо подписанные утверждения Ed25519, где закрытый ключ остаётся у вас, а мы храним только публичную половину.

Одна Грамматика Для Каждого Типа

Одни и те же пять глаголов, list, get, create, update, delete, для каждого типа контента. tmc content <type> это единая форма и единственный путь к самостоятельным типам release и media.

Связи Без Повторной Отправки

Теги, медиа, релизы, ссылки, источники и элементы коллекций управляются отдельно, так что добавить один скриншот, не значит переписывать весь объект.

Массово, В Пределах Каждого Лимита

Создавайте или редактируйте из JSON-файла любой длины. Каждое серверное ограничение, 25 на запись, 100 на удаление, 200 участников связи, 500 ключей при удалении связи, 20 частей загрузки, разбивается на пакеты за вас, а при частичной ошибке называется именно тот элемент, который не прошёл, чтобы можно было продолжить, а не начинать заново.

Опечатки Ловятся Локально

Имена полей сверяются с локальной копией схем API, с подсказкой ближайшего совпадения: у «mod» нет поля «tgs». Возможно, вы имели в виду «tags»? tmc schema mod печатает тот список полей, с которым идёт сверка.

Сделан Для Пайпов

Семь форматов вывода, table, json, jsonl, csv, tsv, yaml, ids, и --field, чтобы оставить только нужные столбцы. Данные в stdout, прогресс в stderr, и tmc completion bash|zsh|fish для вашей оболочки.

Ограничения Скорости Обрабатываются

Ответ 429 ожидается ровно столько, сколько просит API, но не дольше --retry-wait-max, после чего вам сообщают, сколько осталось, вместо того чтобы подвесить сборку. Повторы применяются только к идемпотентным методам, POST никогда не повторяется молча.
Учётные Данные

Ключи, Профили И Публичная Половина

Войдите один раз, и учётные данные сохранятся в ~/.config/tmc/config.json с правами 0600, или передайте их через окружение, и диск вообще не будет затронут. По одному профилю на сайт или на ключ, и --anon для той половины API, которой ключ не нужен вовсе.

auth.sh
# A bearer key, or an Ed25519 key you hold the private half of
tmc auth login --token tmc_xxxxxxxxxxxx
tmc auth login --jwt --key-id tmcak_xxxxxxxx --private-key ~/keys/tmc.pem

# A second key on the same site, a scoped one for scripts
tmc auth login --profile ci \
  --base-url https://moddingcommunity.com --token tmc_…
tmc auth use ci

# Which key is active, and what the server lets it do
tmc auth whoami
tmc auth doctor

# …or no key at all
tmc mod list --anon --app 4 -o json

Профиль На Сайт Или На Ключ

--profile указывает нужный, tmc auth use меняет профиль по умолчанию, и у каждого сохранённого значения есть двойник в окружении, TMC_TOKEN, TMC_KEY_ID, TMC_PRIVATE_KEY_FILE, TMC_BASE_URL, TMC_PROFILE.

Узнать, Что Ключ Действительно Может

auth whoami выясняет реальные права ключа тремя намеренно безобидными пробами, чтением, созданием, которое не может пройти валидацию, и удалением пустого списка идентификаторов. Ничего не создаётся и не удаляется; --read-only отправляет только чтение.

Диагностика Настройки

auth doctor сообщает, какой бэкенд подписи используется, безопасны ли права вашего конфигурационного файла и отвечает ли сайт вообще, три вещи, которые стоит знать до отладки конвейера.

Чтение Вообще Без Ключа

--anon читает публичную сводку по семи типам без заголовка Authorization, это действительно другой эндпоинт, поэтому он отклоняет запись ещё до отправки запроса, отбрасывает фильтры, которых у этой поверхности нет, вместо того чтобы делать вид, будто они сработали, и никогда не переключается тихо на сохранённый ключ. --set apiPublic=false, способ вывести объект оттуда.
Связи

Четыре Глагола, Которые Говорят, Что Делают

Опасный, set: в этом API PUT к связи означает «теперь это полный набор», поэтому PUT с одним участником удаляет у объекта всё остальное. CLI держит все четыре раздельно и предупреждает перед отправкой разрушительного.

Команда Метод Значение
tmc rel add POST Добавить эти, остальное не трогать
tmc rel set PUT Теперь это полный набор
tmc rel rm DELETE Убрать названные
tmc rel clear DELETE Убрать все
relations.sh
# Add two tags. The rest of the item is untouched
tmc tags add mod 5 pvp vanilla

# Upload a screenshot and attach it in one step
tmc media add mod 5 --file shot.png --title Screenshot

# Read one relation back
tmc rel get mod 5 releases

# …and the one that REPLACES the whole gallery
tmc rel set mod 5 media --from-file gallery.json
Автоматизация

Используйте В CI

Собирайте проект, выпускайте релиз и загружайте его файлы при каждом теге. Учётные данные берутся из окружения, поэтому на раннере ничего не пишется на диск.

release.yml
- name: Publish to TMC
  env:
    TMC_TOKEN: ${{ secrets.TMC_TOKEN }}
  run: |
    tmc release publish --mod 42 \
      --version "${GITHUB_REF_NAME#v}" \
      --content-file CHANGELOG.md \
      --file 'dist/*.zip'

# Clean pipes: data on stdout, progress on stderr
tmc mod list --mine --all -o ids | xargs -n1 tmc mod get -o json

На Раннер Ничего Не Пишется

Учётные данные приходят из окружения, поэтому профиль не создаётся и ничто не переживает задачу. Ключ JWT может остаться секретным файлом, который монтирует раннер, мы храним только его публичную половину.

Осмысленные Коды Возврата

2, использование, 3, аутентификация, 4, не найдено, 5, валидация, 6, превышен лимит, 7, сервер, 8, сеть, чтобы конвейер отличал «повторить» от «почини свой ключ».

Устаревшая Сборка Не Заблокирует

Поле, о котором инструмент ещё не знает, всё равно пройдёт с --allow-unknown-fields, а tmc raw отправит вообще любой запрос.

Проверено На Настоящем

203 тестов гоняют настоящий CLI против внутрипроцессного макета API через реальный сокет, оба режима аутентификации, разбиение на пакеты, семантику связей и путь повторных попыток.

Ключи Живут В Аккаунт → Ключи API

Ключ бесплатен, создаётся за секунду и несёт права на чтение / запись / удаление, срок действия, списки разрешённых IP и собственный лимит частоты, так что ключ, отданный сборочному серверу, сможет делать ровно одно. Каждая команда, флаг и код возврата описаны в документации.

Ключ полезен уже сегодня: API работает, документация написана. tmc-cli с открытым кодом и ставится из клона своего репозитория. На PyPI его пока нет.