From 2b259157903495b14685ef701dc8704fa3816a9c Mon Sep 17 00:00:00 2001 From: Rustam Khalimov Date: Fri, 11 Sep 2026 16:33:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D1=84=D0=B8=D0=BA=D1=81=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D0=B2=D0=BE=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=81=D1=8B=20=D0=BF=D0=BE=20=D0=B2=D0=BB=D0=BE=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=D0=BC=20=D0=B8=D1=81=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Отдельный документ по media→Storage и remote-просмотру исходника: что готово, что требует живого Telegram, предлагаемый план. Ссылки в backlog. --- backlog.md | 2 +- .../2026-09-11-source-attachments-вопросы.md | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 docs/superpowers/specs/2026-09-11-source-attachments-вопросы.md diff --git a/backlog.md b/backlog.md index f10a75f..9f1cc6f 100644 --- a/backlog.md +++ b/backlog.md @@ -58,7 +58,7 @@ | TD-TEST-HARNESS | Историческая гонка `FreeTcpPort` — устранена; следить за новыми хост-хелперами | этап 12, E | P3 | TECHDEBT | | TD-OLD-DOCS | Исторические доки несут старые термины под пометками (переписывать не нужно) | docs sweep | P3 | TECHDEBT | | TD-SOURCE-PROVIDER | `ISourceContentProvider`/`SourceContentResolver` и `GET /api/cards/{id}/source` добавлены; осталось — реализовать провайдеры источников (telegram/local/file) с ленивой догрузкой | generic source 2026-09-11 | P1 | BACKLOG | -| TD-STORE-ATTACH | Выгрузка вложений источника в Storage-сервис адаптером + `TelegramSourceContentProvider` (только telegram-сервис) + рендер `DataRef` в UI | generic source 2026-09-11 | P1 | BACKLOG | +| TD-STORE-ATTACH | Выгрузка вложений источника в Storage-сервис адаптером + `TelegramSourceContentProvider` (только telegram-сервис) + рендер `DataRef` данными. Требует живого Telegram API (см. `docs/superpowers/specs/2026-09-11-source-attachments-вопросы.md`) | generic source 2026-09-11 | P1 | BACKLOG | | TD-TG-CORE-SPLIT | Перенести оставшуюся Telegram-специфику ядра (`TelegramStore`, таблицы `Dialogs`/`TgMessages`, Discovery) в telegram-сервис. **Сделано (2026-09-11):** входящий поток переведён на generic `sources.proto`/`PushSource`, `PushMessage` удалён, приём в ядре generic (`SourceIngressGrpcService`). Осталось: каталог (`SyncDialogs`/`ReportStatus`), `TelegramStore`, Discovery | generic source 2026-09-11 | P2 | TECHDEBT | | TD-SOURCE-CONTACTS | Квалификатор контактов знает форматы профилей (t.me/`@handle`) — вынести в расширяемые правила источников | generic source 2026-09-11 | P3 | BACKLOG | | TD-APIMAP-COUNT | Ручной подсчёт числа операторских ручек в `api-map` (может расходиться с группировкой) | docs sweep | P3 | TECHDEBT | diff --git a/docs/superpowers/specs/2026-09-11-source-attachments-вопросы.md b/docs/superpowers/specs/2026-09-11-source-attachments-вопросы.md new file mode 100644 index 0000000..22ddbf3 --- /dev/null +++ b/docs/superpowers/specs/2026-09-11-source-attachments-вопросы.md @@ -0,0 +1,61 @@ +# Открытые вопросы: вложения источников (media → Storage) и просмотр исходника + +Дата: 2026-09-11. Статус: часть требует живого внешнего контура (Telegram API) для проверки. + +## 1. Что уже готово (не требует решений) + +- Единый контракт источника несёт вложения: `SourceContent.Data: IReadOnlyList` — + ссылки на объекты 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 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, рендер) реализована.