diff --git a/modules/RichSiteSummary/Service/MessageSender.cs b/modules/RichSiteSummary/Service/MessageSender.cs index 2995e99..8b67760 100644 --- a/modules/RichSiteSummary/Service/MessageSender.cs +++ b/modules/RichSiteSummary/Service/MessageSender.cs @@ -39,8 +39,12 @@ namespace Kruzya.TelegramBot.RichSiteSummary.Service _logger.LogDebug($"Message for {message.ChatId.Identifier} dequeued."); try { - await _bot.BotClient.SendTextMessageAsync(message.ChatId, message.Text, message.ParseMode, - message.DisableWebPagePreview); + if (string.IsNullOrWhiteSpace(message.ImageUrl)) + await _bot.BotClient.SendTextMessageAsync(message.ChatId, message.Text, message.ParseMode, + message.DisableWebPagePreview); + else + await _bot.BotClient.SendPhotoAsync(message.ChatId, message.ImageUrl, message.Text, + message.ParseMode, message.DisableWebPagePreview); } catch (ApiRequestException e) { diff --git a/modules/RichSiteSummary/Service/RssFetch.cs b/modules/RichSiteSummary/Service/RssFetch.cs index 251ae94..db592ca 100644 --- a/modules/RichSiteSummary/Service/RssFetch.cs +++ b/modules/RichSiteSummary/Service/RssFetch.cs @@ -1,8 +1,13 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; +using System.Text.RegularExpressions; +using System.Linq; using CodeHollow.FeedReader; using Kruzya.TelegramBot.Core.Extensions; using Kruzya.TelegramBot.RichSiteSummary.Data; @@ -14,6 +19,10 @@ using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; using Feed = Kruzya.TelegramBot.RichSiteSummary.Data.Feed; +using CodeHollow.FeedReader.Feeds; +using CodeHollow.FeedReader.Feeds.Itunes; +using CodeHollow.FeedReader.Feeds.MediaRSS; + namespace Kruzya.TelegramBot.RichSiteSummary.Service { /// @@ -123,6 +132,7 @@ namespace Kruzya.TelegramBot.RichSiteSummary.Service Post feedPost; var newPosts = new List(); + var postsImages = new Dictionary(); foreach (var post in parsedFeed.Items) { feedPost = await dbContext.Posts.ByFeedAndUrl(post.Link, feed); @@ -137,7 +147,8 @@ namespace Kruzya.TelegramBot.RichSiteSummary.Service feedPost.Title = post.Title; feedPost.Url = post.Link; feedPost.PostedAt = post.PublishingDate.GetValueOrDefault(DateTime.Now); - + + postsImages.Add(feedPost, await FetchImageUrl(post)); newPosts.Add(feedPost); } @@ -152,7 +163,7 @@ namespace Kruzya.TelegramBot.RichSiteSummary.Service var message = new UserMessage() { ChatId = new ChatId(subscriber.SubscriberId), DisableWebPagePreview = true, - ParseMode = ParseMode.Html, Text = text + ParseMode = ParseMode.Html, Text = text, ImageUrl = postsImages.GetValueOrDefault(post) }; _queue.Enqueue(message); @@ -174,7 +185,80 @@ namespace Kruzya.TelegramBot.RichSiteSummary.Service return null; } } - + + #endregion + + #region Image parsing + + private async Task FetchImageUrl(FeedItem item) + { + var feedItem = item.SpecificItem; + if (feedItem is Rss20FeedItem) + { + var enclosure = ((Rss20FeedItem)feedItem).Enclosure; + if (enclosure != null && IsValidImage(enclosure.MediaType)) + { + return enclosure.Url; + } + } + + return await FetchImageUrl(item.Description); + } + + private async Task FetchImageUrl(string description) + { + if (string.IsNullOrWhiteSpace(description)) + { + return ""; + } + + using var httpClientHandler = new HttpClientHandler() { AllowAutoRedirect = true }; + using var httpClient = new HttpClient(httpClientHandler); + + var imgRegEx = new Regex(@"]+>"); + var srcRegEx = new Regex("src=\"([^\"]+)\""); + var matches = imgRegEx.Matches(description); + + var img = ""; + foreach (Match match in matches) + { + try + { + var srcData = srcRegEx.Match(match.Value); + var content = srcData.Groups[1].Value; + + var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, content)); + if (response.StatusCode == HttpStatusCode.OK && + IsValidImage(response.Content.Headers.ContentType.MediaType)) + { + img = content; + break; + } + } + catch (Exception e) + { + _logger.LogError($"Caused error when fetching image for RSS post: {e.Message}"); + } + } + + return img; + } + + /// + /// Checks if image is valid for Telegram Bot API. + /// + /// Content-type for received content + /// True, if Telegram maybe can "use" this image, false if not. + private static bool IsValidImage(string contentType) + { + return new string[] + { + "image/jpeg", + "image/bmp", + "image/png" + }.Contains(contentType); + } + #endregion } } \ No newline at end of file diff --git a/modules/RichSiteSummary/UserMessage.cs b/modules/RichSiteSummary/UserMessage.cs index 4bc93e2..f6a5399 100644 --- a/modules/RichSiteSummary/UserMessage.cs +++ b/modules/RichSiteSummary/UserMessage.cs @@ -9,5 +9,6 @@ namespace Kruzya.TelegramBot.RichSiteSummary public string Text; public ParseMode ParseMode; public bool DisableWebPagePreview; + public string ImageUrl; } } \ No newline at end of file