Browse Source

added AP unfollow + tests + db update

master
Nicolas Constant 2 years ago
parent
commit
eaae2f1f47
No known key found for this signature in database GPG Key ID: 1E9F677FB01A5688
12 changed files with 126 additions and 35 deletions
  1. +3
    -3
      src/BirdsiteLive.Domain/BusinessUseCases/ProcessFollowUser.cs
  2. +1
    -1
      src/BirdsiteLive.Domain/UserService.cs
  3. +32
    -3
      src/BirdsiteLive.Moderation/Actions/RemoveFollowerAction.cs
  4. +27
    -3
      src/BirdsiteLive.Moderation/Actions/RemoveTwitterAccountAction.cs
  5. +14
    -6
      src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/DbInitializerPostgresDal.cs
  6. +3
    -3
      src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/FollowersPostgresDal.cs
  7. +13
    -0
      src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs
  8. +1
    -1
      src/DataAccessLayers/BirdsiteLive.DAL/Contracts/IFollowersDal.cs
  9. +1
    -0
      src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ITwitterUserDal.cs
  10. +1
    -0
      src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs
  11. +24
    -12
      src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/FollowersPostgresDalTests.cs
  12. +6
    -3
      src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessFollowUserTests.cs

+ 3
- 3
src/BirdsiteLive.Domain/BusinessUseCases/ProcessFollowUser.cs View File

@ -5,7 +5,7 @@ namespace BirdsiteLive.Domain.BusinessUseCases
{
public interface IProcessFollowUser
{
Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox);
Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox, string followerActorId);
}
public class ProcessFollowUser : IProcessFollowUser
@ -21,13 +21,13 @@ namespace BirdsiteLive.Domain.BusinessUseCases
}
#endregion
public async Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox)
public async Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername, string followerInbox, string sharedInbox, string followerActorId)
{
// Get Follower and Twitter Users
var follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain);
if (follower == null)
{
await _followerDal.CreateFollowerAsync(followerUsername, followerDomain, followerInbox, sharedInbox);
await _followerDal.CreateFollowerAsync(followerUsername, followerDomain, followerInbox, sharedInbox, followerActorId);
follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain);
}


+ 1
- 1
src/BirdsiteLive.Domain/UserService.cs View File

@ -161,7 +161,7 @@ namespace BirdsiteLive.Domain
if (!user.Protected)
{
// Execute
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, twitterUser, followerInbox, followerSharedInbox);
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, twitterUser, followerInbox, followerSharedInbox, activity.actor);
return await SendAcceptFollowAsync(activity, followerHost);
}


+ 32
- 3
src/BirdsiteLive.Moderation/Actions/RemoveFollowerAction.cs View File

@ -1,7 +1,12 @@
using System.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub;
using BirdsiteLive.ActivityPub.Converters;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.DAL.Contracts;
using BirdsiteLive.DAL.Models;
using BirdsiteLive.Domain;
namespace BirdsiteLive.Moderation.Actions
{
@ -14,19 +19,23 @@ namespace BirdsiteLive.Moderation.Actions
{
private readonly IFollowersDal _followersDal;
private readonly ITwitterUserDal _twitterUserDal;
private readonly IUserService _userService;
private readonly InstanceSettings _instanceSettings;
#region Ctor
public RemoveFollowerAction(IFollowersDal followersDal, ITwitterUserDal twitterUserDal)
public RemoveFollowerAction(IFollowersDal followersDal, ITwitterUserDal twitterUserDal, IUserService userService, InstanceSettings instanceSettings)
{
_followersDal = followersDal;
_twitterUserDal = twitterUserDal;
_userService = userService;
_instanceSettings = instanceSettings;
}
#endregion
public async Task ProcessAsync(Follower follower)
{
// Perform undo following to user instance
// TODO: Insert ActivityPub magic here
await RejectAllFollowingsAsync(follower);
// Remove twitter users if no more followers
var followings = follower.Followings;
@ -40,5 +49,25 @@ namespace BirdsiteLive.Moderation.Actions
// Remove follower from DB
await _followersDal.DeleteFollowerAsync(follower.Id);
}
private async Task RejectAllFollowingsAsync(Follower follower)
{
foreach (var following in follower.Followings)
{
try
{
var f = await _twitterUserDal.GetTwitterUserAsync(following);
var activityFollowing = new ActivityFollow
{
type = "Follow",
actor = follower.ActorId,
apObject = UrlFactory.GetActorUrl(_instanceSettings.Domain, f.Acct)
};
await _userService.SendRejectFollowAsync(activityFollowing, follower.Host);
}
catch (Exception) { }
}
}
}
}

+ 27
- 3
src/BirdsiteLive.Moderation/Actions/RemoveTwitterAccountAction.cs View File

@ -1,7 +1,12 @@
using System.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub;
using BirdsiteLive.ActivityPub.Converters;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.DAL.Contracts;
using BirdsiteLive.DAL.Models;
using BirdsiteLive.Domain;
namespace BirdsiteLive.Moderation.Actions
{
@ -14,12 +19,16 @@ namespace BirdsiteLive.Moderation.Actions
{
private readonly IFollowersDal _followersDal;
private readonly ITwitterUserDal _twitterUserDal;
private readonly IUserService _userService;
private readonly InstanceSettings _instanceSettings;
#region Ctor
public RemoveTwitterAccountAction(IFollowersDal followersDal, ITwitterUserDal twitterUserDal)
public RemoveTwitterAccountAction(IFollowersDal followersDal, ITwitterUserDal twitterUserDal, InstanceSettings instanceSettings, IUserService userService)
{
_followersDal = followersDal;
_twitterUserDal = twitterUserDal;
_instanceSettings = instanceSettings;
_userService = userService;
}
#endregion
@ -33,7 +42,7 @@ namespace BirdsiteLive.Moderation.Actions
foreach (var follower in followers)
{
// Perform undo following to user instance
// TODO: Insert ActivityPub magic here
await RejectFollowingAsync(follower, twitterUser);
// Remove following from DB
if (follower.Followings.Contains(twitterUserId))
@ -51,5 +60,20 @@ namespace BirdsiteLive.Moderation.Actions
// Remove twitter user
await _twitterUserDal.DeleteTwitterUserAsync(twitterUser.Acct);
}
private async Task RejectFollowingAsync(Follower follower, SyncTwitterUser twitterUser)
{
try
{
var activityFollowing = new ActivityFollow
{
type = "Follow",
actor = follower.ActorId,
apObject = UrlFactory.GetActorUrl(_instanceSettings.Domain, twitterUser.Acct)
};
await _userService.SendRejectFollowAsync(activityFollowing, follower.Host);
}
catch (Exception) { }
}
}
}

+ 14
- 6
src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/DbInitializerPostgresDal.cs View File

@ -23,7 +23,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
public class DbInitializerPostgresDal : PostgresBase, IDbInitializerDal
{
private readonly PostgresTools _tools;
private readonly Version _currentVersion = new Version(2, 0);
private readonly Version _currentVersion = new Version(2, 1);
private const string DbVersionType = "db-version";
#region Ctor
@ -131,7 +131,8 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
{
return new[]
{
new Tuple<Version, Version>(new Version(1,0), new Version(2,0))
new Tuple<Version, Version>(new Version(1,0), new Version(2,0)),
new Tuple<Version, Version>(new Version(2,0), new Version(2,1))
};
}
@ -144,12 +145,19 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
var addIndex = $@"CREATE INDEX IF NOT EXISTS lastsync_twitteruser ON {_settings.TwitterUserTableName}(lastSync)";
await _tools.ExecuteRequestAsync(addIndex);
await UpdateDbVersionAsync(to);
return to;
}
else if (from == new Version(2, 0) && to == new Version(2, 1))
{
var addActorId = $@"ALTER TABLE {_settings.FollowersTableName} ADD actorId VARCHAR(2048)";
await _tools.ExecuteRequestAsync(addActorId);
}
else
{
throw new NotImplementedException();
}
throw new NotImplementedException();
await UpdateDbVersionAsync(to);
return to;
}
private async Task UpdateDbVersionAsync(Version newVersion)


+ 3
- 3
src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/FollowersPostgresDal.cs View File

@ -20,7 +20,7 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
}
#endregion
public async Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, int[] followings = null, Dictionary<int, long> followingSyncStatus = null)
public async Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, string actorId, 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,inboxRoute,sharedInboxRoute,followings,followingsSyncStatus) VALUES(@acct,@host,@inboxRoute,@sharedInboxRoute,@followings,CAST(@followingsSyncStatus as json))",
new { acct, host, inboxRoute, sharedInboxRoute, followings, followingsSyncStatus = serializedDic });
$"INSERT INTO {_settings.FollowersTableName} (acct,host,inboxRoute,sharedInboxRoute,followings,followingsSyncStatus,actorId) VALUES(@acct,@host,@inboxRoute,@sharedInboxRoute,@followings,CAST(@followingsSyncStatus as json),@actorId)",
new { acct, host, inboxRoute, sharedInboxRoute, followings, followingsSyncStatus = serializedDic, actorId });
}
}


+ 13
- 0
src/DataAccessLayers/BirdsiteLive.DAL.Postgres/DataAccessLayers/TwitterUserPostgresDal.cs View File

@ -49,6 +49,19 @@ namespace BirdsiteLive.DAL.Postgres.DataAccessLayers
}
}
public async Task<SyncTwitterUser> GetTwitterUserAsync(int id)
{
var query = $"SELECT * FROM {_settings.TwitterUserTableName} WHERE id = @id";
using (var dbConnection = Connection)
{
dbConnection.Open();
var result = (await dbConnection.QueryAsync<SyncTwitterUser>(query, new { id })).FirstOrDefault();
return result;
}
}
public async Task DeleteTwitterUserAsync(int id)
{
throw new NotImplementedException();


+ 1
- 1
src/DataAccessLayers/BirdsiteLive.DAL/Contracts/IFollowersDal.cs View File

@ -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 inboxRoute, string sharedInboxRoute, int[] followings = null,
Task CreateFollowerAsync(string acct, string host, string inboxRoute, string sharedInboxRoute, string actorId, int[] followings = null,
Dictionary<int, long> followingSyncStatus = null);
Task<Follower[]> GetFollowersAsync(int followedUserId);
Task<Follower[]> GetAllFollowersAsync();


+ 1
- 0
src/DataAccessLayers/BirdsiteLive.DAL/Contracts/ITwitterUserDal.cs View File

@ -8,6 +8,7 @@ namespace BirdsiteLive.DAL.Contracts
{
Task CreateTwitterUserAsync(string acct, long lastTweetPostedId);
Task<SyncTwitterUser> GetTwitterUserAsync(string acct);
Task<SyncTwitterUser> GetTwitterUserAsync(int id);
Task<SyncTwitterUser[]> GetAllTwitterUsersAsync(int maxNumber);
Task<SyncTwitterUser[]> GetAllTwitterUsersAsync();
Task UpdateTwitterUserAsync(int id, long lastTweetPostedId, long lastTweetSynchronizedForAllFollowersId, DateTime lastSync);


+ 1
- 0
src/DataAccessLayers/BirdsiteLive.DAL/Models/Follower.cs View File

@ -9,6 +9,7 @@ namespace BirdsiteLive.DAL.Models
public List<int> Followings { get; set; }
public Dictionary<int, long> FollowingsSyncStatus { get; set; }
public string ActorId { get; set; }
public string Acct { get; set; }
public string Host { get; set; }
public string InboxRoute { get; set; }


+ 24
- 12
src/Tests/BirdsiteLive.DAL.Postgres.Tests/DataAccessLayers/FollowersPostgresDalTests.cs View File

@ -41,9 +41,10 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
};
var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var actorId = $"https://{host}/{acct}";
var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host);
@ -73,9 +74,10 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
};
var inboxRoute = "/myhandle/inbox";
string sharedInboxRoute = null;
var actorId = $"https://{host}/{acct}";
var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host);
@ -103,7 +105,8 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
var followingSync = new Dictionary<int, long>();
var inboxRoute = "/myhandle1/inbox";
var sharedInboxRoute = "/inbox";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var actorId = $"https://{host}/{acct}";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
//User 2
acct = "myhandle2";
@ -111,7 +114,8 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
following = new[] { 2, 4, 5 };
inboxRoute = "/myhandle2/inbox";
sharedInboxRoute = "/inbox2";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
actorId = $"https://{host}/{acct}";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
//User 2
acct = "myhandle3";
@ -119,7 +123,8 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
following = new[] { 1 };
inboxRoute = "/myhandle3/inbox";
sharedInboxRoute = "/inbox3";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
actorId = $"https://{host}/{acct}";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
var result = await dal.GetFollowersAsync(2);
Assert.AreEqual(2, result.Length);
@ -146,7 +151,8 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
var followingSync = new Dictionary<int, long>();
var inboxRoute = "/myhandle1/inbox";
var sharedInboxRoute = "/inbox";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
var actorId = $"https://{host}/{acct}";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
//User 2
acct = "myhandle2";
@ -154,7 +160,8 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
following = new[] { 2, 4, 5 };
inboxRoute = "/myhandle2/inbox";
sharedInboxRoute = "/inbox2";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
actorId = $"https://{host}/{acct}";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
//User 2
acct = "myhandle3";
@ -162,7 +169,8 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
following = new[] { 1 };
inboxRoute = "/myhandle3/inbox";
sharedInboxRoute = "/inbox3";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
actorId = $"https://{host}/{acct}";
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
result = await dal.GetFollowersCountAsync();
Assert.AreEqual(3, result);
@ -182,9 +190,10 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
};
var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var actorId = $"https://{host}/{acct}";
var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host);
var updatedFollowing = new List<int> { 12, 19, 23, 24 };
@ -222,9 +231,10 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
};
var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var actorId = $"https://{host}/{acct}";
var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host);
var updatedFollowing = new[] { 12, 19 };
@ -260,9 +270,10 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
};
var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var actorId = $"https://{host}/{acct}";
var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host);
Assert.IsNotNull(result);
@ -286,9 +297,10 @@ namespace BirdsiteLive.DAL.Postgres.Tests.DataAccessLayers
};
var inboxRoute = "/myhandle/inbox";
var sharedInboxRoute = "/inbox";
var actorId = $"https://{host}/{acct}";
var dal = new FollowersPostgresDal(_settings);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, following, followingSync);
await dal.CreateFollowerAsync(acct, host, inboxRoute, sharedInboxRoute, actorId, following, followingSync);
var result = await dal.GetFollowerAsync(acct, host);
Assert.IsNotNull(result);


+ 6
- 3
src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessFollowUserTests.cs View File

@ -21,6 +21,7 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
var twitterName = "handle";
var followerInbox = "/user/testest";
var inbox = "/inbox";
var actorId = "actorUrl";
var follower = new Follower
{
@ -55,6 +56,7 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
It.Is<string>(y => y == domain),
It.Is<string>(y => y == followerInbox),
It.Is<string>(y => y == inbox),
It.Is<string>(y => y == actorId),
null,
null))
.Returns(Task.CompletedTask);
@ -80,7 +82,7 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
#endregion
var action = new ProcessFollowUser(followersDalMock.Object, twitterUserDalMock.Object);
await action.ExecuteAsync(username, domain, twitterName, followerInbox, inbox);
await action.ExecuteAsync(username, domain, twitterName, followerInbox, inbox, actorId);
#region Validations
followersDalMock.VerifyAll();
@ -97,7 +99,8 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
var twitterName = "handle";
var followerInbox = "/user/testest";
var inbox = "/inbox";
var actorId = "actorUrl";
var follower = new Follower
{
Id = 1,
@ -138,7 +141,7 @@ namespace BirdsiteLive.Domain.Tests.BusinessUseCases
#endregion
var action = new ProcessFollowUser(followersDalMock.Object, twitterUserDalMock.Object);
await action.ExecuteAsync(username, domain, twitterName, followerInbox, inbox);
await action.ExecuteAsync(username, domain, twitterName, followerInbox, inbox, actorId);
#region Validations
followersDalMock.VerifyAll();


Loading…
Cancel
Save