Files
Deal/docs/superpowers/specs/2026-09-11-source-attachments-вопросы.md
T
Rustam Khalimov 27c7831910
ci / build-test (push) Canceled after 0s
Deal — единая кодовая база
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/
Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue,
контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер),
Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог).

Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0,
тесты 1340/130/52/38/9 зелёные.
2026-09-11 23:56:47 +03:00

87 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Открытые вопросы: вложения источников (media → Storage) и просмотр исходника
Дата: 2026-09-11. Статус: решения владельца получены (см. §0).
## 0. Решения владельца (2026-09-11)
- **А) Медиа-посты пропускаем.** Сообщения без текста (только медиа/вложение) в систему не попадают.
Извлечение вложений Telegram и выгрузка их в Storage не делаются. Generic-контракт по-прежнему умеет
нести `DataRef` — этим смогут пользоваться другие источники (файл/диск/таблица) и ручные вложения карточки.
- **Б) Проверка без живого Telegram** — реализуем с юнит-тестами на фейковой сессии/фейковом Storage,
без реального API.
- **В)** Объяснение термина — в §1.5. **Решение: делаем.** Реализован remote-просмотр: `TelegramService.ReadSource`,
`ITelegramGateway.ReadSourceAsync`, `TelegramSourceContentProvider` (Kind=telegram) в ядре,
`GET /api/cards/{id}/source` и кнопка «Обновить из источника» в подробной карточке.
## 1.5. Что такое «remote-просмотр исходника»
Карточка хранит **ссылку на источник** (`SourceRef`) и **содержимое** (`SourceContent`). Содержимое попадает
в карточку в момент приёма. «Просмотр исходника» — это возможность по кнопке догрузить/показать **оригинальное
сообщение у источника** (то, что было в канале/письме/строке), если контент в карточке устарел или урезан.
Сейчас содержимое уже отдаётся в `CardDto.content` и через `GET /api/cards/{id}/source`. Для локальных
источников этого достаточно. Для **внешних** источников (например Telegram) данные лежат не в ядре, а в
сервисе-владельце; чтобы их догрузить, ядру нужен провайдер `ISourceContentProvider` для `kind`, который
ходит по gRPC к сервису-владельцу (условный RPC `ReadSource(dialogId, msgId)`) и возвращает исходный текст/медиа.
Это и есть «remote-просмотр» — расширение extension-point, которое не требуется до появления реальной
необходимости (напр. если карточки хранят урезанный текст или нужно открыть живой первоисточник).
## 1. Что уже готово (не требует решений)
- Единый контракт источника несёт вложения: `SourceContent.Data: IReadOnlyList<DataRef>`
ссылки на объекты Storage-сервиса (`DataRef.Id/Ref/Kind/MimeType/...`).
- Контракт входящего потока (`src/contracts/sources.proto`, `PushSource`) передаёт
`DataRefProto`/`ContactRefProto` — источник может прислать вложения сразу со ссылками.
- Storage-сервис (`src/storage-service/Deal.Storage`, `storage.proto`) умеет `Upload/Download/Stat/Delete`,
сам определяет `kind`/`mimeType`/размеры (контент-снифинг), бэкенд — MinIO.
- Ядро хранит `SourceContent` карточки (в т.ч. `Data`) и отдаёт его в `CardDto.content` и через
`GET /api/cards/{id}/source` (extension-point `ISourceContentProvider` + `SourceContentResolver`).
- Фронт рендерит вложения: `SourceContentView.vue` (image/video/audio/document/archive по `kind`),
ссылки и контакты — списками.
## 2. Проблемная часть (требует живого Telegram)
Извлечение и выгрузка медиа из Telegram не проверяемы офлайн:
1. **Медиа-сообщения сейчас отбрасываются.** `TlMessageMapper.ToMessage`
(`src/telegram-service/Deal.Telegram/Telegram/TlMessageMapper.cs`) принимает только `Message`
с непустым `message` (текстом). Посты с одним вложением и подписью (`media` + `caption`) не попадают
в поток вообще. Нужно: определять `Message.media`, читать `caption`, тип/размеры/длительность.
2. **Скачивание и выгрузка.** Требуется `client.DownloadMedia(...)` (WTelegram) → поток →
`StorageService.Upload(meta + data)``DataRefProto`. В telegram-сервисе нет gRPC-клиента Storage
и соответствующей конфигурации в compose (endpoint/токен). Проверить можно только с реальным
Telegram-аккаунтом и живым MinIO.
3. **Подпись без текста.** Даже если вложение извлечено, в посте может не быть текста: нужен ли такой
пост «карточкой» (сейчас `PipelineIngestService` пропускает записи без `Content.Text`)? Предлагается
принимать запись, если есть текст **или** вложения/ссылки/контакты, а классификацию медиа-онли
строить по подписи (`caption`) и метаданным. Требуется подтверждение продуктовой логики.
4. **Просмотр исходника из другого контура.** «Открыть исходник» для remote-источников (Telegram — это
лишь один из них) требует провайдера `ISourceContentProvider`, который ходит по gRPC к сервису-владельцу
источника (новый RPC, например `ReadSource(dialogId, msgId)`), возвращая текст/медиа. Это тоже
живой Telegram.
## 3. Предлагаемый план (после подтверждения)
1. `TelegramMessage` расширить моделью `TelegramAttachment` (caption, fileName, mimeType, size, width,
height, durationSec, `Task<Stream> Open(cancellationToken)`), заполнять в `TlMessageMapper` из
`Message.media`/`Document`/`Photo`.
2. В telegram-сервисе добавить `StorageClient` (gRPC, `Deal.Grpc.Storage`) + `SourceAttachmentUploader`:
загрузка каждого вложения → `DataRefProto`.
3. `DialogProtoMapper.ToSourceRequest(message, dataRefs)` — прокинуть `content.data` и `caption`.
4. `PipelineIngestService`: принимать запись при непустом тексте **или** непустых `Data`/`Links`/`Contacts`
(нужно продуктовое решение по п.2.3).
5. `ISourceContentProvider` для `kind="telegram"` — gRPC-провайдер к telegram-сервису (RPC `ReadSource`).
6. Настройки: endpoint/токен Storage в `deploy/compose.dev.yml`/`compose.prod.yml` для telegram-сервиса.
## 4. Что нужно от владельца
- А) Делать ли медиа-сообщения без текста карточками (по подписи/метаданным), или пропускать?
- Б) Для проверки вложений нужны живые Telegram api_id/api_hash и работающий MinIO — будет ли прогон
на вашей стороне, или реализуем «слепо» с юнит-тестами на фейковой сессии и фейковом Storage?
- В) Нужен ли remote-просмотр исходника (`ReadSource`) в этом объёме, или достаточно того, что
содержимое хранится в карточке?
Пока эти пункты не закрыты, они вынесены в `backlog.md` (`TD-STORE-ATTACH`, `TD-SOURCE-PROVIDER`),
а generic-часть (контракт, Storage-сервис, хранение, API, рендер) реализована.