Добавить remote-просмотр исходника карточки

RPC TelegramService.ReadSource + ISessionClient.GetMessageAsync, порт ITelegramGateway.ReadSourceAsync и провайдер TelegramSourceContentProvider в ядре; GET /api/cards/{id}/source догружает исходник у сервиса-владельца, в UI — кнопка «Обновить из источника». Медиа-посты пропускаются.
This commit is contained in:
Rustam Khalimov
2026-09-11 17:22:06 +03:00
parent c06ee1cf79
commit db4455a554
30 changed files with 742 additions and 10 deletions
@@ -264,6 +264,36 @@ public sealed class WTelegramSessionClient : ISessionClient
return items;
}
/// <inheritdoc />
public async Task<TelegramMessage?> GetMessageAsync(
string dialogId,
long msgId,
CancellationToken cancellationToken)
{
// Диалог обязан быть доступен аккаунту (иначе INVALID_ARGUMENT, как в истории).
await ResolvePeerAsync(dialogId, cancellationToken).ConfigureAwait(false);
// messages.getMessages по голому id (InputMessageID) — без привязки к peer.
InputMessage[] ids = [new InputMessageID { id = (int)msgId }];
Messages_MessagesBase result = await RunTlCallAsync(
() => _client.Messages_GetMessages(ids),
cancellationToken).ConfigureAwait(false);
(MessageBase[] messages, Dictionary<long, ChatBase> chats, Dictionary<long, User> users) = UnpackMessages(result);
CacheEntities(chats.Values, users.Values);
foreach (MessageBase message in messages)
{
TelegramMessage? mapped = TlMessageMapper.ToMessage(message, chats, users);
if (mapped is not null && mapped.Id == msgId && mapped.DialogId == dialogId)
{
return mapped;
}
}
// Медиа без текста или удалённое сообщение — не найдено (found=false).
return null;
}
/// <inheritdoc />
public async Task MarkReadAsync(string dialogId, CancellationToken cancellationToken)
{