diff --git a/src/BirdsiteLive.Common/Regexes/UrlRegexes.cs b/src/BirdsiteLive.Common/Regexes/UrlRegexes.cs index 1f2b279..cb93838 100644 --- a/src/BirdsiteLive.Common/Regexes/UrlRegexes.cs +++ b/src/BirdsiteLive.Common/Regexes/UrlRegexes.cs @@ -5,5 +5,7 @@ namespace BirdsiteLive.Common.Regexes public class UrlRegexes { public static readonly Regex Url = new Regex(@"(.?)(((http|ftp|https):\/\/)[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?)"); + + public static readonly Regex Domain = new Regex(@"^[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)+$"); } } \ No newline at end of file diff --git a/src/BirdsiteLive.Domain/MigrationService.cs b/src/BirdsiteLive.Domain/MigrationService.cs index 975272e..4a9a734 100644 --- a/src/BirdsiteLive.Domain/MigrationService.cs +++ b/src/BirdsiteLive.Domain/MigrationService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using BirdsiteLive.Twitter; using System.Security.Cryptography; @@ -11,25 +12,31 @@ using BirdsiteLive.ActivityPub.Converters; using BirdsiteLive.Common.Settings; using BirdsiteLive.DAL.Models; using BirdsiteLive.Domain.Enum; +using System.Net.Http; +using BirdsiteLive.Common.Regexes; namespace BirdsiteLive.Domain { public class MigrationService { private readonly InstanceSettings _instanceSettings; + private readonly ITheFedInfoService _theFedInfoService; private readonly ITwitterTweetsService _twitterTweetsService; private readonly IActivityPubService _activityPubService; private readonly ITwitterUserDal _twitterUserDal; private readonly IFollowersDal _followersDal; - + private readonly IHttpClientFactory _httpClientFactory; + #region Ctor - public MigrationService(ITwitterTweetsService twitterTweetsService, IActivityPubService activityPubService, ITwitterUserDal twitterUserDal, IFollowersDal followersDal, InstanceSettings instanceSettings) + public MigrationService(ITwitterTweetsService twitterTweetsService, IActivityPubService activityPubService, ITwitterUserDal twitterUserDal, IFollowersDal followersDal, InstanceSettings instanceSettings, ITheFedInfoService theFedInfoService, IHttpClientFactory httpClientFactory) { _twitterTweetsService = twitterTweetsService; _activityPubService = activityPubService; _twitterUserDal = twitterUserDal; _followersDal = followersDal; _instanceSettings = instanceSettings; + _theFedInfoService = theFedInfoService; + _httpClientFactory = httpClientFactory; } #endregion @@ -63,7 +70,7 @@ namespace BirdsiteLive.Domain if (tweet.CreatorName.Trim().ToLowerInvariant() != acct.Trim().ToLowerInvariant()) throw new Exception($"Tweet not published by @{acct}"); - + if (!tweet.MessageContent.Contains(code)) { var message = "Tweet don't have migration code"; @@ -245,14 +252,58 @@ namespace BirdsiteLive.Domain }); } - public async Task TriggerRemoteMigrationAsync(string id, string tweetid, string handle) + public async Task TriggerRemoteMigrationAsync(string id, string tweetIdStg, string handle) { - //TODO + var url = $"https://{{0}}/migration/move/{{1}}/{{2}}/{handle}"; + await ProcessRemoteMigrationAsync(id, tweetIdStg, url); + } - public async Task TriggerRemoteDeleteAsync(string id, string tweetid) + public async Task TriggerRemoteDeleteAsync(string id, string tweetIdStg) { - //TODO + var url = $"https://{{0}}/migration/delete/{{1}}/{{2}}"; + await ProcessRemoteMigrationAsync(id, tweetIdStg, url); + } + + private async Task ProcessRemoteMigrationAsync(string id, string tweetIdStg, string urlPattern) + { + try + { + var instances = await RetrieveCompatibleBslInstancesAsync(); + var tweetId = ExtractedTweetId(tweetIdStg); + + foreach (var instance in instances) + { + try + { + var host = instance.Host; + if(!UrlRegexes.Domain.IsMatch(host)) continue; + + var url = string.Format(urlPattern, host, id, tweetId); + + var client = _httpClientFactory.CreateClient(); + var result = await client.PostAsync(url, null); + result.EnsureSuccessStatusCode(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + private async Task> RetrieveCompatibleBslInstancesAsync() + { + var instances = await _theFedInfoService.GetBslInstanceListAsync(); + var filteredInstances = instances + .Where(x => x.Version >= new Version(0, 21, 0)) + .ToList(); + return filteredInstances; } private byte[] GetHash(string inputString) diff --git a/src/BirdsiteLive/Controllers/MigrationController.cs b/src/BirdsiteLive/Controllers/MigrationController.cs index 0261876..1b69c87 100644 --- a/src/BirdsiteLive/Controllers/MigrationController.cs +++ b/src/BirdsiteLive/Controllers/MigrationController.cs @@ -104,7 +104,7 @@ namespace BirdsiteLive.Controllers try { await _migrationService.MigrateAccountAsync(fediverseUserValidation, id); - await _migrationService.TriggerRemoteMigrationAsync(id, tweetid, handle); + _migrationService.TriggerRemoteMigrationAsync(id, tweetid, handle); data.MigrationSuccess = true; } catch (Exception e) @@ -157,7 +157,7 @@ namespace BirdsiteLive.Controllers try { await _migrationService.DeleteAccountAsync(id); - await _migrationService.TriggerRemoteDeleteAsync(id, tweetid); + _migrationService.TriggerRemoteDeleteAsync(id, tweetid); data.MigrationSuccess = true; } catch (Exception e) diff --git a/src/Tests/BirdsiteLive.Common.Tests/Regexes/UrlRegexesTests.cs b/src/Tests/BirdsiteLive.Common.Tests/Regexes/UrlRegexesTests.cs new file mode 100644 index 0000000..78a06be --- /dev/null +++ b/src/Tests/BirdsiteLive.Common.Tests/Regexes/UrlRegexesTests.cs @@ -0,0 +1,72 @@ +using BirdsiteLive.Common.Regexes; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace BirdsiteLive.Common.Tests +{ + [TestClass] + public class UrlRegexesTests + { + [TestMethod] + public void Url_Test() + { + var input = "https://misskey.tdl/users/8hwf6zy2k1#main-key"; + Assert.IsTrue(UrlRegexes.Url.IsMatch(input)); + } + + [TestMethod] + public void Url_Not_Test() + { + var input = "misskey.tdl/users/8hwf6zy2k1#main-key"; + Assert.IsFalse(UrlRegexes.Url.IsMatch(input)); + } + + [TestMethod] + public void Domain_Test() + { + var input = "misskey-data_sq.tdl"; + Assert.IsTrue(UrlRegexes.Domain.IsMatch(input)); + } + + [TestMethod] + public void Domain_Numbers_Test() + { + var input = "miss45654QAzedqskey-data_sq.tdl"; + Assert.IsTrue(UrlRegexes.Domain.IsMatch(input)); + } + + [TestMethod] + public void Domain_Subdomain_Test() + { + var input = "s.sub.dqdq-_Dz9sd.tdl"; + Assert.IsTrue(UrlRegexes.Domain.IsMatch(input)); + } + + [TestMethod] + public void Domain_Not_Test() + { + var input = "mis$s45654QAzedqskey-data_sq.tdl"; + Assert.IsFalse(UrlRegexes.Domain.IsMatch(input)); + } + + [TestMethod] + public void Domain_Slash_Test() + { + var input = "miss45654QAz/edqskey-data_sq.tdl"; + Assert.IsFalse(UrlRegexes.Domain.IsMatch(input)); + } + + [TestMethod] + public void Domain_NotSub_Test() + { + var input = ".mis$s45654QAzedqskey-data_sq.tdl"; + Assert.IsFalse(UrlRegexes.Domain.IsMatch(input)); + } + + [TestMethod] + public void Domain_NotExt_Test() + { + var input = ".mis$s45654QAzedqskey-data_sq.tdl"; + Assert.IsFalse(UrlRegexes.Domain.IsMatch(input)); + } + } +} \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs b/src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs index 37f4572..6ebc242 100644 --- a/src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs +++ b/src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using BirdsiteLive.Common.Regexes; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -27,6 +28,7 @@ namespace BirdsiteLive.Domain.Tests foreach (var instanceInfo in bslInstanceList) { Assert.IsFalse(string.IsNullOrWhiteSpace(instanceInfo.Host)); + Assert.IsTrue(UrlRegexes.Domain.IsMatch(instanceInfo.Host)); Assert.IsTrue(instanceInfo.Version > new Version(0, 1, 0)); } }