From bc2e87c56fa2f8cd20b3ebba22805153ac9628b2 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Wed, 3 Sep 2025 00:32:44 +0800 Subject: [PATCH] :lipstick: Optimized activity feed --- .../Activity/ActivityService.cs | 125 ++++++++++-------- 1 file changed, 70 insertions(+), 55 deletions(-) diff --git a/DysonNetwork.Sphere/Activity/ActivityService.cs b/DysonNetwork.Sphere/Activity/ActivityService.cs index 0aeaed9..bfc41ac 100644 --- a/DysonNetwork.Sphere/Activity/ActivityService.cs +++ b/DysonNetwork.Sphere/Activity/ActivityService.cs @@ -34,22 +34,6 @@ public class ActivityService( var activities = new List(); debugInclude ??= new HashSet(); - // Add realm discovery if needed - if (cursor == null && (debugInclude.Contains("realms") || Random.Shared.NextDouble() < 0.2)) - { - var realmActivity = await GetRealmDiscoveryActivity(); - if (realmActivity != null) - activities.Add(realmActivity); - } - - // Add article discovery if needed - if (debugInclude.Contains("articles") || Random.Shared.NextDouble() < 0.2) - { - var articleActivity = await GetArticleDiscoveryActivity(); - if (articleActivity != null) - activities.Add(articleActivity); - } - // Get and process posts var postsQuery = db.Posts .Include(e => e.RepliedPost) @@ -66,8 +50,20 @@ public class ActivityService( var posts = await GetAndProcessPosts(postsQuery); posts = RankPosts(posts, take); - // Add posts to activities - activities.AddRange(posts.Select(post => post.ToActivity())); + var interleaved = new List(); + var random = new Random(); + foreach (var post in posts) + { + // Randomly insert a discovery activity before some posts + if (random.NextDouble() < 0.15) + { + var discovery = await MaybeGetDiscoveryActivity(debugInclude, cursor: cursor); + if (discovery != null) + interleaved.Add(discovery); + } + interleaved.Add(post.ToActivity()); + } + activities.AddRange(interleaved); if (activities.Count == 0) activities.Add(Activity.Empty()); @@ -83,7 +79,7 @@ public class ActivityService( HashSet? debugInclude = null) { var activities = new List(); - debugInclude ??= []; + debugInclude ??= new HashSet(); // Get user's friends and publishers var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest @@ -93,34 +89,6 @@ public class ActivityService( var userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList(); var userPublishers = await pub.GetUserPublishers(Guid.Parse(currentUser.Id)); - // Add discovery activities if no specific filter is applied - if (string.IsNullOrEmpty(filter)) - { - // Add realm discovery if needed - if (cursor == null && (debugInclude.Contains("realms") || Random.Shared.NextDouble() < 0.2)) - { - var realmActivity = await GetRealmDiscoveryActivity(); - if (realmActivity != null) - activities.Add(realmActivity); - } - - // Add publisher discovery if needed - if (cursor == null && (debugInclude.Contains("publishers") || Random.Shared.NextDouble() < 0.2)) - { - var publisherActivity = await GetPublisherDiscoveryActivity(); - if (publisherActivity != null) - activities.Add(publisherActivity); - } - - // Add article discovery if needed - if (debugInclude.Contains("articles") || Random.Shared.NextDouble() < 0.2) - { - var articleActivity = await GetArticleDiscoveryActivity(); - if (articleActivity != null) - activities.Add(articleActivity); - } - } - // Get publishers based on filter var filteredPublishers = await GetFilteredPublishers(filter, currentUser, userFriends); var filteredPublishersId = filteredPublishers?.Select(e => e.Id).ToList(); @@ -149,8 +117,19 @@ public class ActivityService( posts = RankPosts(posts, take); - // Add posts to activities - activities.AddRange(posts.Select(post => post.ToActivity())); + var interleaved = new List(); + var random = new Random(); + foreach (var post in posts) + { + if (random.NextDouble() < 0.15) + { + var discovery = await MaybeGetDiscoveryActivity(debugInclude, cursor: cursor); + if (discovery != null) + interleaved.Add(discovery); + } + interleaved.Add(post.ToActivity()); + } + activities.AddRange(interleaved); if (activities.Count == 0) activities.Add(Activity.Empty()); @@ -158,11 +137,26 @@ public class ActivityService( return activities; } + private async Task MaybeGetDiscoveryActivity(HashSet debugInclude, Instant? cursor) + { + if (cursor != null) return null; + var options = new List>>(); + if (debugInclude.Contains("realms") || Random.Shared.NextDouble() < 0.2) + options.Add(() => GetRealmDiscoveryActivity()); + if (debugInclude.Contains("publishers") || Random.Shared.NextDouble() < 0.2) + options.Add(() => GetPublisherDiscoveryActivity()); + if (debugInclude.Contains("articles") || Random.Shared.NextDouble() < 0.2) + options.Add(() => GetArticleDiscoveryActivity()); + if (debugInclude.Contains("shuffledPosts") || Random.Shared.NextDouble() < 0.2) + options.Add(() => GetShuffledPostsActivity()); + if (options.Count == 0) return null; + var random = new Random(); + var pick = options[random.Next(options.Count)]; + return await pick(); + } + private static List RankPosts(List posts, int take) { - // TODO: This feature is disabled for now - // Uncomment and implement when ready - /* var now = SystemClock.Instance.GetCurrentInstant(); return posts .Select(p => new { Post = p, Rank = CalculateHotRank(p, now) }) @@ -170,8 +164,7 @@ public class ActivityService( .Select(x => x.Post) .Take(take) .ToList(); - */ - return posts.Take(take).ToList(); + // return posts.Take(take).ToList(); } private async Task> GetPopularPublishers(int take) @@ -215,9 +208,31 @@ public class ActivityService( : null; } + private async Task GetShuffledPostsActivity(int count = 5) + { + var posts = await db.Posts + .Include(p => p.Categories) + .Include(p => p.Tags) + .Include(p => p.Realm) + .Where(p => p.RepliedPostId == null) + .OrderBy(_ => EF.Functions.Random()) + .Take(count) + .ToListAsync(); + + if (posts.Count == 0) + return null; + + return new DiscoveryActivity(posts.Select(x => new DiscoveryItem("post", x)).ToList()).ToActivity(); + } + private async Task GetArticleDiscoveryActivity(int count = 5, int feedSampleSize = 10) { + var now = SystemClock.Instance.GetCurrentInstant(); + var today = now.InZone(DateTimeZone.Utc).Date; + var todayBegin = today.AtStartOfDayInZone(DateTimeZone.Utc).ToInstant(); + var todayEnd = today.PlusDays(1).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant(); var recentFeedIds = await db.WebArticles + .Where(a => a.CreatedAt >= todayBegin && a.CreatedAt < todayEnd) .GroupBy(a => a.FeedId) .OrderByDescending(g => g.Max(a => a.PublishedAt)) .Take(feedSampleSize) @@ -288,7 +303,7 @@ public class ActivityService( .OrderByDescending(p => p.PublishedAt) .AsQueryable(); - if (filteredPublishersId != null && filteredPublishersId.Any()) + if (filteredPublishersId != null && filteredPublishersId.Count != 0) query = query.Where(p => filteredPublishersId.Contains(p.PublisherId)); if (userRealms == null) query = query.Where(p => p.Realm == null || p.Realm.IsPublic);