using System.Globalization; using DysonNetwork.Messager.Chat; using DysonNetwork.Shared.Models; using DysonNetwork.Shared.Proto; using DysonNetwork.Shared.Registry; using Grpc.Core; using Microsoft.EntityFrameworkCore; using NodaTime; using PostReactionAttitude = DysonNetwork.Shared.Proto.PostReactionAttitude; namespace DysonNetwork.Messager.Rewind; public class MessagerRewindServiceGrpc( AppDatabase db, RemoteAccountService remoteAccounts, ChatRoomService crs ) : RewindService.RewindServiceBase { public override async Task GetRewindEvent( RequestRewindEvent request, ServerCallContext context ) { var accountId = Guid.Parse(request.AccountId); var year = request.Year; var startDate = new LocalDate(year - 1, 12, 26).AtMidnight().InUtc().ToInstant(); var endDate = new LocalDate(year, 12, 26).AtMidnight().InUtc().ToInstant(); // Chat data var messagesQuery = db .ChatMessages.Include(m => m.Sender) .Include(m => m.ChatRoom) .Where(m => m.CreatedAt >= startDate && m.CreatedAt < endDate) .Where(m => m.Sender.AccountId == accountId) .AsQueryable(); var mostMessagedChatInfo = await messagesQuery .Where(m => m.ChatRoom.Type == ChatRoomType.Group) .GroupBy(m => m.ChatRoomId) .OrderByDescending(g => g.Count()) .Select(g => new { ChatRoom = g.First().ChatRoom, MessageCount = g.Count() }) .FirstOrDefaultAsync(); var mostMessagedChat = mostMessagedChatInfo?.ChatRoom; var mostMessagedDirectChatInfo = await messagesQuery .Where(m => m.ChatRoom.Type == ChatRoomType.DirectMessage) .GroupBy(m => m.ChatRoomId) .OrderByDescending(g => g.Count()) .Select(g => new { ChatRoom = g.First().ChatRoom, MessageCount = g.Count() }) .FirstOrDefaultAsync(); var mostMessagedDirectChat = mostMessagedDirectChatInfo is not null ? await crs.LoadDirectMessageMembers(mostMessagedDirectChatInfo.ChatRoom, accountId) : null; // Call data var callQuery = db .ChatRealtimeCall.Include(c => c.Sender) .Include(c => c.Room) .Where(c => c.CreatedAt >= startDate && c.CreatedAt < endDate) .Where(c => c.Sender.AccountId == accountId) .AsQueryable(); var now = SystemClock.Instance.GetCurrentInstant(); var groupCallRecords = await callQuery .Where(c => c.Room.Type == ChatRoomType.Group) .Select(c => new { c.RoomId, c.CreatedAt, c.EndedAt, }) .ToListAsync(); var callDurations = groupCallRecords .Select(c => new { c.RoomId, Duration = (c.EndedAt ?? now).Minus(c.CreatedAt).Seconds }) .ToList(); var mostCalledRoomInfo = callDurations .GroupBy(c => c.RoomId) .Select(g => new { RoomId = g.Key, TotalDuration = g.Sum(c => c.Duration) }) .OrderByDescending(g => g.TotalDuration) .FirstOrDefault(); var mostCalledRoom = mostCalledRoomInfo != null && mostCalledRoomInfo.RoomId != Guid.Empty ? await db.ChatRooms.FindAsync(mostCalledRoomInfo.RoomId) : null; List? mostCalledChatTopMembers = null; if (mostCalledRoom != null) mostCalledChatTopMembers = await crs.GetTopActiveMembers( mostCalledRoom.Id, startDate, endDate ); var directCallRecords = await callQuery .Where(c => c.Room.Type == ChatRoomType.DirectMessage) .Select(c => new { c.RoomId, c.CreatedAt, c.EndedAt, c.Room, }) .ToListAsync(); var directCallDurations = directCallRecords .Select(c => new { c.RoomId, c.Room, Duration = (c.EndedAt ?? now).Minus(c.CreatedAt).Seconds, }) .ToList(); var mostCalledDirectRooms = directCallDurations .GroupBy(c => c.RoomId) .Select(g => new { ChatRoom = g.First().Room, TotalDuration = g.Sum(c => c.Duration) }) .OrderByDescending(g => g.TotalDuration) .Take(3) .ToList(); var accountIds = new List(); foreach (var item in mostCalledDirectRooms) { var room = await crs.LoadDirectMessageMembers(item.ChatRoom, accountId); var otherMember = room.DirectMembers.FirstOrDefault(m => m.AccountId != accountId); if (otherMember != null) accountIds.Add(otherMember.AccountId); } var accounts = await remoteAccounts.GetAccountBatch(accountIds); var mostCalledAccounts = accounts .Zip( mostCalledDirectRooms, (account, room) => new Dictionary { ["account"] = account, ["duration"] = room.TotalDuration, } ) .ToList(); var data = new Dictionary { ["most_messaged_chat"] = mostMessagedChatInfo is not null ? new Dictionary { ["chat"] = mostMessagedChat, ["message_counts"] = mostMessagedChatInfo.MessageCount, } : null, ["most_messaged_direct_chat"] = mostMessagedDirectChatInfo is not null ? new Dictionary { ["chat"] = mostMessagedDirectChat, ["message_counts"] = mostMessagedDirectChatInfo.MessageCount, } : null, ["most_called_chat"] = new Dictionary { ["chat"] = mostCalledRoom, ["duration"] = mostCalledRoomInfo?.TotalDuration, }, ["most_called_chat_top_members"] = mostCalledChatTopMembers, ["most_called_accounts"] = mostCalledAccounts, }; return new RewindEvent { ServiceId = "messager", AccountId = request.AccountId, Data = GrpcTypeHelper.ConvertObjectToByteString(data, withoutIgnore: true), }; } }