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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(',');
|
||||
|
|
|
@ -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();
|
||||
|
|
Reference in a new issue