- убраны клиент бота, транспорт обновлений, отправитель сообщений, сборка входа при старте, секция настроек и зависимость go-telegram-bot-api; из конвейера ушла доставка ответа отправителю — исход виден опросом готовности. Колонки адресата и значение источника остались в схеме: применённые шаги не переписываются - шаг 202608140003 запрещает пустого владельца у аудиозаписи и у файла; существующие строки он не проверяет, и это принято сознательно — искать их надо запросом до выкладки - ревью нашло два пред-существующих дефекта, оба закрыты: пустой второй ответ распознавателя стирал сохранённую расшифровку, а пустая расшифровка перестала быть заметной вместе с убранной доставкой. Попутно поднят golang.org/x/image до v0.45.0 — красный шаг vulns, воспроизводился и на чистом master
61 lines
3.9 KiB
Go
61 lines
3.9 KiB
Go
package contract
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
|
|
"git.vakhrushev.me/av/transcriber/internal/entity"
|
|
)
|
|
|
|
type AudioInfo struct {
|
|
Seconds int // Длина аудиофайла в секундах
|
|
}
|
|
|
|
// Контекст первым доводом несут все интерфейсы, за которыми стоит внешний
|
|
// собеседник — процесс `ffmpeg`, S3, SpeechKit. Он здесь не украшение: остановка
|
|
// сервиса обязана доходить до чужой работы, а не оставлять её сиротой. Без него
|
|
// конвертация шестичасовой записи переживает остановку воркера, а запрос к
|
|
// платному распознаванию висит до собственного таймаута библиотеки.
|
|
type AudioMetaViewer interface {
|
|
GetInfo(ctx context.Context, src string) (*AudioInfo, error)
|
|
}
|
|
|
|
type AudioFileConverter interface {
|
|
Convert(ctx context.Context, src, dest string) error
|
|
}
|
|
|
|
// AudioRecognizer — внешний распознаватель речи.
|
|
//
|
|
// Заливка и отправка операции разделены: это два обращения с разной ценой
|
|
// повтора. Повтор заливки бесплатен и кладёт объект под тем же ключом; повтор
|
|
// отправки оплачивается наружу, и шаг обязан проверить сделанное прежде, чем
|
|
// платить второй раз.
|
|
//
|
|
// Результат отдаётся **доменным** — реплики со временем, плоский текст и байты
|
|
// ответа на хранение, — а не сырым форматом провайдера: разбор потока это
|
|
// обязанность адаптера, и ни один шаг конвейера не знает, каким потоком и какими
|
|
// полями провайдер отвечает.
|
|
type AudioRecognizer interface {
|
|
// Provider — имя провайдера, под которым сохраняется попытка.
|
|
Provider() string
|
|
// Model — имя модели распознавания.
|
|
Model() string
|
|
// Upload кладёт аудио туда, откуда провайдер его прочитает, и отдаёт адрес.
|
|
// Повтор кладёт объект под тем же ключом и оплаты не стоит.
|
|
Upload(ctx context.Context, file io.Reader, objectKey string) (sourceURI string, err error)
|
|
// ObjectExists отвечает, лежит ли объект нужного размера. По нему шаг решает,
|
|
// повторять ли заливку; сверка содержимого хешем ненадёжна — признак
|
|
// целостности у составного объекта не равен отпечатку содержимого.
|
|
ObjectExists(ctx context.Context, objectKey string, size int64) (bool, error)
|
|
// Submit заводит операцию распознавания по адресу аудио. Оплачивается
|
|
// наружу.
|
|
Submit(ctx context.Context, sourceURI string) (operationID string, err error)
|
|
// CheckStatus опрашивает операцию.
|
|
CheckStatus(ctx context.Context, operationID string) (*entity.RecognitionResult, error)
|
|
// Fetch забирает готовый результат и отдаёт его доменным.
|
|
Fetch(ctx context.Context, operationID string) (*entity.RecognitionOutcome, error)
|
|
// Parse строит доменный результат из **сохранённого** ответа провайдера, не
|
|
// обращаясь к нему. По нему архив пересчитывается без единого рубля.
|
|
Parse(raw []byte) (*entity.RecognitionOutcome, error)
|
|
}
|