diff options
Diffstat (limited to 'internal/processing')
28 files changed, 306 insertions, 150 deletions
diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go index 1e8be372f..e94b7e844 100644 --- a/internal/processing/account/account.go +++ b/internal/processing/account/account.go @@ -19,6 +19,7 @@ package account import ( "code.superseriousbusiness.org/gotosocial/internal/federation" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/media" @@ -39,6 +40,7 @@ type Processor struct { converter *typeutils.Converter mediaManager *media.Manager visFilter *visibility.Filter + statusFilter *status.Filter formatter *text.Formatter federator *federation.Federator parseMention gtsmodel.ParseMentionFunc @@ -53,6 +55,7 @@ func New( mediaManager *media.Manager, federator *federation.Federator, visFilter *visibility.Filter, + statusFilter *status.Filter, parseMention gtsmodel.ParseMentionFunc, ) Processor { return Processor{ @@ -61,6 +64,7 @@ func New( converter: converter, mediaManager: mediaManager, visFilter: visFilter, + statusFilter: statusFilter, formatter: text.NewFormatter(state.DB), federator: federator, parseMention: parseMention, diff --git a/internal/processing/account/account_test.go b/internal/processing/account/account_test.go index d4fb6ddfb..b322ee771 100644 --- a/internal/processing/account/account_test.go +++ b/internal/processing/account/account_test.go @@ -26,6 +26,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/email" "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/media" @@ -107,8 +108,9 @@ func (suite *AccountStandardTestSuite) SetupTest() { visFilter := visibility.NewFilter(&suite.state) mutesFilter := mutes.NewFilter(&suite.state) - common := common.New(&suite.state, suite.mediaManager, suite.tc, suite.federator, visFilter, mutesFilter) - suite.accountProcessor = account.New(&common, &suite.state, suite.tc, suite.mediaManager, suite.federator, visFilter, processing.GetParseMentionFunc(&suite.state, suite.federator)) + statusFilter := status.NewFilter(&suite.state) + common := common.New(&suite.state, suite.mediaManager, suite.tc, suite.federator, visFilter, mutesFilter, statusFilter) + suite.accountProcessor = account.New(&common, &suite.state, suite.tc, suite.mediaManager, suite.federator, visFilter, statusFilter, processing.GetParseMentionFunc(&suite.state, suite.federator)) testrig.StandardDBSetup(suite.db, nil) testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") } diff --git a/internal/processing/account/bookmarks.go b/internal/processing/account/bookmarks.go index 7a0ff9915..468c6ad62 100644 --- a/internal/processing/account/bookmarks.go +++ b/internal/processing/account/bookmarks.go @@ -74,7 +74,7 @@ func (p *Processor) BookmarksGet(ctx context.Context, requestingAccount *gtsmode } // Convert the status. - item, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, gtsmodel.FilterContextNone) + item, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount) if err != nil { log.Errorf(ctx, "error converting bookmarked status to api: %s", err) continue diff --git a/internal/processing/account/statuses.go b/internal/processing/account/statuses.go index f0024d489..e55c1e81c 100644 --- a/internal/processing/account/statuses.go +++ b/internal/processing/account/statuses.go @@ -96,13 +96,33 @@ func (p *Processor) StatusesGet( return nil, gtserror.NewErrorInternalError(err) } - for _, s := range filtered { + for _, status := range filtered { + // ... + filtered, hide, err := p.statusFilter.StatusFilterResultsInContext(ctx, + requestingAccount, + status, + gtsmodel.FilterContextAccount, + ) + if err != nil { + log.Errorf(ctx, "error filtering status: %v", err) + continue + } + + if hide { + // Don't show. + continue + } + // Convert filtered statuses to API statuses. - item, err := p.converter.StatusToAPIStatus(ctx, s, requestingAccount, gtsmodel.FilterContextAccount) + item, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount) if err != nil { log.Errorf(ctx, "error convering to api status: %v", err) continue } + + // Set any filter results. + item.Filtered = filtered + items = append(items, item) } diff --git a/internal/processing/admin/admin_test.go b/internal/processing/admin/admin_test.go index 8f2eb23f2..857afcae1 100644 --- a/internal/processing/admin/admin_test.go +++ b/internal/processing/admin/admin_test.go @@ -25,6 +25,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/interaction" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/media" @@ -116,6 +117,7 @@ func (suite *AdminStandardTestSuite) SetupTest() { visibility.NewFilter(&suite.state), mutes.NewFilter(&suite.state), interaction.NewFilter(&suite.state), + status.NewFilter(&suite.state), ) testrig.StartWorkers(&suite.state, suite.processor.Workers()) diff --git a/internal/processing/common/common.go b/internal/processing/common/common.go index bebbdffea..2b3adb9a0 100644 --- a/internal/processing/common/common.go +++ b/internal/processing/common/common.go @@ -20,6 +20,7 @@ package common import ( "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/media" "code.superseriousbusiness.org/gotosocial/internal/state" @@ -30,12 +31,13 @@ import ( // common to multiple logical domains of the // processing subsection of the codebase. type Processor struct { - state *state.State - media *media.Manager - converter *typeutils.Converter - federator *federation.Federator - visFilter *visibility.Filter - muteFilter *mutes.Filter + state *state.State + media *media.Manager + converter *typeutils.Converter + federator *federation.Federator + visFilter *visibility.Filter + muteFilter *mutes.Filter + statusFilter *status.Filter } // New returns a new Processor instance. @@ -46,13 +48,15 @@ func New( federator *federation.Federator, visFilter *visibility.Filter, muteFilter *mutes.Filter, + statusFilter *status.Filter, ) Processor { return Processor{ - state: state, - media: media, - converter: converter, - federator: federator, - visFilter: visFilter, - muteFilter: muteFilter, + state: state, + media: media, + converter: converter, + federator: federator, + visFilter: visFilter, + muteFilter: muteFilter, + statusFilter: statusFilter, } } diff --git a/internal/processing/common/status.go b/internal/processing/common/status.go index f5f230e98..2bcf89a02 100644 --- a/internal/processing/common/status.go +++ b/internal/processing/common/status.go @@ -27,7 +27,6 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/gtserror" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/log" - "code.superseriousbusiness.org/gotosocial/internal/typeutils" ) // GetOwnStatus fetches the given status with ID, @@ -213,7 +212,6 @@ func (p *Processor) GetAPIStatus( apiStatus, err := p.converter.StatusToAPIStatus(ctx, target, requester, - gtsmodel.FilterContextNone, ) if err != nil { err := gtserror.Newf("error converting: %w", err) @@ -271,22 +269,33 @@ func (p *Processor) GetVisibleAPIStatuses( continue } + // Check whether status is filtered in context by requesting account. + filtered, hide, err := p.statusFilter.StatusFilterResultsInContext(ctx, + requester, + status, + filterCtx, + ) + if err != nil { + l.Errorf("error filtering: %v", err) + continue + } + + if hide { + continue + } + // Convert to API status, taking mute / filter into account. apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requester, - filterCtx, ) - if err != nil && !errors.Is(err, typeutils.ErrHideStatus) { + if err != nil { l.Errorf("error converting: %v", err) continue } - if apiStatus == nil { - // Status was - // filtered out. - continue - } + // Set filter results on status. + apiStatus.Filtered = filtered // Append converted status to return slice. apiStatuses = append(apiStatuses, *apiStatus) diff --git a/internal/processing/conversations/conversations.go b/internal/processing/conversations/conversations.go index 70fafa437..b80ba659a 100644 --- a/internal/processing/conversations/conversations.go +++ b/internal/processing/conversations/conversations.go @@ -23,6 +23,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/db" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtserror" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" @@ -31,10 +32,11 @@ import ( ) type Processor struct { - state *state.State - converter *typeutils.Converter - visFilter *visibility.Filter - muteFilter *mutes.Filter + state *state.State + converter *typeutils.Converter + visFilter *visibility.Filter + muteFilter *mutes.Filter + statusFilter *status.Filter } func New( @@ -42,12 +44,14 @@ func New( converter *typeutils.Converter, visFilter *visibility.Filter, muteFilter *mutes.Filter, + statusFilter *status.Filter, ) Processor { return Processor{ - state: state, - converter: converter, - visFilter: visFilter, - muteFilter: muteFilter, + state: state, + converter: converter, + visFilter: visFilter, + muteFilter: muteFilter, + statusFilter: statusFilter, } } @@ -95,21 +99,3 @@ func (p *Processor) getConversationOwnedBy( return conversation, nil } - -// getFiltersAndMutes gets the given account's filters and compiled mute list. -func (p *Processor) getFilters( - ctx context.Context, - requestingAccount *gtsmodel.Account, -) ([]*gtsmodel.Filter, gtserror.WithCode) { - filters, err := p.state.DB.GetFiltersByAccountID(ctx, requestingAccount.ID) - if err != nil { - return nil, gtserror.NewErrorInternalError( - gtserror.Newf( - "DB error getting filters for account %s: %w", - requestingAccount.ID, - err, - ), - ) - } - return filters, nil -} diff --git a/internal/processing/conversations/conversations_test.go b/internal/processing/conversations/conversations_test.go index 383938564..407623964 100644 --- a/internal/processing/conversations/conversations_test.go +++ b/internal/processing/conversations/conversations_test.go @@ -28,6 +28,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/email" "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/log" @@ -118,7 +119,7 @@ func (suite *ConversationsTestSuite) SetupTest() { suite.sentEmails = make(map[string]string) suite.emailSender = testrig.NewEmailSender("../../../web/template/", suite.sentEmails) - suite.conversationsProcessor = conversations.New(&suite.state, suite.tc, suite.visFilter, suite.muteFilter) + suite.conversationsProcessor = conversations.New(&suite.state, suite.tc, suite.visFilter, suite.muteFilter, status.NewFilter(&suite.state)) testrig.StandardDBSetup(suite.db, nil) testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") diff --git a/internal/processing/conversations/get.go b/internal/processing/conversations/get.go index 5324466c9..cdc0756c3 100644 --- a/internal/processing/conversations/get.go +++ b/internal/processing/conversations/get.go @@ -64,17 +64,26 @@ func (p *Processor) GetAll( items := make([]interface{}, 0, count) - filters, errWithCode := p.getFilters(ctx, requestingAccount) - if errWithCode != nil { - return nil, errWithCode - } - for _, conversation := range conversations { + // Check whether status if filtered by local participant in context. + filtered, hide, err := p.statusFilter.StatusFilterResultsInContext(ctx, + requestingAccount, + conversation.LastStatus, + gtsmodel.FilterContextNotifications, + ) + if err != nil { + log.Errorf(ctx, "error filtering status: %v", err) + continue + } + + if hide { + continue + } + // Convert conversation to frontend API model. apiConversation, err := p.converter.ConversationToAPIConversation(ctx, conversation, requestingAccount, - filters, ) if err != nil { log.Errorf(ctx, @@ -85,6 +94,9 @@ func (p *Processor) GetAll( continue } + // Set filter results on attached status model. + apiConversation.LastStatus.Filtered = filtered + // Append conversation to return items. items = append(items, apiConversation) } diff --git a/internal/processing/conversations/read.go b/internal/processing/conversations/read.go index 4d16a4eeb..c7e4f1acd 100644 --- a/internal/processing/conversations/read.go +++ b/internal/processing/conversations/read.go @@ -23,6 +23,7 @@ import ( apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" "code.superseriousbusiness.org/gotosocial/internal/gtserror" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" + "code.superseriousbusiness.org/gotosocial/internal/log" "code.superseriousbusiness.org/gotosocial/internal/util" ) @@ -44,20 +45,27 @@ func (p *Processor) Read( return nil, gtserror.NewErrorInternalError(err) } - filters, errWithCode := p.getFilters(ctx, requestingAccount) - if errWithCode != nil { - return nil, errWithCode + // Check whether status if filtered by local participant in context. + filtered, _, err := p.statusFilter.StatusFilterResultsInContext(ctx, + requestingAccount, + conversation.LastStatus, + gtsmodel.FilterContextNotifications, + ) + if err != nil { + log.Errorf(ctx, "error filtering status: %v", err) } apiConversation, err := p.converter.ConversationToAPIConversation(ctx, conversation, requestingAccount, - filters, ) if err != nil { err = gtserror.Newf("error converting conversation %s to API representation: %w", id, err) return nil, gtserror.NewErrorInternalError(err) } + // Set filter results on attached status model. + apiConversation.LastStatus.Filtered = filtered + return apiConversation, nil } diff --git a/internal/processing/conversations/update.go b/internal/processing/conversations/update.go index cf81d6906..21f1cf915 100644 --- a/internal/processing/conversations/update.go +++ b/internal/processing/conversations/update.go @@ -27,7 +27,6 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/id" "code.superseriousbusiness.org/gotosocial/internal/log" - "code.superseriousbusiness.org/gotosocial/internal/typeutils" "code.superseriousbusiness.org/gotosocial/internal/util" ) @@ -158,26 +157,6 @@ func (p *Processor) UpdateConversationsForStatus(ctx context.Context, status *gt continue } - // Convert the conversation to API representation. - apiConversation, err := p.converter.ConversationToAPIConversation(ctx, - conversation, - localAccount, - nil, - ) - if err != nil { - // If the conversation's last status matched a hide filter, skip it. - // If there was another kind of error, log that and skip it anyway. - if !errors.Is(err, typeutils.ErrHideStatus) { - log.Errorf(ctx, - "error converting conversation %s to API representation for account %s: %v", - status.ID, - localAccount.ID, - err, - ) - } - continue - } - // If status was authored by this participant, // don't bother notifying, they already know! if status.AccountID == localAccount.ID { @@ -198,6 +177,38 @@ func (p *Processor) UpdateConversationsForStatus(ctx context.Context, status *gt continue } + // Check whether status if filtered by local participant in context. + filtered, hide, err := p.statusFilter.StatusFilterResultsInContext(ctx, + localAccount, + status, + gtsmodel.FilterContextNotifications, + ) + if err != nil { + log.Errorf(ctx, "error filtering status: %v", err) + continue + } + + if hide { + continue + } + + // Convert the conversation to API representation. + apiConversation, err := p.converter.ConversationToAPIConversation(ctx, + conversation, + localAccount, + ) + if err != nil { + log.Errorf(ctx, "error converting conversation %s to API representation for account %s: %v", + status.ID, + localAccount.ID, + err, + ) + continue + } + + // Set filter results on attached status model. + apiConversation.LastStatus.Filtered = filtered + // Generate a notification, notifications = append(notifications, ConversationNotification{ AccountID: localAccount.ID, diff --git a/internal/processing/media/media_test.go b/internal/processing/media/media_test.go index f2462a972..01506cc6f 100644 --- a/internal/processing/media/media_test.go +++ b/internal/processing/media/media_test.go @@ -21,6 +21,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/admin" "code.superseriousbusiness.org/gotosocial/internal/db" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/media" @@ -85,7 +86,8 @@ func (suite *MediaStandardTestSuite) SetupTest() { federator := testrig.NewTestFederator(&suite.state, suite.transportController, suite.mediaManager) visFilter := visibility.NewFilter(&suite.state) muteFilter := mutes.NewFilter(&suite.state) - common := common.New(&suite.state, suite.mediaManager, suite.tc, federator, visFilter, muteFilter) + statusFilter := status.NewFilter(&suite.state) + common := common.New(&suite.state, suite.mediaManager, suite.tc, federator, visFilter, muteFilter, statusFilter) suite.mediaProcessor = mediaprocessing.New(&common, &suite.state, suite.tc, federator, suite.mediaManager, suite.transportController) testrig.StandardDBSetup(suite.db, nil) diff --git a/internal/processing/polls/poll_test.go b/internal/processing/polls/poll_test.go index 848c3f169..2fd46c5ee 100644 --- a/internal/processing/polls/poll_test.go +++ b/internal/processing/polls/poll_test.go @@ -25,6 +25,7 @@ import ( apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtserror" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" @@ -60,7 +61,8 @@ func (suite *PollTestSuite) SetupTest() { federator := testrig.NewTestFederator(&suite.state, controller, mediaMgr) suite.visFilter = visibility.NewFilter(&suite.state) suite.muteFilter = mutes.NewFilter(&suite.state) - common := common.New(&suite.state, mediaMgr, converter, federator, suite.visFilter, suite.muteFilter) + statusFilter := status.NewFilter(&suite.state) + common := common.New(&suite.state, mediaMgr, converter, federator, suite.visFilter, suite.muteFilter, statusFilter) suite.polls = polls.New(&common, &suite.state, converter) } diff --git a/internal/processing/processor.go b/internal/processing/processor.go index c35c807e0..a5cea5da4 100644 --- a/internal/processing/processor.go +++ b/internal/processing/processor.go @@ -23,6 +23,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/interaction" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + statusfilter "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" mm "code.superseriousbusiness.org/gotosocial/internal/media" @@ -193,7 +194,8 @@ func (p *Processor) Workers() *workers.Processor { return &p.workers } -// NewProcessor returns a new Processor. +// NewProcessor returns +// a new Processor. func NewProcessor( cleaner *cleaner.Cleaner, subscriptions *subscriptions.Subscriptions, @@ -207,6 +209,7 @@ func NewProcessor( visFilter *visibility.Filter, muteFilter *mutes.Filter, intFilter *interaction.Filter, + statusFilter *statusfilter.Filter, ) *Processor { parseMentionFunc := GetParseMentionFunc(state, federator) processor := &Processor{ @@ -221,18 +224,18 @@ func NewProcessor( // // Start with sub processors that will // be required by the workers processor. - common := common.New(state, mediaManager, converter, federator, visFilter, muteFilter) - processor.account = account.New(&common, state, converter, mediaManager, federator, visFilter, parseMentionFunc) + common := common.New(state, mediaManager, converter, federator, visFilter, muteFilter, statusFilter) + processor.account = account.New(&common, state, converter, mediaManager, federator, visFilter, statusFilter, parseMentionFunc) processor.media = media.New(&common, state, converter, federator, mediaManager, federator.TransportController()) processor.stream = stream.New(state, oauthServer) filterCommon := filterCommon.New(state, &processor.stream) // Instantiate the rest of the sub // processors + pin them to this struct. - processor.account = account.New(&common, state, converter, mediaManager, federator, visFilter, parseMentionFunc) + processor.account = account.New(&common, state, converter, mediaManager, federator, visFilter, statusFilter, parseMentionFunc) processor.admin = admin.New(&common, state, cleaner, subscriptions, federator, converter, mediaManager, federator.TransportController(), emailSender) processor.application = application.New(state, converter) - processor.conversations = conversations.New(state, converter, visFilter, muteFilter) + processor.conversations = conversations.New(state, converter, visFilter, muteFilter, statusFilter) processor.fedi = fedi.New(state, &common, converter, federator, visFilter) processor.filtersv1 = filtersv1.New(state, converter, filterCommon) processor.filtersv2 = filtersv2.New(state, converter, filterCommon) @@ -243,7 +246,7 @@ func NewProcessor( processor.push = push.New(state, converter) processor.report = report.New(state, converter) processor.tags = tags.New(state, converter) - processor.timeline = timeline.New(state, converter, visFilter, muteFilter) + processor.timeline = timeline.New(state, converter, visFilter, muteFilter, statusFilter) processor.search = search.New(state, federator, converter, visFilter) processor.status = status.New(state, &common, &processor.polls, &processor.interactionRequests, federator, converter, visFilter, intFilter, parseMentionFunc) processor.user = user.New(state, converter, oauthServer, emailSender) @@ -261,6 +264,7 @@ func NewProcessor( converter, visFilter, muteFilter, + statusFilter, emailSender, webPushSender, &processor.account, diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go index 847de29cf..3c564b929 100644 --- a/internal/processing/processor_test.go +++ b/internal/processing/processor_test.go @@ -28,6 +28,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/interaction" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/media" @@ -133,6 +134,7 @@ func (suite *ProcessingStandardTestSuite) SetupTest() { visibility.NewFilter(&suite.state), mutes.NewFilter(&suite.state), interaction.NewFilter(&suite.state), + status.NewFilter(&suite.state), ) testrig.StartWorkers(&suite.state, suite.processor.Workers()) diff --git a/internal/processing/search/util.go b/internal/processing/search/util.go index fc105940f..441f3f946 100644 --- a/internal/processing/search/util.go +++ b/internal/processing/search/util.go @@ -113,7 +113,7 @@ func (p *Processor) packageStatuses( continue } - apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, gtsmodel.FilterContextNone) + apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount) 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/status_test.go b/internal/processing/status/status_test.go index 091d9716b..d709d435f 100644 --- a/internal/processing/status/status_test.go +++ b/internal/processing/status/status_test.go @@ -23,6 +23,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/interaction" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + statusfilter "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/media" @@ -95,8 +96,9 @@ func (suite *StatusStandardTestSuite) SetupTest() { visFilter := visibility.NewFilter(&suite.state) muteFilter := mutes.NewFilter(&suite.state) intFilter := interaction.NewFilter(&suite.state) + statusFilter := statusfilter.NewFilter(&suite.state) - common := common.New(&suite.state, suite.mediaManager, suite.typeConverter, suite.federator, visFilter, muteFilter) + common := common.New(&suite.state, suite.mediaManager, suite.typeConverter, suite.federator, visFilter, muteFilter, statusFilter) polls := polls.New(&common, &suite.state, suite.typeConverter) intReqs := interactionrequests.New(&common, &suite.state, suite.typeConverter) diff --git a/internal/processing/stream/statusupdate_test.go b/internal/processing/stream/statusupdate_test.go index a3ec0415e..a82348c31 100644 --- a/internal/processing/stream/statusupdate_test.go +++ b/internal/processing/stream/statusupdate_test.go @@ -22,7 +22,6 @@ import ( "encoding/json" "testing" - "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/stream" "code.superseriousbusiness.org/gotosocial/internal/typeutils" "github.com/stretchr/testify/suite" @@ -39,7 +38,7 @@ func (suite *StatusUpdateTestSuite) TestStreamNotification() { suite.NoError(errWithCode) editedStatus := suite.testStatuses["remote_account_1_status_1"] - apiStatus, err := typeutils.NewConverter(&suite.state).StatusToAPIStatus(suite.T().Context(), editedStatus, account, gtsmodel.FilterContextNotifications) + apiStatus, err := typeutils.NewConverter(&suite.state).StatusToAPIStatus(suite.T().Context(), editedStatus, account) suite.NoError(err) suite.streamProcessor.StatusUpdate(suite.T().Context(), account, apiStatus, stream.TimelineHome) diff --git a/internal/processing/timeline/faved.go b/internal/processing/timeline/faved.go index 65b23c702..9218af9c8 100644 --- a/internal/processing/timeline/faved.go +++ b/internal/processing/timeline/faved.go @@ -26,7 +26,6 @@ import ( apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util" "code.superseriousbusiness.org/gotosocial/internal/db" "code.superseriousbusiness.org/gotosocial/internal/gtserror" - "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/log" "code.superseriousbusiness.org/gotosocial/internal/util" ) @@ -56,7 +55,7 @@ func (p *Processor) FavedTimelineGet(ctx context.Context, authed *apiutil.Auth, continue } - apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, authed.Account, gtsmodel.FilterContextNone) + apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, authed.Account) if err != nil { log.Errorf(ctx, "error convering to api status: %v", err) continue diff --git a/internal/processing/timeline/notification.go b/internal/processing/timeline/notification.go index 784b2b824..b17dda8a7 100644 --- a/internal/processing/timeline/notification.go +++ b/internal/processing/timeline/notification.go @@ -31,7 +31,6 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/log" "code.superseriousbusiness.org/gotosocial/internal/paging" - "code.superseriousbusiness.org/gotosocial/internal/typeutils" "code.superseriousbusiness.org/gotosocial/internal/util" ) @@ -93,8 +92,12 @@ func (p *Processor) NotificationsGet( continue } + var filtered []apimodel.FilterResult + if n.Status != nil { - // A status is attached, check whether status muted. + var hide bool + + // Check whether notification status is muted by requester. muted, err = p.muteFilter.StatusNotificationsMuted(ctx, requester, n.Status, @@ -107,16 +110,34 @@ func (p *Processor) NotificationsGet( if muted { continue } + + // Check whether notification status is filtered by requester in notifs. + filtered, hide, err = p.statusFilter.StatusFilterResultsInContext(ctx, + requester, + n.Status, + gtsmodel.FilterContextNotifications, + ) + if err != nil { + log.Errorf(ctx, "error checking status filtering: %v", err) + continue + } + + if hide { + continue + } } - item, err := p.converter.NotificationToAPINotification(ctx, n, true) + item, err := p.converter.NotificationToAPINotification(ctx, n) if err != nil { - if !errors.Is(err, typeutils.ErrHideStatus) { - log.Debugf(ctx, "skipping notification %s because it couldn't be converted to its api representation: %s", n.ID, err) - } continue } + if item.Status != nil { + // Set filter results on status, + // in case any were set above. + item.Status.Filtered = filtered + } + items = append(items, item) } @@ -154,7 +175,7 @@ func (p *Processor) NotificationGet(ctx context.Context, account *gtsmodel.Accou // or mute checking for a notification directly // fetched by ID. only from timelines etc. - apiNotif, err := p.converter.NotificationToAPINotification(ctx, notif, false) + apiNotif, err := p.converter.NotificationToAPINotification(ctx, notif) if err != nil { err := gtserror.Newf("error converting to api model: %w", err) return nil, gtserror.WrapWithCode(http.StatusInternalServerError, err) diff --git a/internal/processing/timeline/timeline.go b/internal/processing/timeline/timeline.go index 64d33e430..06580b3c7 100644 --- a/internal/processing/timeline/timeline.go +++ b/internal/processing/timeline/timeline.go @@ -19,13 +19,13 @@ package timeline import ( "context" - "errors" "net/http" "net/url" apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" timelinepkg "code.superseriousbusiness.org/gotosocial/internal/cache/timeline" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtserror" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" @@ -46,18 +46,26 @@ var ( ) type Processor struct { - state *state.State - converter *typeutils.Converter - visFilter *visibility.Filter - muteFilter *mutes.Filter + state *state.State + converter *typeutils.Converter + visFilter *visibility.Filter + muteFilter *mutes.Filter + statusFilter *status.Filter } -func New(state *state.State, converter *typeutils.Converter, visFilter *visibility.Filter, muteFilter *mutes.Filter) Processor { +func New( + state *state.State, + converter *typeutils.Converter, + visFilter *visibility.Filter, + muteFilter *mutes.Filter, + statusFilter *status.Filter, +) Processor { return Processor{ - state: state, - converter: converter, - visFilter: visFilter, - muteFilter: muteFilter, + state: state, + converter: converter, + visFilter: visFilter, + muteFilter: muteFilter, + statusFilter: statusFilter, } } @@ -116,15 +124,30 @@ func (p *Processor) getStatusTimeline( return nil, nil } + // Check whether this status is filtered by requester in this context. + filters, hide, err := p.statusFilter.StatusFilterResultsInContext(ctx, + requester, + status, + filterCtx, + ) + if err != nil { + return nil, err + } else if hide { + return nil, nil + } + // Finally, pass status to get converted to API model. apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requester, - filterCtx, ) - if err != nil && !errors.Is(err, typeutils.ErrHideStatus) { + if err != nil { return nil, err } + + // Set any filters on status. + apiStatus.Filtered = filters + return apiStatus, nil }, ) diff --git a/internal/processing/timeline/timeline_test.go b/internal/processing/timeline/timeline_test.go index 01197b767..ce7817df6 100644 --- a/internal/processing/timeline/timeline_test.go +++ b/internal/processing/timeline/timeline_test.go @@ -21,6 +21,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/admin" "code.superseriousbusiness.org/gotosocial/internal/db" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/processing/timeline" @@ -64,6 +65,7 @@ func (suite *TimelineStandardTestSuite) SetupTest() { typeutils.NewConverter(&suite.state), visibility.NewFilter(&suite.state), mutes.NewFilter(&suite.state), + status.NewFilter(&suite.state), ) testrig.StandardDBSetup(suite.db, suite.testAccounts) diff --git a/internal/processing/workers/fromclientapi_test.go b/internal/processing/workers/fromclientapi_test.go index 4453095fd..7da34ff42 100644 --- a/internal/processing/workers/fromclientapi_test.go +++ b/internal/processing/workers/fromclientapi_test.go @@ -212,7 +212,6 @@ func (suite *FromClientAPITestSuite) statusJSON( ctx, status, requestingAccount, - gtsmodel.FilterContextNone, ) if err != nil { suite.FailNow(err.Error()) @@ -236,7 +235,6 @@ func (suite *FromClientAPITestSuite) conversationJSON( ctx, conversation, requestingAccount, - nil, ) if err != nil { suite.FailNow(err.Error()) @@ -344,7 +342,7 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithNotification() { suite.FailNow("timed out waiting for new status notification") } - apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, false) + apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif) if err != nil { suite.FailNow(err.Error()) } @@ -2031,7 +2029,7 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv suite.FailNow("timed out waiting for new status notification") } - apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, false) + apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif) if err != nil { suite.FailNow(err.Error()) } @@ -2216,7 +2214,7 @@ func (suite *FromClientAPITestSuite) TestProcessUpdateStatusInteractedWith() { suite.FailNow("timed out waiting for edited status notification") } - apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, false) + apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif) if err != nil { suite.FailNow(err.Error()) } diff --git a/internal/processing/workers/surface.go b/internal/processing/workers/surface.go index 69758692f..e0e441479 100644 --- a/internal/processing/workers/surface.go +++ b/internal/processing/workers/surface.go @@ -20,6 +20,7 @@ package workers import ( "code.superseriousbusiness.org/gotosocial/internal/email" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/processing/conversations" "code.superseriousbusiness.org/gotosocial/internal/processing/stream" @@ -40,6 +41,7 @@ type Surface struct { Stream *stream.Processor VisFilter *visibility.Filter MuteFilter *mutes.Filter + StatusFilter *status.Filter EmailSender email.Sender WebPushSender webpush.Sender Conversations *conversations.Processor diff --git a/internal/processing/workers/surfacenotify.go b/internal/processing/workers/surfacenotify.go index de7e3d95a..15ad79b26 100644 --- a/internal/processing/workers/surfacenotify.go +++ b/internal/processing/workers/surfacenotify.go @@ -22,12 +22,12 @@ import ( "errors" "strings" + apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model" "code.superseriousbusiness.org/gotosocial/internal/db" "code.superseriousbusiness.org/gotosocial/internal/gtscontext" "code.superseriousbusiness.org/gotosocial/internal/gtserror" "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/id" - "code.superseriousbusiness.org/gotosocial/internal/typeutils" "code.superseriousbusiness.org/gotosocial/internal/util" "code.superseriousbusiness.org/gotosocial/internal/util/xslices" ) @@ -727,6 +727,8 @@ func (s *Surface) Notify( return nil } + var filtered []apimodel.FilterResult + if status != nil { // Check whether status is muted by the target account. muted, err := s.MuteFilter.StatusNotificationsMuted(ctx, @@ -741,17 +743,35 @@ func (s *Surface) Notify( // Don't notify. return nil } + + var hide bool + + // Check whether notification status is filtered by requester in notifs. + filtered, hide, err = s.StatusFilter.StatusFilterResultsInContext(ctx, + targetAccount, + status, + gtsmodel.FilterContextNotifications, + ) + if err != nil { + return gtserror.Newf("error checking status filtering: %w", err) + } + + if hide { + // Don't notify. + return nil + } } - // Convert the notification to frontend API model for streaming / web push. - apiNotif, err := s.Converter.NotificationToAPINotification(ctx, notif, true) - if err != nil && !errors.Is(err, typeutils.ErrHideStatus) { + // Convert notification to frontend API model for streaming / web push. + apiNotif, err := s.Converter.NotificationToAPINotification(ctx, notif) + if err != nil { return gtserror.Newf("error converting notification to api representation: %w", err) } - if apiNotif == nil { - // Filtered. - return nil + if apiNotif.Status != nil { + // Set filter results on status, + // in case any were set above. + apiNotif.Status.Filtered = filtered } // Stream notification to the user. diff --git a/internal/processing/workers/surfacetimeline.go b/internal/processing/workers/surfacetimeline.go index 5e677c626..b1177cd28 100644 --- a/internal/processing/workers/surfacetimeline.go +++ b/internal/processing/workers/surfacetimeline.go @@ -19,7 +19,6 @@ package workers import ( "context" - "errors" "code.superseriousbusiness.org/gotosocial/internal/cache/timeline" "code.superseriousbusiness.org/gotosocial/internal/gtscontext" @@ -27,7 +26,6 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/gtsmodel" "code.superseriousbusiness.org/gotosocial/internal/log" "code.superseriousbusiness.org/gotosocial/internal/stream" - "code.superseriousbusiness.org/gotosocial/internal/typeutils" "code.superseriousbusiness.org/gotosocial/internal/util" ) @@ -350,28 +348,40 @@ func (s *Surface) timelineStatus( streamType string, filterCtx gtsmodel.FilterContext, ) bool { + // Check whether status is filtered in this context by timeline account. + filtered, hide, err := s.StatusFilter.StatusFilterResultsInContext(ctx, + account, + status, + filterCtx, + ) + if err != nil { + log.Errorf(ctx, "error filtering status %s: %v", status.URI, err) + } + + if hide { + // Don't even show to + // timeline account. + return false + } // Attempt to convert status to frontend API representation, // this will check whether status is filtered / muted. apiModel, err := s.Converter.StatusToAPIStatus(ctx, status, account, - filterCtx, ) - if err != nil && !errors.Is(err, typeutils.ErrHideStatus) { + if err != nil { log.Error(ctx, "error converting status %s to frontend: %v", status.URI, err) + } else { + + // Attach any filter results. + apiModel.Filtered = filtered } // Insert status to timeline cache regardless of // if API model was succesfully prepared or not. repeatBoost := timeline.InsertOne(status, apiModel) - if apiModel == nil { - // Status was - // filtered. - return false - } - if !repeatBoost { // Only stream if not repeated boost of recent status. s.Stream.Update(ctx, account, apiModel, streamType) @@ -683,26 +693,34 @@ func (s *Surface) timelineStreamStatusUpdate( status *gtsmodel.Status, streamType string, ) (bool, error) { - - // Convert updated database model to frontend model. - apiStatus, err := s.Converter.StatusToAPIStatus(ctx, - status, + // Check whether status is filtered in this context by timeline account. + filtered, hide, err := s.StatusFilter.StatusFilterResultsInContext(ctx, account, + status, gtsmodel.FilterContextHome, ) + if err != nil { + return false, gtserror.Newf("error filtering status: %w", err) + } - switch { - case err == nil: - // no issue. - - case errors.Is(err, typeutils.ErrHideStatus): - // Don't put this status in the stream. + if hide { + // Don't even show to + // timeline account. return false, nil + } - default: + // Convert updated database model to frontend model. + apiStatus, err := s.Converter.StatusToAPIStatus(ctx, + status, + account, + ) + if err != nil { return false, gtserror.Newf("error converting status: %w", err) } + // Attach any filter results. + apiStatus.Filtered = filtered + // The status was updated so stream it to the user. s.Stream.StatusUpdate(ctx, account, apiStatus, streamType) diff --git a/internal/processing/workers/workers.go b/internal/processing/workers/workers.go index 1f4ef465f..67e928db0 100644 --- a/internal/processing/workers/workers.go +++ b/internal/processing/workers/workers.go @@ -21,6 +21,7 @@ import ( "code.superseriousbusiness.org/gotosocial/internal/email" "code.superseriousbusiness.org/gotosocial/internal/federation" "code.superseriousbusiness.org/gotosocial/internal/filter/mutes" + "code.superseriousbusiness.org/gotosocial/internal/filter/status" "code.superseriousbusiness.org/gotosocial/internal/filter/visibility" "code.superseriousbusiness.org/gotosocial/internal/processing/account" "code.superseriousbusiness.org/gotosocial/internal/processing/common" @@ -46,6 +47,7 @@ func New( converter *typeutils.Converter, visFilter *visibility.Filter, muteFilter *mutes.Filter, + statusFilter *status.Filter, emailSender email.Sender, webPushSender webpush.Sender, account *account.Processor, @@ -69,6 +71,7 @@ func New( Stream: stream, VisFilter: visFilter, MuteFilter: muteFilter, + StatusFilter: statusFilter, EmailSender: emailSender, WebPushSender: webPushSender, Conversations: conversations, |
