mirror of
				https://github.com/NicolasConstant/BirdsiteLive
				synced 2025-06-05 21:49:16 +02:00 
			
		
		
		
	added shared inbox serialization
This commit is contained in:
		@@ -73,27 +73,8 @@ namespace BirdsiteLive.Domain
 | 
			
		||||
                to = note.to,
 | 
			
		||||
                cc = note.cc,
 | 
			
		||||
                apObject = note
 | 
			
		||||
                //apObject = new Note()
 | 
			
		||||
                //{
 | 
			
		||||
                //    id = noteUri,
 | 
			
		||||
                //    summary = null,
 | 
			
		||||
                //    inReplyTo = null,
 | 
			
		||||
                //    published = nowString,
 | 
			
		||||
                //    url = noteUrl,
 | 
			
		||||
                //    attributedTo = actor,
 | 
			
		||||
                //    to = new[] { to },
 | 
			
		||||
                //    //cc = new [] { apPublic },
 | 
			
		||||
                //    sensitive = false,
 | 
			
		||||
                //    content = "<p>Woooot</p>",
 | 
			
		||||
                //    attachment = new string[0],
 | 
			
		||||
                //    tag = new string[0]
 | 
			
		||||
                //}
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            //TODO Remove this
 | 
			
		||||
            if (targetInbox.Contains(targetHost))
 | 
			
		||||
                targetInbox = targetInbox.Split(new []{ targetHost }, StringSplitOptions.RemoveEmptyEntries).Last();
 | 
			
		||||
 | 
			
		||||
            return await PostDataAsync(noteActivity, targetHost, actor, targetInbox);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace BirdsiteLive.Domain.BusinessUseCases
 | 
			
		||||
{
 | 
			
		||||
    public interface IProcessFollowUser
 | 
			
		||||
    {
 | 
			
		||||
        Task ExecuteAsync(string followerUsername, string followerDomain, string followerInbox, string twitterUser);
 | 
			
		||||
        Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class ProcessFollowUser : IProcessFollowUser
 | 
			
		||||
@@ -21,13 +21,13 @@ namespace BirdsiteLive.Domain.BusinessUseCases
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        public async Task ExecuteAsync(string followerUsername, string followerDomain, string followerInbox, string twitterUsername)
 | 
			
		||||
        public async Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox)
 | 
			
		||||
        {
 | 
			
		||||
            // Get Follower and Twitter Users
 | 
			
		||||
            var follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain);
 | 
			
		||||
            if (follower == null)
 | 
			
		||||
            {
 | 
			
		||||
                await _followerDal.CreateFollowerAsync(followerUsername, followerDomain, followerInbox);
 | 
			
		||||
                await _followerDal.CreateFollowerAsync(followerUsername, followerDomain, followerInbox, sharedInbox);
 | 
			
		||||
                follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -88,8 +88,15 @@ namespace BirdsiteLive.Domain
 | 
			
		||||
            var followerUserName = sigValidation.User.name.ToLowerInvariant();
 | 
			
		||||
            var followerHost = sigValidation.User.url.Replace("https://", string.Empty).Split('/').First();
 | 
			
		||||
            var followerInbox = sigValidation.User.inbox;
 | 
			
		||||
            var followerSharedInbox = sigValidation.User?.endpoints?.sharedInbox;
 | 
			
		||||
            var twitterUser = activity.apObject.Split('/').Last().Replace("@", string.Empty);
 | 
			
		||||
            await _processFollowUser.ExecuteAsync(followerUserName, followerHost, followerInbox, twitterUser);
 | 
			
		||||
 | 
			
		||||
            // Make sure to only keep routes
 | 
			
		||||
            followerInbox = OnlyKeepRoute(followerInbox, followerHost);
 | 
			
		||||
            followerSharedInbox = OnlyKeepRoute(followerSharedInbox, followerHost);
 | 
			
		||||
            
 | 
			
		||||
            // Execute
 | 
			
		||||
            await _processFollowUser.ExecuteAsync(followerUserName, followerHost, twitterUser, followerInbox, followerSharedInbox);
 | 
			
		||||
 | 
			
		||||
            // Send Accept Activity
 | 
			
		||||
            var acceptFollow = new ActivityAcceptFollow()
 | 
			
		||||
@@ -110,6 +117,17 @@ namespace BirdsiteLive.Domain
 | 
			
		||||
            return result == HttpStatusCode.Accepted;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private string OnlyKeepRoute(string inbox, string host)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(inbox)) 
 | 
			
		||||
                return null;
 | 
			
		||||
 | 
			
		||||
            if (inbox.Contains(host))
 | 
			
		||||
                inbox = inbox.Split(new[] { host }, StringSplitOptions.RemoveEmptyEntries).Last();
 | 
			
		||||
 | 
			
		||||
            return inbox;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString,
 | 
			
		||||
            Dictionary<string, string> requestHeaders, ActivityUndoFollow activity)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -51,11 +51,13 @@ namespace BirdsiteLive.Pipeline.Processors
 | 
			
		||||
            return userWithTweetsToSync;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task ProcessFollowerAsync(IEnumerable<ExtractedTweet> tweets, Follower follower, int userId,
 | 
			
		||||
            SyncTwitterUser user)
 | 
			
		||||
        private async Task ProcessFollowerAsync(IEnumerable<ExtractedTweet> tweets, Follower follower, int userId, SyncTwitterUser user)
 | 
			
		||||
        {
 | 
			
		||||
            var fromStatusId = follower.FollowingsSyncStatus[userId];
 | 
			
		||||
            var tweetsToSend = tweets.Where(x => x.Id > fromStatusId).OrderBy(x => x.Id).ToList();
 | 
			
		||||
            var inbox = string.IsNullOrWhiteSpace(follower.SharedInboxRoute)
 | 
			
		||||
                ? follower.InboxRoute
 | 
			
		||||
                : follower.SharedInboxRoute;
 | 
			
		||||
 | 
			
		||||
            var syncStatus = fromStatusId;
 | 
			
		||||
            try
 | 
			
		||||
@@ -63,8 +65,7 @@ namespace BirdsiteLive.Pipeline.Processors
 | 
			
		||||
                foreach (var tweet in tweetsToSend)
 | 
			
		||||
                {
 | 
			
		||||
                    var note = _statusService.GetStatus(user.Acct, tweet);
 | 
			
		||||
                    var result = await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), follower.Host,
 | 
			
		||||
                        follower.InboxUrl);
 | 
			
		||||
                    var result = await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), follower.Host, inbox);
 | 
			
		||||
 | 
			
		||||
                    if (result == HttpStatusCode.Accepted) 
 | 
			
		||||
                        syncStatus = tweet.Id;
 | 
			
		||||
 
 | 
			
		||||
@@ -108,7 +108,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
 | 
			
		||||
 | 
			
		||||
                acct VARCHAR(50) NOT NULL, 
 | 
			
		||||
                host VARCHAR(253) NOT NULL,
 | 
			
		||||
                inboxUrl VARCHAR(2048) NOT NULL,
 | 
			
		||||
                inboxRoute VARCHAR(2048) NOT NULL,
 | 
			
		||||
                sharedInboxRoute VARCHAR(2048),
 | 
			
		||||
                UNIQUE (acct, host)
 | 
			
		||||
		    );";
 | 
			
		||||
            await _tools.ExecuteRequestAsync(createFollowers);
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        public async Task CreateFollowerAsync(string acct, string host, string inboxUrl, int[] followings = null, Dictionary<int, long> followingSyncStatus = null)
 | 
			
		||||
        public async Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, int[] followings = null, Dictionary<int, long> followingSyncStatus = null)
 | 
			
		||||
        {
 | 
			
		||||
            if(followings == null) followings = new int[0];
 | 
			
		||||
            if(followingSyncStatus == null) followingSyncStatus = new Dictionary<int, long>();
 | 
			
		||||
@@ -35,8 +35,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
 | 
			
		||||
                dbConnection.Open();
 | 
			
		||||
 | 
			
		||||
                await dbConnection.ExecuteAsync(
 | 
			
		||||
                    $"INSERT INTO {_settings.FollowersTableName} (acct,host,inboxUrl,followings,followingsSyncStatus) VALUES(@acct,@host,@inboxUrl,@followings,CAST(@followingsSyncStatus as json))",
 | 
			
		||||
                    new { acct, host, inboxUrl, followings,  followingsSyncStatus = serializedDic });
 | 
			
		||||
                    $"INSERT INTO {_settings.FollowersTableName} (acct,host,inboxRoute,sharedInboxRoute,followings,followingsSyncStatus) VALUES(@acct,@host,@inboxRoute,@sharedInboxRoute,@followings,CAST(@followingsSyncStatus as json))",
 | 
			
		||||
                    new { acct, host, inboxRoute, sharedInboxRoute, followings, followingsSyncStatus = serializedDic });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -128,7 +128,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
 | 
			
		||||
                Id = follower.Id,
 | 
			
		||||
                Acct = follower.Acct,
 | 
			
		||||
                Host = follower.Host,
 | 
			
		||||
                InboxUrl = follower.InboxUrl,
 | 
			
		||||
                InboxRoute = follower.InboxRoute,
 | 
			
		||||
                SharedInboxRoute = follower.SharedInboxRoute,
 | 
			
		||||
                Followings = follower.Followings.ToList(),
 | 
			
		||||
                FollowingsSyncStatus = JsonConvert.DeserializeObject<Dictionary<int,long>>(follower.FollowingsSyncStatus)
 | 
			
		||||
            };
 | 
			
		||||
@@ -143,6 +144,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
 | 
			
		||||
 | 
			
		||||
        public string Acct { get; set; }
 | 
			
		||||
        public string Host { get; set; }
 | 
			
		||||
        public string InboxUrl { get; set; }
 | 
			
		||||
        public string InboxRoute { get; set; }
 | 
			
		||||
        public string SharedInboxRoute { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace BirdsiteLive.DAL.Contracts
 | 
			
		||||
    public interface IFollowersDal
 | 
			
		||||
    {
 | 
			
		||||
        Task<Follower> GetFollowerAsync(string acct, string host);
 | 
			
		||||
        Task CreateFollowerAsync(string acct, string host, string inboxUrl, int[] followings = null,
 | 
			
		||||
        Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, int[] followings = null,
 | 
			
		||||
            Dictionary<int, long> followingSyncStatus = null);
 | 
			
		||||
        Task<Follower[]> GetFollowersAsync(int followedUserId);
 | 
			
		||||
        Task UpdateFollowerAsync(Follower follower);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ namespace BirdsiteLive.DAL.Models
 | 
			
		||||
 | 
			
		||||
        public string Acct { get; set; }
 | 
			
		||||
        public string Host { get; set; }
 | 
			
		||||
        public string InboxUrl { get; set; }
 | 
			
		||||
        public string InboxRoute { get; set; }
 | 
			
		||||
        public string SharedInboxRoute { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -45,17 +45,51 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
 | 
			
		||||
                {19, 166L},
 | 
			
		||||
                {23, 167L}
 | 
			
		||||
            };
 | 
			
		||||
            var inboxUrl = "https://domain.ext/myhandle/inbox";
 | 
			
		||||
            var inboxRoute = "/myhandle/inbox";
 | 
			
		||||
            var sharedInboxRoute = "/inbox";
 | 
			
		||||
            
 | 
			
		||||
            var dal = new FollowersPostgresDal(_settings);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
 | 
			
		||||
            var result = await dal.GetFollowerAsync(acct, host);
 | 
			
		||||
 | 
			
		||||
            Assert.IsNotNull(result);
 | 
			
		||||
            Assert.AreEqual(acct, result.Acct);
 | 
			
		||||
            Assert.AreEqual(host, result.Host);
 | 
			
		||||
            Assert.AreEqual(inboxUrl, result.InboxUrl);
 | 
			
		||||
            Assert.AreEqual(inboxRoute, result.InboxRoute);
 | 
			
		||||
            Assert.AreEqual(sharedInboxRoute, result.SharedInboxRoute);
 | 
			
		||||
            Assert.AreEqual(following.Length, result.Followings.Count);
 | 
			
		||||
            Assert.AreEqual(following[0], result.Followings[0]);
 | 
			
		||||
            Assert.AreEqual(followingSync.Count, result.FollowingsSyncStatus.Count);
 | 
			
		||||
            Assert.AreEqual(followingSync.First().Key, result.FollowingsSyncStatus.First().Key);
 | 
			
		||||
            Assert.AreEqual(followingSync.First().Value, result.FollowingsSyncStatus.First().Value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [TestMethod]
 | 
			
		||||
        public async Task CreateAndGetFollower_NoSharedInbox()
 | 
			
		||||
        {
 | 
			
		||||
            var acct = "myhandle";
 | 
			
		||||
            var host = "domain.ext";
 | 
			
		||||
            var following = new[] { 12, 19, 23 };
 | 
			
		||||
            var followingSync = new Dictionary<int, long>()
 | 
			
		||||
            {
 | 
			
		||||
                {12, 165L},
 | 
			
		||||
                {19, 166L},
 | 
			
		||||
                {23, 167L}
 | 
			
		||||
            };
 | 
			
		||||
            var inboxRoute = "/myhandle/inbox";
 | 
			
		||||
            string sharedInboxRoute = null;
 | 
			
		||||
 | 
			
		||||
            var dal = new FollowersPostgresDal(_settings);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
 | 
			
		||||
            var result = await dal.GetFollowerAsync(acct, host);
 | 
			
		||||
 | 
			
		||||
            Assert.IsNotNull(result);
 | 
			
		||||
            Assert.AreEqual(acct, result.Acct);
 | 
			
		||||
            Assert.AreEqual(host, result.Host);
 | 
			
		||||
            Assert.AreEqual(inboxRoute, result.InboxRoute);
 | 
			
		||||
            Assert.AreEqual(sharedInboxRoute, result.SharedInboxRoute);
 | 
			
		||||
            Assert.AreEqual(following.Length, result.Followings.Count);
 | 
			
		||||
            Assert.AreEqual(following[0], result.Followings[0]);
 | 
			
		||||
            Assert.AreEqual(followingSync.Count, result.FollowingsSyncStatus.Count);
 | 
			
		||||
@@ -73,22 +107,25 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
 | 
			
		||||
            var host = "domain.ext";
 | 
			
		||||
            var following = new[] { 1,2,3 };
 | 
			
		||||
            var followingSync = new Dictionary<int, long>();
 | 
			
		||||
            var inboxUrl = "https://domain.ext/myhandle1/inbox";
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            var inboxRoute = "/myhandle1/inbox";
 | 
			
		||||
            var sharedInboxRoute = "/inbox";
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
 | 
			
		||||
            //User 2 
 | 
			
		||||
            acct = "myhandle2";
 | 
			
		||||
            host = "domain.ext";
 | 
			
		||||
            following = new[] { 2, 4, 5 };
 | 
			
		||||
            inboxUrl = "https://domain.ext/myhandle2/inbox";
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            inboxRoute = "/myhandle2/inbox";
 | 
			
		||||
            sharedInboxRoute = "/inbox2";
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
 | 
			
		||||
            //User 2 
 | 
			
		||||
            acct = "myhandle3";
 | 
			
		||||
            host = "domain.ext";
 | 
			
		||||
            following = new[] { 1 };
 | 
			
		||||
            inboxUrl = "https://domain.ext/myhandle3/inbox";
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            inboxRoute = "/myhandle3/inbox";
 | 
			
		||||
            sharedInboxRoute = "/inbox3";
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
 | 
			
		||||
            var result = await dal.GetFollowersAsync(2);
 | 
			
		||||
            Assert.AreEqual(2, result.Length);
 | 
			
		||||
@@ -112,10 +149,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
 | 
			
		||||
                {19, 166L},
 | 
			
		||||
                {23, 167L}
 | 
			
		||||
            };
 | 
			
		||||
            var inboxUrl = "https://domain.ext/myhandle/inbox";
 | 
			
		||||
            var inboxRoute = "/myhandle/inbox";
 | 
			
		||||
            var sharedInboxRoute = "/inbox";
 | 
			
		||||
 | 
			
		||||
            var dal = new FollowersPostgresDal(_settings);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
            var result = await dal.GetFollowerAsync(acct, host);
 | 
			
		||||
 | 
			
		||||
            var updatedFollowing = new List<int> { 12, 19, 23, 24 };
 | 
			
		||||
@@ -151,10 +189,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
 | 
			
		||||
                {19, 166L},
 | 
			
		||||
                {23, 167L}
 | 
			
		||||
            };
 | 
			
		||||
            var inboxUrl = "https://domain.ext/myhandle/inbox";
 | 
			
		||||
            var inboxRoute = "/myhandle/inbox";
 | 
			
		||||
            var sharedInboxRoute = "/inbox";
 | 
			
		||||
 | 
			
		||||
            var dal = new FollowersPostgresDal(_settings);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
            var result = await dal.GetFollowerAsync(acct, host);
 | 
			
		||||
 | 
			
		||||
            var updatedFollowing = new[] { 12, 19 };
 | 
			
		||||
@@ -188,10 +227,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
 | 
			
		||||
                {19, 166L},
 | 
			
		||||
                {23, 167L}
 | 
			
		||||
            };
 | 
			
		||||
            var inboxUrl = "https://domain.ext/myhandle/inbox";
 | 
			
		||||
            var inboxRoute = "/myhandle/inbox";
 | 
			
		||||
            var sharedInboxRoute = "/inbox";
 | 
			
		||||
 | 
			
		||||
            var dal = new FollowersPostgresDal(_settings);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
            var result = await dal.GetFollowerAsync(acct, host);
 | 
			
		||||
            Assert.IsNotNull(result);
 | 
			
		||||
 | 
			
		||||
@@ -213,10 +253,11 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
 | 
			
		||||
                {19, 166L},
 | 
			
		||||
                {23, 167L}
 | 
			
		||||
            };
 | 
			
		||||
            var inboxUrl = "https://domain.ext/myhandle/inbox";
 | 
			
		||||
            var inboxRoute = "/myhandle/inbox";
 | 
			
		||||
            var sharedInboxRoute = "/inbox";
 | 
			
		||||
 | 
			
		||||
            var dal = new FollowersPostgresDal(_settings);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxUrl, following, followingSync);
 | 
			
		||||
            await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
 | 
			
		||||
            var result = await dal.GetFollowerAsync(acct, host);
 | 
			
		||||
            Assert.IsNotNull(result);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user