diff --git a/internal/processing/fromclientapi.go b/internal/processing/fromclientapi.go index 5fd88d8e4..79860b5a9 100644 --- a/internal/processing/fromclientapi.go +++ b/internal/processing/fromclientapi.go @@ -73,7 +73,7 @@ func (p *processor) ProcessFromClientAPI(ctx context.Context, clientMsg messages return errors.New("fave was not parseable as *gtsmodel.StatusFave") } - if err := p.notifyFave(ctx, fave, clientMsg.TargetAccount); err != nil { + if err := p.notifyFave(ctx, fave); err != nil { return err } diff --git a/internal/processing/fromcommon.go b/internal/processing/fromcommon.go index 613ad5fca..88d7405a8 100644 --- a/internal/processing/fromcommon.go +++ b/internal/processing/fromcommon.go @@ -190,8 +190,17 @@ func (p *processor) notifyFollow(ctx context.Context, follow *gtsmodel.Follow, t return nil } -func (p *processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave, targetAccount *gtsmodel.Account) error { - // return if this isn't a local account +func (p *processor) notifyFave(ctx context.Context, fave *gtsmodel.StatusFave) error { + if fave.TargetAccount == nil { + a, err := p.db.GetAccountByID(ctx, fave.TargetAccountID) + if err != nil { + return err + } + fave.TargetAccount = a + } + targetAccount := fave.TargetAccount + + // just return if target isn't a local account if targetAccount.Domain != "" { return nil } diff --git a/internal/processing/fromfederator.go b/internal/processing/fromfederator.go index 20027f2a1..d0e340b5e 100644 --- a/internal/processing/fromfederator.go +++ b/internal/processing/fromfederator.go @@ -73,7 +73,7 @@ func (p *processor) ProcessFromFederator(ctx context.Context, federatorMsg messa return errors.New("like was not parseable as *gtsmodel.StatusFave") } - if err := p.notifyFave(ctx, incomingFave, federatorMsg.ReceivingAccount); err != nil { + if err := p.notifyFave(ctx, incomingFave); err != nil { return err } case ap.ActivityFollow: diff --git a/internal/processing/fromfederator_test.go b/internal/processing/fromfederator_test.go index c58334787..ba2aaf03e 100644 --- a/internal/processing/fromfederator_test.go +++ b/internal/processing/fromfederator_test.go @@ -151,6 +151,131 @@ func (suite *FromFederatorTestSuite) TestProcessReplyMention() { suite.False(notif.Read) } +func (suite *FromFederatorTestSuite) TestProcessFave() { + favedAccount := suite.testAccounts["local_account_1"] + favedStatus := suite.testStatuses["local_account_1_status_1"] + favingAccount := suite.testAccounts["remote_account_1"] + + stream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), favedAccount, "user") + suite.NoError(errWithCode) + + fave := >smodel.StatusFave{ + ID: "01FGKJPXFTVQPG9YSSZ95ADS7Q", + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + AccountID: favingAccount.ID, + Account: favingAccount, + TargetAccountID: favedAccount.ID, + TargetAccount: favedAccount, + StatusID: favedStatus.ID, + Status: favedStatus, + URI: favingAccount.URI + "/faves/aaaaaaaaaaaa", + } + + err := suite.db.Put(context.Background(), fave) + suite.NoError(err) + + err = suite.processor.ProcessFromFederator(context.Background(), messages.FromFederator{ + APObjectType: ap.ActivityLike, + APActivityType: ap.ActivityCreate, + GTSModel: fave, + ReceivingAccount: favedAccount, + }) + suite.NoError(err) + + // side effects should be triggered + // 1. a notification should exist for the fave + where := []db.Where{ + { + Key: "status_id", + Value: favedStatus.ID, + }, + { + Key: "origin_account_id", + Value: favingAccount.ID, + }, + } + + notif := >smodel.Notification{} + err = suite.db.GetWhere(context.Background(), where, notif) + suite.NoError(err) + suite.Equal(gtsmodel.NotificationFave, notif.NotificationType) + suite.Equal(fave.TargetAccountID, notif.TargetAccountID) + suite.Equal(fave.AccountID, notif.OriginAccountID) + suite.Equal(fave.StatusID, notif.StatusID) + suite.False(notif.Read) + + // 2. a notification should be streamed + msg := <-stream.Messages + suite.Equal("notification", msg.Event) + suite.NotEmpty(msg.Payload) + suite.EqualValues([]string{"user"}, msg.Stream) +} + +// TestProcessFaveWithDifferentReceivingAccount ensures that when an account receives a fave that's for +// another account in their AP inbox, a notification isn't streamed to the receiving account. +// +// This tests for an issue we were seeing where Misskey sends out faves to inboxes of people that don't own +// the fave, but just follow the actor who received the fave. +func (suite *FromFederatorTestSuite) TestProcessFaveWithDifferentReceivingAccount() { + receivingAccount := suite.testAccounts["local_account_2"] + favedAccount := suite.testAccounts["local_account_1"] + favedStatus := suite.testStatuses["local_account_1_status_1"] + favingAccount := suite.testAccounts["remote_account_1"] + + stream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), receivingAccount, "user") + suite.NoError(errWithCode) + + fave := >smodel.StatusFave{ + ID: "01FGKJPXFTVQPG9YSSZ95ADS7Q", + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + AccountID: favingAccount.ID, + Account: favingAccount, + TargetAccountID: favedAccount.ID, + TargetAccount: favedAccount, + StatusID: favedStatus.ID, + Status: favedStatus, + URI: favingAccount.URI + "/faves/aaaaaaaaaaaa", + } + + err := suite.db.Put(context.Background(), fave) + suite.NoError(err) + + err = suite.processor.ProcessFromFederator(context.Background(), messages.FromFederator{ + APObjectType: ap.ActivityLike, + APActivityType: ap.ActivityCreate, + GTSModel: fave, + ReceivingAccount: receivingAccount, + }) + suite.NoError(err) + + // side effects should be triggered + // 1. a notification should exist for the fave + where := []db.Where{ + { + Key: "status_id", + Value: favedStatus.ID, + }, + { + Key: "origin_account_id", + Value: favingAccount.ID, + }, + } + + notif := >smodel.Notification{} + err = suite.db.GetWhere(context.Background(), where, notif) + suite.NoError(err) + suite.Equal(gtsmodel.NotificationFave, notif.NotificationType) + suite.Equal(fave.TargetAccountID, notif.TargetAccountID) + suite.Equal(fave.AccountID, notif.OriginAccountID) + suite.Equal(fave.StatusID, notif.StatusID) + suite.False(notif.Read) + + // 2. no notification should be streamed to the account that received the fave message, because they weren't the target + suite.Empty(stream.Messages) +} + func TestFromFederatorTestSuite(t *testing.T) { suite.Run(t, &FromFederatorTestSuite{}) }