using DysonNetwork.Sphere.Account; using Microsoft.EntityFrameworkCore; using NodaTime; namespace DysonNetwork.Sphere.Post; public class PublisherSubscriptionService(AppDatabase db, NotificationService nty) { /// /// Checks if a subscription exists between the account and publisher /// /// The account ID /// The publisher ID /// True if a subscription exists, false otherwise public async Task SubscriptionExistsAsync(long accountId, long publisherId) { return await db.PublisherSubscriptions .AnyAsync(ps => ps.AccountId == accountId && ps.PublisherId == publisherId && ps.Status == SubscriptionStatus.Active); } /// /// Gets a subscription by account and publisher ID /// /// The account ID /// The publisher ID /// The subscription or null if not found public async Task GetSubscriptionAsync(long accountId, long publisherId) { return await db.PublisherSubscriptions .Include(ps => ps.Publisher) .FirstOrDefaultAsync(ps => ps.AccountId == accountId && ps.PublisherId == publisherId); } /// /// Notifies all subscribers about a new post from a publisher /// /// The new post /// The number of subscribers notified public async Task NotifySubscribersPostAsync(Post post) { var subscribers = await db.PublisherSubscriptions .Include(ps => ps.Account) .Where(ps => ps.PublisherId == post.Publisher.Id && ps.Status == SubscriptionStatus.Active) .ToListAsync(); if (subscribers.Count == 0) return 0; // Create notification data var title = $"@{post.Publisher.Name} Posted"; var message = !string.IsNullOrEmpty(post.Title) ? post.Title : (post.Content?.Length > 100 ? string.Concat(post.Content.AsSpan(0, 97), "...") : post.Content); // Data to include with the notification var data = new Dictionary { { "post_id", post.Id.ToString() }, { "publisher_id", post.Publisher.Id.ToString() } }; // Notify each subscriber var notifiedCount = 0; foreach (var subscription in subscribers) { try { await nty.SendNotification( subscription.Account, "posts.new", title, post.Description?.Length > 40 ? post.Description[..37] + "..." : post.Description, message, data ); notifiedCount++; } catch (Exception) { // Log the error but continue with other notifications // We don't want one failed notification to stop the others } } return notifiedCount; } /// /// Gets all active subscriptions for an account /// /// The account ID /// A list of active subscriptions public async Task> GetAccountSubscriptionsAsync(long accountId) { return await db.PublisherSubscriptions .Include(ps => ps.Publisher) .Where(ps => ps.AccountId == accountId && ps.Status == SubscriptionStatus.Active) .ToListAsync(); } /// /// Gets all active subscribers for a publisher /// /// The publisher ID /// A list of active subscriptions public async Task> GetPublisherSubscribersAsync(long publisherId) { return await db.PublisherSubscriptions .Include(ps => ps.Account) .Where(ps => ps.PublisherId == publisherId && ps.Status == SubscriptionStatus.Active) .ToListAsync(); } /// /// Creates a new subscription between an account and a publisher /// /// The account ID /// The publisher ID /// Optional subscription tier /// The created subscription public async Task CreateSubscriptionAsync( long accountId, long publisherId, int tier = 0 ) { // Check if a subscription already exists var existingSubscription = await GetSubscriptionAsync(accountId, publisherId); if (existingSubscription != null) { // If it exists but is not active, reactivate it if (existingSubscription.Status == SubscriptionStatus.Active) return existingSubscription; existingSubscription.Status = SubscriptionStatus.Active; existingSubscription.Tier = tier; await db.SaveChangesAsync(); return existingSubscription; // If it's already active, just return it } // Create a new subscription var subscription = new PublisherSubscription { AccountId = accountId, PublisherId = publisherId, Status = SubscriptionStatus.Active, Tier = tier, }; db.PublisherSubscriptions.Add(subscription); await db.SaveChangesAsync(); return subscription; } /// /// Cancels a subscription /// /// The account ID /// The publisher ID /// True if the subscription was cancelled, false if it wasn't found public async Task CancelSubscriptionAsync(long accountId, long publisherId) { var subscription = await GetSubscriptionAsync(accountId, publisherId); if (subscription is not { Status: SubscriptionStatus.Active }) { return false; } subscription.Status = SubscriptionStatus.Cancelled; await db.SaveChangesAsync(); return true; } }