CI / ruff · mypy · pytest (pull_request) Successful in 1h3m54s
- invites.max_uses/uses: обычное приглашение — 1 вход, демо-QR — до DEMO_INVITE_MAX_USES (50), живёт DEMO_INVITE_TTL_HOURS (3 ч); захват атомарный, UPDATE … WHERE uses < max_uses - users.guest: вошедшие по демо-QR; «🧹 Отключить гостей демо» отзывает всех и гасит демо-QR - «👥 Участники»: кнопка «🎤 Демо-QR», гости в списке — 🎟 - миграция 0003 (ADD COLUMN без batch — SQLite не пересоздаёт users); проверена на Postgres - SPEC A-3a, ADR-017, DEMO.md, .env.example Refs #55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
303 lines
15 KiB
YAML
303 lines
15 KiB
YAML
# Тексты интерфейса бота. Ключи читает app/texts.py; отсутствующий ключ — ошибка при старте.
|
||
|
||
menu:
|
||
commit: "📝 Коммит"
|
||
status: "🩺 Статус"
|
||
logs: "📜 Логи"
|
||
alerts: "🚨 Алерты"
|
||
members: "👥 Участники"
|
||
back: "⬅️ Назад"
|
||
cancel: "✖️ Отмена"
|
||
placeholder: "Выберите раздел"
|
||
|
||
start:
|
||
welcome: |-
|
||
Привет! Я tgit — git в телеграме.
|
||
|
||
📝 Коммит — изменить файлы и запушить
|
||
🩺 Статус — контейнеры и их health
|
||
📜 Логи — хвост, свой grep или вопрос агенту
|
||
🚨 Алерты — падения, перегрузка, список
|
||
no_access: "Доступ к боту — только по приглашению. Попросите QR-код у администратора."
|
||
invite_rejected: "Приглашение недействительно: истекло, уже использовано или ссылка неверная. Попросите новое у администратора."
|
||
|
||
help:
|
||
text: |-
|
||
/menu — главное меню из любого места
|
||
/help — эта справка
|
||
|
||
Разделы открываются кнопками внизу. В любом шаге есть «⬅️ Назад» и «✖️ Отмена».
|
||
|
||
section:
|
||
cancelled: "Отменено. Главное меню — кнопками внизу."
|
||
|
||
members:
|
||
title: "👥 Участники: {count}"
|
||
line: "{icon} {name}{username} · с {joined} · был(а) {seen}"
|
||
never: "—"
|
||
invite: "➕ Пригласить"
|
||
revoke: "🚫 {name}"
|
||
confirm: "Отозвать доступ у {name}? Его черновик удалится, кнопки перестанут работать."
|
||
confirm_yes: "Да, отозвать"
|
||
confirm_no: "Нет"
|
||
revoked: "Доступ отозван: {name}."
|
||
not_revoked: "Нельзя отозвать: администратор или уже без доступа."
|
||
invite_caption: |-
|
||
Приглашение в tgit — отсканируйте QR или откройте ссылку:
|
||
{link}
|
||
|
||
Одноразовое, действует до {expires} UTC.
|
||
demo_invite: "🎤 Демо-QR"
|
||
demo_invite_caption: |-
|
||
🎤 Демо-доступ к tgit — отсканируйте QR или откройте ссылку:
|
||
{link}
|
||
|
||
До {max_uses} человек, действует до {expires} UTC.
|
||
revoke_guests: "🧹 Отключить гостей демо"
|
||
confirm_guests: "Отключить всех, кто вошёл по демо-QR, и погасить сам QR? Их черновики удалятся."
|
||
guests_revoked: "Гости демо отключены: {count}. Демо-QR больше не работает."
|
||
|
||
commit:
|
||
title: "📝 Коммит · /{path}"
|
||
empty: "Папка пуста."
|
||
page: "Стр. {page} из {pages}"
|
||
up: "⬆️ Вверх"
|
||
prev: "◀️"
|
||
next: "▶️"
|
||
dir_button: "📁 {name}"
|
||
file_button: "📄 {name} · {size}"
|
||
new_file: "➕ Новый файл"
|
||
new_dir: "📁 Новая папка"
|
||
file_card: |-
|
||
📄 /{path}
|
||
Размер: {size}
|
||
last_commit: |-
|
||
Последний коммит: {sha} · {author} · {date}
|
||
{subject}
|
||
no_commits: "Коммитов по файлу нет."
|
||
view: "👁 Показать"
|
||
replace: "✏️ Заменить"
|
||
delete: "🗑 Удалить"
|
||
to_dir: "⬅️ К папке"
|
||
soon: "Будет в следующей версии."
|
||
unavailable: "Репозиторий загрузок недоступен, попробуйте через минуту."
|
||
sent_as_file: "Файл большой или бинарный — отправил документом."
|
||
ask_file_name: "Имя нового файла в /{dir} (латиница, цифры, «.», «_», «-»):"
|
||
ask_dir_name: "Имя новой папки в /{dir}:"
|
||
bad_name: "Недопустимое имя: латиница, цифры, «.», «_», «-», до 100 символов. Пришлите другое."
|
||
exists: "Такое имя уже есть в этой папке. Пришлите другое."
|
||
protected: "Это служебный путь репозитория — из бота его менять нельзя."
|
||
ask_content: "Пришлите содержимое /{path} — текстом сообщения или файлом (до 100 КБ)."
|
||
too_big: "Больше 100 КБ — не принимаю."
|
||
binary: "Похоже на бинарный файл — принимаю только текст."
|
||
encoding: "Файл не в кодировке UTF-8."
|
||
syntax: "⚠️ Синтаксическая ошибка, строка {line}: {message}"
|
||
fix: "✏️ Исправить"
|
||
force: "Всё равно добавить"
|
||
added: "🆕 /{path} — добавлен в черновик."
|
||
modified: "✏️ /{path} — в черновике: +{added} −{removed}."
|
||
deleted: "🗑 /{path} — в черновике на удаление."
|
||
draft_button: "🧾 Черновик · {count}"
|
||
draft_title: "🧾 Черновик · изменений: {count}"
|
||
draft_empty: "Черновик пуст."
|
||
unstage: "↩️ убрать {name}"
|
||
draft_full: "В черновике уже 50 изменений — запушьте или уберите лишнее."
|
||
in_draft_new: "В черновике: новый файл."
|
||
in_draft_modified: "В черновике: изменён."
|
||
in_draft_deleted: "В черновике: удалён."
|
||
mark_new: "🆕 "
|
||
mark_modified: "✏️ "
|
||
mark_deleted: "🗑 "
|
||
op_add: "A"
|
||
op_modify: "M"
|
||
op_delete: "D"
|
||
message_button: "💬 Сообщение коммита"
|
||
message_current: "💬 {message}"
|
||
message_missing: "💬 Сообщение коммита не задано."
|
||
ask_message: "Сообщение коммита: до 200 символов, первая строка — до 72."
|
||
bad_message: "Сообщение: 1–200 символов, первая строка — до 72. Пришлите другое."
|
||
push_button: "🚀 Push"
|
||
commit_button: "🚀 Закоммитить · {count}"
|
||
edit_message: "✏️ Изменить сообщение"
|
||
push_confirm: |-
|
||
Запушить в main?
|
||
|
||
{summary}
|
||
|
||
💬 {message}
|
||
push_go: "✅ Запушить"
|
||
pushing: "⏳ Пушу…"
|
||
pushed: "✅ Запушено: {sha} — {message}"
|
||
conflict: |-
|
||
⚠️ Пока вы готовили черновик, в main изменились те же файлы:
|
||
{paths}
|
||
|
||
Перезаписать их вашей версией?
|
||
overwrite: "Перезаписать"
|
||
push_empty: "В main уже всё так же — коммитить нечего. Черновик очищен."
|
||
push_limit: "Лимит — 20 пушей в час. Попробуйте позже."
|
||
push_failed: "Не удалось запушить: репозиторий недоступен. Черновик сохранён — попробуйте позже."
|
||
|
||
pipeline:
|
||
ci_pending: "CI: ⏳ ждёт раннер"
|
||
ci_running: "CI: ⏳ идёт проверка"
|
||
ci_success: "CI: ✅ пройден"
|
||
ci_failed: "CI: ❌ упал: {step}"
|
||
ci_timeout: "CI: ⌛ не завершился за {minutes} мин"
|
||
cd_waiting: "CD: ⏸ {services} — после CI"
|
||
cd_running: "CD: 🚀 разворачиваю {services}…"
|
||
cd_success: "CD: 🚀 {services} развёрнуто"
|
||
cd_failed: "CD: ⛔ не развёрнуто: {services}"
|
||
cd_not_run: "CD: ⏭ не запускался — CI не прошёл"
|
||
cd_superseded: "CD: ⏭ эти сервисы развернёт более новый пуш"
|
||
notify_ci_ok: |-
|
||
✅ CI пройден · {sha}
|
||
{url}
|
||
notify_ci_failed: |-
|
||
❌ CI упал на шаге <b>{step}</b> · {sha}
|
||
Сервисы не развёрнуты. {url}
|
||
notify_ci_timeout: "⌛ CI не завершился за {minutes} мин · {sha} — сервисы не развёрнуты."
|
||
notify_cd_ok: "🚀 CD: сервисы {services} развёрнуты, ревизия {sha}"
|
||
deploy_failed: |-
|
||
⛔ <b>{service}</b> не развёрнут: {reason}
|
||
Работает прежняя версия, если она была.
|
||
removed: "🗑 <b>{service}</b> удалён из репозитория — остановлен на хосте"
|
||
log_file: "Полный лог CI"
|
||
|
||
status:
|
||
title: "🩺 Статус · {count} конт. · {time} UTC"
|
||
line: "{light} {name} · {state}"
|
||
empty: "Контейнеров tgit не найдено."
|
||
unavailable: "Docker недоступен, попробуйте через минуту."
|
||
card: |-
|
||
{light} {name}
|
||
Сервис: {service} · образ: {image}
|
||
Состояние: {state} · health: {health}
|
||
Аптайм: {uptime} · рестартов: {restarts}
|
||
CPU: {cpu} · RAM: {mem} из {limit} ({mem_pct})
|
||
Ревизия: {sha} · деплой: {deployed}
|
||
none: "—"
|
||
no_limit: "без лимита"
|
||
check_button: "🩺 Запустить проверки"
|
||
refresh: "🔄 Обновить"
|
||
to_list: "⬅️ К списку"
|
||
to_card: "⬅️ К контейнеру"
|
||
checking: "🩺 Проверяю контейнеры…"
|
||
report_title: "🩺 Проверки · без ошибок {ok} из {total} · {time} UTC"
|
||
report_container: "{light} {name}"
|
||
report_line: " {verdict} {title}{detail}"
|
||
check_running: "запущен"
|
||
check_health: "Docker health"
|
||
check_restarts: "рестарты за 5 мин не растут"
|
||
check_cpu: "CPU ниже порога"
|
||
check_mem: "RAM ниже порога"
|
||
check_log_errors: "нет ошибок в логе за 5 мин"
|
||
check_http: "HTTP-проба"
|
||
days: "д"
|
||
hours: "ч"
|
||
minutes: "мин"
|
||
seconds: "с"
|
||
restart: "♻️ Перезапустить"
|
||
restarted: "♻️ Перезапущен"
|
||
restart_failed: "Не удалось перезапустить: {reason}"
|
||
|
||
logs:
|
||
title: "📜 Логи · выберите контейнер"
|
||
empty: "Контейнеров tgit не найдено."
|
||
unavailable: "Docker недоступен, попробуйте через минуту."
|
||
container: |-
|
||
📜 Логи · {name}
|
||
|
||
Хвост, свой grep по последним 24 ч или вопрос агенту.
|
||
tail: "{n} строк"
|
||
since: "за {period}"
|
||
minutes: "{n} мин"
|
||
hours: "{n} ч"
|
||
grep_button: "🔎 Свой grep"
|
||
agent_button: "🤖 Спросить агента"
|
||
agent_ask: |-
|
||
🤖 Вопрос агенту — по логу {name} или другого контейнера, если назовёте его.
|
||
Например: «когда были ошибки подключения за сутки?», «сколько таймаутов за час?»
|
||
|
||
Агент сам построит grep и покажет его. Осталось вопросов на сегодня: {left} из {quota}.
|
||
agent_off: "Агент не настроен: боту нужен LLM (LLM_PROVIDER)."
|
||
agent_thinking: "🤖 Строю запрос…"
|
||
agent_refused: "С таким запросом помочь не могу. Спросите о содержимом логов: ошибки, таймауты, падения, подключения."
|
||
agent_quota: "Лимит — {quota} вопросов агенту в сутки, он исчерпан. Свой grep работает без лимита."
|
||
agent_unavailable: "Агент сейчас недоступен. Попробуйте позже или воспользуйтесь своим grep."
|
||
agent_not_understood: "Не получилось построить запрос. Переформулируйте вопрос или воспользуйтесь своим grep."
|
||
head_agent: "🤖 <code>{command}</code>\n<b>{name}</b> · за {period} · совпадений: {matched}"
|
||
to_list: "⬅️ К контейнерам"
|
||
to_container: "⬅️ К контейнеру"
|
||
ask_grep: |-
|
||
🔎 Выражение grep — ищу по логу {name} за 24 ч.
|
||
|
||
Например: <code>-i -C 2 error</code>
|
||
Флаги: <code>-i</code> без регистра, <code>-v</code> кроме, <code>-E</code> расширенный regex, <code>-c</code> только число, <code>-C N</code> контекст.
|
||
head_tail: "📜 <b>{name}</b> · {scope} · строк: {lines}"
|
||
head_grep: "🔎 <code>{command}</code>\n<b>{name}</b> · за 24 ч · совпадений: {matched}"
|
||
nothing: "Пусто."
|
||
truncated: "Показаны первые {n} строк."
|
||
partial: "Поиск остановлен по времени — просмотрена часть лога."
|
||
skipped: "Пропущено строк из-за тяжёлого шаблона: {n}."
|
||
sent_as_file: "Результат длинный — отправил файлом."
|
||
err_empty: "Нужен шаблон, например: <code>-i error</code>"
|
||
err_quotes: "Не закрыта кавычка."
|
||
err_flag: "Флаг {arg} не поддерживается. Можно: -i -v -E -c -C N"
|
||
err_context: "После -C нужно число от 0 до 10."
|
||
err_extra: "Нужен один шаблон, а «{arg}» лишнее. Шаблон с пробелами — в кавычках."
|
||
err_too_long: "Шаблон длиннее 200 символов."
|
||
err_regex: "Шаблон не разбирается: {arg}"
|
||
|
||
alerts:
|
||
kinds:
|
||
crash: "💥 Краш"
|
||
oom: "🧠 OOM"
|
||
restart_loop: "🔁 Цикл рестартов"
|
||
unhealthy: "🩺 Unhealthy"
|
||
overload: "🔥 Перегрузка"
|
||
deploy_refused: "⛔ Не запустился после деплоя"
|
||
cd_failed: "📦 CD упал"
|
||
disk_low: "💾 Мало места на хосте"
|
||
dependency_down: "🔌 Зависимость недоступна"
|
||
agent_attack: "🛡 Атака через агента"
|
||
details:
|
||
crash: "Контейнер вышел с кодом {exit_code}."
|
||
oom: "Убит по памяти (OOM), код {exit_code}."
|
||
restart_loop: "{restarts} рестартов за {window_min} мин."
|
||
unhealthy: "Health-check не проходит."
|
||
overload: "CPU {cpu_pct}% · RAM {mem_pct}% от лимита — дольше минуты."
|
||
deploy_refused: "{detail}"
|
||
cd_failed: "{detail}"
|
||
disk_low: "Свободно {free_pct}% диска."
|
||
dependency_down: "Не отвечает с {since}."
|
||
agent_attack: "{who} · отсечено: {stage} · {reason}"
|
||
message: |-
|
||
{title} · <b>{service}</b>
|
||
{detail}
|
||
🕒 {opened} UTC{repeats}
|
||
repeats: " · повторов: {count}"
|
||
resolved: "✅ Восстановлено · {title} · {service} · длилось {duration}"
|
||
logs_button: "📜 Логи"
|
||
status_button: "🩺 Статус"
|
||
list_button: "📋 Список алертов"
|
||
list_title: "🚨 Алерты · активных: {active} · всего за 7 дней: {total}"
|
||
list_empty: "За 7 дней алертов не было."
|
||
list_line: "{mark} {title} · {service} · {time}{repeats}"
|
||
active_mark: "🔴"
|
||
resolved_mark: "✅"
|
||
page: "Стр. {page} из {pages}"
|
||
prev: "◀️"
|
||
next: "▶️"
|
||
card: |-
|
||
{title} · <b>{service}</b>
|
||
{detail}
|
||
|
||
Начало: {opened} UTC
|
||
Конец: {resolved}
|
||
Повторов: {count}
|
||
ongoing: "продолжается"
|
||
to_list: "⬅️ К списку"
|
||
host: "хост"
|
||
gone: "Алерт не найден."
|