Файловая подсистема
Файловая подсистема отвечает за приём, хранение, чтение и отправку файлов пользователей через мессенджеры. Она интегрирована на всех уровнях: от адаптеров каналов до WASM host API.
Типы модели
Входящие файлы
internal/model/file.go — FileRef, FileType
internal/model/input.go — FileInput (implements UserInput)FileInput реализует интерфейс UserInput. Если caption начинается с / — роутится как команда с прикреплёнными файлами. Если caption пустой и нет активного диалога — игнорируется.
type FileInput struct {
Caption string // текст/caption сообщения
Files []FileRef // прикреплённые файлы
}
type FileRef struct {
ID string // UUID в FileStore
Name string // оригинальное имя файла
MIMEType string // MIME-тип
Size int64 // размер в байтах
FileType FileType // photo, document, audio, video, voice, sticker
}Исходящие файлы
internal/model/message.go — FileBlock (implements ContentBlock)FileBlock — блок контента для отправки файлов в сообщении:
type FileBlock struct {
FileRef FileRef
Caption string
}Event data
MessengerTriggerData.Files и CommandRequest.Files — пробрасывают []FileRef от адаптера до плагина. EventResponse.ReplyFiles — файлы, которые плагин хочет отправить обратно.
FileStore
internal/filestore/filestore.go — интерфейс
internal/filestore/s3.go — S3 реализацияОтдельная абстракция (не переиспользует admin BlobStore), т.к. нужны метаданные, TTL и cleanup.
type FileStore interface {
Store(ctx, meta FileMeta, data io.Reader) (FileRef, error)
Get(ctx, id string) (io.ReadCloser, *FileMeta, error)
Meta(ctx, id string) (*FileMeta, error)
Delete(ctx, id string) error
URL(ctx, id string, expiry time.Duration) (string, error)
Cleanup(ctx) (int, error)
}Для эффективного чтения чанков backend может дополнительно реализовать range-capability:
type RangeReader interface {
GetRange(ctx context.Context, id string, offset, length int64) (io.ReadCloser, *FileMeta, error)
}FileMeta
type FileMeta struct {
ID string // генерируется при Store
Name string
MIMEType string
Size int64
FileType model.FileType
PluginID string // кто сохранил ("" для входящих)
CreatedAt time.Time
ExpiresAt *time.Time // TTL (nil = без ограничения)
}S3
Файлы хранятся в S3-совместимом хранилище (AWS S3, MinIO) как два объекта:
<prefix><id>.data — содержимое
<prefix><id>.meta.json — метаданные (JSON)URL()— возвращает presigned GET URL для прямого скачивания из S3Cleanup()— листинг.meta.jsonобъектов, проверкаExpiresAt, удаление просроченных. Запускается горутиной каждый час
Чтение файла из WASM
Начиная с protocol v4 метод ctx.FileRead(...) использует низкоуровневый host ABI file_read_into. Буфер для чтения выделяется самим плагином, а host заполняет его напрямую.
Преимущества этой схемы:
- нет большого blob-ответа через result arena хоста
- меньше копирований при чтении чанков
- S3 backend может использовать
Range-запросы вместо повторного чтения префиксов
Deprecated host ABI
file_read помечен как deprecated и сохранён только для обратной совместимости со старыми WASM-плагинами. На уровне SDK по-прежнему используйте ctx.FileRead(...) и ctx.FileReadAll(...).
Поток данных: входящий файл
Поток данных: исходящий файл
Конфигурация
filestore:
default_ttl: 24h # TTL по умолчанию
max_file_size: 52428800 # 50 МБ
s3:
bucket: my-bucket
region: eu-central-1
endpoint: http://localhost:9000 # MinIO для dev
access_key: minioadmin
secret_key: minioadmin
prefix: files/