Инструкции для Claude, конфиг линтера, актуализация README
CLAUDE.md описывает конвейер задач, подвохи конфига и правило про четыре списка колонок в репозитории sqlite. .golangci.yml включает стандартный набор плюс errorlint; осознанные непроверенные вызовы вынесены в исключения. README приведён к коду: эндпоинты /api/audio и /api/status/:id, состояния задач, структура internal/, фактические колонки таблиц.
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
version: "2"
|
||||||
|
|
||||||
|
linters:
|
||||||
|
default: standard
|
||||||
|
enable:
|
||||||
|
- errorlint
|
||||||
|
settings:
|
||||||
|
errcheck:
|
||||||
|
exclude-functions:
|
||||||
|
# Закрытие через defer и лучшая-попытка уборки файла — осознанно без проверки
|
||||||
|
- (io.Closer).Close
|
||||||
|
- (*database/sql.DB).Close
|
||||||
|
- (*os.File).Close
|
||||||
|
- (io.ReadCloser).Close
|
||||||
|
- os.Remove
|
||||||
|
# Метод сам логирует ошибку отправки, вызывающему она не нужна
|
||||||
|
- (*git.vakhrushev.me/av/transcriber/internal/controller/tg.TelegramController).send
|
||||||
|
exclusions:
|
||||||
|
rules:
|
||||||
|
- path: _test\.go
|
||||||
|
linters:
|
||||||
|
- errcheck
|
||||||
|
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
# 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_`.
|
||||||
@@ -1,22 +1,26 @@
|
|||||||
# Transcriber Service
|
# Transcriber Service
|
||||||
|
|
||||||
Сервис для расшифровки аудиозаписей с REST API.
|
Сервис расшифровки аудиозаписей. Два входа — Telegram-бот и HTTP API.
|
||||||
|
|
||||||
## Возможности
|
## Возможности
|
||||||
|
|
||||||
- Загрузка аудиофайлов любого формата
|
- Приём аудио из Telegram: голосовые сообщения, аудиофайлы и документы с аудио
|
||||||
- Автоматическая генерация UUID для файлов
|
- Приём аудиофайлов через HTTP API
|
||||||
- Сохранение файлов на диск
|
- Конвертация в ogg через ffmpeg
|
||||||
|
- Распознавание речи через Yandex SpeechKit
|
||||||
- Отслеживание статуса задач расшифровки
|
- Отслеживание статуса задач расшифровки
|
||||||
- SQLite база данных для хранения метаданных
|
- SQLite для хранения метаданных, метрики Prometheus
|
||||||
|
|
||||||
## Технологии
|
## Технологии
|
||||||
|
|
||||||
- **Веб-фреймворк**: gin-gonic/gin
|
- **Веб-фреймворк**: gin-gonic/gin
|
||||||
|
- **Telegram**: go-telegram-bot-api
|
||||||
|
- **Распознавание**: Yandex SpeechKit + Yandex Object Storage (S3)
|
||||||
|
- **Конвертация**: ffmpeg
|
||||||
- **SQL Builder**: doug-martin/goqu
|
- **SQL Builder**: doug-martin/goqu
|
||||||
- **Миграции БД**: pressly/goose
|
- **Миграции БД**: pressly/goose
|
||||||
- **База данных**: SQLite
|
- **База данных**: SQLite
|
||||||
- **UUID**: google/uuid
|
- **Метрики**: prometheus/client_golang
|
||||||
|
|
||||||
## Установка и запуск
|
## Установка и запуск
|
||||||
|
|
||||||
@@ -25,12 +29,30 @@
|
|||||||
```bash
|
```bash
|
||||||
go mod tidy
|
go mod tidy
|
||||||
```
|
```
|
||||||
3. Запустите приложение:
|
3. Скопируйте образец конфига и заполните его:
|
||||||
```bash
|
```bash
|
||||||
go run main.go
|
cp config.dist.toml config.toml
|
||||||
|
```
|
||||||
|
4. Запустите приложение:
|
||||||
|
```bash
|
||||||
|
go run . -c config.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
Сервер запустится на порту 8080.
|
Сервер запустится на порту из `[server] port`, по умолчанию 8080. Нужен
|
||||||
|
установленный `ffmpeg`.
|
||||||
|
|
||||||
|
### Белый список Telegram
|
||||||
|
|
||||||
|
Бот отвечает только тем, кто перечислен в `[server] users_while_list`. В
|
||||||
|
`config.dist.toml` этого ключа нет — добавьте его сами:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server]
|
||||||
|
users_while_list = ["@username"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Значения сверяются со строкой автора сообщения из Telegram (`@username` либо имя
|
||||||
|
с фамилией), а не с числовым id.
|
||||||
|
|
||||||
## Деплой
|
## Деплой
|
||||||
|
|
||||||
@@ -46,9 +68,9 @@ inv pl -- transcriber
|
|||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
### POST /api/transcribe
|
### POST /api/audio
|
||||||
|
|
||||||
Загружает аудиофайл и создает задачу на расшифровку.
|
Загружает аудиофайл и создает задачу на расшифровку. Отвечает `201 Created`.
|
||||||
|
|
||||||
**Параметры:**
|
**Параметры:**
|
||||||
- `audio` (form-data) - аудиофайл для расшифровки
|
- `audio` (form-data) - аудиофайл для расшифровки
|
||||||
@@ -56,7 +78,7 @@ inv pl -- transcriber
|
|||||||
**Пример запроса:**
|
**Пример запроса:**
|
||||||
```bash
|
```bash
|
||||||
curl -X POST \
|
curl -X POST \
|
||||||
http://localhost:8080/api/transcribe \
|
http://localhost:8080/api/audio \
|
||||||
-F "audio=@/path/to/your/audio.mp3"
|
-F "audio=@/path/to/your/audio.mp3"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -64,31 +86,34 @@ curl -X POST \
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
"file_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
"status": "created"
|
||||||
"status": "pending"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /api/transcribe/:id
|
### GET /api/status/:id
|
||||||
|
|
||||||
Получает статус задачи расшифровки по ID.
|
Получает статус задачи расшифровки по ID. Поле `transcription_text` появляется,
|
||||||
|
когда задача перешла в `done`.
|
||||||
|
|
||||||
**Пример запроса:**
|
**Пример запроса:**
|
||||||
```bash
|
```bash
|
||||||
curl http://localhost:8080/api/transcribe/550e8400-e29b-41d4-a716-446655440000
|
curl http://localhost:8080/api/status/550e8400-e29b-41d4-a716-446655440000
|
||||||
```
|
```
|
||||||
|
|
||||||
**Ответ:**
|
**Ответ:**
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
"job_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
"status": "pending",
|
"status": "done",
|
||||||
"file_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
||||||
"created_at": "2024-01-01T12:00:00Z",
|
"created_at": "2024-01-01T12:00:00Z",
|
||||||
"updated_at": "2024-01-01T12:00:00Z"
|
"transcription_text": "расшифрованный текст"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### GET /metrics
|
||||||
|
|
||||||
|
Метрики Prometheus с префиксом `transcriber_`.
|
||||||
|
|
||||||
### GET /health
|
### GET /health
|
||||||
|
|
||||||
Проверка работоспособности сервиса.
|
Проверка работоспособности сервиса.
|
||||||
@@ -101,51 +126,72 @@ curl http://localhost:8080/api/transcribe/550e8400-e29b-41d4-a716-446655440000
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Статусы задач
|
## Состояния задач
|
||||||
|
|
||||||
- `pending` - задача создана, ожидает обработки
|
- `created` - задача создана, файл сохранён, ждёт конвертации
|
||||||
- `processing` - задача выполняется
|
- `converted` - файл сконвертирован в ogg, ждёт отправки на распознавание
|
||||||
- `completed` - задача завершена успешно
|
- `transcribe` - распознавание запущено в Yandex SpeechKit, ждём результата
|
||||||
|
- `done` - задача завершена успешно
|
||||||
- `failed` - задача завершена с ошибкой
|
- `failed` - задача завершена с ошибкой
|
||||||
|
|
||||||
|
Задачи двигают три фоновых воркера: `conversion_worker`, `transcribe_worker` и
|
||||||
|
`check_worker`. Каждый опрашивает базу раз в секунду и делает один шаг.
|
||||||
|
|
||||||
## Структура проекта
|
## Структура проекта
|
||||||
|
|
||||||
```
|
```
|
||||||
transcriber/
|
transcriber/
|
||||||
├── main.go # Точка входа приложения
|
├── main.go # Точка входа: конфиг, миграции, сборка зависимостей, запуск
|
||||||
├── go.mod # Зависимости Go
|
├── internal/
|
||||||
├── models/
|
│ ├── entity/ # Модели: задача, файл, результат распознавания
|
||||||
│ └── models.go # Модели данных
|
│ ├── contract/ # Интерфейсы адаптеров и репозиториев, типы ошибок
|
||||||
├── database/
|
│ ├── config/ # Разбор config.toml
|
||||||
│ └── database.go # Слой работы с БД
|
│ ├── metrics/ # Метрики Prometheus
|
||||||
├── handlers/
|
│ ├── service/ # Конвейер расшифровки
|
||||||
│ └── transcribe.go # HTTP обработчики
|
│ ├── controller/
|
||||||
├── migrations/
|
│ │ ├── http/ # HTTP-обработчики
|
||||||
│ ├── 001_create_files_table.sql
|
│ │ ├── tg/ # Telegram-бот
|
||||||
│ └── 002_create_transcribe_jobs_table.sql
|
│ │ └── worker/ # Фоновые воркеры
|
||||||
|
│ └── adapter/
|
||||||
|
│ ├── converter/ffmpeg/ # Конвертация аудио
|
||||||
|
│ ├── metaviewer/ffmpeg/ # Длительность аудио
|
||||||
|
│ ├── recognizer/yandex/ # SpeechKit + Object Storage
|
||||||
|
│ ├── telegram/ # Отправка сообщений
|
||||||
|
│ └── repo/sqlite/ # Репозитории
|
||||||
|
├── migrations/ # Миграции goose, вшиты в бинарник через go:embed
|
||||||
└── data/
|
└── data/
|
||||||
├── files/ # Директория для сохранения файлов
|
├── files/ # Директория для сохранения файлов
|
||||||
└── transcriber.db # SQLite база данных (создается автоматически)
|
└── transcriber.db # SQLite база данных (создается автоматически)
|
||||||
```
|
```
|
||||||
|
|
||||||
## База данных
|
## База данных
|
||||||
|
|
||||||
### Таблица `files`
|
### Таблица `files`
|
||||||
- `id` (TEXT) - UUID файла
|
- `id` (TEXT) - UUID файла
|
||||||
- `type` (TEXT) - MIME-тип файла
|
- `storage` (TEXT) - где лежит файл: `local` или `s3`
|
||||||
|
- `file_name` (TEXT) - имя файла в хранилище
|
||||||
- `size` (INTEGER) - размер файла в байтах
|
- `size` (INTEGER) - размер файла в байтах
|
||||||
- `created_at` (DATETIME) - время создания
|
- `created_at` (DATETIME) - время создания
|
||||||
|
|
||||||
### Таблица `transcribe_jobs`
|
### Таблица `transcribe_jobs`
|
||||||
- `id` (TEXT) - UUID задачи
|
- `id` (TEXT) - UUID задачи
|
||||||
- `status` (TEXT) - статус задачи
|
- `state` (TEXT) - состояние задачи
|
||||||
- `file_id` (TEXT) - ссылка на файл
|
- `source` (TEXT) - откуда пришла задача: `api`, `telegram` или `unknown`
|
||||||
- `created_at` (DATETIME) - время создания
|
- `file_id` (TEXT) - ссылка на текущий файл задачи
|
||||||
- `updated_at` (DATETIME) - время последнего обновления
|
- `delay_time` (DATETIME) - не брать задачу раньше этого времени
|
||||||
|
- `acquisition_id`, `acquire_time` - захват задачи воркером
|
||||||
|
- `recognition_op_id` (TEXT) - ID операции распознавания в Yandex Cloud
|
||||||
|
- `transcription_text` (TEXT) - результат распознавания
|
||||||
|
- `is_error` (BOOLEAN), `error_text` (TEXT) - признак и текст ошибки
|
||||||
|
- `tg_chat_id`, `tg_reply_message_id` - куда отправить результат в Telegram
|
||||||
|
- `created_at`, `updated_at` (DATETIME) - времена создания и обновления
|
||||||
|
|
||||||
## Разработка
|
## Разработка
|
||||||
|
|
||||||
Для добавления новых миграций используйте goose:
|
Миграции накатываются автоматически при старте сервиса — они вшиты в бинарник
|
||||||
|
через `//go:embed`. Достаточно положить новый файл в `migrations/`.
|
||||||
|
|
||||||
|
Создать файл миграции и накатить или откатить её вручную:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Создание новой миграции
|
# Создание новой миграции
|
||||||
@@ -156,3 +202,14 @@ goose -dir migrations sqlite3 data/transcriber.db up
|
|||||||
|
|
||||||
# Откат миграций
|
# Откат миграций
|
||||||
goose -dir migrations sqlite3 data/transcriber.db down
|
goose -dir migrations sqlite3 data/transcriber.db down
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверки перед коммитом:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go build ./...
|
||||||
|
go vet ./...
|
||||||
|
gofmt -l .
|
||||||
|
go test ./...
|
||||||
|
golangci-lint run
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user