diff options
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  | 
