[performance] add statusfilter cache to cache calculated status filtering results (#4303)

this adds another 'filter' type cache, similar to the visibility and mute caches, to cache the results of status filtering checks. for the moment this keeps all the check calls themselves within the frontend typeconversion code, but i may move this out of the typeconverter in a future PR (also removing the ErrHideStatus means of propagating a hidden status).

also tweaks some of the cache invalidation hooks to not make unnecessary calls.

Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4303
Co-authored-by: kim <grufwub@gmail.com>
Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
kim 2025-07-01 16:00:04 +02:00 committed by kim
commit 4f2aa792b3
50 changed files with 1017 additions and 544 deletions

View file

@ -74,11 +74,12 @@ func (p *Processor) BookmarksGet(ctx context.Context, requestingAccount *gtsmode
}
// Convert the status.
item, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, gtsmodel.FilterContextNone, nil)
item, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, gtsmodel.FilterContextNone)
if err != nil {
log.Errorf(ctx, "error converting bookmarked status to api: %s", err)
continue
}
items = append(items, item)
}

View file

@ -96,15 +96,9 @@ func (p *Processor) StatusesGet(
return nil, gtserror.NewErrorInternalError(err)
}
filters, err := p.state.DB.GetFiltersByAccountID(ctx, requestingAccount.ID)
if err != nil {
err = gtserror.Newf("couldn't retrieve filters for account %s: %w", requestingAccount.ID, err)
return nil, gtserror.NewErrorInternalError(err)
}
for _, s := range filtered {
// Convert filtered statuses to API statuses.
item, err := p.converter.StatusToAPIStatus(ctx, s, requestingAccount, gtsmodel.FilterContextAccount, filters)
item, err := p.converter.StatusToAPIStatus(ctx, s, requestingAccount, gtsmodel.FilterContextAccount)
if err != nil {
log.Errorf(ctx, "error convering to api status: %v", err)
continue

View file

@ -24,10 +24,10 @@ import (
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
"code.superseriousbusiness.org/gotosocial/internal/db"
"code.superseriousbusiness.org/gotosocial/internal/federation/dereferencing"
statusfilter "code.superseriousbusiness.org/gotosocial/internal/filter/status"
"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,
@ -214,7 +214,6 @@ func (p *Processor) GetAPIStatus(
target,
requester,
gtsmodel.FilterContextNone,
nil,
)
if err != nil {
err := gtserror.Newf("error converting: %w", err)
@ -235,7 +234,6 @@ func (p *Processor) GetVisibleAPIStatuses(
requester *gtsmodel.Account,
statuses []*gtsmodel.Status,
filterCtx gtsmodel.FilterContext,
filters []*gtsmodel.Filter,
) []apimodel.Status {
// Start new log entry with
@ -278,9 +276,8 @@ func (p *Processor) GetVisibleAPIStatuses(
status,
requester,
filterCtx,
filters,
)
if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) {
if err != nil && !errors.Is(err, typeutils.ErrHideStatus) {
l.Errorf("error converting: %v", err)
continue
}

View file

@ -23,11 +23,11 @@ import (
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
"code.superseriousbusiness.org/gotosocial/internal/db"
statusfilter "code.superseriousbusiness.org/gotosocial/internal/filter/status"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"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"
)
@ -167,7 +167,7 @@ func (p *Processor) UpdateConversationsForStatus(ctx context.Context, status *gt
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, statusfilter.ErrHideStatus) {
if !errors.Is(err, typeutils.ErrHideStatus) {
log.Errorf(ctx,
"error converting conversation %s to API representation for account %s: %v",
status.ID,

View file

@ -28,12 +28,19 @@ import (
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
"code.superseriousbusiness.org/gotosocial/internal/log"
"code.superseriousbusiness.org/gotosocial/internal/processing/stream"
"code.superseriousbusiness.org/gotosocial/internal/state"
)
type Processor struct{ state *state.State }
type Processor struct {
state *state.State
stream *stream.Processor
}
func New(state *state.State) *Processor { return &Processor{state} }
func New(state *state.State, stream *stream.Processor) *Processor {
return &Processor{state, stream}
}
// CheckFilterExists calls .GetFilter() with a barebones context to not
// fetch any sub-models, and not returning the result. this functionally
@ -160,6 +167,27 @@ func (p *Processor) GetFilterKeyword(
return keyword, filter, nil
}
// OnFilterChanged ...
func (p *Processor) OnFilterChanged(ctx context.Context, requester *gtsmodel.Account) {
// Get list of list IDs created by this requesting account.
listIDs, err := p.state.DB.GetListIDsByAccountID(ctx, requester.ID)
if err != nil {
log.Errorf(ctx, "error getting account '%s' lists: %v", requester.Username, err)
}
// Unprepare this requester's home timeline.
p.state.Caches.Timelines.Home.Unprepare(requester.ID)
// Unprepare list timelines.
for _, id := range listIDs {
p.state.Caches.Timelines.List.Unprepare(id)
}
// Send filter changed event for account.
p.stream.FiltersChanged(ctx, requester)
}
// FromAPIContexts converts a slice of frontend API model FilterContext types to our internal FilterContexts bit field.
func FromAPIContexts(apiContexts []apimodel.FilterContext) (gtsmodel.FilterContexts, gtserror.WithCode) {
var contexts gtsmodel.FilterContexts

View file

@ -91,8 +91,8 @@ func (p *Processor) Create(ctx context.Context, requester *gtsmodel.Account, for
return nil, gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
// Return as converted frontend filter keyword model.
return typeutils.FilterKeywordToAPIFilterV1(filter, filterKeyword), nil

View file

@ -63,8 +63,8 @@ func (p *Processor) Delete(
}
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
return nil
}

View file

@ -19,7 +19,6 @@ package v1
import (
"code.superseriousbusiness.org/gotosocial/internal/processing/filters/common"
"code.superseriousbusiness.org/gotosocial/internal/processing/stream"
"code.superseriousbusiness.org/gotosocial/internal/state"
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
)
@ -30,15 +29,13 @@ type Processor struct {
state *state.State
converter *typeutils.Converter
stream *stream.Processor
}
func New(state *state.State, converter *typeutils.Converter, common *common.Processor, stream *stream.Processor) Processor {
func New(state *state.State, converter *typeutils.Converter, common *common.Processor) Processor {
return Processor{
c: common,
state: state,
converter: converter,
stream: stream,
}
}

View file

@ -166,8 +166,8 @@ func (p *Processor) Update(
return nil, gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
// Return as converted frontend filter keyword model.
return typeutils.FilterKeywordToAPIFilterV1(filter, filterKeyword), nil

View file

@ -34,13 +34,13 @@ import (
// Create a new filter for the given account, using the provided parameters.
// These params should have already been validated by the time they reach this function.
func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.FilterCreateRequestV2) (*apimodel.FilterV2, gtserror.WithCode) {
func (p *Processor) Create(ctx context.Context, requester *gtsmodel.Account, form *apimodel.FilterCreateRequestV2) (*apimodel.FilterV2, gtserror.WithCode) {
var errWithCode gtserror.WithCode
// Create new filter model.
filter := &gtsmodel.Filter{
ID: id.NewULID(),
AccountID: account.ID,
AccountID: requester.ID,
Title: form.Title,
}
@ -104,8 +104,8 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
return nil, gtserror.NewErrorInternalError(err)
}
// Send a filters changed event.
p.stream.FiltersChanged(ctx, account)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
// Return as converted frontend filter model.
return typeutils.FilterToAPIFilterV2(filter), nil

View file

@ -44,8 +44,8 @@ func (p *Processor) Delete(
return gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
return nil
}

View file

@ -19,7 +19,6 @@ package v2
import (
"code.superseriousbusiness.org/gotosocial/internal/processing/filters/common"
"code.superseriousbusiness.org/gotosocial/internal/processing/stream"
"code.superseriousbusiness.org/gotosocial/internal/state"
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
)
@ -30,15 +29,13 @@ type Processor struct {
state *state.State
converter *typeutils.Converter
stream *stream.Processor
}
func New(state *state.State, converter *typeutils.Converter, common *common.Processor, stream *stream.Processor) Processor {
func New(state *state.State, converter *typeutils.Converter, common *common.Processor) Processor {
return Processor{
c: common,
state: state,
converter: converter,
stream: stream,
}
}

View file

@ -72,8 +72,8 @@ func (p *Processor) KeywordCreate(ctx context.Context, requester *gtsmodel.Accou
return nil, gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
return typeutils.FilterKeywordToAPIFilterKeyword(filterKeyword), nil
}

View file

@ -54,8 +54,8 @@ func (p *Processor) KeywordDelete(
return gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
return nil
}

View file

@ -63,8 +63,8 @@ func (p *Processor) KeywordUpdate(
return nil, gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
return typeutils.FilterKeywordToAPIFilterKeyword(filterKeyword), nil
}

View file

@ -71,8 +71,8 @@ func (p *Processor) StatusCreate(ctx context.Context, requester *gtsmodel.Accoun
return nil, gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
return typeutils.FilterStatusToAPIFilterStatus(filterStatus), nil
}

View file

@ -54,8 +54,8 @@ func (p *Processor) StatusDelete(
return gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
return nil
}

View file

@ -135,8 +135,8 @@ func (p *Processor) Update(
return nil, gtserror.NewErrorInternalError(err)
}
// Stream a filters changed event to WS.
p.stream.FiltersChanged(ctx, requester)
// Handle filter change side-effects.
p.c.OnFilterChanged(ctx, requester)
// Return as converted frontend filter model.
return typeutils.FilterToAPIFilterV2(filter), nil

View file

@ -225,7 +225,7 @@ func NewProcessor(
processor.account = account.New(&common, state, converter, mediaManager, federator, visFilter, parseMentionFunc)
processor.media = media.New(&common, state, converter, federator, mediaManager, federator.TransportController())
processor.stream = stream.New(state, oauthServer)
filterCommon := filterCommon.New(state)
filterCommon := filterCommon.New(state, &processor.stream)
// Instantiate the rest of the sub
// processors + pin them to this struct.
@ -234,8 +234,8 @@ func NewProcessor(
processor.application = application.New(state, converter)
processor.conversations = conversations.New(state, converter, visFilter, muteFilter)
processor.fedi = fedi.New(state, &common, converter, federator, visFilter)
processor.filtersv1 = filtersv1.New(state, converter, filterCommon, &processor.stream)
processor.filtersv2 = filtersv2.New(state, converter, filterCommon, &processor.stream)
processor.filtersv1 = filtersv1.New(state, converter, filterCommon)
processor.filtersv2 = filtersv2.New(state, converter, filterCommon)
processor.interactionRequests = interactionrequests.New(&common, state, converter)
processor.list = list.New(state, converter)
processor.markers = markers.New(state, converter)

View file

@ -113,7 +113,7 @@ func (p *Processor) packageStatuses(
continue
}
apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, gtsmodel.FilterContextNone, nil)
apiStatus, err := p.converter.StatusToAPIStatus(ctx, status, requestingAccount, gtsmodel.FilterContextNone)
if err != nil {
log.Debugf(ctx, "skipping status %s because it couldn't be converted to its api representation: %s", status.ID, err)
continue

View file

@ -275,20 +275,6 @@ func (p *Processor) ContextGet(
requester *gtsmodel.Account,
targetStatusID string,
) (*apimodel.ThreadContext, gtserror.WithCode) {
// Retrieve filters as they affect
// what should be shown to requester.
filters, err := p.state.DB.GetFiltersByAccountID(
ctx, // Populate filters.
requester.ID,
)
if err != nil {
err = gtserror.Newf(
"couldn't retrieve filters for account %s: %w",
requester.ID, err,
)
return nil, gtserror.NewErrorInternalError(err)
}
// Retrieve the full thread context.
threadContext, errWithCode := p.contextGet(ctx,
requester,
@ -305,7 +291,6 @@ func (p *Processor) ContextGet(
requester,
threadContext.ancestors,
gtsmodel.FilterContextThread,
filters,
)
// Convert and filter the thread context descendants
@ -313,7 +298,6 @@ func (p *Processor) ContextGet(
requester,
threadContext.descendants,
gtsmodel.FilterContextThread,
filters,
)
return &apiContext, nil

View file

@ -39,7 +39,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, nil)
apiStatus, err := typeutils.NewConverter(&suite.state).StatusToAPIStatus(suite.T().Context(), editedStatus, account, gtsmodel.FilterContextNotifications)
suite.NoError(err)
suite.streamProcessor.StatusUpdate(suite.T().Context(), account, apiStatus, stream.TimelineHome)

View file

@ -56,7 +56,7 @@ func (p *Processor) FavedTimelineGet(ctx context.Context, authed *apiutil.Auth,
continue
}
apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, authed.Account, gtsmodel.FilterContextNone, nil)
apiStatus, err := p.converter.StatusToAPIStatus(ctx, s, authed.Account, gtsmodel.FilterContextNone)
if err != nil {
log.Errorf(ctx, "error convering to api status: %v", err)
continue

View file

@ -27,11 +27,11 @@ import (
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
"code.superseriousbusiness.org/gotosocial/internal/db"
"code.superseriousbusiness.org/gotosocial/internal/filter/status"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"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"
)
@ -59,12 +59,6 @@ func (p *Processor) NotificationsGet(
return util.EmptyPageableResponse(), nil
}
filters, err := p.state.DB.GetFiltersByAccountID(ctx, requester.ID)
if err != nil {
err = gtserror.Newf("error getting account %s filters: %w", requester.ID, err)
return nil, gtserror.NewErrorInternalError(err)
}
var (
items = make([]interface{}, 0, count)
@ -115,9 +109,9 @@ func (p *Processor) NotificationsGet(
}
}
item, err := p.converter.NotificationToAPINotification(ctx, n, filters)
item, err := p.converter.NotificationToAPINotification(ctx, n, true)
if err != nil {
if !errors.Is(err, status.ErrHideStatus) {
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
@ -160,7 +154,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, nil)
apiNotif, err := p.converter.NotificationToAPINotification(ctx, notif, false)
if err != nil {
err := gtserror.Newf("error converting to api model: %w", err)
return nil, gtserror.WrapWithCode(http.StatusInternalServerError, err)

View file

@ -25,9 +25,7 @@ import (
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
timelinepkg "code.superseriousbusiness.org/gotosocial/internal/cache/timeline"
"code.superseriousbusiness.org/gotosocial/internal/db"
"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/gtserror"
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
@ -79,18 +77,6 @@ func (p *Processor) getStatusTimeline(
gtserror.WithCode,
) {
var err error
var filters []*gtsmodel.Filter
if requester != nil {
// Fetch all filters relevant for requesting account.
filters, err = p.state.DB.GetFiltersByAccountID(ctx,
requester.ID,
)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
err := gtserror.Newf("error getting account filters: %w", err)
return nil, gtserror.NewErrorInternalError(err)
}
}
// Ensure we have valid
// input paging cursor.
@ -135,9 +121,8 @@ func (p *Processor) getStatusTimeline(
status,
requester,
filterCtx,
filters,
)
if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) {
if err != nil && !errors.Is(err, typeutils.ErrHideStatus) {
return nil, err
}
return apiStatus, nil

View file

@ -213,7 +213,6 @@ func (suite *FromClientAPITestSuite) statusJSON(
status,
requestingAccount,
gtsmodel.FilterContextNone,
nil,
)
if err != nil {
suite.FailNow(err.Error())
@ -345,7 +344,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, false)
if err != nil {
suite.FailNow(err.Error())
}
@ -2032,7 +2031,7 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv
suite.FailNow("timed out waiting for new status notification")
}
apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, nil)
apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, false)
if err != nil {
suite.FailNow(err.Error())
}
@ -2217,7 +2216,7 @@ func (suite *FromClientAPITestSuite) TestProcessUpdateStatusInteractedWith() {
suite.FailNow("timed out waiting for edited status notification")
}
apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, nil)
apiNotif, err := testStructs.TypeConverter.NotificationToAPINotification(ctx, notif, false)
if err != nil {
suite.FailNow(err.Error())
}

View file

@ -23,11 +23,11 @@ import (
"strings"
"code.superseriousbusiness.org/gotosocial/internal/db"
statusfilter "code.superseriousbusiness.org/gotosocial/internal/filter/status"
"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"
)
@ -743,14 +743,9 @@ func (s *Surface) Notify(
}
}
filters, err := s.State.DB.GetFiltersByAccountID(ctx, targetAccount.ID)
if err != nil {
return gtserror.Newf("couldn't retrieve filters for account %s: %w", targetAccount.ID, err)
}
// Convert the notification to frontend API model for streaming / push.
apiNotif, err := s.Converter.NotificationToAPINotification(ctx, notif, filters)
if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) {
// 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) {
return gtserror.Newf("error converting notification to api representation: %w", err)
}

View file

@ -22,12 +22,12 @@ import (
"errors"
"code.superseriousbusiness.org/gotosocial/internal/cache/timeline"
statusfilter "code.superseriousbusiness.org/gotosocial/internal/filter/status"
"code.superseriousbusiness.org/gotosocial/internal/gtscontext"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"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"
)
@ -147,19 +147,10 @@ func (s *Surface) timelineAndNotifyStatusForFollowers(
continue
}
// Get relevant filters for this follow's account.
// (note the origin account of the follow is receiver of status).
filters, err := s.getFilters(ctx, follow.AccountID)
if err != nil {
log.Error(ctx, err)
continue
}
// Add status to any relevant lists for this follow, if applicable.
listTimelined, exclusive, err := s.listTimelineStatusForFollow(ctx,
status,
follow,
filters,
)
if err != nil {
log.Errorf(ctx, "error list timelining status: %v", err)
@ -181,7 +172,6 @@ func (s *Surface) timelineAndNotifyStatusForFollowers(
status,
stream.TimelineHome,
gtsmodel.FilterContextHome,
filters,
); homeTimelined {
// If hometimelined, add to list of returned account IDs.
@ -239,7 +229,6 @@ func (s *Surface) listTimelineStatusForFollow(
ctx context.Context,
status *gtsmodel.Status,
follow *gtsmodel.Follow,
filters []*gtsmodel.Filter,
) (timelined bool, exclusive bool, err error) {
// Get all lists that contain this given follow.
@ -276,7 +265,6 @@ func (s *Surface) listTimelineStatusForFollow(
status,
stream.TimelineList+":"+list.ID, // key streamType to this specific list
gtsmodel.FilterContextHome,
filters,
)
// Update flag based on if timelined.
@ -286,15 +274,6 @@ func (s *Surface) listTimelineStatusForFollow(
return timelined, exclusive, nil
}
// getFiltersAndMutes returns an account's filters and mutes.
func (s *Surface) getFilters(ctx context.Context, accountID string) ([]*gtsmodel.Filter, error) {
filters, err := s.State.DB.GetFiltersByAccountID(ctx, accountID)
if err != nil {
return nil, gtserror.Newf("couldn't retrieve filters for account %s: %w", accountID, err)
}
return filters, err
}
// listEligible checks if the given status is eligible
// for inclusion in the list that that the given listEntry
// belongs to, based on the replies policy of the list.
@ -370,7 +349,6 @@ func (s *Surface) timelineStatus(
status *gtsmodel.Status,
streamType string,
filterCtx gtsmodel.FilterContext,
filters []*gtsmodel.Filter,
) bool {
// Attempt to convert status to frontend API representation,
@ -379,9 +357,8 @@ func (s *Surface) timelineStatus(
status,
account,
filterCtx,
filters,
)
if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) {
if err != nil && !errors.Is(err, typeutils.ErrHideStatus) {
log.Error(ctx, "error converting status %s to frontend: %v", status.URI, err)
}
@ -425,19 +402,12 @@ func (s *Surface) timelineAndNotifyStatusForTagFollowers(
// Insert the status into the home timeline of each tag follower.
errs := gtserror.MultiError{}
for _, tagFollowerAccount := range tagFollowerAccounts {
filters, err := s.getFilters(ctx, tagFollowerAccount.ID)
if err != nil {
errs.Append(err)
continue
}
_ = s.timelineStatus(ctx,
s.State.Caches.Timelines.Home.MustGet(tagFollowerAccount.ID),
tagFollowerAccount,
status,
stream.TimelineHome,
gtsmodel.FilterContextHome,
filters,
)
}
@ -605,19 +575,10 @@ func (s *Surface) timelineStatusUpdateForFollowers(
continue
}
// Get relevant filters and mutes for this follow's account.
// (note the origin account of the follow is receiver of status).
filters, err := s.getFilters(ctx, follow.AccountID)
if err != nil {
log.Error(ctx, err)
continue
}
// Add status to relevant lists for this follow, if applicable.
_, exclusive, err := s.listTimelineStatusUpdateForFollow(ctx,
status,
follow,
filters,
)
if err != nil {
log.Errorf(ctx, "error list timelining status: %v", err)
@ -637,7 +598,6 @@ func (s *Surface) timelineStatusUpdateForFollowers(
follow.Account,
status,
stream.TimelineHome,
filters,
)
if err != nil {
log.Errorf(ctx, "error home timelining status: %v", err)
@ -662,7 +622,6 @@ func (s *Surface) listTimelineStatusUpdateForFollow(
ctx context.Context,
status *gtsmodel.Status,
follow *gtsmodel.Follow,
filters []*gtsmodel.Filter,
) (bool, bool, error) {
// Get all lists that contain this given follow.
@ -701,7 +660,6 @@ func (s *Surface) listTimelineStatusUpdateForFollow(
follow.Account,
status,
stream.TimelineList+":"+list.ID, // key streamType to this specific list
filters,
)
if err != nil {
log.Errorf(ctx, "error adding status to list timeline: %v", err)
@ -724,7 +682,6 @@ func (s *Surface) timelineStreamStatusUpdate(
account *gtsmodel.Account,
status *gtsmodel.Status,
streamType string,
filters []*gtsmodel.Filter,
) (bool, error) {
// Convert updated database model to frontend model.
@ -732,14 +689,13 @@ func (s *Surface) timelineStreamStatusUpdate(
status,
account,
gtsmodel.FilterContextHome,
filters,
)
switch {
case err == nil:
// no issue.
case errors.Is(err, statusfilter.ErrHideStatus):
case errors.Is(err, typeutils.ErrHideStatus):
// Don't put this status in the stream.
return false, nil
@ -774,18 +730,11 @@ func (s *Surface) timelineStatusUpdateForTagFollowers(
// Stream the update to the home timeline of each tag follower.
errs := gtserror.MultiError{}
for _, tagFollowerAccount := range tagFollowerAccounts {
filters, err := s.getFilters(ctx, tagFollowerAccount.ID)
if err != nil {
errs.Append(err)
continue
}
if _, err := s.timelineStreamStatusUpdate(
ctx,
tagFollowerAccount,
status,
stream.TimelineHome,
filters,
); err != nil {
errs.Appendf(
"error updating status %s on home timeline for account %s: %w",