using Knot.Modules.Messaging.Application.Abstractions; using Knot.Modules.Messaging.Domain; using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using MediatR; using Knot.Shared.Kernel; using Knot.Modules.Conversations.Application.DTOs; using Knot.Modules.Conversations.Domain; using Knot.Modules.Conversations.Application.Abstractions; 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) { 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 messageMediaColl = message.Media; var mediaLinks = (messageMediaColl ?? Enumerable.Empty()).Where(media => media.Type?.ToString().ToLower() == "link").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 = message.Media; if (messageMedia == null || !messageMedia.Any()) { continue; } var filteredMedia = messageMedia.Where(media => { var mediaType = media.Type?.ToLower() ?? "file"; var isGif = mediaType == "image" && (media.Url.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase) || media.Url.EndsWith(".gif", StringComparison.OrdinalIgnoreCase)); if (filterType == "gifs") { return isGif; } if (filterType == "files") { return mediaType != "image" && mediaType != "video" && mediaType != "link"; } if (filterType == "media") { return mediaType == "image" || mediaType == "video"; } 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, message.Quote, message.StoryId, message.StoryMediaUrl, message.StoryMediaType, message.IsEdited, message.Type, filteredMedia.Select(media => new MediaDto(media.Id, media.Type, media.Url, media.Filename, media.Size)).ToList() )); } } return Result.Success(result.OrderByDescending(x => x.CreatedAt).ToList()); } }