From 93110d997208e72a8e23a3e296562ee6ea37c167 Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 15:00:14 -0500
Subject: [PATCH 01/10] refactoring

---
 .../Controllers/MigrationController.cs        | 22 +------------------
 src/BirdsiteLive/Models/MigrationData.cs      | 21 ++++++++++++++++++
 .../Views/Migration/Delete.cshtml             |  2 +-
 src/BirdsiteLive/Views/Migration/Index.cshtml |  2 +-
 4 files changed, 24 insertions(+), 23 deletions(-)
 create mode 100644 src/BirdsiteLive/Models/MigrationData.cs

diff --git a/src/BirdsiteLive/Controllers/MigrationController.cs b/src/BirdsiteLive/Controllers/MigrationController.cs
index 386644a..0261876 100644
--- a/src/BirdsiteLive/Controllers/MigrationController.cs
+++ b/src/BirdsiteLive/Controllers/MigrationController.cs
@@ -7,6 +7,7 @@ using Npgsql.TypeHandlers;
 using BirdsiteLive.Domain;
 using BirdsiteLive.Domain.Enum;
 using BirdsiteLive.DAL.Contracts;
+using BirdsiteLive.Models;
 
 namespace BirdsiteLive.Controllers
 {
@@ -213,25 +214,4 @@ namespace BirdsiteLive.Controllers
             return StatusCode(400);
         }
     }
-
-
-
-    public class MigrationData
-    {
-        public string Acct { get; set; }
-
-        public string FediverseAccount { get; set; }
-        public string TweetId { get; set; }
-
-        public string MigrationCode { get; set; }
-
-        public bool IsTweetProvided { get; set; }
-        public bool IsAcctProvided { get; set; }
-
-        public bool IsTweetValid { get; set; }
-        public bool IsAcctValid { get; set; }
-
-        public string ErrorMessage { get; set; }
-        public bool MigrationSuccess { get; set; }
-    }
 }
diff --git a/src/BirdsiteLive/Models/MigrationData.cs b/src/BirdsiteLive/Models/MigrationData.cs
new file mode 100644
index 0000000..77185f9
--- /dev/null
+++ b/src/BirdsiteLive/Models/MigrationData.cs
@@ -0,0 +1,21 @@
+﻿namespace BirdsiteLive.Models
+{
+    public class MigrationData
+    {
+        public string Acct { get; set; }
+
+        public string FediverseAccount { get; set; }
+        public string TweetId { get; set; }
+
+        public string MigrationCode { get; set; }
+
+        public bool IsTweetProvided { get; set; }
+        public bool IsAcctProvided { get; set; }
+
+        public bool IsTweetValid { get; set; }
+        public bool IsAcctValid { get; set; }
+
+        public string ErrorMessage { get; set; }
+        public bool MigrationSuccess { get; set; }
+    }
+}
\ No newline at end of file
diff --git a/src/BirdsiteLive/Views/Migration/Delete.cshtml b/src/BirdsiteLive/Views/Migration/Delete.cshtml
index 9df9f62..568b08a 100644
--- a/src/BirdsiteLive/Views/Migration/Delete.cshtml
+++ b/src/BirdsiteLive/Views/Migration/Delete.cshtml
@@ -1,4 +1,4 @@
-﻿@model BirdsiteLive.Controllers.MigrationData
+﻿@model MigrationData
 @{
     ViewData["Title"] = "Migration";
 }
diff --git a/src/BirdsiteLive/Views/Migration/Index.cshtml b/src/BirdsiteLive/Views/Migration/Index.cshtml
index 670a209..2d1db53 100644
--- a/src/BirdsiteLive/Views/Migration/Index.cshtml
+++ b/src/BirdsiteLive/Views/Migration/Index.cshtml
@@ -1,4 +1,4 @@
-﻿@model BirdsiteLive.Controllers.MigrationData
+﻿@model MigrationData
 @{
     ViewData["Title"] = "Migration";
 }

From d64063b273f25f5f5d01b41886a0e70068cda8d4 Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 16:13:34 -0500
Subject: [PATCH 02/10] added federationinfo service

---
 src/BirdsiteLive.Domain/TheFedInfoService.cs  | 153 ++++++++++++++++++
 .../TheFedInfoServiceTests.cs                 |  34 ++++
 2 files changed, 187 insertions(+)
 create mode 100644 src/BirdsiteLive.Domain/TheFedInfoService.cs
 create mode 100644 src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs

diff --git a/src/BirdsiteLive.Domain/TheFedInfoService.cs b/src/BirdsiteLive.Domain/TheFedInfoService.cs
new file mode 100644
index 0000000..cbb8491
--- /dev/null
+++ b/src/BirdsiteLive.Domain/TheFedInfoService.cs
@@ -0,0 +1,153 @@
+﻿using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace BirdsiteLive.Domain
+{
+    public interface ITheFedInfoService
+    {
+        Task<List<BslInstanceInfo>> GetBslInstanceListAsync();
+    }
+
+    public class TheFedInfoService : ITheFedInfoService
+    {
+        private readonly IHttpClientFactory _httpClientFactory;
+        
+        #region Ctor
+        public TheFedInfoService(IHttpClientFactory httpClientFactory)
+        {
+            _httpClientFactory = httpClientFactory;
+        }
+        #endregion
+
+        public async Task<List<BslInstanceInfo>> GetBslInstanceListAsync()
+        {
+            var cancellationToken = CancellationToken.None;
+
+            var result = await CallGraphQlAsync<MyResponseData>(
+                new Uri("https://the-federation.info/graphql"),
+                HttpMethod.Get,
+                "query ($platform: String!) { nodes(platform: $platform) { host, version } }",
+                new
+                {
+                    platform = "birdsitelive",
+                },
+                cancellationToken);
+
+            var convertedResults = ConvertResults(result);
+            return convertedResults;
+        }
+
+        private List<BslInstanceInfo> ConvertResults(GraphQLResponse<MyResponseData> qlData)
+        {
+            var results = new List<BslInstanceInfo>();
+
+            foreach (var instanceInfo in qlData.Data.Nodes)
+            {
+                var rawVersion = instanceInfo.Version.Split('+').FirstOrDefault();
+                var version = Version.Parse(rawVersion);
+
+                var instance = new BslInstanceInfo
+                {
+                    Host = instanceInfo.Host,
+                    Version = version
+                };
+                results.Add(instance);
+            }
+
+            return results;
+        }
+
+        private async Task<GraphQLResponse<TResponse>> CallGraphQlAsync<TResponse>(Uri endpoint, HttpMethod method, string query, object variables, CancellationToken cancellationToken)
+        {
+            var content = new StringContent(SerializeGraphQlCall(query, variables), Encoding.UTF8, "application/json");
+            var httpRequestMessage = new HttpRequestMessage
+            {
+                Method = method,
+                Content = content,
+                RequestUri = endpoint,
+            };
+            //add authorization headers if necessary here
+            httpRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
+            var httpClient = _httpClientFactory.CreateClient();
+            using (var response = await httpClient.SendAsync(httpRequestMessage, cancellationToken))
+            {
+                //if (response.IsSuccessStatusCode)
+                if (response?.Content.Headers.ContentType?.MediaType == "application/json")
+                {
+                    var responseString = await response.Content.ReadAsStringAsync(); //cancellationToken supported for .NET 5/6
+                    return DeserializeGraphQlCall<TResponse>(responseString);
+                }
+                else
+                {
+                    throw new ApplicationException($"Unable to contact '{endpoint}': {response.StatusCode} - {response.ReasonPhrase}");
+                }
+            }
+        }
+
+        private string SerializeGraphQlCall(string query, object variables)
+        {
+            var sb = new StringBuilder();
+            var textWriter = new StringWriter(sb);
+            var serializer = new JsonSerializer();
+            serializer.Serialize(textWriter, new
+            {
+                query = query,
+                variables = variables,
+            });
+            return sb.ToString();
+        }
+
+        private GraphQLResponse<TResponse> DeserializeGraphQlCall<TResponse>(string response)
+        {
+            var serializer = new JsonSerializer();
+            var stringReader = new StringReader(response);
+            var jsonReader = new JsonTextReader(stringReader);
+            var result = serializer.Deserialize<GraphQLResponse<TResponse>>(jsonReader);
+            return result;
+        }
+        
+        private class GraphQLResponse<TResponse>
+        {
+            public List<GraphQLError> Errors { get; set; }
+            public TResponse Data { get; set; }
+        }
+
+        private class GraphQLError
+        {
+            public string Message { get; set; }
+            public List<GraphQLErrorLocation> Locations { get; set; }
+            public List<object> Path { get; set; } //either int or string
+        }
+
+        private class GraphQLErrorLocation
+        {
+            public int Line { get; set; }
+            public int Column { get; set; }
+        }
+        
+        private class MyResponseData
+        {
+            public Node[] Nodes { get; set; }
+        }
+
+        private class Node
+        {
+            public string Host { get; set; }
+            public string Version { get; set; }
+        }
+    }
+
+    public class BslInstanceInfo
+    {
+        public string Host { get; set; }
+        public Version Version { get; set; }
+    }
+}
\ No newline at end of file
diff --git a/src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs b/src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs
new file mode 100644
index 0000000..37f4572
--- /dev/null
+++ b/src/Tests/BirdsiteLive.Domain.Tests/TheFedInfoServiceTests.cs
@@ -0,0 +1,34 @@
+﻿using System;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+
+namespace BirdsiteLive.Domain.Tests
+{
+    [TestClass]
+    public class TheFedInfoServiceTests
+    {
+        [TestMethod]
+        public async Task GetBslInstanceListAsyncTest()
+        {
+            var httpClientFactoryMock = new Mock<IHttpClientFactory>();
+            httpClientFactoryMock
+                .Setup(x => x.CreateClient(It.IsAny<string>()))
+                .Returns(new HttpClient());
+
+            var service = new TheFedInfoService(httpClientFactoryMock.Object);
+
+            var bslInstanceList = await service.GetBslInstanceListAsync();
+
+            Assert.IsTrue(bslInstanceList.Count > 0);
+
+            foreach (var instanceInfo in bslInstanceList)
+            {
+                Assert.IsFalse(string.IsNullOrWhiteSpace(instanceInfo.Host));
+                Assert.IsTrue(instanceInfo.Version > new Version(0, 1, 0));
+            }
+        }
+    }
+}
\ No newline at end of file

From f7ca9fd86d194442df44c9624620334dcad50972 Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 16:16:10 -0500
Subject: [PATCH 03/10] added robustness

---
 src/BirdsiteLive.Domain/TheFedInfoService.cs | 25 +++++++++++++-------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/src/BirdsiteLive.Domain/TheFedInfoService.cs b/src/BirdsiteLive.Domain/TheFedInfoService.cs
index cbb8491..e85d7fa 100644
--- a/src/BirdsiteLive.Domain/TheFedInfoService.cs
+++ b/src/BirdsiteLive.Domain/TheFedInfoService.cs
@@ -51,15 +51,24 @@ namespace BirdsiteLive.Domain
 
             foreach (var instanceInfo in qlData.Data.Nodes)
             {
-                var rawVersion = instanceInfo.Version.Split('+').FirstOrDefault();
-                var version = Version.Parse(rawVersion);
-
-                var instance = new BslInstanceInfo
+                try
                 {
-                    Host = instanceInfo.Host,
-                    Version = version
-                };
-                results.Add(instance);
+                    var rawVersion = instanceInfo.Version.Split('+').First();
+                    if (string.IsNullOrWhiteSpace(rawVersion)) continue;
+                    var version = Version.Parse(rawVersion);
+                    if(version <= new Version(0,1,0)) continue;
+
+                    var instance = new BslInstanceInfo
+                    {
+                        Host = instanceInfo.Host,
+                        Version = version
+                    };
+                    results.Add(instance);
+                }
+                catch (Exception e)
+                {
+                    Console.WriteLine(e);
+                }
             }
 
             return results;

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 04/10] 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));
             }
         }

From eccd9bdd28bcf6c1e1d0f5eb8e6da7b978ebc38a Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 17:56:25 -0500
Subject: [PATCH 05/10] don't call itself

---
 src/BirdsiteLive.Domain/MigrationService.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/BirdsiteLive.Domain/MigrationService.cs b/src/BirdsiteLive.Domain/MigrationService.cs
index 4a9a734..3dc2eb9 100644
--- a/src/BirdsiteLive.Domain/MigrationService.cs
+++ b/src/BirdsiteLive.Domain/MigrationService.cs
@@ -302,6 +302,9 @@ namespace BirdsiteLive.Domain
             var instances = await _theFedInfoService.GetBslInstanceListAsync();
             var filteredInstances = instances
                 .Where(x => x.Version >= new Version(0, 21, 0))
+                .Where(x => string.Compare(x.Host, 
+                    _instanceSettings.Domain, 
+                    StringComparison.InvariantCultureIgnoreCase) != 0)
                 .ToList();
             return filteredInstances;
         }

From e950801f56cb402d84a92e63f110fdc2c0d922d3 Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 18:00:24 -0500
Subject: [PATCH 06/10] better loggin

---
 src/BirdsiteLive.Domain/MigrationService.cs | 31 ++++++++++++++++-----
 1 file changed, 24 insertions(+), 7 deletions(-)

diff --git a/src/BirdsiteLive.Domain/MigrationService.cs b/src/BirdsiteLive.Domain/MigrationService.cs
index 3dc2eb9..e0dfdb5 100644
--- a/src/BirdsiteLive.Domain/MigrationService.cs
+++ b/src/BirdsiteLive.Domain/MigrationService.cs
@@ -14,6 +14,7 @@ using BirdsiteLive.DAL.Models;
 using BirdsiteLive.Domain.Enum;
 using System.Net.Http;
 using BirdsiteLive.Common.Regexes;
+using Microsoft.Extensions.Logging;
 
 namespace BirdsiteLive.Domain
 {
@@ -26,9 +27,10 @@ namespace BirdsiteLive.Domain
         private readonly ITwitterUserDal _twitterUserDal;
         private readonly IFollowersDal _followersDal;
         private readonly IHttpClientFactory _httpClientFactory;
-        
+        private readonly ILogger<MigrationService> _logger;
+
         #region Ctor
-        public MigrationService(ITwitterTweetsService twitterTweetsService, IActivityPubService activityPubService, ITwitterUserDal twitterUserDal, IFollowersDal followersDal, InstanceSettings instanceSettings, ITheFedInfoService theFedInfoService, IHttpClientFactory httpClientFactory)
+        public MigrationService(ITwitterTweetsService twitterTweetsService, IActivityPubService activityPubService, ITwitterUserDal twitterUserDal, IFollowersDal followersDal, InstanceSettings instanceSettings, ITheFedInfoService theFedInfoService, IHttpClientFactory httpClientFactory, ILogger<MigrationService> logger)
         {
             _twitterTweetsService = twitterTweetsService;
             _activityPubService = activityPubService;
@@ -37,6 +39,7 @@ namespace BirdsiteLive.Domain
             _instanceSettings = instanceSettings;
             _theFedInfoService = theFedInfoService;
             _httpClientFactory = httpClientFactory;
+            _logger = logger;
         }
         #endregion
 
@@ -187,7 +190,7 @@ namespace BirdsiteLive.Domain
                     }
                     catch (Exception e)
                     {
-                        Console.WriteLine(e);
+                        _logger.LogError(e, e.Message);
                     }
                 }
             });
@@ -232,7 +235,14 @@ namespace BirdsiteLive.Domain
 
                     var t1 = Task.Run(async () =>
                     {
-                        await _activityPubService.DeleteUserAsync(acct, host, sharedInbox);
+                        try
+                        {
+                            await _activityPubService.DeleteUserAsync(acct, host, sharedInbox);
+                        }
+                        catch (Exception e)
+                        {
+                            _logger.LogError(e, e.Message);
+                        }
                     });
                 }
 
@@ -246,7 +256,14 @@ namespace BirdsiteLive.Domain
 
                     var t1 = Task.Run(async () =>
                     {
-                        await _activityPubService.DeleteUserAsync(acct, host, sharedInbox);
+                        try
+                        {
+                            await _activityPubService.DeleteUserAsync(acct, host, sharedInbox);
+                        }
+                        catch (Exception e)
+                        {
+                            _logger.LogError(e, e.Message);
+                        }
                     });
                 }
             });
@@ -287,13 +304,13 @@ namespace BirdsiteLive.Domain
                     }
                     catch (Exception e)
                     {
-                        Console.WriteLine(e);
+                        _logger.LogError(e, e.Message);
                     }
                 }
             }
             catch (Exception e)
             {
-                Console.WriteLine(e);
+                _logger.LogError(e, e.Message);
             }
         }
 

From 21ff67e3a8dff2a683c7cca7b09f6ecd48430078 Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 18:21:10 -0500
Subject: [PATCH 07/10] added checks

---
 src/BirdsiteLive/Controllers/MigrationController.cs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/BirdsiteLive/Controllers/MigrationController.cs b/src/BirdsiteLive/Controllers/MigrationController.cs
index 1b69c87..1ec0a2a 100644
--- a/src/BirdsiteLive/Controllers/MigrationController.cs
+++ b/src/BirdsiteLive/Controllers/MigrationController.cs
@@ -77,8 +77,8 @@ namespace BirdsiteLive.Controllers
                 data.ErrorMessage = "This account has been deleted, it can't be migrated";
                 return View("Index", data);
             }
-            if (twitterAccount != null && 
-                (!string.IsNullOrWhiteSpace(twitterAccount.MovedTo) 
+            if (twitterAccount != null &&
+                (!string.IsNullOrWhiteSpace(twitterAccount.MovedTo)
                  || !string.IsNullOrWhiteSpace(twitterAccount.MovedToAcct)))
             {
                 data.ErrorMessage = "This account has been moved already, it can't be migrated again";
@@ -132,7 +132,7 @@ namespace BirdsiteLive.Controllers
 
                 TweetId = tweetid
             };
-            
+
             //Verify can be deleted 
             var twitterAccount = await _twitterUserDal.GetTwitterUserAsync(id);
             if (twitterAccount != null && twitterAccount.Deleted)
@@ -176,9 +176,9 @@ namespace BirdsiteLive.Controllers
         {
             //Verify can be migrated 
             var twitterAccount = await _twitterUserDal.GetTwitterUserAsync(id);
-            if (twitterAccount.Deleted 
+            if (twitterAccount != null && (twitterAccount.Deleted 
                 || !string.IsNullOrWhiteSpace(twitterAccount.MovedTo) 
-                || !string.IsNullOrWhiteSpace(twitterAccount.MovedToAcct))
+                || !string.IsNullOrWhiteSpace(twitterAccount.MovedToAcct)))
                 return Ok();
 
             // Start migration
@@ -200,7 +200,7 @@ namespace BirdsiteLive.Controllers
         {
             //Verify can be deleted 
             var twitterAccount = await _twitterUserDal.GetTwitterUserAsync(id);
-            if (twitterAccount.Deleted) return Ok();
+            if (twitterAccount != null && twitterAccount.Deleted) return Ok();
 
             // Start deletion
             var isTweetValid = _migrationService.ValidateTweet(id, tweetid, MigrationTypeEnum.Deletion);

From 4461884975247d6fe1adb430bf65c2e11a6485df Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Wed, 28 Dec 2022 18:29:57 -0500
Subject: [PATCH 08/10] added input checks

---
 src/BirdsiteLive/Controllers/MigrationController.cs | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/BirdsiteLive/Controllers/MigrationController.cs b/src/BirdsiteLive/Controllers/MigrationController.cs
index 1ec0a2a..f2cde09 100644
--- a/src/BirdsiteLive/Controllers/MigrationController.cs
+++ b/src/BirdsiteLive/Controllers/MigrationController.cs
@@ -8,6 +8,7 @@ using BirdsiteLive.Domain;
 using BirdsiteLive.Domain.Enum;
 using BirdsiteLive.DAL.Contracts;
 using BirdsiteLive.Models;
+using System.Reflection.Metadata;
 
 namespace BirdsiteLive.Controllers
 {
@@ -174,6 +175,11 @@ namespace BirdsiteLive.Controllers
         [Route("/migration/move/{id}/{tweetid}/{handle}")]
         public async Task<IActionResult> RemoteMigrateMove(string id, string tweetid, string handle)
         {
+            //Check inputs 
+            if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(tweetid) ||
+                string.IsNullOrWhiteSpace(handle))
+                return StatusCode(422);
+
             //Verify can be migrated 
             var twitterAccount = await _twitterUserDal.GetTwitterUserAsync(id);
             if (twitterAccount != null && (twitterAccount.Deleted 
@@ -198,6 +204,10 @@ namespace BirdsiteLive.Controllers
         [Route("/migration/delete/{id}/{tweetid}")]
         public async Task<IActionResult> RemoteMigrateDelete(string id, string tweetid)
         {
+            //Check inputs 
+            if (string.IsNullOrWhiteSpace(id) || string.IsNullOrWhiteSpace(tweetid))
+                return StatusCode(422);
+
             //Verify can be deleted 
             var twitterAccount = await _twitterUserDal.GetTwitterUserAsync(id);
             if (twitterAccount != null && twitterAccount.Deleted) return Ok();

From f489e03a2b1f815137e5f9166d464fd06f145bd6 Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Thu, 29 Dec 2022 16:54:54 -0500
Subject: [PATCH 09/10] changed display order of migration info

---
 src/BirdsiteLive/Views/Users/Index.cshtml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/BirdsiteLive/Views/Users/Index.cshtml b/src/BirdsiteLive/Views/Users/Index.cshtml
index 2ff2ce3..eba3079 100644
--- a/src/BirdsiteLive/Views/Users/Index.cshtml
+++ b/src/BirdsiteLive/Views/Users/Index.cshtml
@@ -31,13 +31,7 @@
     <br />
     <br />
 
-    @if (ViewData.Model.Protected)
-    {
-        <div class="alert alert-danger" role="alert">
-            This account is protected, BirdsiteLIVE cannot fetch their tweets and will not provide follow support until it is unprotected again.
-        </div>
-    }
-    else if (ViewData.Model.Deleted)
+    @if (ViewData.Model.Deleted)
     {
         <div class="alert alert-danger" role="alert">
             This mirror has been deleted by its Twitter owner.
@@ -50,6 +44,12 @@
             You can follow this user at <a href="@ViewData.Model.MovedTo">@ViewData.Model.MovedToAcct</a>.
         </div>
     }
+    else if (ViewData.Model.Protected)
+    {
+        <div class="alert alert-danger" role="alert">
+            This account is protected, BirdsiteLIVE cannot fetch their tweets and will not provide follow support until it is unprotected again.
+        </div>
+    }
     else
     {
         <div>

From 8a3ca81731c67da2cd5a28130c68b278e613b9b5 Mon Sep 17 00:00:00 2001
From: Nicolas Constant <github@nicolas-constant.com>
Date: Fri, 30 Dec 2022 15:54:06 -0500
Subject: [PATCH 10/10] road to 0.22.0

---
 src/BirdsiteLive/BirdsiteLive.csproj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/BirdsiteLive/BirdsiteLive.csproj b/src/BirdsiteLive/BirdsiteLive.csproj
index a81920d..122ff88 100644
--- a/src/BirdsiteLive/BirdsiteLive.csproj
+++ b/src/BirdsiteLive/BirdsiteLive.csproj
@@ -4,7 +4,7 @@
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <UserSecretsId>d21486de-a812-47eb-a419-05682bb68856</UserSecretsId>
     <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
-    <Version>0.21.0</Version>
+    <Version>0.22.0</Version>
   </PropertyGroup>
 
   <ItemGroup>
