diff options
author | 2024-06-06 09:38:02 -0700 | |
---|---|---|
committer | 2024-06-06 16:38:02 +0000 | |
commit | 5e2d4fdb19eb4fcd4c0bbfb3e2f29067a58c88c8 (patch) | |
tree | 607006af6b4bb63bb625b39f3ca0fe869eb6ba95 /internal/processing | |
parent | [bugfix] update media if more than just url changes (#2970) (diff) | |
download | gotosocial-5e2d4fdb19eb4fcd4c0bbfb3e2f29067a58c88c8.tar.xz |
[feature] User muting (#2960)
* User muting
* Address review feedback
* Rename uniqueness constraint on user_mutes to match convention
* Remove unused account_id from where clause
* Add UserMute to NewTestDB
* Update test/envparsing.sh with new and fixed cache stuff
* Address tobi's review comments
* Make compiledUserMuteListEntry.expired consistent with UserMute.Expired
* Make sure mute_expires_at is serialized as an explicit null for indefinite mutes
---------
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Diffstat (limited to 'internal/processing')
-rw-r--r-- | internal/processing/account/bookmarks.go | 2 | ||||
-rw-r--r-- | internal/processing/account/mute.go | 198 | ||||
-rw-r--r-- | internal/processing/account/statuses.go | 2 | ||||
-rw-r--r-- | internal/processing/common/status.go | 2 | ||||
-rw-r--r-- | internal/processing/search/util.go | 2 | ||||
-rw-r--r-- | internal/processing/status/get.go | 12 | ||||
-rw-r--r-- | internal/processing/stream/statusupdate_test.go | 2 | ||||
-rw-r--r-- | internal/processing/timeline/faved.go | 2 | ||||
-rw-r--r-- | internal/processing/timeline/home.go | 11 | ||||
-rw-r--r-- | internal/processing/timeline/list.go | 11 | ||||
-rw-r--r-- | internal/processing/timeline/notification.go | 25 | ||||
-rw-r--r-- | internal/processing/timeline/public.go | 12 | ||||
-rw-r--r-- | internal/processing/timeline/tag.go | 11 | ||||
-rw-r--r-- | internal/processing/workers/fromclientapi_test.go | 3 | ||||
-rw-r--r-- | internal/processing/workers/surfacenotify.go | 13 | ||||
-rw-r--r-- | internal/processing/workers/surfacetimeline.go | 26 |
16 files changed, 317 insertions, 17 deletions
diff --git a/internal/processing/account/bookmarks.go b/internal/processing/account/bookmarks.go index 5618934ae..b9ecf0217 100644 --- a/internal/processing/account/bookmarks.go +++ b/internal/processing/account/bookmarks.go @@ -75,7 +75,7 @@ func (p *Processor) BookmarksGet(ctx context.Context, requestingAccount *gtsmode } // Convert the status. - item, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextNone, nil) + item, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextNone, nil, nil) if err != nil { log.Errorf(ctx, "error converting bookmarked status to api: %s", err) continue diff --git a/internal/processing/account/mute.go b/internal/processing/account/mute.go new file mode 100644 index 000000000..00bb9dd22 --- /dev/null +++ b/internal/processing/account/mute.go @@ -0,0 +1,198 @@ +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +package account + +import ( + "context" + "errors" + "time" + + 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/log" + "github.com/superseriousbusiness/gotosocial/internal/paging" + "github.com/superseriousbusiness/gotosocial/internal/util" +) + +// MuteCreate handles the creation or updating of a mute from requestingAccount to targetAccountID. +// The form params should have already been normalized by the time they reach this function. +func (p *Processor) MuteCreate( + ctx context.Context, + requestingAccount *gtsmodel.Account, + targetAccountID string, + form *apimodel.UserMuteCreateUpdateRequest, +) (*apimodel.Relationship, gtserror.WithCode) { + targetAccount, existingMute, errWithCode := p.getMuteTarget(ctx, requestingAccount, targetAccountID) + if errWithCode != nil { + return nil, errWithCode + } + + if existingMute != nil && + *existingMute.Notifications == *form.Notifications && + existingMute.ExpiresAt.IsZero() && form.Duration == nil { + // Mute already exists and doesn't require updating, nothing to do. + return p.RelationshipGet(ctx, requestingAccount, targetAccountID) + } + + // Create a new mute or update an existing one. + mute := >smodel.UserMute{ + AccountID: requestingAccount.ID, + Account: requestingAccount, + TargetAccountID: targetAccountID, + TargetAccount: targetAccount, + Notifications: form.Notifications, + } + if existingMute != nil { + mute.ID = existingMute.ID + } else { + mute.ID = id.NewULID() + } + if form.Duration != nil { + mute.ExpiresAt = time.Now().Add(time.Second * time.Duration(*form.Duration)) + } + + if err := p.state.DB.PutMute(ctx, mute); err != nil { + err = gtserror.Newf("error creating or updating mute in db: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + return p.RelationshipGet(ctx, requestingAccount, targetAccountID) +} + +// MuteRemove handles the removal of a mute from requestingAccount to targetAccountID. +func (p *Processor) MuteRemove( + ctx context.Context, + requestingAccount *gtsmodel.Account, + targetAccountID string, +) (*apimodel.Relationship, gtserror.WithCode) { + _, existingMute, errWithCode := p.getMuteTarget(ctx, requestingAccount, targetAccountID) + if errWithCode != nil { + return nil, errWithCode + } + + if existingMute == nil { + // Already not muted, nothing to do. + return p.RelationshipGet(ctx, requestingAccount, targetAccountID) + } + + // We got a mute, remove it from the db. + if err := p.state.DB.DeleteMuteByID(ctx, existingMute.ID); err != nil { + err := gtserror.Newf("error removing mute from db: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + return p.RelationshipGet(ctx, requestingAccount, targetAccountID) +} + +// MutesGet retrieves the user's list of muted accounts, with an extra field for mute expiration (if applicable). +func (p *Processor) MutesGet( + ctx context.Context, + requestingAccount *gtsmodel.Account, + page *paging.Page, +) (*apimodel.PageableResponse, gtserror.WithCode) { + mutes, err := p.state.DB.GetAccountMutes(ctx, + requestingAccount.ID, + page, + ) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err = gtserror.Newf("couldn't list account's mutes: %w", err) + return nil, gtserror.NewErrorInternalError(err) + } + + // Check for empty response. + count := len(mutes) + if len(mutes) == 0 { + return util.EmptyPageableResponse(), nil + } + + // Get the lowest and highest + // ID values, used for paging. + lo := mutes[count-1].ID + hi := mutes[0].ID + + items := make([]interface{}, 0, count) + + now := time.Now() + for _, mute := range mutes { + // Skip accounts for which the mute has expired. + if mute.Expired(now) { + continue + } + + // Convert target account to frontend API model. (target will never be nil) + account, err := p.converter.AccountToAPIAccountPublic(ctx, mute.TargetAccount) + if err != nil { + log.Errorf(ctx, "error converting account to public api account: %v", err) + continue + } + mutedAccount := &apimodel.MutedAccount{ + Account: *account, + } + // Add the mute expiration field (unique to this API). + if !mute.ExpiresAt.IsZero() { + mutedAccount.MuteExpiresAt = util.Ptr(util.FormatISO8601(mute.ExpiresAt)) + } + + // Append target to return items. + items = append(items, mutedAccount) + } + + return paging.PackageResponse(paging.ResponseParams{ + Items: items, + Path: "/api/v1/mutes", + Next: page.Next(lo, hi), + Prev: page.Prev(lo, hi), + }), nil +} + +func (p *Processor) getMuteTarget( + ctx context.Context, + requestingAccount *gtsmodel.Account, + targetAccountID string, +) (*gtsmodel.Account, *gtsmodel.UserMute, gtserror.WithCode) { + // Account should not mute or unmute itself. + if requestingAccount.ID == targetAccountID { + err := gtserror.Newf("account %s cannot mute or unmute itself", requestingAccount.ID) + return nil, nil, gtserror.NewErrorNotAcceptable(err, err.Error()) + } + + // Ensure target account retrievable. + targetAccount, err := p.state.DB.GetAccountByID(ctx, targetAccountID) + if err != nil { + if !errors.Is(err, db.ErrNoEntries) { + // Real db error. + err = gtserror.Newf("db error looking for target account %s: %w", targetAccountID, err) + return nil, nil, gtserror.NewErrorInternalError(err) + } + // Account not found. + err = gtserror.Newf("target account %s not found in the db", targetAccountID) + return nil, nil, gtserror.NewErrorNotFound(err, err.Error()) + } + + // Check if currently muted. + mute, err := p.state.DB.GetMute(ctx, requestingAccount.ID, targetAccountID) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err = gtserror.Newf("db error checking existing mute: %w", err) + return nil, nil, gtserror.NewErrorInternalError(err) + } + + return targetAccount, mute, nil +} diff --git a/internal/processing/account/statuses.go b/internal/processing/account/statuses.go index 8f0548371..2513f17c7 100644 --- a/internal/processing/account/statuses.go +++ b/internal/processing/account/statuses.go @@ -105,7 +105,7 @@ func (p *Processor) StatusesGet( for _, s := range filtered { // Convert filtered statuses to API statuses. - item, err := p.converter.StatusToAPIStatus(ctx, s, requestingAccount, statusfilter.FilterContextAccount, filters) + item, err := p.converter.StatusToAPIStatus(ctx, s, requestingAccount, statusfilter.FilterContextAccount, filters, nil) if err != nil { log.Errorf(ctx, "error convering to api status: %v", err) continue diff --git a/internal/processing/common/status.go b/internal/processing/common/status.go index bb46ee38c..2ffc90035 100644 --- a/internal/processing/common/status.go +++ b/internal/processing/common/status.go @@ -185,7 +185,7 @@ func (p *Processor) GetAPIStatus( apiStatus *apimodel.Status, errWithCode gtserror.WithCode, ) { - apiStatus, err := p.converter.StatusToAPIStatus(ctx, target, requester, statusfilter.FilterContextNone, nil) + apiStatus, err := p.converter.StatusToAPIStatus(ctx, target, requester, statusfilter.FilterContextNone, nil, nil) if err != nil { err = gtserror.Newf("error converting status: %w", err) return nil, gtserror.NewErrorInternalError(err) diff --git a/internal/processing/search/util.go b/internal/processing/search/util.go index 196fef5fc..190289155 100644 --- a/internal/processing/search/util.go +++ b/internal/processing/search/util.go @@ -114,7 +114,7 @@ func (p *Processor) packageStatuses( continue } - apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextNone, nil) + apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextNone, nil, nil) if err != nil { log.Debugf(ctx, "skipping status %s because it couldn't be converted to its api representation: %s", status.ID, err) continue diff --git a/internal/processing/status/get.go b/internal/processing/status/get.go index c05f3effd..16f55b439 100644 --- a/internal/processing/status/get.go +++ b/internal/processing/status/get.go @@ -24,6 +24,8 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/util" @@ -286,8 +288,16 @@ func (p *Processor) ContextGet(ctx context.Context, requestingAccount *gtsmodel. err = gtserror.Newf("couldn't retrieve filters for account %s: %w", requestingAccount.ID, err) return nil, gtserror.NewErrorInternalError(err) } + + mutes, err := p.state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), requestingAccount.ID, nil) + if err != nil { + err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", requestingAccount.ID, err) + return nil, gtserror.NewErrorInternalError(err) + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + convert := func(ctx context.Context, status *gtsmodel.Status, requestingAccount *gtsmodel.Account) (*apimodel.Status, error) { - return p.converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextThread, filters) + return p.converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextThread, filters, compiledMutes) } return p.contextGet(ctx, requestingAccount, targetStatusID, convert) } diff --git a/internal/processing/stream/statusupdate_test.go b/internal/processing/stream/statusupdate_test.go index 12971caa1..359212ee6 100644 --- a/internal/processing/stream/statusupdate_test.go +++ b/internal/processing/stream/statusupdate_test.go @@ -40,7 +40,7 @@ func (suite *StatusUpdateTestSuite) TestStreamNotification() { suite.NoError(errWithCode) editedStatus := suite.testStatuses["remote_account_1_status_1"] - apiStatus, err := typeutils.NewConverter(&suite.state).StatusToAPIStatus(context.Background(), editedStatus, account, statusfilter.FilterContextNotifications, nil) + apiStatus, err := typeutils.NewConverter(&suite.state).StatusToAPIStatus(context.Background(), editedStatus, account, statusfilter.FilterContextNotifications, nil, nil) suite.NoError(err) suite.streamProcessor.StatusUpdate(context.Background(), account, apiStatus, stream.TimelineHome) diff --git a/internal/processing/timeline/faved.go b/internal/processing/timeline/faved.go index c3b0e1837..cd3729465 100644 --- a/internal/processing/timeline/faved.go +++ b/internal/processing/timeline/faved.go @@ -55,7 +55,7 @@ func (p *Processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, ma continue } - apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, authed.Account, statusfilter.FilterContextNone, nil) + apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, authed.Account, statusfilter.FilterContextNone, nil, nil) if err != nil { log.Errorf(ctx, "error convering to api status: %v", err) continue diff --git a/internal/processing/timeline/home.go b/internal/processing/timeline/home.go index e174b3428..8bf8dd428 100644 --- a/internal/processing/timeline/home.go +++ b/internal/processing/timeline/home.go @@ -24,7 +24,9 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -105,7 +107,14 @@ func HomeTimelineStatusPrepare(state *state.State, converter *typeutils.Converte return nil, err } - return converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextHome, filters) + mutes, err := state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), requestingAccount.ID, nil) + if err != nil { + err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", requestingAccount.ID, err) + return nil, err + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + + return converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextHome, filters, compiledMutes) } } diff --git a/internal/processing/timeline/list.go b/internal/processing/timeline/list.go index 60cdbac7a..2065256e3 100644 --- a/internal/processing/timeline/list.go +++ b/internal/processing/timeline/list.go @@ -24,7 +24,9 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/oauth" @@ -117,7 +119,14 @@ func ListTimelineStatusPrepare(state *state.State, converter *typeutils.Converte return nil, err } - return converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextHome, filters) + mutes, err := state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), requestingAccount.ID, nil) + if err != nil { + err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", requestingAccount.ID, err) + return nil, err + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + + return converter.StatusToAPIStatus(ctx, status, requestingAccount, statusfilter.FilterContextHome, filters, compiledMutes) } } diff --git a/internal/processing/timeline/notification.go b/internal/processing/timeline/notification.go index f99664d62..5156a1cdf 100644 --- a/internal/processing/timeline/notification.go +++ b/internal/processing/timeline/notification.go @@ -24,6 +24,9 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" @@ -49,6 +52,13 @@ func (p *Processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, ma return nil, gtserror.NewErrorInternalError(err) } + mutes, err := p.state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), authed.Account.ID, nil) + if err != nil { + err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", authed.Account.ID, err) + return nil, gtserror.NewErrorInternalError(err) + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + var ( items = make([]interface{}, 0, count) nextMaxIDValue string @@ -76,9 +86,11 @@ func (p *Processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, ma continue } - item, err := p.converter.NotificationToAPINotification(ctx, n, filters) + item, err := p.converter.NotificationToAPINotification(ctx, n, filters, compiledMutes) if err != nil { - log.Debugf(ctx, "skipping notification %s because it couldn't be converted to its api representation: %s", n.ID, err) + if !errors.Is(err, status.ErrHideStatus) { + log.Debugf(ctx, "skipping notification %s because it couldn't be converted to its api representation: %s", n.ID, err) + } continue } @@ -116,7 +128,14 @@ func (p *Processor) NotificationGet(ctx context.Context, account *gtsmodel.Accou return nil, gtserror.NewErrorInternalError(err) } - apiNotif, err := p.converter.NotificationToAPINotification(ctx, notif, filters) + mutes, err := p.state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), account.ID, nil) + if err != nil { + err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", account.ID, err) + return nil, gtserror.NewErrorInternalError(err) + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + + apiNotif, err := p.converter.NotificationToAPINotification(ctx, notif, filters, compiledMutes) if err != nil { if errors.Is(err, db.ErrNoEntries) { return nil, gtserror.NewErrorNotFound(err) diff --git a/internal/processing/timeline/public.go b/internal/processing/timeline/public.go index a0e594629..28062fb2e 100644 --- a/internal/processing/timeline/public.go +++ b/internal/processing/timeline/public.go @@ -25,6 +25,8 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" @@ -48,6 +50,7 @@ func (p *Processor) PublicTimelineGet( ) var filters []*gtsmodel.Filter + var compiledMutes *usermute.CompiledUserMuteList if requester != nil { var err error filters, err = p.state.DB.GetFiltersForAccountID(ctx, requester.ID) @@ -55,6 +58,13 @@ func (p *Processor) PublicTimelineGet( err = gtserror.Newf("couldn't retrieve filters for account %s: %w", requester.ID, err) return nil, gtserror.NewErrorInternalError(err) } + + mutes, err := p.state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), requester.ID, nil) + if err != nil { + err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", requester.ID, err) + return nil, gtserror.NewErrorInternalError(err) + } + compiledMutes = usermute.NewCompiledUserMuteList(mutes) } // Try a few times to select appropriate public @@ -98,7 +108,7 @@ outer: continue inner } - apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, requester, statusfilter.FilterContextPublic, filters) + apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, requester, statusfilter.FilterContextPublic, filters, compiledMutes) if errors.Is(err, statusfilter.ErrHideStatus) { continue } diff --git a/internal/processing/timeline/tag.go b/internal/processing/timeline/tag.go index 5308cac59..4320f6adc 100644 --- a/internal/processing/timeline/tag.go +++ b/internal/processing/timeline/tag.go @@ -25,6 +25,8 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/log" @@ -118,6 +120,13 @@ func (p *Processor) packageTagResponse( return nil, gtserror.NewErrorInternalError(err) } + mutes, err := p.state.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), requestingAcct.ID, nil) + if err != nil { + err = gtserror.Newf("couldn't retrieve mutes for account %s: %w", requestingAcct.ID, err) + return nil, gtserror.NewErrorInternalError(err) + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + for _, s := range statuses { timelineable, err := p.filter.StatusTagTimelineable(ctx, requestingAcct, s) if err != nil { @@ -129,7 +138,7 @@ func (p *Processor) packageTagResponse( continue } - apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, requestingAcct, statusfilter.FilterContextPublic, filters) + apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, requestingAcct, statusfilter.FilterContextPublic, filters, compiledMutes) if errors.Is(err, statusfilter.ErrHideStatus) { continue } diff --git a/internal/processing/workers/fromclientapi_test.go b/internal/processing/workers/fromclientapi_test.go index 6a12ce043..15be23baf 100644 --- a/internal/processing/workers/fromclientapi_test.go +++ b/internal/processing/workers/fromclientapi_test.go @@ -157,6 +157,7 @@ func (suite *FromClientAPITestSuite) statusJSON( requestingAccount, statusfilter.FilterContextNone, nil, + nil, ) if err != nil { suite.FailNow(err.Error()) @@ -261,7 +262,7 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithNotification() { suite.FailNow("timed out waiting for new status notification") } - apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, nil) + apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, nil, nil) if err != nil { suite.FailNow(err.Error()) } diff --git a/internal/processing/workers/surfacenotify.go b/internal/processing/workers/surfacenotify.go index a31946cc8..edeb4b57e 100644 --- a/internal/processing/workers/surfacenotify.go +++ b/internal/processing/workers/surfacenotify.go @@ -23,6 +23,8 @@ import ( "strings" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -472,8 +474,17 @@ func (s *Surface) Notify( return gtserror.Newf("couldn't retrieve filters for account %s: %w", targetAccount.ID, err) } - apiNotif, err := s.Converter.NotificationToAPINotification(ctx, notif, filters) + mutes, err := s.State.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), targetAccount.ID, nil) if err != nil { + return gtserror.Newf("couldn't retrieve mutes for account %s: %w", targetAccount.ID, err) + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + + apiNotif, err := s.Converter.NotificationToAPINotification(ctx, notif, filters, compiledMutes) + if err != nil { + if errors.Is(err, status.ErrHideStatus) { + return nil + } return gtserror.Newf("error converting notification to api representation: %w", err) } s.Stream.Notify(ctx, targetAccount, apiNotif) diff --git a/internal/processing/workers/surfacetimeline.go b/internal/processing/workers/surfacetimeline.go index 32fdd66e2..41d7f6f2a 100644 --- a/internal/processing/workers/surfacetimeline.go +++ b/internal/processing/workers/surfacetimeline.go @@ -23,6 +23,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/db" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" + "github.com/superseriousbusiness/gotosocial/internal/filter/usermute" "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -117,6 +118,12 @@ func (s *Surface) timelineAndNotifyStatusForFollowers( return gtserror.Newf("couldn't retrieve filters for account %s: %w", follow.AccountID, err) } + mutes, err := s.State.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), follow.AccountID, nil) + if err != nil { + return gtserror.Newf("couldn't retrieve mutes for account %s: %w", follow.AccountID, err) + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + // Add status to any relevant lists // for this follow, if applicable. s.listTimelineStatusForFollow( @@ -125,6 +132,7 @@ func (s *Surface) timelineAndNotifyStatusForFollowers( follow, &errs, filters, + compiledMutes, ) // Add status to home timeline for owner @@ -137,6 +145,7 @@ func (s *Surface) timelineAndNotifyStatusForFollowers( status, stream.TimelineHome, filters, + compiledMutes, ) if err != nil { errs.Appendf("error home timelining status: %w", err) @@ -189,6 +198,7 @@ func (s *Surface) listTimelineStatusForFollow( follow *gtsmodel.Follow, errs *gtserror.MultiError, filters []*gtsmodel.Filter, + mutes *usermute.CompiledUserMuteList, ) { // To put this status in appropriate list timelines, // we need to get each listEntry that pertains to @@ -232,6 +242,7 @@ func (s *Surface) listTimelineStatusForFollow( status, stream.TimelineList+":"+listEntry.ListID, // key streamType to this specific list filters, + mutes, ); err != nil { errs.Appendf("error adding status to timeline for list %s: %w", listEntry.ListID, err) // implicit continue @@ -343,6 +354,7 @@ func (s *Surface) timelineStatus( status *gtsmodel.Status, streamType string, filters []*gtsmodel.Filter, + mutes *usermute.CompiledUserMuteList, ) (bool, error) { // Ingest status into given timeline using provided function. if inserted, err := ingest(ctx, timelineID, status); err != nil { @@ -359,6 +371,7 @@ func (s *Surface) timelineStatus( account, statusfilter.FilterContextHome, filters, + mutes, ) if err != nil { err = gtserror.Newf("error converting status %s to frontend representation: %w", status.ID, err) @@ -478,6 +491,12 @@ func (s *Surface) timelineStatusUpdateForFollowers( return gtserror.Newf("couldn't retrieve filters for account %s: %w", follow.AccountID, err) } + mutes, err := s.State.DB.GetAccountMutes(gtscontext.SetBarebones(ctx), follow.AccountID, nil) + if err != nil { + return gtserror.Newf("couldn't retrieve mutes for account %s: %w", follow.AccountID, err) + } + compiledMutes := usermute.NewCompiledUserMuteList(mutes) + // Add status to any relevant lists // for this follow, if applicable. s.listTimelineStatusUpdateForFollow( @@ -486,6 +505,7 @@ func (s *Surface) timelineStatusUpdateForFollowers( follow, &errs, filters, + compiledMutes, ) // Add status to home timeline for owner @@ -496,6 +516,7 @@ func (s *Surface) timelineStatusUpdateForFollowers( status, stream.TimelineHome, filters, + compiledMutes, ) if err != nil { errs.Appendf("error home timelining status: %w", err) @@ -514,6 +535,7 @@ func (s *Surface) listTimelineStatusUpdateForFollow( follow *gtsmodel.Follow, errs *gtserror.MultiError, filters []*gtsmodel.Filter, + mutes *usermute.CompiledUserMuteList, ) { // To put this status in appropriate list timelines, // we need to get each listEntry that pertains to @@ -555,6 +577,7 @@ func (s *Surface) listTimelineStatusUpdateForFollow( status, stream.TimelineList+":"+listEntry.ListID, // key streamType to this specific list filters, + mutes, ); err != nil { errs.Appendf("error adding status to timeline for list %s: %w", listEntry.ListID, err) // implicit continue @@ -570,8 +593,9 @@ func (s *Surface) timelineStreamStatusUpdate( status *gtsmodel.Status, streamType string, filters []*gtsmodel.Filter, + mutes *usermute.CompiledUserMuteList, ) error { - apiStatus, err := s.Converter.StatusToAPIStatus(ctx, status, account, statusfilter.FilterContextHome, filters) + apiStatus, err := s.Converter.StatusToAPIStatus(ctx, status, account, statusfilter.FilterContextHome, filters, mutes) if errors.Is(err, statusfilter.ErrHideStatus) { // Don't put this status in the stream. return nil |