[chore] move status filtering from type converter (#4306)

This finalizes the moving status filtering out of the type converter, and into its own `./internal/filter/` subpkg :)

Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4306
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
kim 2025-07-04 15:30:39 +02:00 committed by kim
commit 66e1ec14aa
38 changed files with 565 additions and 846 deletions

View file

@ -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())
}

View file

@ -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

View file

@ -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.

View file

@ -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) {
// 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)
}
if hide {
// Don't even show to
// timeline account.
return false, nil
}
// Convert updated database model to frontend model.
apiStatus, err := s.Converter.StatusToAPIStatus(ctx,
status,
account,
gtsmodel.FilterContextHome,
)
switch {
case err == nil:
// no issue.
case errors.Is(err, typeutils.ErrHideStatus):
// Don't put this status in the stream.
return false, nil
default:
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)

View file

@ -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,