unfollows from gts => remote now working

This commit is contained in:
tsmethurst 2021-05-20 14:56:03 +02:00
commit 3623205fd7
21 changed files with 368 additions and 47 deletions

View file

@ -309,6 +309,57 @@ func (p *processor) AccountFollowersGet(authed *oauth.Auth, targetAccountID stri
return accounts, nil
}
func (p *processor) AccountFollowingGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode) {
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
if err != nil {
return nil, NewErrorInternalError(err)
}
if blocked {
return nil, NewErrorNotFound(fmt.Errorf("block exists between accounts"))
}
following := []gtsmodel.Follow{}
accounts := []apimodel.Account{}
if err := p.db.GetFollowingByAccountID(targetAccountID, &following); err != nil {
if _, ok := err.(db.ErrNoEntries); ok {
return accounts, nil
}
return nil, NewErrorInternalError(err)
}
for _, f := range following {
blocked, err := p.db.Blocked(authed.Account.ID, f.AccountID)
if err != nil {
return nil, NewErrorInternalError(err)
}
if blocked {
continue
}
a := &gtsmodel.Account{}
if err := p.db.GetByID(f.TargetAccountID, a); err != nil {
if _, ok := err.(db.ErrNoEntries); ok {
continue
}
return nil, NewErrorInternalError(err)
}
// derefence account fields in case we haven't done it already
if err := p.dereferenceAccountFields(a, authed.Account.Username); err != nil {
// don't bail if we can't fetch them, we'll try another time
p.log.WithField("func", "AccountFollowingGet").Debugf("error dereferencing account fields: %s", err)
}
account, err := p.tc.AccountToMastoPublic(a)
if err != nil {
return nil, NewErrorInternalError(err)
}
accounts = append(accounts, *account)
}
return accounts, nil
}
func (p *processor) AccountRelationshipGet(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode) {
if authed == nil || authed.Account == nil {
return nil, NewErrorForbidden(errors.New("not authed"))
@ -410,3 +461,85 @@ func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.Accou
// return whatever relationship results from this
return p.AccountRelationshipGet(authed, form.TargetAccountID)
}
func (p *processor) AccountFollowRemove(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode) {
// if there's a block between the accounts we shouldn't do anything
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
if err != nil {
return nil, NewErrorInternalError(err)
}
if blocked {
return nil, NewErrorNotFound(fmt.Errorf("AccountFollowRemove: block exists between accounts"))
}
// make sure the target account actually exists in our db
targetAcct := &gtsmodel.Account{}
if err := p.db.GetByID(targetAccountID, targetAcct); err != nil {
if _, ok := err.(db.ErrNoEntries); ok {
return nil, NewErrorNotFound(fmt.Errorf("AccountFollowRemove: account %s not found in the db: %s", targetAccountID, err))
}
}
// check if a follow request exists, and remove it if it does (storing the URI for later)
var frChanged bool
var frURI string
fr := &gtsmodel.FollowRequest{}
if err := p.db.GetWhere([]db.Where{
{Key: "account_id", Value: authed.Account.ID},
{Key: "target_account_id", Value: targetAccountID},
}, fr); err == nil {
frURI = fr.URI
if err := p.db.DeleteByID(fr.ID, fr); err != nil {
return nil, NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow request from db: %s", err))
}
frChanged = true
}
// now do the same thing for any existing follow
var fChanged bool
var fURI string
f := &gtsmodel.Follow{}
if err := p.db.GetWhere([]db.Where{
{Key: "account_id", Value: authed.Account.ID},
{Key: "target_account_id", Value: targetAccountID},
}, f); err == nil {
fURI = f.URI
if err := p.db.DeleteByID(f.ID, f); err != nil {
return nil, NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow from db: %s", err))
}
fChanged = true
}
// follow request status changed so send the UNDO activity to the channel for async processing
if frChanged {
p.fromClientAPI <- gtsmodel.FromClientAPI{
APObjectType: gtsmodel.ActivityStreamsFollow,
APActivityType: gtsmodel.ActivityStreamsUndo,
GTSModel: &gtsmodel.Follow{
AccountID: authed.Account.ID,
TargetAccountID: targetAccountID,
URI: frURI,
},
OriginAccount: authed.Account,
TargetAccount: targetAcct,
}
}
// follow status changed so send the UNDO activity to the channel for async processing
if fChanged {
p.fromClientAPI <- gtsmodel.FromClientAPI{
APObjectType: gtsmodel.ActivityStreamsFollow,
APActivityType: gtsmodel.ActivityStreamsUndo,
GTSModel: &gtsmodel.Follow{
AccountID: authed.Account.ID,
TargetAccountID: targetAccountID,
URI: fURI,
},
OriginAccount: authed.Account,
TargetAccount: targetAcct,
}
}
// return whatever relationship results from all this
return p.AccountRelationshipGet(authed, targetAccountID)
}

View file

@ -46,7 +46,7 @@ func (p *processor) authenticateAndDereferenceFediRequest(username string, r *ht
// we might already have an entry for this account so check that first
requestingAccount := &gtsmodel.Account{}
err = p.db.GetWhere("uri", requestingAccountURI.String(), requestingAccount)
err = p.db.GetWhere([]db.Where{{Key: "uri", Value: requestingAccountURI.String()}}, requestingAccount)
if err == nil {
// we do have it yay, return it
return requestingAccount, nil

View file

@ -71,6 +71,17 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error
return errors.New("accept was not parseable as *gtsmodel.Follow")
}
return p.federateAcceptFollowRequest(follow)
case gtsmodel.ActivityStreamsUndo:
// UNDO
switch clientMsg.APObjectType {
// UNDO FOLLOW
case gtsmodel.ActivityStreamsFollow:
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
if !ok {
return errors.New("undo was not parseable as *gtsmodel.Follow")
}
return p.federateUnfollow(follow, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
}
return nil
}
@ -117,11 +128,46 @@ func (p *processor) federateFollow(follow *gtsmodel.Follow, originAccount *gtsmo
return err
}
func (p *processor) federateUnfollow(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
// recreate the follow
asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount)
if err != nil {
return fmt.Errorf("federateUnfollow: error converting follow to as format: %s", err)
}
targetAccountURI, err := url.Parse(targetAccount.URI)
if err != nil {
return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
}
// create an Undo and set the appropriate actor on it
undo := streams.NewActivityStreamsUndo()
undo.SetActivityStreamsActor(asFollow.GetActivityStreamsActor())
// Set the recreated follow as the 'object' property.
undoObject := streams.NewActivityStreamsObjectProperty()
undoObject.AppendActivityStreamsFollow(asFollow)
undo.SetActivityStreamsObject(undoObject)
// Set the To of the undo as the target of the recreated follow
undoTo := streams.NewActivityStreamsToProperty()
undoTo.AppendIRI(targetAccountURI)
undo.SetActivityStreamsTo(undoTo)
outboxIRI, err := url.Parse(originAccount.OutboxURI)
if err != nil {
return fmt.Errorf("federateUnfollow: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
}
// send off the Undo
_, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, undo)
return err
}
func (p *processor) federateAcceptFollowRequest(follow *gtsmodel.Follow) error {
// TODO: tidy up this whole function -- move most of the logic for the conversion to the type converter because this is just a mess! Shame on me!
followAccepter := &gtsmodel.Account{}
if err := p.db.GetByID(follow.TargetAccountID, followAccepter); err != nil {
return fmt.Errorf("error federating follow accept: %s", err)

View file

@ -157,7 +157,7 @@ func (p *processor) dereferenceStatusFields(status *gtsmodel.Status) error {
// it might have been processed elsewhere so check first if it's already in the database or not
maybeAttachment := &gtsmodel.MediaAttachment{}
err := p.db.GetWhere("remote_url", a.RemoteURL, maybeAttachment)
err := p.db.GetWhere([]db.Where{{Key: "remote_url", Value: a.RemoteURL}}, maybeAttachment)
if err == nil {
// we already have it in the db, dereferenced, no need to do it again
l.Debugf("attachment already exists with id %s", maybeAttachment.ID)
@ -206,7 +206,7 @@ func (p *processor) dereferenceStatusFields(status *gtsmodel.Status) error {
m.OriginAccountURI = status.GTSAccount.URI
targetAccount := &gtsmodel.Account{}
if err := p.db.GetWhere("uri", uri.String(), targetAccount); err != nil {
if err := p.db.GetWhere([]db.Where{{Key: "uri", Value: uri.String()}}, targetAccount); err != nil {
// proper error
if _, ok := err.(db.ErrNoEntries); !ok {
return fmt.Errorf("db error checking for account with uri %s", uri.String())

View file

@ -22,12 +22,13 @@ import (
"fmt"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
func (p *processor) InstanceGet(domain string) (*apimodel.Instance, ErrorWithCode) {
i := &gtsmodel.Instance{}
if err := p.db.GetWhere("domain", domain, i); err != nil {
if err := p.db.GetWhere([]db.Where{{Key: "domain", Value: domain}}, i); err != nil {
return nil, NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err))
}

View file

@ -73,10 +73,14 @@ type Processor interface {
AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, ErrorWithCode)
// AccountFollowersGet fetches a list of the target account's followers.
AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode)
// AccountFollowingGet fetches a list of the accounts that target account is following.
AccountFollowingGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode)
// AccountRelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
AccountRelationshipGet(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode)
// AccountFollowCreate handles a follow request to an account, either remote or local.
AccountFollowCreate(authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, ErrorWithCode)
// AccountFollowRemove handles the removal of a follow/follow request to an account, either remote or local.
AccountFollowRemove(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode)
// AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form.
AdminEmojiCreate(authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error)
@ -204,15 +208,11 @@ func (p *processor) Start() error {
DistLoop:
for {
select {
// case clientMsg := <-p.toClientAPI:
// p.log.Infof("received message TO client API: %+v", clientMsg)
case clientMsg := <-p.fromClientAPI:
p.log.Infof("received message FROM client API: %+v", clientMsg)
if err := p.processFromClientAPI(clientMsg); err != nil {
p.log.Error(err)
}
// case federatorMsg := <-p.toFederator:
// p.log.Infof("received message TO federator: %+v", federatorMsg)
case federatorMsg := <-p.fromFederator:
p.log.Infof("received message FROM federator: %+v", federatorMsg)
if err := p.processFromFederator(federatorMsg); err != nil {