diff --git a/src/BirdsiteLive.Domain/ActivityPubService.cs b/src/BirdsiteLive.Domain/ActivityPubService.cs index 53c656c..fa3bf70 100644 --- a/src/BirdsiteLive.Domain/ActivityPubService.cs +++ b/src/BirdsiteLive.Domain/ActivityPubService.cs @@ -9,6 +9,7 @@ using BirdsiteLive.ActivityPub; using BirdsiteLive.ActivityPub.Converters; using BirdsiteLive.ActivityPub.Models; using BirdsiteLive.Common.Settings; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Org.BouncyCastle.Bcpg; @@ -27,13 +28,15 @@ namespace BirdsiteLive.Domain private readonly InstanceSettings _instanceSettings; private readonly IHttpClientFactory _httpClientFactory; private readonly ICryptoService _cryptoService; + private readonly ILogger _logger; #region Ctor - public ActivityPubService(ICryptoService cryptoService, InstanceSettings instanceSettings, IHttpClientFactory httpClientFactory) + public ActivityPubService(ICryptoService cryptoService, InstanceSettings instanceSettings, IHttpClientFactory httpClientFactory, ILogger logger) { _cryptoService = cryptoService; _instanceSettings = instanceSettings; _httpClientFactory = httpClientFactory; + _logger = logger; } #endregion @@ -48,26 +51,34 @@ namespace BirdsiteLive.Domain public async Task PostNewNoteActivity(Note note, string username, string noteId, string targetHost, string targetInbox) { - var actor = UrlFactory.GetActorUrl(_instanceSettings.Domain, username); - var noteUri = UrlFactory.GetNoteUrl(_instanceSettings.Domain, username, noteId); - - var now = DateTime.UtcNow; - var nowString = now.ToString("s") + "Z"; - - var noteActivity = new ActivityCreateNote() + try { - context = "https://www.w3.org/ns/activitystreams", - id = $"{noteUri}/activity", - type = "Create", - actor = actor, - published = nowString, + var actor = UrlFactory.GetActorUrl(_instanceSettings.Domain, username); + var noteUri = UrlFactory.GetNoteUrl(_instanceSettings.Domain, username, noteId); - to = note.to, - cc = note.cc, - apObject = note - }; + var now = DateTime.UtcNow; + var nowString = now.ToString("s") + "Z"; - await PostDataAsync(noteActivity, targetHost, actor, targetInbox); + var noteActivity = new ActivityCreateNote() + { + context = "https://www.w3.org/ns/activitystreams", + id = $"{noteUri}/activity", + type = "Create", + actor = actor, + published = nowString, + + to = note.to, + cc = note.cc, + apObject = note + }; + + await PostDataAsync(noteActivity, targetHost, actor, targetInbox); + } + catch (Exception e) + { + _logger.LogError(e, "Error sending {Username} post ({NoteId}) to {Host}{Inbox}", username, noteId, targetHost, targetInbox); + throw; + } } public async Task PostDataAsync(T data, string targetHost, string actorUrl, string inbox = null) diff --git a/src/BirdsiteLive.Domain/Tools/StatusExtractor.cs b/src/BirdsiteLive.Domain/Tools/StatusExtractor.cs index 5cbff68..4e98baf 100644 --- a/src/BirdsiteLive.Domain/Tools/StatusExtractor.cs +++ b/src/BirdsiteLive.Domain/Tools/StatusExtractor.cs @@ -81,12 +81,16 @@ namespace BirdsiteLive.Domain.Tools } var url = $"https://{_instanceSettings.Domain}/tags/{tag}"; - tags.Add(new Tag + + if (tags.All(x => x.href != url)) { - name = $"#{tag}", - href = url, - type = "Hashtag" - }); + tags.Add(new Tag + { + name = $"#{tag}", + href = url, + type = "Hashtag" + }); + } messageContent = Regex.Replace(messageContent, Regex.Escape(m.Groups[0].ToString()), $@"{m.Groups[1]}#{tag}{m.Groups[3]}"); @@ -96,7 +100,7 @@ namespace BirdsiteLive.Domain.Tools if (extractMentions) { var mentionMatch = OrderByLength(UserRegexes.Mention.Matches(messageContent)); - foreach (Match m in mentionMatch.OrderByDescending(x => x.Length)) + foreach (Match m in mentionMatch) { var mention = m.Groups[2].ToString(); @@ -109,13 +113,16 @@ namespace BirdsiteLive.Domain.Tools var url = $"https://{_instanceSettings.Domain}/users/{mention}"; var name = $"@{mention}@{_instanceSettings.Domain}"; - tags.Add(new Tag + if (tags.All(x => x.href != url)) { - name = name, - href = url, - type = "Mention" - }); - + tags.Add(new Tag + { + name = name, + href = url, + type = "Mention" + }); + } + messageContent = Regex.Replace(messageContent, Regex.Escape(m.Groups[0].ToString()), $@"{m.Groups[1]}@{mention}{m.Groups[3]}"); } @@ -123,13 +130,17 @@ namespace BirdsiteLive.Domain.Tools return (messageContent.Trim(), tags.ToArray()); } - + private IEnumerable OrderByLength(MatchCollection matches) { var result = new List(); - foreach (Match m in matches) result.Add(m); - result = result.OrderByDescending(x => x.Length).ToList(); + + result = result + .OrderBy(x => x.Length) + .GroupBy(p => p.Value) + .Select(g => g.First()) + .ToList(); return result; } diff --git a/src/BirdsiteLive/BirdsiteLive.csproj b/src/BirdsiteLive/BirdsiteLive.csproj index 42670a7..9daebca 100644 --- a/src/BirdsiteLive/BirdsiteLive.csproj +++ b/src/BirdsiteLive/BirdsiteLive.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 d21486de-a812-47eb-a419-05682bb68856 Linux - 0.16.0 + 0.16.1 diff --git a/src/Tests/BirdsiteLive.Domain.Tests/Tools/StatusExtractorTests.cs b/src/Tests/BirdsiteLive.Domain.Tests/Tools/StatusExtractorTests.cs index a279dd0..a86b56e 100644 --- a/src/Tests/BirdsiteLive.Domain.Tests/Tools/StatusExtractorTests.cs +++ b/src/Tests/BirdsiteLive.Domain.Tests/Tools/StatusExtractorTests.cs @@ -364,7 +364,32 @@ namespace BirdsiteLive.Domain.Tests.Tools Assert.IsTrue(result.content.Contains(@"@mynickname")); #endregion } - + + [TestMethod] + public void Extract_MultiMentionTag_MultiOccurrence_Test() + { + #region Stubs + var message = $"[RT @yamenbousrih]{Environment.NewLine}@KiwixOffline @photos_floues Bla. Cc @Pyb75 @photos_floues @KiwixOffline"; + #endregion + + #region Mocks + var logger = new Mock>(); + #endregion + + var service = new StatusExtractor(_settings, logger.Object); + var result = service.Extract(message); + + #region Validations + logger.VerifyAll(); + Assert.AreEqual(4, result.tags.Length); + Assert.AreEqual("Mention", result.tags.First().type); + + Assert.IsTrue(result.content.Contains(@"@photos_floues")); + Assert.IsTrue(result.content.Contains(@"@KiwixOffline @photos_floues")); + Assert.IsTrue(result.content.Contains(@"Cc @Pyb75 @photos_floues @KiwixOffline")); + #endregion + } + [TestMethod] public void Extract_SingleMentionTag_RT_Test() {