diff options
Diffstat (limited to 'internal/processing/synchronous/status')
-rw-r--r-- | internal/processing/synchronous/status/boost.go | 73 | ||||
-rw-r--r-- | internal/processing/synchronous/status/boostedby.go | 68 | ||||
-rw-r--r-- | internal/processing/synchronous/status/context.go | 69 | ||||
-rw-r--r-- | internal/processing/synchronous/status/create.go | 106 | ||||
-rw-r--r-- | internal/processing/synchronous/status/delete.go | 55 | ||||
-rw-r--r-- | internal/processing/synchronous/status/fave.go | 101 | ||||
-rw-r--r-- | internal/processing/synchronous/status/favedby.go | 68 | ||||
-rw-r--r-- | internal/processing/synchronous/status/get.go | 52 | ||||
-rw-r--r-- | internal/processing/synchronous/status/status.go | 57 | ||||
-rw-r--r-- | internal/processing/synchronous/status/unboost.go | 95 | ||||
-rw-r--r-- | internal/processing/synchronous/status/unfave.go | 77 | ||||
-rw-r--r-- | internal/processing/synchronous/status/util.go | 269 |
12 files changed, 0 insertions, 1090 deletions
diff --git a/internal/processing/synchronous/status/boost.go b/internal/processing/synchronous/status/boost.go deleted file mode 100644 index 93d0f19de..000000000 --- a/internal/processing/synchronous/status/boost.go +++ /dev/null @@ -1,73 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) Boost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { - l := p.log.WithField("func", "StatusBoost") - - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) - } - - l.Tracef("going to search for target account %s", targetStatus.AccountID) - targetAccount := >smodel.Account{} - if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)) - } - - l.Trace("going to see if status is visible") - visible, err := p.filter.StatusVisible(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) - } - - if !visible { - return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) - } - - if targetStatus.VisibilityAdvanced != nil { - if !targetStatus.VisibilityAdvanced.Boostable { - return nil, gtserror.NewErrorForbidden(errors.New("status is not boostable")) - } - } - - // it's visible! it's boostable! so let's boost the FUCK out of it - boostWrapperStatus, err := p.tc.StatusToBoost(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - boostWrapperStatus.CreatedWithApplicationID = application.ID - boostWrapperStatus.GTSBoostedAccount = targetAccount - - // put the boost in the database - if err := p.db.Put(boostWrapperStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // send it back to the processor for async processing - p.fromClientAPI <- gtsmodel.FromClientAPI{ - APObjectType: gtsmodel.ActivityStreamsAnnounce, - APActivityType: gtsmodel.ActivityStreamsCreate, - GTSModel: boostWrapperStatus, - OriginAccount: account, - TargetAccount: targetAccount, - } - - // return the frontend representation of the new status to the submitter - mastoStatus, err := p.tc.StatusToMasto(boostWrapperStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) - } - - return mastoStatus, nil -} diff --git a/internal/processing/synchronous/status/boostedby.go b/internal/processing/synchronous/status/boostedby.go deleted file mode 100644 index b352178e3..000000000 --- a/internal/processing/synchronous/status/boostedby.go +++ /dev/null @@ -1,68 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) BoostedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) { - l := p.log.WithField("func", "StatusBoostedBy") - - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching status %s: %s", targetStatusID, err)) - } - - l.Tracef("going to search for target account %s", targetStatus.AccountID) - targetAccount := >smodel.Account{} - if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching target account %s: %s", targetStatus.AccountID, err)) - } - - l.Trace("going to see if status is visible") - visible, err := p.filter.StatusVisible(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err)) - } - - if !visible { - return nil, gtserror.NewErrorNotFound(errors.New("StatusBoostedBy: status is not visible")) - } - - // get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff - favingAccounts, err := p.db.WhoBoostedStatus(targetStatus) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing who boosted status: %s", err)) - } - - // filter the list so the user doesn't see accounts they blocked or which blocked them - filteredAccounts := []*gtsmodel.Account{} - for _, acc := range favingAccounts { - blocked, err := p.db.Blocked(account.ID, acc.ID) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error checking blocks: %s", err)) - } - if !blocked { - filteredAccounts = append(filteredAccounts, acc) - } - } - - // TODO: filter other things here? suspended? muted? silenced? - - // now we can return the masto representation of those accounts - mastoAccounts := []*apimodel.Account{} - for _, acc := range filteredAccounts { - mastoAccount, err := p.tc.AccountToMastoPublic(acc) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusFavedBy: error converting account to api model: %s", err)) - } - mastoAccounts = append(mastoAccounts, mastoAccount) - } - - return mastoAccounts, nil -} diff --git a/internal/processing/synchronous/status/context.go b/internal/processing/synchronous/status/context.go deleted file mode 100644 index 72b9b5623..000000000 --- a/internal/processing/synchronous/status/context.go +++ /dev/null @@ -1,69 +0,0 @@ -package status - -import ( - "fmt" - "sort" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) { - - context := &apimodel.Context{ - Ancestors: []apimodel.Status{}, - Descendants: []apimodel.Status{}, - } - - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - if _, ok := err.(db.ErrNoEntries); ok { - return nil, gtserror.NewErrorNotFound(err) - } - return nil, gtserror.NewErrorInternalError(err) - } - - visible, err := p.filter.StatusVisible(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorNotFound(err) - } - if !visible { - return nil, gtserror.NewErrorForbidden(fmt.Errorf("account with id %s does not have permission to view status %s", account.ID, targetStatusID)) - } - - parents, err := p.db.StatusParents(targetStatus) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - for _, status := range parents { - if v, err := p.filter.StatusVisible(status, account); err == nil && v { - mastoStatus, err := p.tc.StatusToMasto(status, account) - if err == nil { - context.Ancestors = append(context.Ancestors, *mastoStatus) - } - } - } - - sort.Slice(context.Ancestors, func(i int, j int) bool { - return context.Ancestors[i].ID < context.Ancestors[j].ID - }) - - children, err := p.db.StatusChildren(targetStatus) - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - for _, status := range children { - if v, err := p.filter.StatusVisible(status, account); err == nil && v { - mastoStatus, err := p.tc.StatusToMasto(status, account) - if err == nil { - context.Descendants = append(context.Descendants, *mastoStatus) - } - } - } - - return context, nil -} diff --git a/internal/processing/synchronous/status/create.go b/internal/processing/synchronous/status/create.go deleted file mode 100644 index aa7468ae5..000000000 --- a/internal/processing/synchronous/status/create.go +++ /dev/null @@ -1,106 +0,0 @@ -package status - -import ( - "fmt" - "time" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/id" - "github.com/superseriousbusiness/gotosocial/internal/util" -) - -func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) { - uris := util.GenerateURIsForAccount(account.Username, p.config.Protocol, p.config.Host) - thisStatusID, err := id.NewULID() - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - thisStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, thisStatusID) - thisStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, thisStatusID) - - newStatus := >smodel.Status{ - ID: thisStatusID, - URI: thisStatusURI, - URL: thisStatusURL, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - Local: true, - AccountID: account.ID, - AccountURI: account.URI, - ContentWarning: form.SpoilerText, - ActivityStreamsType: gtsmodel.ActivityStreamsNote, - Sensitive: form.Sensitive, - Language: form.Language, - CreatedWithApplicationID: application.ID, - Text: form.Status, - } - - // check if replyToID is ok - if err := p.processReplyToID(form, account.ID, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // check if mediaIDs are ok - if err := p.processMediaIDs(form, account.ID, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // check if visibility settings are ok - if err := p.processVisibility(form, account.Privacy, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // handle language settings - if err := p.processLanguage(form, account.Language, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // handle mentions - if err := p.processMentions(form, account.ID, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - if err := p.processTags(form, account.ID, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - if err := p.processEmojis(form, account.ID, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - if err := p.processContent(form, account.ID, newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // put the new status in the database, generating an ID for it in the process - if err := p.db.Put(newStatus); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // change the status ID of the media attachments to the new status - for _, a := range newStatus.GTSMediaAttachments { - a.StatusID = newStatus.ID - a.UpdatedAt = time.Now() - if err := p.db.UpdateByID(a.ID, a); err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - } - - // send it back to the processor for async processing - p.fromClientAPI <- gtsmodel.FromClientAPI{ - APObjectType: gtsmodel.ActivityStreamsNote, - APActivityType: gtsmodel.ActivityStreamsCreate, - GTSModel: newStatus, - OriginAccount: account, - } - - // return the frontend representation of the new status to the submitter - mastoStatus, err := p.tc.StatusToMasto(newStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", newStatus.ID, err)) - } - - return mastoStatus, nil -} diff --git a/internal/processing/synchronous/status/delete.go b/internal/processing/synchronous/status/delete.go deleted file mode 100644 index 5da196a9f..000000000 --- a/internal/processing/synchronous/status/delete.go +++ /dev/null @@ -1,55 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) Delete(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { - l := p.log.WithField("func", "StatusDelete") - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - if _, ok := err.(db.ErrNoEntries); !ok { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) - } - // status is already gone - return nil, nil - } - - if targetStatus.AccountID != account.ID { - return nil, gtserror.NewErrorForbidden(errors.New("status doesn't belong to requesting account")) - } - - var boostOfStatus *gtsmodel.Status - if targetStatus.BoostOfID != "" { - boostOfStatus = >smodel.Status{} - if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)) - } - } - - mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) - } - - if err := p.db.DeleteByID(targetStatus.ID, >smodel.Status{}); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error deleting status from the database: %s", err)) - } - - // send it back to the processor for async processing - p.fromClientAPI <- gtsmodel.FromClientAPI{ - APObjectType: gtsmodel.ActivityStreamsNote, - APActivityType: gtsmodel.ActivityStreamsDelete, - GTSModel: targetStatus, - OriginAccount: account, - } - - return mastoStatus, nil -} diff --git a/internal/processing/synchronous/status/fave.go b/internal/processing/synchronous/status/fave.go deleted file mode 100644 index 23f0d2944..000000000 --- a/internal/processing/synchronous/status/fave.go +++ /dev/null @@ -1,101 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/id" - "github.com/superseriousbusiness/gotosocial/internal/util" -) - -func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { - l := p.log.WithField("func", "StatusFave") - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) - } - - l.Tracef("going to search for target account %s", targetStatus.AccountID) - targetAccount := >smodel.Account{} - if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)) - } - - var boostOfStatus *gtsmodel.Status - if targetStatus.BoostOfID != "" { - boostOfStatus = >smodel.Status{} - if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)) - } - } - - l.Trace("going to see if status is visible") - visible, err := p.filter.StatusVisible(targetStatus, account) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) - } - - if !visible { - return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) - } - - // is the status faveable? - if targetStatus.VisibilityAdvanced != nil { - if !targetStatus.VisibilityAdvanced.Likeable { - return nil, gtserror.NewErrorForbidden(errors.New("status is not faveable")) - } - } - - // first check if the status is already faved, if so we don't need to do anything - newFave := true - gtsFave := >smodel.StatusFave{} - if err := p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave); err == nil { - // we already have a fave for this status - newFave = false - } - - if newFave { - thisFaveID, err := id.NewRandomULID() - if err != nil { - return nil, gtserror.NewErrorInternalError(err) - } - - // we need to create a new fave in the database - gtsFave := >smodel.StatusFave{ - ID: thisFaveID, - AccountID: account.ID, - TargetAccountID: targetAccount.ID, - StatusID: targetStatus.ID, - URI: util.GenerateURIForLike(account.Username, p.config.Protocol, p.config.Host, thisFaveID), - GTSStatus: targetStatus, - GTSTargetAccount: targetAccount, - GTSFavingAccount: account, - } - - if err := p.db.Put(gtsFave); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error putting fave in database: %s", err)) - } - - // send it back to the processor for async processing - p.fromClientAPI <- gtsmodel.FromClientAPI{ - APObjectType: gtsmodel.ActivityStreamsLike, - APActivityType: gtsmodel.ActivityStreamsCreate, - GTSModel: gtsFave, - OriginAccount: account, - TargetAccount: targetAccount, - } - } - - // return the mastodon representation of the target status - mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) - } - - return mastoStatus, nil -} diff --git a/internal/processing/synchronous/status/favedby.go b/internal/processing/synchronous/status/favedby.go deleted file mode 100644 index 5194cc258..000000000 --- a/internal/processing/synchronous/status/favedby.go +++ /dev/null @@ -1,68 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) FavedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) { - l := p.log.WithField("func", "StatusFavedBy") - - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) - } - - l.Tracef("going to search for target account %s", targetStatus.AccountID) - targetAccount := >smodel.Account{} - if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)) - } - - l.Trace("going to see if status is visible") - visible, err := p.filter.StatusVisible(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) - } - - if !visible { - return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) - } - - // get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff - favingAccounts, err := p.db.WhoFavedStatus(targetStatus) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing who faved status: %s", err)) - } - - // filter the list so the user doesn't see accounts they blocked or which blocked them - filteredAccounts := []*gtsmodel.Account{} - for _, acc := range favingAccounts { - blocked, err := p.db.Blocked(account.ID, acc.ID) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking blocks: %s", err)) - } - if !blocked { - filteredAccounts = append(filteredAccounts, acc) - } - } - - // TODO: filter other things here? suspended? muted? silenced? - - // now we can return the masto representation of those accounts - mastoAccounts := []*apimodel.Account{} - for _, acc := range filteredAccounts { - mastoAccount, err := p.tc.AccountToMastoPublic(acc) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) - } - mastoAccounts = append(mastoAccounts, mastoAccount) - } - - return mastoAccounts, nil -} diff --git a/internal/processing/synchronous/status/get.go b/internal/processing/synchronous/status/get.go deleted file mode 100644 index 9a70185b0..000000000 --- a/internal/processing/synchronous/status/get.go +++ /dev/null @@ -1,52 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) Get(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { - l := p.log.WithField("func", "StatusGet") - - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) - } - - l.Tracef("going to search for target account %s", targetStatus.AccountID) - targetAccount := >smodel.Account{} - if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)) - } - - l.Trace("going to see if status is visible") - visible, err := p.filter.StatusVisible(targetStatus, account) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) - } - - if !visible { - return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) - } - - var boostOfStatus *gtsmodel.Status - if targetStatus.BoostOfID != "" { - boostOfStatus = >smodel.Status{} - if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)) - } - } - - mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) - } - - return mastoStatus, nil - -} diff --git a/internal/processing/synchronous/status/status.go b/internal/processing/synchronous/status/status.go deleted file mode 100644 index d83c325fd..000000000 --- a/internal/processing/synchronous/status/status.go +++ /dev/null @@ -1,57 +0,0 @@ -package status - -import ( - "github.com/sirupsen/logrus" - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/config" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/typeutils" - "github.com/superseriousbusiness/gotosocial/internal/visibility" -) - -// Processor wraps a bunch of functions for processing statuses. -type Processor interface { - // Create processes the given form to create a new status, returning the api model representation of that status if it's OK. - Create(account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) - // Delete processes the delete of a given status, returning the deleted status if the delete goes through. - Delete(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) - // Fave processes the faving of a given status, returning the updated status if the fave goes through. - Fave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) - // Boost processes the boost/reblog of a given status, returning the newly-created boost if all is well. - Boost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) - // Unboost processes the unboost/unreblog of a given status, returning the status if all is well. - Unboost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) - // BoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings. - BoostedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) - // FavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings. - FavedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) - // Get gets the given status, taking account of privacy settings and blocks etc. - Get(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) - // Unfave processes the unfaving of a given status, returning the updated status if the fave goes through. - Unfave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) - // Context returns the context (previous and following posts) from the given status ID - Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) -} - -type processor struct { - tc typeutils.TypeConverter - config *config.Config - db db.DB - filter visibility.Filter - fromClientAPI chan gtsmodel.FromClientAPI - log *logrus.Logger -} - -// New returns a new status processor. -func New(db db.DB, tc typeutils.TypeConverter, config *config.Config, fromClientAPI chan gtsmodel.FromClientAPI, log *logrus.Logger) Processor { - return &processor{ - tc: tc, - config: config, - db: db, - filter: visibility.NewFilter(db, log), - fromClientAPI: fromClientAPI, - log: log, - } -} diff --git a/internal/processing/synchronous/status/unboost.go b/internal/processing/synchronous/status/unboost.go deleted file mode 100644 index 2a1394695..000000000 --- a/internal/processing/synchronous/status/unboost.go +++ /dev/null @@ -1,95 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) Unboost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { - l := p.log.WithField("func", "Unboost") - - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) - } - - l.Tracef("going to search for target account %s", targetStatus.AccountID) - targetAccount := >smodel.Account{} - if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)) - } - - l.Trace("going to see if status is visible") - visible, err := p.filter.StatusVisible(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) - } - - if !visible { - return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) - } - - // check if we actually have a boost for this status - var toUnboost bool - - gtsBoost := >smodel.Status{} - where := []db.Where{ - { - Key: "boost_of_id", - Value: targetStatusID, - }, - { - Key: "account_id", - Value: account.ID, - }, - } - err = p.db.GetWhere(where, gtsBoost) - if err == nil { - // we have a boost - toUnboost = true - } - - if err != nil { - // something went wrong in the db finding the boost - if _, ok := err.(db.ErrNoEntries); !ok { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching existing boost from database: %s", err)) - } - // we just don't have a boost - toUnboost = false - } - - if toUnboost { - // we had a boost, so take some action to get rid of it - if err := p.db.DeleteWhere(where, >smodel.Status{}); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unboosting status: %s", err)) - } - - // pin some stuff onto the boost while we have it out of the db - gtsBoost.GTSBoostedStatus = targetStatus - gtsBoost.GTSBoostedStatus.GTSAuthorAccount = targetAccount - gtsBoost.GTSBoostedAccount = targetAccount - gtsBoost.GTSAuthorAccount = account - - // send it back to the processor for async processing - p.fromClientAPI <- gtsmodel.FromClientAPI{ - APObjectType: gtsmodel.ActivityStreamsAnnounce, - APActivityType: gtsmodel.ActivityStreamsUndo, - GTSModel: gtsBoost, - OriginAccount: account, - TargetAccount: targetAccount, - } - } - - mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) - } - - return mastoStatus, nil -} diff --git a/internal/processing/synchronous/status/unfave.go b/internal/processing/synchronous/status/unfave.go deleted file mode 100644 index b51daacb9..000000000 --- a/internal/processing/synchronous/status/unfave.go +++ /dev/null @@ -1,77 +0,0 @@ -package status - -import ( - "errors" - "fmt" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtserror" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" -) - -func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { - l := p.log.WithField("func", "StatusUnfave") - l.Tracef("going to search for target status %s", targetStatusID) - targetStatus := >smodel.Status{} - if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) - } - - l.Tracef("going to search for target account %s", targetStatus.AccountID) - targetAccount := >smodel.Account{} - if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)) - } - - l.Trace("going to see if status is visible") - visible, err := p.filter.StatusVisible(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) - } - - if !visible { - return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) - } - - // check if we actually have a fave for this status - var toUnfave bool - - gtsFave := >smodel.StatusFave{} - err = p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave) - if err == nil { - // we have a fave - toUnfave = true - } - if err != nil { - // something went wrong in the db finding the fave - if _, ok := err.(db.ErrNoEntries); !ok { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching existing fave from database: %s", err)) - } - // we just don't have a fave - toUnfave = false - } - - if toUnfave { - // we had a fave, so take some action to get rid of it - if err := p.db.DeleteWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err)) - } - - // send it back to the processor for async processing - p.fromClientAPI <- gtsmodel.FromClientAPI{ - APObjectType: gtsmodel.ActivityStreamsLike, - APActivityType: gtsmodel.ActivityStreamsUndo, - GTSModel: gtsFave, - OriginAccount: account, - TargetAccount: targetAccount, - } - } - - mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) - if err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) - } - - return mastoStatus, nil -} diff --git a/internal/processing/synchronous/status/util.go b/internal/processing/synchronous/status/util.go deleted file mode 100644 index 0a023eab6..000000000 --- a/internal/processing/synchronous/status/util.go +++ /dev/null @@ -1,269 +0,0 @@ -package status - -import ( - "errors" - "fmt" - "strings" - - apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" - "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/id" - "github.com/superseriousbusiness/gotosocial/internal/util" -) - -func (p *processor) processVisibility(form *apimodel.AdvancedStatusCreateForm, accountDefaultVis gtsmodel.Visibility, status *gtsmodel.Status) error { - // by default all flags are set to true - gtsAdvancedVis := >smodel.VisibilityAdvanced{ - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, - } - - var gtsBasicVis gtsmodel.Visibility - // Advanced takes priority if it's set. - // If it's not set, take whatever masto visibility is set. - // If *that's* not set either, then just take the account default. - // If that's also not set, take the default for the whole instance. - if form.VisibilityAdvanced != nil { - gtsBasicVis = gtsmodel.Visibility(*form.VisibilityAdvanced) - } else if form.Visibility != "" { - gtsBasicVis = p.tc.MastoVisToVis(form.Visibility) - } else if accountDefaultVis != "" { - gtsBasicVis = accountDefaultVis - } else { - gtsBasicVis = gtsmodel.VisibilityDefault - } - - switch gtsBasicVis { - case gtsmodel.VisibilityPublic: - // for public, there's no need to change any of the advanced flags from true regardless of what the user filled out - break - case gtsmodel.VisibilityUnlocked: - // for unlocked the user can set any combination of flags they like so look at them all to see if they're set and then apply them - if form.Federated != nil { - gtsAdvancedVis.Federated = *form.Federated - } - - if form.Boostable != nil { - gtsAdvancedVis.Boostable = *form.Boostable - } - - if form.Replyable != nil { - gtsAdvancedVis.Replyable = *form.Replyable - } - - if form.Likeable != nil { - gtsAdvancedVis.Likeable = *form.Likeable - } - - case gtsmodel.VisibilityFollowersOnly, gtsmodel.VisibilityMutualsOnly: - // for followers or mutuals only, boostable will *always* be false, but the other fields can be set so check and apply them - gtsAdvancedVis.Boostable = false - - if form.Federated != nil { - gtsAdvancedVis.Federated = *form.Federated - } - - if form.Replyable != nil { - gtsAdvancedVis.Replyable = *form.Replyable - } - - if form.Likeable != nil { - gtsAdvancedVis.Likeable = *form.Likeable - } - - case gtsmodel.VisibilityDirect: - // direct is pretty easy: there's only one possible setting so return it - gtsAdvancedVis.Federated = true - gtsAdvancedVis.Boostable = false - gtsAdvancedVis.Federated = true - gtsAdvancedVis.Likeable = true - } - - status.Visibility = gtsBasicVis - status.VisibilityAdvanced = gtsAdvancedVis - return nil -} - -func (p *processor) processReplyToID(form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error { - if form.InReplyToID == "" { - return nil - } - - // If this status is a reply to another status, we need to do a bit of work to establish whether or not this status can be posted: - // - // 1. Does the replied status exist in the database? - // 2. Is the replied status marked as replyable? - // 3. Does a block exist between either the current account or the account that posted the status it's replying to? - // - // If this is all OK, then we fetch the repliedStatus and the repliedAccount for later processing. - repliedStatus := >smodel.Status{} - repliedAccount := >smodel.Account{} - // check replied status exists + is replyable - if err := p.db.GetByID(form.InReplyToID, repliedStatus); err != nil { - if _, ok := err.(db.ErrNoEntries); ok { - return fmt.Errorf("status with id %s not replyable because it doesn't exist", form.InReplyToID) - } - return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) - } - - if repliedStatus.VisibilityAdvanced != nil { - if !repliedStatus.VisibilityAdvanced.Replyable { - return fmt.Errorf("status with id %s is marked as not replyable", form.InReplyToID) - } - } - - // check replied account is known to us - if err := p.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil { - if _, ok := err.(db.ErrNoEntries); ok { - return fmt.Errorf("status with id %s not replyable because account id %s is not known", form.InReplyToID, repliedStatus.AccountID) - } - return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) - } - // check if a block exists - if blocked, err := p.db.Blocked(thisAccountID, repliedAccount.ID); err != nil { - if _, ok := err.(db.ErrNoEntries); !ok { - return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) - } - } else if blocked { - return fmt.Errorf("status with id %s not replyable", form.InReplyToID) - } - status.InReplyToID = repliedStatus.ID - status.InReplyToAccountID = repliedAccount.ID - - return nil -} - -func (p *processor) processMediaIDs(form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error { - if form.MediaIDs == nil { - return nil - } - - gtsMediaAttachments := []*gtsmodel.MediaAttachment{} - attachments := []string{} - for _, mediaID := range form.MediaIDs { - // check these attachments exist - a := >smodel.MediaAttachment{} - if err := p.db.GetByID(mediaID, a); err != nil { - return fmt.Errorf("invalid media type or media not found for media id %s", mediaID) - } - // check they belong to the requesting account id - if a.AccountID != thisAccountID { - return fmt.Errorf("media with id %s does not belong to account %s", mediaID, thisAccountID) - } - // check they're not already used in a status - if a.StatusID != "" || a.ScheduledStatusID != "" { - return fmt.Errorf("media with id %s is already attached to a status", mediaID) - } - gtsMediaAttachments = append(gtsMediaAttachments, a) - attachments = append(attachments, a.ID) - } - status.GTSMediaAttachments = gtsMediaAttachments - status.Attachments = attachments - return nil -} - -func (p *processor) processLanguage(form *apimodel.AdvancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error { - if form.Language != "" { - status.Language = form.Language - } else { - status.Language = accountDefaultLanguage - } - if status.Language == "" { - return errors.New("no language given either in status create form or account default") - } - return nil -} - -func (p *processor) processMentions(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error { - menchies := []string{} - gtsMenchies, err := p.db.MentionStringsToMentions(util.DeriveMentionsFromStatus(form.Status), accountID, status.ID) - if err != nil { - return fmt.Errorf("error generating mentions from status: %s", err) - } - for _, menchie := range gtsMenchies { - menchieID, err := id.NewRandomULID() - if err != nil { - return err - } - menchie.ID = menchieID - - if err := p.db.Put(menchie); err != nil { - return fmt.Errorf("error putting mentions in db: %s", err) - } - menchies = append(menchies, menchie.ID) - } - // add full populated gts menchies to the status for passing them around conveniently - status.GTSMentions = gtsMenchies - // add just the ids of the mentioned accounts to the status for putting in the db - status.Mentions = menchies - return nil -} - -func (p *processor) processTags(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error { - tags := []string{} - gtsTags, err := p.db.TagStringsToTags(util.DeriveHashtagsFromStatus(form.Status), accountID, status.ID) - if err != nil { - return fmt.Errorf("error generating hashtags from status: %s", err) - } - for _, tag := range gtsTags { - if err := p.db.Upsert(tag, "name"); err != nil { - return fmt.Errorf("error putting tags in db: %s", err) - } - tags = append(tags, tag.ID) - } - // add full populated gts tags to the status for passing them around conveniently - status.GTSTags = gtsTags - // add just the ids of the used tags to the status for putting in the db - status.Tags = tags - return nil -} - -func (p *processor) processEmojis(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error { - emojis := []string{} - gtsEmojis, err := p.db.EmojiStringsToEmojis(util.DeriveEmojisFromStatus(form.Status), accountID, status.ID) - if err != nil { - return fmt.Errorf("error generating emojis from status: %s", err) - } - for _, e := range gtsEmojis { - emojis = append(emojis, e.ID) - } - // add full populated gts emojis to the status for passing them around conveniently - status.GTSEmojis = gtsEmojis - // add just the ids of the used emojis to the status for putting in the db - status.Emojis = emojis - return nil -} - -func (p *processor) processContent(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error { - if form.Status == "" { - status.Content = "" - return nil - } - - // surround the whole status in '<p>' - content := fmt.Sprintf(`<p>%s</p>`, form.Status) - - // format mentions nicely - for _, menchie := range status.GTSMentions { - targetAccount := >smodel.Account{} - if err := p.db.GetByID(menchie.TargetAccountID, targetAccount); err == nil { - mentionContent := fmt.Sprintf(`<span class="h-card"><a href="%s" class="u-url mention">@<span>%s</span></a></span>`, targetAccount.URL, targetAccount.Username) - content = strings.ReplaceAll(content, menchie.NameString, mentionContent) - } - } - - // format tags nicely - for _, tag := range status.GTSTags { - tagContent := fmt.Sprintf(`<a href="%s" class="mention hashtag" rel="tag">#<span>%s</span></a>`, tag.URL, tag.Name) - content = strings.ReplaceAll(content, fmt.Sprintf("#%s", tag.Name), tagContent) - } - - // replace newlines with breaks - content = strings.ReplaceAll(content, "\n", "<br />") - - status.Content = content - return nil -} |