using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Knot.Contracts.Messaging.Application.Abstractions; using Knot.Contracts.Messaging.Domain; using Knot.Contracts.Conversations.Application.Abstractions; using Knot.Modules.Conversations.Application.DTOs; using Knot.Contracts.Conversations.Domain; using Knot.Shared.Kernel; using MediatR; namespace Knot.Modules.Conversations.Application.Messages.GetSharedMedia; public record GetSharedMediaQuery(Guid UserId, Guid ChatId, string? Type) : IQuery>; internal sealed class GetSharedMediaQueryHandler : IQueryHandler> { private readonly IMessageRepository _messageRepository; private readonly IUserDisplayNameProvider _userProvider; private readonly IChatRepository _chatRepository; public GetSharedMediaQueryHandler(IMessageRepository messageRepository, IUserDisplayNameProvider userProvider, IChatRepository chatRepository) { _messageRepository = messageRepository; _userProvider = userProvider; _chatRepository = chatRepository; } public async Task>> Handle(GetSharedMediaQuery request, CancellationToken cancellationToken) { var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken); if (chat == null || !chat.Members.Any(member => member.UserId == request.UserId)) { return Result.Failure>(ChatErrors.ChatsForbidden); } var messages = await _messageRepository.GetChatMessagesAsync(request.ChatId, ChatConstants.MaxSharedMediaQueryLimit, 0, cancellationToken); messages = messages.Where(message => !message.IsDeletedForUser(request.UserId)).ToList(); var result = new List(); var filterType = request.Type?.ToLower(); var userIds = messages.Select(message => message.SenderId).Distinct(); var senders = await _userProvider.GetUsersInfoAsync(userIds, cancellationToken); foreach (var message in messages) { var mediaMessage = message as MediaMessage; var textMessage = message as TextMessage; var storyMessage = message as StoryMessage; if (filterType == "links") { var messageContent = message.Content; var linkRegex = new Regex(@"https?://[^\s]+", RegexOptions.IgnoreCase); var contentLinks = !string.IsNullOrEmpty(messageContent) ? linkRegex.Matches(messageContent).Select(match => match.Value).ToList() : new List(); var mediaLinks = (mediaMessage?.Media ?? Enumerable.Empty()) .Where(media => media.Type?.ToString().ToLower() == "link" && !string.IsNullOrEmpty(media.Url)) .Select(media => media.Url!) .ToList(); var allLinks = contentLinks.Concat(mediaLinks).Distinct().ToList(); if (allLinks.Any()) { senders.TryGetValue(message.SenderId, out var sender); result.Add(new SharedMediaDto( message.Id, messageContent, message.CreatedAt, allLinks, sender != null ? new MessageSenderDto(sender.Id, sender.Username, sender.DisplayName, sender.Avatar) : null, null, null, null, null, null, null, null, null )); } continue; } var messageMedia = mediaMessage?.Media; if (messageMedia == null || !messageMedia.Any()) { continue; } var filteredMedia = messageMedia.Where(media => { var mType = media.Type?.ToLower() ?? "file"; var filename = media.Filename?.ToLower() ?? ""; var url = media.Url?.ToLower() ?? ""; var isGif = mType == "gif" || (mType == "image" && (filename.EndsWith(".mp4") || filename.EndsWith(".gif") || url.EndsWith(".gif") || filename.Contains("gif"))) || (mType == "video" && (filename.Contains("animation") || filename.Contains("gif"))); if (filterType == "gifs") return isGif; if (filterType == "media") return (mType == "image" || mType == "video") && !isGif; if (filterType == "files") return (mType == "file" || mType == "audio") && !isGif && mType != "image" && mType != "video"; if (filterType == "links") return mType == "link"; return true; }).ToList(); if (filteredMedia.Any()) { senders.TryGetValue(message.SenderId, out var sender); result.Add(new SharedMediaDto( message.Id, message.Content, message.CreatedAt, null, sender != null ? new MessageSenderDto(sender.Id, sender.Username, sender.DisplayName, sender.Avatar) : null, message.ReplyToId, textMessage?.Quote, storyMessage?.StoryId, storyMessage?.StoryMediaUrl, storyMessage?.StoryMediaType, message.IsEdited, message.Type, filteredMedia.Select(media => new MediaDto(media.Id, media.Type, media.Url, media.Filename, media.Size, media.Duration)).ToList() )); } } return Result.Success(result.OrderByDescending(x => x.CreatedAt).ToList()); } }