diff --git a/src/TwitterSky/Models/TweetArchiveModel.cs b/src/TwitterSky/Models/TweetArchiveModel.cs index 5d9eb22..8f898bb 100644 --- a/src/TwitterSky/Models/TweetArchiveModel.cs +++ b/src/TwitterSky/Models/TweetArchiveModel.cs @@ -1,5 +1,7 @@ using System.Text.Json.Serialization; +#nullable disable + namespace TwitterSky.Models { public partial class TweetArchiveModel @@ -15,7 +17,7 @@ public partial class Tweet public bool Retweeted { get; set; } [JsonPropertyName("entities")] - public Entities? Entities { get; set; } + public Entities Entities { get; set; } [JsonPropertyName("id")] public string Id { get; set; } @@ -39,7 +41,7 @@ public partial class Tweet public partial class Entities { [JsonPropertyName("hashtags")] - public List Hashtags { get; set; } + public List Hashtags { get; set; } [JsonPropertyName("user_mentions")] public List UserMentions { get; set; } @@ -48,6 +50,15 @@ public partial class Entities public List Urls { get; set; } } + public partial class Hashtag + { + [JsonPropertyName("text")] + public string Text { get; set; } + + [JsonPropertyName("indices")] + public List Indices { get; set; } + } + public partial class ExtendedEntities { [JsonPropertyName("media")] diff --git a/src/TwitterSky/TweetImporter.cs b/src/TwitterSky/TweetImporter.cs index 30ae548..c5615da 100644 --- a/src/TwitterSky/TweetImporter.cs +++ b/src/TwitterSky/TweetImporter.cs @@ -176,6 +176,46 @@ private static (string, List) ReplaceUrls(string tweetContent, List return (tweetContent, facets); } + /// + /// Gets the hashtags from the tweet content and creates facets for them. + /// + /// The content of the tweet. + /// List of hashtags to replace + /// A list of facets + private static List GetHashtags(string tweetContent, List? hashtags) + { + if (hashtags == null || hashtags.Count == 0) + { + return []; + } + + List facets = []; + if (hashtags.Count > 0) + { + foreach (Hashtag hashtag in hashtags) + { + FacetIndex index; + if (hashtag.Indices.Count == 2) + { + index = new(Convert.ToInt32(hashtag.Indices[0]), Convert.ToInt32(hashtag.Indices[1])); + } + else + { + // If the indices are not present, we need to find the start and end of the hashtag text. + // This is done as a "ByteSlice." + int promptStart = tweetContent.IndexOf(hashtag.Text, StringComparison.InvariantCulture); + int promptEnd = promptStart + Encoding.Default.GetBytes(hashtag.Text).Length; + index = new(promptStart, promptEnd); + } + + FacetFeature tag = FacetFeature.CreateHashtag(hashtag.Text); + facets.Add(new Facet(index, tag)); + } + } + + return facets; + } + /// /// Requests the cancellation of the import operation. /// @@ -244,7 +284,10 @@ public async Task ImportTweetAsync() parentPostId = value; } - await PostToBskyAsync(tweet.Tweet.Id, content, imageUrls, DateTime.ParseExact(tweet.Tweet.CreatedAt, TW_DATE_FORMAT, CultureInfo.InvariantCulture), facets, parentPostId); + // Get the hashtags from the tweet content + facets.AddRange(GetHashtags(content, tweet.Tweet?.Entities?.Hashtags)); + + await PostToBskyAsync(tweet.Tweet!.Id, content, imageUrls, DateTime.ParseExact(tweet.Tweet.CreatedAt, TW_DATE_FORMAT, CultureInfo.InvariantCulture), facets, parentPostId); await SaveLastParsedTweet(tweet.Tweet.Id); @@ -271,7 +314,7 @@ public async Task ImportTweetAsync() /// The creation date of the tweet. /// The list of facets for the tweet. /// A task that represents the asynchronous operation. - private async Task PostToBskyAsync(string tweetId, string textContent, List imageUrls, DateTime createdAt, List facets, CreatePostResponse inReplyTo) + private async Task PostToBskyAsync(string tweetId, string textContent, List imageUrls, DateTime createdAt, List facets, CreatePostResponse? inReplyTo) { Reply? reply = null; if (inReplyTo != null) @@ -309,7 +352,7 @@ await blobResult.SwitchAsync( // Blob is uploaded. Console.WriteLine($"Uploading image..."); // Converts the blob to an image. - FishyFlip.Models.Image? image = success.Blob.ToImage(); + Image? image = success.Blob.ToImage(); bskyImages.Add(image); }, async error => @@ -327,7 +370,7 @@ await blobResult.SwitchAsync( postResult = await _bskyProtocol.Repo.CreatePostAsync(textContent, facets: [.. facets], createdAt: createdAt, reply: reply); } - if (_tweetIdToBskyId.ContainsKey(tweetId) && postResult.Value is CreatePostResponse postResponse) + if (_tweetIdToBskyId.ContainsKey(tweetId) && postResult?.Value is CreatePostResponse postResponse) { _tweetIdToBskyId[tweetId] = postResponse; }