From f45e9ed9f78d42cfc5f9d171f9c8ab9ebc0c21fb Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 17:52:05 -0500
Subject: [PATCH] added migration calls

---
 src/BirdsiteLive.Common/Regexes/UrlRegexes.cs |  2 +
 src/BirdsiteLive.Domain/MigrationService.cs   | 65 +++++++++++++++--
 .../Controllers/MigrationController.cs        |  4 +-
 .../Regexes/UrlRegexesTests.cs                | 72 +++++++++++++++++++
 .../TheFedInfoServiceTests.cs                 |  2 +
 5 files changed, 136 insertions(+), 9 deletions(-)
 create mode 100644 src/Tests/BirdsiteLive.Common.Tests/Regexes/UrlRegexesTests.cs

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\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?)");
+
+        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<List<BslInstanceInfo>> 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));
             }
         }
