using System; using System.Collections.Generic; using System.IO; using System.Linq; using Argotic.Common; using Argotic.Extensions.Core; using Argotic.Syndication; using Serilog; using SharpRss.Models; namespace SharpRss { public struct SyndicationContainer { public GenericSyndicationFeed SyndicationFeed { get; set; } public CategoryModel Category { get; set; } public FeedModel FeedModel { get; set; } public HashSet FeedItems { get; set; } public bool Fetched; } public static class SyndicationManager { public static SyndicationContainer CreateSyndication(string feedUrl) { GenericSyndicationFeed? syndicationFeed = null; Uri feedUri = new Uri(feedUrl); try { Log.Debug("Fetching feed: {FeedUri}", feedUri.ToString()); syndicationFeed = GenericSyndicationFeed.Create(feedUri); } catch (Exception e) { Log.Error(e,"Could not get feed: {FeedUrl}", feedUrl); } return ConstructSyndicationContainer(syndicationFeed); } public static Stream FeedToStream(GenericSyndicationFeed syndicationFeed) { MemoryStream memStream = new MemoryStream(); syndicationFeed.Resource.Save(memStream); if (memStream.Length <= 0) Log.Warning("Failed to serialize {FeedType} feed: {FeedUri}", syndicationFeed.Format.ToString(), syndicationFeed.Title); memStream.Position = 0; return memStream; } private static SyndicationContainer ConstructSyndicationContainer(GenericSyndicationFeed? syndicationFeed) { SyndicationContainer container = new SyndicationContainer(); if (syndicationFeed == null) { Log.Error("Could not construct syndication container!"); return container; } container.SyndicationFeed = syndicationFeed; container.FeedModel = new FeedModel(); container.FeedItems = new HashSet(); switch (syndicationFeed.Resource.Format) { case SyndicationContentFormat.Rss: RssFeed rssFeed = (RssFeed)container.SyndicationFeed.Resource; if (rssFeed.Channel == null) break; container.FeedModel.OriginalUrl = rssFeed.Channel.SelfLink?.ToString() ?? string.Empty; container.FeedModel.Title = rssFeed.Channel.Title ?? string.Empty; container.FeedModel.FeedType = rssFeed.Format.ToString() ?? string.Empty; container.FeedModel.FeedVersion = rssFeed.Version?.ToString() ?? string.Empty; container.FeedModel.Description = rssFeed.Channel.Description ?? string.Empty; container.FeedModel.Language = rssFeed.Channel.Language?.ToString() ?? string.Empty; container.FeedModel.Copyright = rssFeed.Channel.Copyright ?? string.Empty; container.FeedModel.PublicationDate = rssFeed.Channel.LastBuildDate is not { Ticks: > 0 } ? new DateTimeOffset(rssFeed.Channel.LastBuildDate) : DateTimeOffset.MinValue; container.FeedModel.Categories = rssFeed.Channel.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty(); container.FeedModel.ImageUrl = rssFeed.Channel.Image?.Url.ToString() ?? string.Empty; foreach (var rssItem in rssFeed.Channel.Items) { FeedItemModel itemModel = new FeedItemModel() { Id = rssItem.Link?.ToString() ?? string.Empty, FeedUrl = container.FeedModel.OriginalUrl ?? string.Empty, Type = container.FeedModel.FeedType ?? string.Empty, Title = rssItem.Title ?? string.Empty, Description = rssItem.Description ?? string.Empty, Link = rssItem.Link?.ToString() ?? string.Empty, PublishingDate = rssItem.PublicationDate is not { Ticks: <= 0 } ? new DateTimeOffset(rssItem.PublicationDate) : DateTimeOffset.MinValue, Authors = rssItem.Author != null ? new []{ rssItem.Author } : Array.Empty(), Categories = rssItem.Categories?.Select(x => x.Value).ToArray() ?? Array.Empty(), Content = rssItem.Extensions?.Where(x => x is SiteSummaryContentSyndicationExtension).Select(x => (x as SiteSummaryContentSyndicationExtension)?.Context.Encoded).FirstOrDefault() ?? string.Empty, CommentsUrl = rssItem.Extensions?.Where(x => x is WellFormedWebCommentsSyndicationExtension).Select(x => (x as WellFormedWebCommentsSyndicationExtension)?.Context.CommentsFeed.ToString()).FirstOrDefault() ?? string.Empty }; container.FeedItems.Add(itemModel); } break; case SyndicationContentFormat.Atom: AtomFeed atomFeed = (AtomFeed)container.SyndicationFeed.Resource; container.FeedModel.OriginalUrl = atomFeed.Id?.Uri.ToString() ?? string.Empty; container.FeedModel.Title = atomFeed.Title?.Content ?? string.Empty; container.FeedModel.FeedType = atomFeed.Format.ToString() ?? string.Empty; container.FeedModel.FeedVersion = atomFeed.Version?.ToString() ?? string.Empty; container.FeedModel.Description = atomFeed.Subtitle?.Content ?? string.Empty; container.FeedModel.Language = atomFeed.Language?.ToString() ?? string.Empty; container.FeedModel.Copyright = atomFeed.Rights?.Content ?? string.Empty; container.FeedModel.PublicationDate = atomFeed.UpdatedOn != null ? new DateTimeOffset(atomFeed.UpdatedOn) : DateTimeOffset.MinValue; container.FeedModel.Categories = atomFeed.Categories?.Select(x => x.Label).ToArray() ?? Array.Empty(); container.FeedModel.ImageUrl = atomFeed.Icon?.Uri.ToString() ?? string.Empty; foreach (var entry in atomFeed.Entries) { FeedItemModel itemModel = new FeedItemModel() { Id = entry.Id?.Uri.ToString() ?? string.Empty, FeedUrl = container.FeedModel?.OriginalUrl ?? string.Empty, Type = container.FeedModel?.FeedType ?? string.Empty, Title = entry.Title?.Content ?? string.Empty, Description = entry.Summary?.Content ?? string.Empty, Link = entry.Id?.Uri.ToString() ?? string.Empty, LastUpdated = entry.UpdatedOn is not { Ticks: <= 0 } ? new DateTimeOffset(entry.UpdatedOn) : DateTimeOffset.MinValue, PublishingDate = entry.PublishedOn is not { Ticks: <= 0 } ? new DateTimeOffset(entry.PublishedOn) : DateTimeOffset.MinValue, Authors = entry.Authors?.Select(auth => auth.Name).ToArray() ?? Array.Empty(), Categories = entry.Categories?.Select(cat => cat.Label).ToArray() ?? Array.Empty(), Content = entry.Content?.Content ?? string.Empty }; container.FeedItems.Add(itemModel); } break; default: Log.Warning("Feed implementation missing!"); break; } container.Fetched = true; return container; } public static void Init() { DbAccess.Initialize(); } } }