added undo follow workflow
This commit is contained in:
parent
60eb472752
commit
387285e645
4 changed files with 108 additions and 13 deletions
|
@ -0,0 +1,10 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BirdsiteLive.ActivityPub
|
||||||
|
{
|
||||||
|
public class ActivityAcceptUndoFollow : Activity
|
||||||
|
{
|
||||||
|
[JsonProperty("object")]
|
||||||
|
public ActivityUndoFollow apObject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,45 @@
|
||||||
namespace BirdsiteLive.Domain.BusinessUseCases
|
using System.Threading.Tasks;
|
||||||
{
|
using BirdsiteLive.DAL.Contracts;
|
||||||
public class ProcessUnfollowUser
|
|
||||||
{
|
|
||||||
|
|
||||||
|
namespace BirdsiteLive.Domain.BusinessUseCases
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,24 +18,27 @@ namespace BirdsiteLive.Domain
|
||||||
public interface IUserService
|
public interface IUserService
|
||||||
{
|
{
|
||||||
Actor GetUser(TwitterUser twitterUser);
|
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);
|
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
|
public class UserService : IUserService
|
||||||
{
|
{
|
||||||
private readonly IProcessFollowUser _processFollowUser;
|
private readonly IProcessFollowUser _processFollowUser;
|
||||||
|
private readonly IProcessUndoFollowUser _processUndoFollowUser;
|
||||||
|
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IActivityPubService _activityPubService;
|
private readonly IActivityPubService _activityPubService;
|
||||||
private readonly string _host;
|
private readonly string _host;
|
||||||
|
|
||||||
#region Ctor
|
#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;
|
_cryptoService = cryptoService;
|
||||||
_activityPubService = activityPubService;
|
_activityPubService = activityPubService;
|
||||||
_processFollowUser = processFollowUser;
|
_processFollowUser = processFollowUser;
|
||||||
|
_processUndoFollowUser = processUndoFollowUser;
|
||||||
_host = $"https://{instanceSettings.Domain.Replace("https://",string.Empty).Replace("http://", string.Empty).TrimEnd('/')}";
|
_host = $"https://{instanceSettings.Domain.Replace("https://",string.Empty).Replace("http://", string.Empty).TrimEnd('/')}";
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -104,6 +107,8 @@ namespace BirdsiteLive.Domain
|
||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
public async Task<bool> FollowRequestedAsync(string signature, string method, string path, string queryString, Dictionary<string, string> requestHeaders, ActivityFollow activity)
|
||||||
{
|
{
|
||||||
// Validate
|
// Validate
|
||||||
|
@ -118,7 +123,6 @@ namespace BirdsiteLive.Domain
|
||||||
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, followerInbox, twitterUser);
|
await _processFollowUser.ExecuteAsync(followerUserName, followerHost, followerInbox, twitterUser);
|
||||||
|
|
||||||
// Send Accept Activity
|
// Send Accept Activity
|
||||||
//var followerHost = activity.actor.Replace("https://", string.Empty).Split('/').First();
|
|
||||||
var acceptFollow = new ActivityAcceptFollow()
|
var acceptFollow = new ActivityAcceptFollow()
|
||||||
{
|
{
|
||||||
context = "https://www.w3.org/ns/activitystreams",
|
context = "https://www.w3.org/ns/activitystreams",
|
||||||
|
@ -137,6 +141,39 @@ namespace BirdsiteLive.Domain
|
||||||
return result == HttpStatusCode.Accepted;
|
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)
|
private async Task<SignatureValidationResult> ValidateSignature(string actor, string rawSig, string method, string path, string queryString, Dictionary<string, string> requestHeaders)
|
||||||
{
|
{
|
||||||
var signatures = rawSig.Split(',');
|
var signatures = rawSig.Split(',');
|
||||||
|
|
|
@ -80,15 +80,25 @@ namespace BirdsiteLive.Controllers
|
||||||
var body = await reader.ReadToEndAsync();
|
var body = await reader.ReadToEndAsync();
|
||||||
var activity = ApDeserializer.ProcessActivity(body);
|
var activity = ApDeserializer.ProcessActivity(body);
|
||||||
// Do something
|
// Do something
|
||||||
|
var signature = r.Headers["Signature"].First();
|
||||||
|
|
||||||
switch (activity?.type)
|
switch (activity?.type)
|
||||||
{
|
{
|
||||||
case "Follow":
|
case "Follow":
|
||||||
var succeeded = await _userService.FollowRequestedAsync(r.Headers["Signature"].First(), r.Method, r.Path, r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
{
|
||||||
|
var succeeded = await _userService.FollowRequestedAsync(signature, r.Method, r.Path,
|
||||||
|
r.QueryString.ToString(), RequestHeaders(r.Headers), activity as ActivityFollow);
|
||||||
if (succeeded) return Accepted();
|
if (succeeded) return Accepted();
|
||||||
else return Unauthorized();
|
else return Unauthorized();
|
||||||
break;
|
}
|
||||||
case "Undo":
|
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();
|
return Accepted();
|
||||||
default:
|
default:
|
||||||
return Accepted();
|
return Accepted();
|
||||||
|
|
Loading…
Add table
Reference in a new issue