using System; using System.Collections.Generic; using System.IO; using System.Linq; using BirdsiteLive.Common.Settings; using BirdsiteLive.Twitter.Models; using Tweetinvi.Models; using Tweetinvi.Models.Entities; namespace BirdsiteLive.Twitter.Extractors { public interface ITweetExtractor { ExtractedTweet Extract(ITweet tweet); } public class TweetExtractor : ITweetExtractor { private readonly InstanceSettings _instanceSettings; #region Ctor public TweetExtractor(InstanceSettings instanceSettings) { this._instanceSettings = instanceSettings; } #endregion public ExtractedTweet Extract(ITweet tweet) { if (tweet == null) throw new ArgumentNullException("no tweet found"); var extractedTweet = new ExtractedTweet { Id = tweet.Id, InReplyToStatusId = tweet.InReplyToStatusId, InReplyToAccount = tweet.InReplyToScreenName, MessageContent = ExtractMessage(tweet), Media = ExtractMedia(tweet), CreatedAt = tweet.CreatedAt.ToUniversalTime(), IsReply = tweet.InReplyToUserId != null, IsThread = tweet.InReplyToUserId != null && tweet.InReplyToUserId == tweet.CreatedBy.Id, IsRetweet = tweet.IsRetweet || tweet.QuotedStatusId != null, RetweetUrl = ExtractRetweetUrl(tweet), IsSensitive = tweet.PossiblySensitive, QuoteTweetUrl = tweet.QuotedStatusId != null ? "https://" + _instanceSettings.Domain + "/users/" + tweet.QuotedTweet.CreatedBy.ScreenName + "/statuses/" + tweet.QuotedStatusId : null }; return extractedTweet; } private string ExtractRetweetUrl(ITweet tweet) { if (tweet.IsRetweet) { if (tweet.RetweetedTweet != null) { var uri = new UriBuilder(tweet.RetweetedTweet.Url); uri.Host = _instanceSettings.TwitterDomain; return uri.Uri.ToString(); } if (tweet.FullText.Contains("https://t.co/")) { var retweetId = tweet.FullText .Split(new[] { "https://t.co/" }, StringSplitOptions.RemoveEmptyEntries) .Last(); return $"https://t.co/{retweetId}"; } } return null; } public string ExtractMessage(ITweet tweet) { var message = tweet.FullText; var tweetUrls = tweet.Media.Select(x => x.URL).Distinct(); if (tweet.IsRetweet && message.StartsWith("RT") && tweet.RetweetedTweet != null) { message = tweet.RetweetedTweet.FullText; tweetUrls = tweet.RetweetedTweet.Media.Select(x => x.URL).Distinct(); } foreach (var tweetUrl in tweetUrls) { if (tweet.IsRetweet) message = tweet.RetweetedTweet.FullText.Replace(tweetUrl, string.Empty).Trim(); else message = message.Replace(tweetUrl, string.Empty).Trim(); } if (tweet.QuotedTweet != null && !_instanceSettings.EnableQuoteRT) { message = $"[Quote {{RT}}]{Environment.NewLine}{message}"; } if (tweet.IsRetweet) { if (tweet.RetweetedTweet != null && !message.StartsWith("RT")) message = $"[{{RT}} @{tweet.RetweetedTweet.CreatedBy.ScreenName}]{Environment.NewLine}{message}"; else if ( tweet.RetweetedTweet != null && message.StartsWith($"RT @{tweet.RetweetedTweet.CreatedBy.ScreenName}:") ) message = message.Replace( $"RT @{tweet.RetweetedTweet.CreatedBy.ScreenName}:", $"[{{RT}} @{tweet.RetweetedTweet.CreatedBy.ScreenName}]{Environment.NewLine}" ); else message = message.Replace("RT", "[{{RT}}]"); } // Expand URLs foreach (var url in tweet.Urls.OrderByDescending(x => x.URL.Length)) { // A bit of a hack if (url.ExpandedURL == tweet.QuotedTweet?.Url && _instanceSettings.EnableQuoteRT) { url.ExpandedURL = ""; } else { var linkUri = new UriBuilder(url.ExpandedURL); if (linkUri.Host == "twitter.com") { linkUri.Host = _instanceSettings.TwitterDomain; url.ExpandedURL = linkUri.Uri.ToString(); } } message = message.Replace(url.URL, url.ExpandedURL); } // Hack return message; } public ExtractedMedia[] ExtractMedia(ITweet tweet) { var media = tweet.Media; if (tweet.IsRetweet && tweet.RetweetedTweet != null) media = tweet.RetweetedTweet.Media; var result = new List(); foreach (var m in media) { var mediaUrl = GetMediaUrl(m); var mediaType = GetMediaType(m.MediaType, mediaUrl); if (mediaType == null) continue; var att = new ExtractedMedia { MediaType = mediaType, Url = mediaUrl }; result.Add(att); } return result.ToArray(); } public string GetMediaUrl(IMediaEntity media) { switch (media.MediaType) { case "photo": return media.MediaURLHttps; case "animated_gif": return media.VideoDetails.Variants[0].URL; case "video": return media.VideoDetails.Variants .OrderByDescending(x => x.Bitrate) .First() .URL; default: return null; } } public string GetMediaType(string mediaType, string mediaUrl) { switch (mediaType) { case "photo": var pExt = Path.GetExtension(mediaUrl); switch (pExt) { case ".jpg": case ".jpeg": return "image/jpeg"; case ".png": return "image/png"; } return null; case "animated_gif": var vExt = Path.GetExtension(mediaUrl); switch (vExt) { case ".gif": return "image/gif"; case ".mp4": return "video/mp4"; } return "image/gif"; case "video": return "video/mp4"; } return null; } } }