diff --git a/DysonNetwork.Sphere/Activity/ActivityService.cs b/DysonNetwork.Sphere/Activity/ActivityService.cs index df08abc..0fea3eb 100644 --- a/DysonNetwork.Sphere/Activity/ActivityService.cs +++ b/DysonNetwork.Sphere/Activity/ActivityService.cs @@ -1,4 +1,5 @@ using DysonNetwork.Sphere.Account; +using DysonNetwork.Sphere.Connection.WebReader; using DysonNetwork.Sphere.Discovery; using DysonNetwork.Sphere.Post; using DysonNetwork.Sphere.Publisher; @@ -39,6 +40,31 @@ public class ActivityService( } } + if (debugInclude.Contains("articles") || Random.Shared.NextDouble() < 0.2) + { + var recentArticlesQuery = db.WebArticles + .Take(20); // Get a larger pool for randomization + + // Apply random ordering 50% of the time + if (Random.Shared.NextDouble() < 0.5) + { + recentArticlesQuery = recentArticlesQuery.OrderBy(_ => EF.Functions.Random()); + } + else + { + recentArticlesQuery = recentArticlesQuery.OrderByDescending(a => a.PublishedAt); + } + + var recentArticles = await recentArticlesQuery.Take(5).ToListAsync(); + + if (recentArticles.Count > 0) + { + activities.Add(new DiscoveryActivity( + recentArticles.Select(x => new DiscoveryItem("article", x)).ToList() + ).ToActivity()); + } + } + // Fetch a larger batch of recent posts to rank var postsQuery = db.Posts .Include(e => e.RepliedPost) @@ -115,6 +141,27 @@ public class ActivityService( ).ToActivity()); } } + + if (debugInclude.Contains("articles") || Random.Shared.NextDouble() < 0.2) + { + var recentArticlesQuery = db.WebArticles + .Take(20); // Get a larger pool for randomization + + // Apply random ordering 50% of the time + if (Random.Shared.NextDouble() < 0.5) + recentArticlesQuery = recentArticlesQuery.OrderBy(_ => EF.Functions.Random()); + else + recentArticlesQuery = recentArticlesQuery.OrderByDescending(a => a.PublishedAt); + + var recentArticles = await recentArticlesQuery.Take(5).ToListAsync(); + + if (recentArticles.Count > 0) + { + activities.Add(new DiscoveryActivity( + recentArticles.Select(x => new DiscoveryItem("article", x)).ToList() + ).ToActivity()); + } + } } // Get publishers based on filter diff --git a/DysonNetwork.Sphere/Connection/WebReader/WebArticleController.cs b/DysonNetwork.Sphere/Connection/WebReader/WebArticleController.cs new file mode 100644 index 0000000..ea7f8be --- /dev/null +++ b/DysonNetwork.Sphere/Connection/WebReader/WebArticleController.cs @@ -0,0 +1,82 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace DysonNetwork.Sphere.Connection.WebReader; + +[ApiController] +[Route("/feeds/articles")] +public class WebArticleController(AppDatabase db) : ControllerBase +{ + /// + /// Get a list of recent web articles + /// + /// Maximum number of articles to return + /// Number of articles to skip + /// Optional feed ID to filter by + /// Optional publisher ID to filter by + /// List of web articles + [HttpGet] + public async Task GetArticles( + [FromQuery] int limit = 20, + [FromQuery] int offset = 0, + [FromQuery] Guid? feedId = null, + [FromQuery] Guid? publisherId = null + ) + { + var query = db.WebArticles + .OrderByDescending(a => a.PublishedAt) + .Include(a => a.Feed) + .AsQueryable(); + + if (feedId.HasValue) + query = query.Where(a => a.FeedId == feedId.Value); + if (publisherId.HasValue) + query = query.Where(a => a.Feed.PublisherId == publisherId.Value); + + var totalCount = await query.CountAsync(); + var articles = await query + .Skip(offset) + .Take(limit) + .ToListAsync(); + + Response.Headers["X-Total"] = totalCount.ToString(); + + return Ok(articles); + } + + /// + /// Get a specific web article by ID + /// + /// The article ID + /// The web article + [HttpGet("{id:guid}")] + [ProducesResponseType(404)] + public async Task GetArticle(Guid id) + { + var article = await db.WebArticles + .Include(a => a.Feed) + .FirstOrDefaultAsync(a => a.Id == id); + + if (article == null) + return NotFound(); + + return Ok(article); + } + + /// + /// Get random web articles + /// + /// Maximum number of articles to return + /// List of random web articles + [HttpGet("random")] + public async Task GetRandomArticles([FromQuery] int limit = 5) + { + var articles = await db.WebArticles + .OrderBy(_ => EF.Functions.Random()) + .Include(a => a.Feed) + .Take(limit) + .ToListAsync(); + + return Ok(articles); + } +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Connection/WebReader/WebFeedService.cs b/DysonNetwork.Sphere/Connection/WebReader/WebFeedService.cs index 6547de8..17c540c 100644 --- a/DysonNetwork.Sphere/Connection/WebReader/WebFeedService.cs +++ b/DysonNetwork.Sphere/Connection/WebReader/WebFeedService.cs @@ -112,7 +112,8 @@ public class WebFeedService( { var scrapedArticle = await webReaderService.ScrapeArticleAsync(itemUrl, cancellationToken); preview = scrapedArticle.LinkEmbed; - content = scrapedArticle.Content; + if (scrapedArticle.Content is not null) + content = scrapedArticle.Content; } else {