ci / build-test (push) Canceled after 0s
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 зелёные.
87 lines
8.6 KiB
Markdown
87 lines
8.6 KiB
Markdown
# Открытые вопросы: вложения источников (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, рендер) реализована.
|