♻️ Move the chat part of the Sphere service to the Messager service

This commit is contained in:
2026-01-01 22:09:08 +08:00
parent c503083df7
commit ab37bbc7b0
50 changed files with 3042 additions and 611 deletions

View File

@@ -0,0 +1,172 @@
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<RewindEvent> 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<SnAccount>? 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<Guid>();
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<string, object?>
{
["account"] = account,
["duration"] = room.TotalDuration,
}
)
.ToList();
var data = new Dictionary<string, object?>
{
["most_messaged_chat"] = mostMessagedChatInfo is not null
? new Dictionary<string, object?>
{
["chat"] = mostMessagedChat,
["message_counts"] = mostMessagedChatInfo.MessageCount,
}
: null,
["most_messaged_direct_chat"] = mostMessagedDirectChatInfo is not null
? new Dictionary<string, object?>
{
["chat"] = mostMessagedDirectChat,
["message_counts"] = mostMessagedDirectChatInfo.MessageCount,
}
: null,
["most_called_chat"] = new Dictionary<string, object?>
{
["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),
};
}
}