gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

relationships.go (5929B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package account
     19 
     20 import (
     21 	"context"
     22 	"errors"
     23 	"fmt"
     24 
     25 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
     26 	"github.com/superseriousbusiness/gotosocial/internal/db"
     27 	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
     28 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
     29 	"github.com/superseriousbusiness/gotosocial/internal/log"
     30 )
     31 
     32 // FollowersGet fetches a list of the target account's followers.
     33 func (p *Processor) FollowersGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
     34 	if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, targetAccountID); err != nil {
     35 		err = fmt.Errorf("FollowersGet: db error checking block: %w", err)
     36 		return nil, gtserror.NewErrorInternalError(err)
     37 	} else if blocked {
     38 		err = errors.New("FollowersGet: block exists between accounts")
     39 		return nil, gtserror.NewErrorNotFound(err)
     40 	}
     41 
     42 	follows, err := p.state.DB.GetAccountFollowers(ctx, targetAccountID)
     43 	if err != nil {
     44 		if !errors.Is(err, db.ErrNoEntries) {
     45 			err = fmt.Errorf("FollowersGet: db error getting followers: %w", err)
     46 			return nil, gtserror.NewErrorInternalError(err)
     47 		}
     48 		return []apimodel.Account{}, nil
     49 	}
     50 
     51 	return p.accountsFromFollows(ctx, follows, requestingAccount.ID)
     52 }
     53 
     54 // FollowingGet fetches a list of the accounts that target account is following.
     55 func (p *Processor) FollowingGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
     56 	if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccount.ID, targetAccountID); err != nil {
     57 		err = fmt.Errorf("FollowingGet: db error checking block: %w", err)
     58 		return nil, gtserror.NewErrorInternalError(err)
     59 	} else if blocked {
     60 		err = errors.New("FollowingGet: block exists between accounts")
     61 		return nil, gtserror.NewErrorNotFound(err)
     62 	}
     63 
     64 	follows, err := p.state.DB.GetAccountFollows(ctx, targetAccountID)
     65 	if err != nil {
     66 		if !errors.Is(err, db.ErrNoEntries) {
     67 			err = fmt.Errorf("FollowingGet: db error getting followers: %w", err)
     68 			return nil, gtserror.NewErrorInternalError(err)
     69 		}
     70 		return []apimodel.Account{}, nil
     71 	}
     72 
     73 	return p.targetAccountsFromFollows(ctx, follows, requestingAccount.ID)
     74 }
     75 
     76 // RelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
     77 func (p *Processor) RelationshipGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
     78 	if requestingAccount == nil {
     79 		return nil, gtserror.NewErrorForbidden(errors.New("not authed"))
     80 	}
     81 
     82 	gtsR, err := p.state.DB.GetRelationship(ctx, requestingAccount.ID, targetAccountID)
     83 	if err != nil {
     84 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relationship: %s", err))
     85 	}
     86 
     87 	r, err := p.tc.RelationshipToAPIRelationship(ctx, gtsR)
     88 	if err != nil {
     89 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting relationship: %s", err))
     90 	}
     91 
     92 	return r, nil
     93 }
     94 
     95 func (p *Processor) accountsFromFollows(ctx context.Context, follows []*gtsmodel.Follow, requestingAccountID string) ([]apimodel.Account, gtserror.WithCode) {
     96 	accounts := make([]apimodel.Account, 0, len(follows))
     97 	for _, follow := range follows {
     98 		if follow.Account == nil {
     99 			// No account set for some reason; just skip.
    100 			log.WithContext(ctx).WithField("follow", follow).Warn("follow had no associated account")
    101 			continue
    102 		}
    103 
    104 		if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccountID, follow.AccountID); err != nil {
    105 			err = fmt.Errorf("accountsFromFollows: db error checking block: %w", err)
    106 			return nil, gtserror.NewErrorInternalError(err)
    107 		} else if blocked {
    108 			continue
    109 		}
    110 
    111 		account, err := p.tc.AccountToAPIAccountPublic(ctx, follow.Account)
    112 		if err != nil {
    113 			err = fmt.Errorf("accountsFromFollows: error converting account to api account: %w", err)
    114 			return nil, gtserror.NewErrorInternalError(err)
    115 		}
    116 
    117 		accounts = append(accounts, *account)
    118 	}
    119 	return accounts, nil
    120 }
    121 
    122 func (p *Processor) targetAccountsFromFollows(ctx context.Context, follows []*gtsmodel.Follow, requestingAccountID string) ([]apimodel.Account, gtserror.WithCode) {
    123 	accounts := make([]apimodel.Account, 0, len(follows))
    124 	for _, follow := range follows {
    125 		if follow.TargetAccount == nil {
    126 			// No account set for some reason; just skip.
    127 			log.WithContext(ctx).WithField("follow", follow).Warn("follow had no associated target account")
    128 			continue
    129 		}
    130 
    131 		if blocked, err := p.state.DB.IsEitherBlocked(ctx, requestingAccountID, follow.TargetAccountID); err != nil {
    132 			err = fmt.Errorf("targetAccountsFromFollows: db error checking block: %w", err)
    133 			return nil, gtserror.NewErrorInternalError(err)
    134 		} else if blocked {
    135 			continue
    136 		}
    137 
    138 		account, err := p.tc.AccountToAPIAccountPublic(ctx, follow.TargetAccount)
    139 		if err != nil {
    140 			err = fmt.Errorf("targetAccountsFromFollows: error converting account to api account: %w", err)
    141 			return nil, gtserror.NewErrorInternalError(err)
    142 		}
    143 
    144 		accounts = append(accounts, *account)
    145 	}
    146 	return accounts, nil
    147 }