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 зелёные.
8.6 KiB
Открытые вопросы: вложения источников (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-pointISourceContentProvider+SourceContentResolver). - Фронт рендерит вложения:
SourceContentView.vue(image/video/audio/document/archive поkind), ссылки и контакты — списками.
2. Проблемная часть (требует живого Telegram)
Извлечение и выгрузка медиа из Telegram не проверяемы офлайн:
- Медиа-сообщения сейчас отбрасываются.
TlMessageMapper.ToMessage(src/telegram-service/Deal.Telegram/Telegram/TlMessageMapper.cs) принимает толькоMessageс непустымmessage(текстом). Посты с одним вложением и подписью (media+caption) не попадают в поток вообще. Нужно: определятьMessage.media, читатьcaption, тип/размеры/длительность. - Скачивание и выгрузка. Требуется
client.DownloadMedia(...)(WTelegram) → поток →StorageService.Upload(meta + data)→DataRefProto. В telegram-сервисе нет gRPC-клиента Storage и соответствующей конфигурации в compose (endpoint/токен). Проверить можно только с реальным Telegram-аккаунтом и живым MinIO. - Подпись без текста. Даже если вложение извлечено, в посте может не быть текста: нужен ли такой
пост «карточкой» (сейчас
PipelineIngestServiceпропускает записи безContent.Text)? Предлагается принимать запись, если есть текст или вложения/ссылки/контакты, а классификацию медиа-онли строить по подписи (caption) и метаданным. Требуется подтверждение продуктовой логики. - Просмотр исходника из другого контура. «Открыть исходник» для remote-источников (Telegram — это
лишь один из них) требует провайдера
ISourceContentProvider, который ходит по gRPC к сервису-владельцу источника (новый RPC, напримерReadSource(dialogId, msgId)), возвращая текст/медиа. Это тоже живой Telegram.
3. Предлагаемый план (после подтверждения)
TelegramMessageрасширить модельюTelegramAttachment(caption, fileName, mimeType, size, width, height, durationSec,Task<Stream> Open(cancellationToken)), заполнять вTlMessageMapperизMessage.media/Document/Photo.- В telegram-сервисе добавить
StorageClient(gRPC,Deal.Grpc.Storage) +SourceAttachmentUploader: загрузка каждого вложения →DataRefProto. DialogProtoMapper.ToSourceRequest(message, dataRefs)— прокинутьcontent.dataиcaption.PipelineIngestService: принимать запись при непустом тексте или непустыхData/Links/Contacts(нужно продуктовое решение по п.2.3).ISourceContentProviderдляkind="telegram"— gRPC-провайдер к telegram-сервису (RPCReadSource).- Настройки: 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, рендер) реализована.