added undo follow workflow

This commit is contained in:
Nicolas Constant 2020-07-08 19:50:58 -04:00
parent 60eb472752
commit 387285e645
No known key found for this signature in database
GPG key ID: 1E9F677FB01A5688
4 changed files with 108 additions and 13 deletions

View file

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace BirdsiteLive.ActivityPub
{
public class ActivityAcceptUndoFollow : Activity
{
[JsonProperty("object")]
public ActivityUndoFollow apObject { get; set; }
}
}

View file

@ -1,7 +1,45 @@
namespace BirdsiteLive.Domain.BusinessUseCases
using System.Threading.Tasks;
using BirdsiteLive.DAL.Contracts;
namespace BirdsiteLive.Domain.BusinessUseCases
{
public class ProcessUnfollowUser
public interface IProcessUndoFollowUser
{
Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername);
}
public class ProcessUndoFollowUser : IProcessUndoFollowUser
{
private readonly IFollowersDal _followerDal;
private readonly ITwitterUserDal _twitterUserDal;
#region Ctor
public ProcessUndoFollowUser(IFollowersDal followerDal, ITwitterUserDal twitterUserDal)
{
_followerDal = followerDal;
_twitterUserDal = twitterUserDal;
}
#endregion
public async Task ExecuteAsync(string followerUsername, string followerDomain, string twitterUsername)
{
// Get Follower and Twitter Users
var follower = await _followerDal.GetFollowerAsync(followerUsername, followerDomain);
if (follower == null) return;
var twitterUser = await _twitterUserDal.GetTwitterUserAsync(twitterUsername);
if (twitterUser == null) return;
// Update Follower
var twitterUserId = twitterUser.Id;
if (follower.Followings.Contains(twitterUserId))
follower.Followings.Remove(twitterUserId);
if (follower.FollowingsSyncStatus.ContainsKey(twitterUserId))
follower.FollowingsSyncStatus.Remove(twitterUserId);
// Save Follower
await _followerDal.UpdateFollowerAsync(follower);
}
}
}

View file

@ -18,24 +18,27 @@ namespace BirdsiteLive.Domain
public interface IUserService
{
Actor GetUser(TwitterUser twitterUser);
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
Note GetStatus(TwitterUser user, ITweet tweet);
Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity);
Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityUndoFollow activity);
}
public class UserService : IUserService
{
private readonly IProcessFollowUser _processFollowUser;
private readonly IProcessUndoFollowUser _processUndoFollowUser;
private readonly ICryptoService _cryptoService;
private readonly IActivityPubService _activityPubService;
private readonly string _host;
#region Ctor
public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService, IProcessFollowUser processFollowUser)
public UserService(InstanceSettings instanceSettings, ICryptoService cryptoService, IActivityPubService activityPubService, IProcessFollowUser processFollowUser, IProcessUndoFollowUser processUndoFollowUser)
{
_cryptoService = cryptoService;
_activityPubService = activityPubService;
_processFollowUser = processFollowUser;
_processUndoFollowUser = processUndoFollowUser;
_host = $"https://{instanceSettings.Domain.Replace("https://",string.Empty).Replace("http://", string.Empty).TrimEnd('/')}";
}
#endregion
@ -104,6 +107,8 @@ namespace BirdsiteLive.Domain
return note;
}
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
{
// Validate
@ -118,7 +123,6 @@ namespace BirdsiteLive.Domain
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, followerInbox, twitterUser);
// Send Accept Activity
//var followerHost = activity.actor.Replace("https://", string.Empty).Split('/').First();
var acceptFollow = new ActivityAcceptFollow()
{
context = "https://www.w3.org/ns/activitystreams",
@ -136,7 +140,40 @@ namespace BirdsiteLive.Domain
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject);
return result == HttpStatusCode.Accepted;
}
public async Task<bool> UndoFollowRequestedAsync(string signature, string method, string path, string queryString,
Dictionary<string, string> requestHeaders, ActivityUndoFollow activity)
{
// Validate
var sigValidation = await ValidateSignature(activity.actor, signature, method, path, queryString, requestHeaders);
if (!sigValidation.SignatureIsValidated) return false;
// Save Follow in DB
var followerUserName = sigValidation.User.name.ToLowerInvariant();
var followerHost = sigValidation.User.url.Replace("https://", string.Empty).Split('/').First();
//var followerInbox = sigValidation.User.inbox;
var twitterUser = activity.apObject.apObject.Split('/').Last().Replace("@", string.Empty);
await _processUndoFollowUser.ExecuteAsync(followerUserName, followerHost, twitterUser);
// Send Accept Activity
var acceptFollow = new ActivityAcceptUndoFollow()
{
context = "https://www.w3.org/ns/activitystreams",
id = $"{activity.apObject.apObject}#accepts/undofollows/{Guid.NewGuid()}",
type = "Accept",
actor = activity.apObject.apObject,
apObject = new ActivityUndoFollow()
{
id = activity.id,
type = activity.type,
actor = activity.actor,
apObject = activity.apObject
}
};
var result = await _activityPubService.PostDataAsync(acceptFollow, followerHost, activity.apObject.apObject);
return result == HttpStatusCode.Accepted;
}
private async Task<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
{
var signatures = rawSig.Split(',');

View file

@ -54,9 +54,9 @@ namespace BirdsiteLive.Controllers
{
if (!long.TryParse(statusId, out var parsedStatusId))
return NotFound();
var tweet = _twitterService.GetTweet(parsedStatusId);
if(tweet == null)
if (tweet == null)
return NotFound();
var user = _twitterService.GetUser(id);
@ -80,15 +80,25 @@ namespace BirdsiteLive.Controllers
var body = await reader.ReadToEndAsync();
var activity = ApDeserializer.ProcessActivity(body);
// Do something
var signature = r.Headers["Signature"].First();
switch (activity?.type)
{
case "Follow":
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
if (succeeded) return Accepted();
else return Unauthorized();
break;
{
var succeeded = await _userService.FollowRequestedAsync(signature, r.Method, r.Path,
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
if (succeeded) return Accepted();
else return Unauthorized();
}
case "Undo":
if (activity is ActivityUndoFollow)
{
var succeeded = await _userService.UndoFollowRequestedAsync(signature, r.Method, r.Path,
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityUndoFollow);
if (succeeded) return Accepted();
else return Unauthorized();
}
return Accepted();
default:
return Accepted();