Exemple #1
0
        public async Task <Feed> SubscribeFeedAsync(string feedOriginalUri, string customGroup, User user)
        {
            Feed   feed        = null;
            string feedUriHash = null;
            var    db          = _dbFactory.CreateDbContext();

            for (var i = 0; i < 10; ++i)
            {
                var feedUri = feedOriginalUri.Trim().ToLower();
                feedUriHash = Utils.Sha256(feedUri);

                // Do we have this feed already?
                var feedInDb = await db.Feeds.FindAsync(feedUriHash);

                if (feedInDb == null)
                {
                    // Get information of this feed.
                    feed = await RefreshFeedAsync(feedOriginalUri, noItems : true);

                    if (feed.Error != null)
                    {
                        // This uri may not be the feed uri. It maybe the webpage, let's try to find out the potential feed uri in this webpage.
                        feedOriginalUri = await DiscoverFeedUriAsync(feedOriginalUri);

                        if (string.IsNullOrWhiteSpace(feedOriginalUri))
                        {
                            break;
                        }
                        continue;
                    }

                    // Save to feed table.
                    feedInDb = new ServerCore.Models.Feed
                    {
                        Description           = feed.Description,
                        IconUri               = feed.IconUri,
                        Id                    = feedUriHash,
                        UriHash               = feedUriHash,
                        Name                  = feed.Name,
                        RegistrationTimeInUtc = DateTime.UtcNow,
                        WebSiteUri            = feed.WebsiteLink,
                        Uri                   = feed.OriginalUri,
                    };
                    db.Feeds.Add(feedInDb);

                    var tryTimes = 0;
                    for (; tryTimes < 10; ++tryTimes)
                    {
                        if (tryTimes == 0)
                        {
                            feedInDb.ShortId = Utils.Crc32(feedInDb.Id);
                        }
                        else
                        {
                            feedInDb.ShortId = Utils.Crc32(feedInDb.Id + tryTimes);
                        }
                        try
                        {
                            await db.SaveChangesAsync();

                            feed.ShortId = feedInDb.ShortId;
                            break;
                        }
                        catch (DbUpdateException ex) when(ex.IsUniqueConstraintException())
                        {
                            if (await _dbFactory.CreateDbContext().Feeds.FindAsync(feedUriHash) != null)
                            {
                                // Save to ignore.
                                break;
                            }
                        }
                    }

                    if (tryTimes < 10)
                    {
                        break;
                    }
                    else
                    {
                        throw new ExternalErrorExcepiton($"Save feed {feed.OriginalUri} failed.");
                    }
                }
                else
                {
                    feed = new Feed
                    {
                        ShortId     = feedInDb.ShortId,
                        Description = feedInDb.Description,
                        IconUri     = feedInDb.IconUri,
                        Name        = feedInDb.Name,
                        OriginalUri = feedInDb.Uri,
                        Uri         = feedUri,
                        WebsiteLink = feedInDb.WebSiteUri,
                    };
                    break;
                }
            }

            Debug.Assert(feed != null);
            Debug.Assert(feedUriHash != null);

            // User user customized group.
            feed.Group = customGroup;

            // Save to usersfeeds table.
            db.UserFeeds.Add(new ServerCore.Models.UserFeed
            {
                UserId = user.Id,
                FeedId = feedUriHash,
                Group  = customGroup
            });
            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateException ex) when(ex.IsUniqueConstraintException())
            {
                // Save to ignore.
            }

            // Return
            return(feed);
        }
Exemple #2
0
        public async Task <Feed> GetFeedItemsAsync(string feedUri, string feedShortId, int page, User user)
        {
            if (string.IsNullOrEmpty(feedUri) && string.IsNullOrEmpty(feedShortId))
            {
                throw new ExternalErrorExcepiton($"FeedUri and FeedShortId can't be empty in both.");
            }

            string feedId = null;
            var    db     = _dbFactory.CreateDbContext();

            if (string.IsNullOrEmpty(feedShortId))
            {
                var originalUri = feedUri;
                feedUri = feedUri.Trim().ToLower();
                feedId  = Utils.Sha256(feedUri);
            }

            // Get feed information.
            // If the user blob is not null, we will get the feed from the user blob because user might customize the group and name on this feed.
            ServerCore.Models.UserFeed userFeed = null;
            ServerCore.Models.Feed     feedInDb = null;
            if (user != null)
            {
                userFeed = await db.UserFeeds.Include(f => f.Feed).FirstOrDefaultAsync(u => u.UserId == user.Id && (u.FeedId == feedId || u.Feed.ShortId == feedShortId));

                feedInDb = userFeed?.Feed;
            }

            // If we didn't get the feed, two possibility:
            // 1. The feed is not subscribed by user yet.
            // 2. Anonymous user.
            // No matter for which case, we will try to get the feed info from feed info table directly.
            if (feedInDb == null)
            {
                feedInDb = await db.Feeds.FirstOrDefaultAsync(f => f.Id == feedId || f.ShortId == feedShortId);
            }

            if (feedInDb == null)
            {
                throw new ExternalErrorExcepiton($"Feed '{feedUri ?? feedShortId}' is not found.");
            }
            feedId = feedInDb.Id;

            var feed = new Feed
            {
                ShortId     = feedInDb.ShortId,
                Description = feedInDb.Description,
                IconUri     = feedInDb.IconUri,
                Name        = feedInDb.Name,
                OriginalUri = feedInDb.Uri,
                Uri         = feedUri,
                WebsiteLink = feedInDb.WebSiteUri,
            };

            if (userFeed != null)
            {
                feed.Group = userFeed.Group;
            }

            // Get feed items.
            var feedItems = await db.FeedItems
                            .Where(f => f.FeedId == feedId)
                            .OrderByDescending(f => f.PublishTimeInUtc)
                            .Skip(page * 50)
                            .Take(50).ToListAsync();

            if (feedItems.Count > 0)
            {
                feed.Items = feedItems.Select(f => new FeedItem
                {
                    Id              = f.Id,
                    Summary         = f.Summary,
                    Content         = f.Content,
                    PermentLink     = f.Uri,
                    PubDate         = f.PublishTimeInUtc,
                    Title           = f.Title,
                    TopicPictureUri = f.TopicPictureUri,
                }).ToList();
            }

            // Mark readed or not.
            if (userFeed != null && userFeed.LastReadedTimeInUtc.Ticks != 0)
            {
                foreach (var feedItem in feed.Items)
                {
                    if (feedItem.PubDate <= userFeed.LastReadedTimeInUtc)
                    {
                        feedItem.IsReaded = true;
                    }
                }
            }

            // Mark stared or not.
            if (user != null)
            {
                // Mark stared or not
                var favorites = db.UserFeedItems.Where(f => f.UserId == user.Id && f.IsFavorite).Select(f => f.FeedItemId).ToList();
                if (favorites.Count > 0)
                {
                    foreach (var feedItem in feed.Items)
                    {
                        if (favorites.Find(id => id == feedItem.PermentLink.Sha256()) != null)
                        {
                            feedItem.IsStared = true;
                        }
                    }
                }
            }

            // All done.
            return(feed);
        }
        public static async Task UpdateFeed(IDbContextFactory <FeedReaderDbContext> dbFactory, ISolrOperations <Solrs.FeedItem> solrFeedItems, ServerCore.Models.Feed feed, ILogger log, HttpClient httpClient = null)
        {
            log.LogInformation($"UpdateFeed: {feed.Uri}");

            // Get original uri.
            var feedUriHash     = feed.Id;
            var feedOriginalUri = feed.Uri;

            // Get feed content.
            if (httpClient == null)
            {
                var handler = new HttpClientHandler()
                {
                    AutomaticDecompression = System.Net.DecompressionMethods.All, AllowAutoRedirect = true
                };
                httpClient = new HttpClient(handler);
                httpClient.DefaultRequestHeaders.Add("User-Agent", "FeedReader");
                httpClient.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue {
                    NoCache = true
                };
            }

            var feedContent = await httpClient.GetStringAsync(feedOriginalUri);

            // Create parser.
            var parser = FeedParser.FeedParser.Create(feedContent);

            // Parse feed info.
            var feedInfo = parser.ParseFeedInfo();

            // Parse html content to get icon uri.
            if (string.IsNullOrWhiteSpace(feedInfo.IconUri))
            {
                string uri = null;
                try
                {
                    if (!string.IsNullOrWhiteSpace(feedInfo.WebsiteLink))
                    {
                        uri = feedInfo.WebsiteLink;
                    }
                    else
                    {
                        uri = new Uri(feedOriginalUri).GetLeftPart(UriPartial.Authority);
                    }

                    var response = await httpClient.GetAsync(uri);

                    feedInfo.IconUri = new HtmlParser(await response.Content.ReadAsStringAsync(), response.RequestMessage.RequestUri.ToString()).ShortcurtIcon;
                }
                catch (HttpRequestException ex)
                {
                    log.LogError($"Try download the content of {uri} failed. Ex: {ex.Message}");
                }
                catch (Exception ex)
                {
                    log.LogError($"Parse html content of {uri} failed. Ex: {ex.Message}");
                }
            }

            // Try to get the favicon.ico file directly, like https://www.cagle.com, we can't get any icon info from the index page, but it has favicon.ico file.
            if (string.IsNullOrWhiteSpace(feedInfo.IconUri))
            {
                string uri = null;
                try
                {
                    var uriBuilder = new UriBuilder(string.IsNullOrWhiteSpace(feedInfo.WebsiteLink) ? feedOriginalUri : feedInfo.WebsiteLink);
                    uriBuilder.Path = "/favicon.ico";
                    uri             = uriBuilder.Uri.ToString();
                    var response = await httpClient.GetAsync(uriBuilder.Uri);

                    if (response.IsSuccessStatusCode)
                    {
                        feedInfo.IconUri = uri;
                    }
                }
                catch (HttpRequestException ex)
                {
                    log.LogError($"Try download the content of {uri} failed. Ex: {ex.Message}");
                }
            }

            // Save feed info to table.
            feed.Description         = feedInfo.Description;
            feed.IconUri             = feedInfo.IconUri;
            feed.LastUpdateTimeInUtc = DateTime.UtcNow;
            feed.Name       = feedInfo.Name;
            feed.WebSiteUri = feedInfo.WebsiteLink;

            // Parse feed items.
            var feedItems = parser.ParseFeedItems();

            // Save feed items to db.
            var db = dbFactory.CreateDbContext();

            foreach (var item in feedItems)
            {
                var feedItemId = item.PermentLink.Sha256();
                if (await db.FeedItems.FindAsync(feedItemId) == null)
                {
                    var feedItem = new ServerCore.Models.FeedItem
                    {
                        Content          = item.Content,
                        FeedId           = feed.Id,
                        Id               = feedItemId,
                        PublishTimeInUtc = item.PubDate.ToUniversalTime(),
                        Summary          = item.Summary,
                        Title            = item.Title,
                        TopicPictureUri  = item.TopicPictureUri,
                        Uri              = item.PermentLink
                    };
                    db.FeedItems.Add(feedItem);
                    solrFeedItems.Add(new Solrs.FeedItem(feedItem, feed));
                }
            }
            await db.SaveChangesAsync();

            // Save to solrs
            try
            {
                await solrFeedItems.CommitAsync();
            }
            catch (Exception ex)
            {
                log.LogError($"Save to solrs failed. ex: {ex.Message}");
            }

            log.LogInformation($"UpdateFeed: {feed.Uri} finished");
        }