get remote follows/accepts working

This commit is contained in:
tsmethurst 2021-05-19 22:07:27 +02:00
commit d69786ef17
16 changed files with 459 additions and 104 deletions

View file

@ -326,3 +326,87 @@ func (p *processor) AccountRelationshipGet(authed *oauth.Auth, targetAccountID s
return r, nil
}
func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, ErrorWithCode) {
// if there's a block between the accounts we shouldn't create the request ofc
blocked, err := p.db.Blocked(authed.Account.ID, form.TargetAccountID)
if err != nil {
return nil, NewErrorInternalError(err)
}
if blocked {
return nil, NewErrorNotFound(fmt.Errorf("accountfollowcreate: block exists between accounts"))
}
// make sure the target account actually exists in our db
targetAcct := &gtsmodel.Account{}
if err := p.db.GetByID(form.TargetAccountID, targetAcct); err != nil {
if _, ok := err.(db.ErrNoEntries); ok {
return nil, NewErrorNotFound(fmt.Errorf("accountfollowcreate: account %s not found in the db: %s", form.TargetAccountID, err))
}
}
// check if a follow exists already
follows, err := p.db.Follows(authed.Account, targetAcct)
if err != nil {
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error checking follow in db: %s", err))
}
if follows {
// already follows so just return the relationship
return p.AccountRelationshipGet(authed, form.TargetAccountID)
}
// check if a follow exists already
followRequested, err := p.db.FollowRequested(authed.Account, targetAcct)
if err != nil {
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error checking follow request in db: %s", err))
}
if followRequested {
// already follow requested so just return the relationship
return p.AccountRelationshipGet(authed, form.TargetAccountID)
}
// make the follow request
fr := &gtsmodel.FollowRequest{
AccountID: authed.Account.ID,
TargetAccountID: form.TargetAccountID,
ShowReblogs: true,
URI: util.GenerateURIForFollow(authed.Account.Username, p.config.Protocol, p.config.Host),
Notify: false,
}
if form.Reblogs != nil {
fr.ShowReblogs = *form.Reblogs
}
if form.Notify != nil {
fr.Notify = *form.Notify
}
// whack it in the database
if err := p.db.Put(fr); err != nil {
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error creating follow request in db: %s", err))
}
// if it's a local account that's not locked we can just straight up accept the follow request
if !targetAcct.Locked && targetAcct.Domain == "" {
if _, err := p.db.AcceptFollowRequest(authed.Account.ID, form.TargetAccountID); err != nil {
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error accepting folow request for local unlocked account: %s", err))
}
// return the new relationship
return p.AccountRelationshipGet(authed, form.TargetAccountID)
}
// otherwise we leave the follow request as it is and we handle the rest of the process asynchronously
p.fromClientAPI <- gtsmodel.FromClientAPI{
APObjectType: gtsmodel.ActivityStreamsFollow,
APActivityType: gtsmodel.ActivityStreamsCreate,
GTSModel: &gtsmodel.Follow{
AccountID: authed.Account.ID,
TargetAccountID: form.TargetAccountID,
URI: fr.URI,
},
OriginAccount: authed.Account,
TargetAccount: targetAcct,
}
// return whatever relationship results from this
return p.AccountRelationshipGet(authed, form.TargetAccountID)
}

View file

@ -49,6 +49,18 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error
return p.federateStatus(status)
}
return nil
case gtsmodel.ActivityStreamsFollow:
// CREATE FOLLOW (request)
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
if !ok {
return errors.New("follow was not parseable as *gtsmodel.Follow")
}
if err := p.notifyFollow(follow); err != nil {
return err
}
return p.federateFollow(follow, clientMsg.OriginAccount, clientMsg.TargetAccount)
}
case gtsmodel.ActivityStreamsUpdate:
// UPDATE
@ -90,8 +102,26 @@ func (p *processor) federateStatus(status *gtsmodel.Status) error {
return nil
}
func (p *processor) federateFollow(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount)
if err != nil {
return fmt.Errorf("federateFollow: error converting follow to as format: %s", err)
}
outboxIRI, err := url.Parse(originAccount.OutboxURI)
if err != nil {
return fmt.Errorf("federateFollow: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
}
_, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, asFollow)
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

@ -23,3 +23,7 @@ import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
func (p *processor) notifyStatus(status *gtsmodel.Status) error {
return nil
}
func (p *processor) notifyFollow(follow *gtsmodel.Follow) error {
return nil
}

View file

@ -71,10 +71,12 @@ type Processor interface {
// AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
// the account given in authed.
AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, ErrorWithCode)
// AccountFollowersGet
// AccountFollowersGet fetches a list of the target account's followers.
AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode)
// AccountRelationshipGet
// 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)
// 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)