# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. Проект и общение по нему ведём по-русски. ## Что это Сервис расшифровки аудио. Два входа — Telegram-бот и HTTP API, один общий конвейер обработки. Распознавание асинхронное, через Yandex SpeechKit; файл едет в Yandex Object Storage, оттуда его забирает SpeechKit. ## Команды ```bash go build ./... # нужен CGO: mattn/go-sqlite3 go test ./... go vet ./... gofmt -l . golangci-lint run go run . -c config.toml # флаг -c или --config, по умолчанию config.toml task image # docker-образ, тег из $BUILD_ID (по умолчанию dev) ``` `golangci-lint run` на чистом `master` даёт 4 замечания в существующем коде (два непроверенных `Close`, два сравнения ошибок через приведение типа вместо `errors.As`). Их пока не чинили — новые замечания отличай от этих. Деплой запускается не отсюда, а из `pet-project-server`: `inv pl -- transcriber`. Плейбук сам зовёт `task image` по контракту роли `app_image`, образ едет на сервер через `docker save`/`load`, реестр не участвует. `lefthook` на pre-commit гоняет `gitleaks git --staged`. ## Конфиг `config.toml` в gitignore, образец — `config.dist.toml`. Разбирается в `internal/config`; значения по умолчанию заданы в `defaultConfig()`, файл их перекрывает. Правишь поле — правь оба места. Два подвоха: - Ключ белого списка пользователей Telegram называется `users_while_list` (опечатка в теге toml), лежит в секции `[server]`, и в `config.dist.toml` его нет вообще. Без него бот отвечает отказом всем. - Список сверяется с `update.Message.From.String()` из tgbotapi — это `@username` либо имя с фамилией, а не числовой id. ## Конвейер задач Состояния `TranscribeJob`: `created` → `converted` → `transcribe` → `done` / `failed` (константы в `internal/entity/job.go`). Три воркера в `main.go` крутят по одному шагу каждый, опрашивая базу раз в секунду: `conversion_worker` (created → converted, ffmpeg в ogg), `transcribe_worker` (converted → transcribe, заливка в S3 и старт распознавания), `check_worker` (transcribe → done/failed, опрос операции Yandex). Задача захватывается через `FindAndAcquire`: один `UPDATE` проставляет `acquisition_id`, дальше строка читается по нему. Задача с истёкшим `acquire_time` достаётся заново — час для конвертации и распознавания, сутки для проверки. `delay_time` откладывает следующую попытку. `contract.NoopJobError` — не ошибка, а «задач в этом состоянии нет». Воркер такой случай не логирует и не считает в метрику. Возвращая ошибку из шага конвейера, не теряй эту семантику. Ошибку, после которой задачу нет смысла повторять, оформляй через `failJob`: он переводит задачу в `failed` и сам шлёт человеку понятный текст в Telegram. Возврат обычной ошибки оставляет задачу в текущем состоянии на повтор. ## Слои `internal/entity` — модели, `internal/contract` — интерфейсы и типы ошибок, `internal/service` — конвейер, `internal/controller/{http,tg,worker}` — входы, `internal/adapter/*` — реализации contract (ffmpeg, yandex, telegram, sqlite). Сервис зависит только от интерфейсов `contract`; конкретные адаптеры собираются в `main.go`. ## База Миграции goose в `migrations/`, вшиты в бинарник через `//go:embed migrations/*.sql` в `main.go` и накатываются при старте. Новый файл достаточно положить в каталог, регистрировать нигде не надо. Диалект — `sqlite3`. Добавляя колонку, правь четыре места: миграцию, структуру в `internal/entity`, и в `internal/adapter/repo/sqlite` — списки колонок в `Create`, `Save`, `GetByID` и `FindAndAcquire`. Списки продублированы, компилятор расхождение не поймает. ## Конвенции - Коммиты прямо в `master`, без веток и PR. - В сообщении коммита не ставить трейлер `Co-Authored-By`. - Логи — `log/slog`, структурные пары ключ-значение, логгер прокидывается конструктором. `log.Printf` в `internal/controller/http/transcribe.go` — остаток, на него не равняться. - Текст, который увидит пользователь Telegram, — по-русски. Логи и комментарии в коде — как в соседних файлах. - Метрики Prometheus объявляются в `internal/metrics` с префиксом `transcriber_`.