mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-30 05:36:15 -06:00
Merge branch 'main' into xvello/import-mutes
This commit is contained in:
commit
f30ab4bae4
328 changed files with 31571 additions and 115701 deletions
|
|
@ -96,7 +96,7 @@ func (p *Processor) Delete(
|
|||
}
|
||||
|
||||
// deleteUserAndTokensForAccount deletes the gtsmodel.User and
|
||||
// any OAuth tokens and applications for the given account.
|
||||
// any OAuth tokens, applications, and Web Push subscriptions for the given account.
|
||||
//
|
||||
// Callers to this function should already have checked that
|
||||
// this is a local account, or else it won't have a user associated
|
||||
|
|
@ -129,6 +129,10 @@ func (p *Processor) deleteUserAndTokensForAccount(ctx context.Context, account *
|
|||
}
|
||||
}
|
||||
|
||||
if err := p.state.DB.DeleteWebPushSubscriptionsByAccountID(ctx, account.ID); err != nil {
|
||||
return gtserror.Newf("db error deleting Web Push subscriptions: %w", err)
|
||||
}
|
||||
|
||||
columns, err := stubbifyUser(user)
|
||||
if err != nil {
|
||||
return gtserror.Newf("error stubbifying user: %w", err)
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ func (suite *AdminStandardTestSuite) SetupTest() {
|
|||
suite.mediaManager,
|
||||
&suite.state,
|
||||
suite.emailSender,
|
||||
testrig.NewNoopWebPushSender(),
|
||||
visibility.NewFilter(&suite.state),
|
||||
interaction.NewFilter(&suite.state),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import (
|
|||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
|
|
@ -72,7 +71,7 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
|||
}
|
||||
|
||||
// Auth passed, generate the proper AP representation.
|
||||
person, err := p.converter.AccountToAS(ctx, receiver)
|
||||
accountable, err := p.converter.AccountToAS(ctx, receiver)
|
||||
if err != nil {
|
||||
err := gtserror.Newf("error converting account: %w", err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
|
|
@ -91,7 +90,7 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
|||
// Instead, we end up in an 'I'll show you mine if you show me
|
||||
// yours' situation, where we sort of agree to reveal each
|
||||
// other's profiles at the same time.
|
||||
return data(person)
|
||||
return data(accountable)
|
||||
}
|
||||
|
||||
// Get requester from auth.
|
||||
|
|
@ -107,13 +106,13 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
|||
return nil, gtserror.NewErrorForbidden(errors.New(text))
|
||||
}
|
||||
|
||||
return data(person)
|
||||
return data(accountable)
|
||||
}
|
||||
|
||||
func data(requestedPerson vocab.ActivityStreamsPerson) (interface{}, gtserror.WithCode) {
|
||||
data, err := ap.Serialize(requestedPerson)
|
||||
func data(accountable ap.Accountable) (interface{}, gtserror.WithCode) {
|
||||
data, err := ap.Serialize(accountable)
|
||||
if err != nil {
|
||||
err := gtserror.Newf("error serializing person: %w", err)
|
||||
err := gtserror.Newf("error serializing accountable: %w", err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/processing/markers"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/polls"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/push"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/report"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/search"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/status"
|
||||
|
|
@ -51,6 +52,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/subscriptions"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/webpush"
|
||||
)
|
||||
|
||||
// Processor groups together processing functions and
|
||||
|
|
@ -88,6 +90,7 @@ type Processor struct {
|
|||
markers markers.Processor
|
||||
media media.Processor
|
||||
polls polls.Processor
|
||||
push push.Processor
|
||||
report report.Processor
|
||||
search search.Processor
|
||||
status status.Processor
|
||||
|
|
@ -146,6 +149,10 @@ func (p *Processor) Polls() *polls.Processor {
|
|||
return &p.polls
|
||||
}
|
||||
|
||||
func (p *Processor) Push() *push.Processor {
|
||||
return &p.push
|
||||
}
|
||||
|
||||
func (p *Processor) Report() *report.Processor {
|
||||
return &p.report
|
||||
}
|
||||
|
|
@ -188,6 +195,7 @@ func NewProcessor(
|
|||
mediaManager *mm.Manager,
|
||||
state *state.State,
|
||||
emailSender email.Sender,
|
||||
webPushSender webpush.Sender,
|
||||
visFilter *visibility.Filter,
|
||||
intFilter *interaction.Filter,
|
||||
) *Processor {
|
||||
|
|
@ -221,6 +229,7 @@ func NewProcessor(
|
|||
processor.list = list.New(state, converter)
|
||||
processor.markers = markers.New(state, converter)
|
||||
processor.polls = polls.New(&common, state, converter)
|
||||
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)
|
||||
|
|
@ -241,6 +250,7 @@ func NewProcessor(
|
|||
converter,
|
||||
visFilter,
|
||||
emailSender,
|
||||
webPushSender,
|
||||
&processor.account,
|
||||
&processor.media,
|
||||
&processor.stream,
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ func (suite *ProcessingStandardTestSuite) SetupTest() {
|
|||
suite.mediaManager,
|
||||
&suite.state,
|
||||
suite.emailSender,
|
||||
testrig.NewNoopWebPushSender(),
|
||||
visibility.NewFilter(&suite.state),
|
||||
interaction.NewFilter(&suite.state),
|
||||
)
|
||||
|
|
|
|||
65
internal/processing/push/create.go
Normal file
65
internal/processing/push/create.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// 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 push
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||
)
|
||||
|
||||
// CreateOrReplace creates a Web Push subscription for the given access token,
|
||||
// or entirely replaces the previously existing subscription for that token.
|
||||
func (p *Processor) CreateOrReplace(
|
||||
ctx context.Context,
|
||||
accountID string,
|
||||
accessToken string,
|
||||
request *apimodel.WebPushSubscriptionCreateRequest,
|
||||
) (*apimodel.WebPushSubscription, gtserror.WithCode) {
|
||||
tokenID, errWithCode := p.getTokenID(ctx, accessToken)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
|
||||
// Clear any previous subscription.
|
||||
if err := p.state.DB.DeleteWebPushSubscriptionByTokenID(ctx, tokenID); err != nil {
|
||||
err := gtserror.Newf("couldn't delete Web Push subscription for token ID %s: %w", tokenID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
// Insert a new one.
|
||||
subscription := >smodel.WebPushSubscription{
|
||||
ID: id.NewULID(),
|
||||
AccountID: accountID,
|
||||
TokenID: tokenID,
|
||||
Endpoint: request.Subscription.Endpoint,
|
||||
Auth: request.Subscription.Keys.Auth,
|
||||
P256dh: request.Subscription.Keys.P256dh,
|
||||
NotificationFlags: alertsToNotificationFlags(request.Data.Alerts),
|
||||
}
|
||||
|
||||
if err := p.state.DB.PutWebPushSubscription(ctx, subscription); err != nil {
|
||||
err := gtserror.Newf("couldn't create Web Push subscription for token ID %s: %w", tokenID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
return p.apiSubscription(ctx, subscription)
|
||||
}
|
||||
39
internal/processing/push/delete.go
Normal file
39
internal/processing/push/delete.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// 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 push
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
// Delete deletes the Web Push subscription for the given access token, if there is one.
|
||||
func (p *Processor) Delete(ctx context.Context, accessToken string) gtserror.WithCode {
|
||||
tokenID, errWithCode := p.getTokenID(ctx, accessToken)
|
||||
if errWithCode != nil {
|
||||
return errWithCode
|
||||
}
|
||||
|
||||
if err := p.state.DB.DeleteWebPushSubscriptionByTokenID(ctx, tokenID); err != nil {
|
||||
err := gtserror.Newf("couldn't delete Web Push subscription for token ID %s: %w", tokenID, err)
|
||||
return gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
47
internal/processing/push/get.go
Normal file
47
internal/processing/push/get.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// 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 push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
// Get returns the Web Push subscription for the given access token.
|
||||
func (p *Processor) Get(ctx context.Context, accessToken string) (*apimodel.WebPushSubscription, gtserror.WithCode) {
|
||||
tokenID, errWithCode := p.getTokenID(ctx, accessToken)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
|
||||
subscription, err := p.state.DB.GetWebPushSubscriptionByTokenID(ctx, tokenID)
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
err := gtserror.Newf("couldn't get Web Push subscription for token ID %s: %w", tokenID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
if subscription == nil {
|
||||
err := errors.New("no Web Push subscription exists for this access token")
|
||||
return nil, gtserror.NewErrorNotFound(err)
|
||||
}
|
||||
|
||||
return p.apiSubscription(ctx, subscription)
|
||||
}
|
||||
85
internal/processing/push/push.go
Normal file
85
internal/processing/push/push.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// 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 push
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
)
|
||||
|
||||
type Processor struct {
|
||||
state *state.State
|
||||
converter *typeutils.Converter
|
||||
}
|
||||
|
||||
func New(state *state.State, converter *typeutils.Converter) Processor {
|
||||
return Processor{
|
||||
state: state,
|
||||
converter: converter,
|
||||
}
|
||||
}
|
||||
|
||||
// getTokenID returns the token ID for a given access token.
|
||||
// Since all push API calls require authentication, this should always be available.
|
||||
func (p *Processor) getTokenID(ctx context.Context, accessToken string) (string, gtserror.WithCode) {
|
||||
token, err := p.state.DB.GetTokenByAccess(ctx, accessToken)
|
||||
if err != nil {
|
||||
err := gtserror.Newf("couldn't find token ID for access token: %w", err)
|
||||
return "", gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
return token.ID, nil
|
||||
}
|
||||
|
||||
// apiSubscription is a shortcut to return the API version of the given Web Push subscription,
|
||||
// or return an appropriate error if conversion fails.
|
||||
func (p *Processor) apiSubscription(ctx context.Context, subscription *gtsmodel.WebPushSubscription) (*apimodel.WebPushSubscription, gtserror.WithCode) {
|
||||
apiSubscription, err := p.converter.WebPushSubscriptionToAPIWebPushSubscription(ctx, subscription)
|
||||
if err != nil {
|
||||
err := gtserror.Newf("error converting Web Push subscription %s to API representation: %w", subscription.ID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
return apiSubscription, nil
|
||||
}
|
||||
|
||||
// alertsToNotificationFlags turns the alerts section of a push subscription API request into a packed bitfield.
|
||||
func alertsToNotificationFlags(alerts *apimodel.WebPushSubscriptionAlerts) gtsmodel.WebPushSubscriptionNotificationFlags {
|
||||
var n gtsmodel.WebPushSubscriptionNotificationFlags
|
||||
|
||||
n.Set(gtsmodel.NotificationFollow, alerts.Follow)
|
||||
n.Set(gtsmodel.NotificationFollowRequest, alerts.FollowRequest)
|
||||
n.Set(gtsmodel.NotificationFavourite, alerts.Favourite)
|
||||
n.Set(gtsmodel.NotificationMention, alerts.Mention)
|
||||
n.Set(gtsmodel.NotificationReblog, alerts.Reblog)
|
||||
n.Set(gtsmodel.NotificationPoll, alerts.Poll)
|
||||
n.Set(gtsmodel.NotificationStatus, alerts.Status)
|
||||
n.Set(gtsmodel.NotificationUpdate, alerts.Update)
|
||||
n.Set(gtsmodel.NotificationAdminSignup, alerts.AdminSignup)
|
||||
n.Set(gtsmodel.NotificationAdminReport, alerts.AdminReport)
|
||||
n.Set(gtsmodel.NotificationPendingFave, alerts.PendingFavourite)
|
||||
n.Set(gtsmodel.NotificationPendingReply, alerts.PendingReply)
|
||||
n.Set(gtsmodel.NotificationPendingReblog, alerts.PendingReblog)
|
||||
|
||||
return n
|
||||
}
|
||||
63
internal/processing/push/update.go
Normal file
63
internal/processing/push/update.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// 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 push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
// Update updates the Web Push subscription for the given access token.
|
||||
func (p *Processor) Update(
|
||||
ctx context.Context,
|
||||
accessToken string,
|
||||
request *apimodel.WebPushSubscriptionUpdateRequest,
|
||||
) (*apimodel.WebPushSubscription, gtserror.WithCode) {
|
||||
tokenID, errWithCode := p.getTokenID(ctx, accessToken)
|
||||
if errWithCode != nil {
|
||||
return nil, errWithCode
|
||||
}
|
||||
|
||||
// Get existing subscription.
|
||||
subscription, err := p.state.DB.GetWebPushSubscriptionByTokenID(ctx, tokenID)
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
err := gtserror.Newf("couldn't get Web Push subscription for token ID %s: %w", tokenID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
if subscription == nil {
|
||||
err := errors.New("no Web Push subscription exists for this access token")
|
||||
return nil, gtserror.NewErrorNotFound(err)
|
||||
}
|
||||
|
||||
// Update it.
|
||||
subscription.NotificationFlags = alertsToNotificationFlags(request.Data.Alerts)
|
||||
if err = p.state.DB.UpdateWebPushSubscription(
|
||||
ctx,
|
||||
subscription,
|
||||
"notification_flags",
|
||||
); err != nil {
|
||||
err := gtserror.Newf("couldn't update Web Push subscription for token ID %s: %w", tokenID, err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
return p.apiSubscription(ctx, subscription)
|
||||
}
|
||||
|
|
@ -184,7 +184,7 @@ func (p *Processor) notifVisible(
|
|||
// If this is a new local account sign-up,
|
||||
// skip normal visibility checking because
|
||||
// origin account won't be confirmed yet.
|
||||
if n.NotificationType == gtsmodel.NotificationSignup {
|
||||
if n.NotificationType == gtsmodel.NotificationAdminSignup {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"context"
|
||||
"net/url"
|
||||
|
||||
"github.com/superseriousbusiness/activity/pub"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
|
|
@ -93,11 +92,6 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account)
|
|||
return err
|
||||
}
|
||||
|
||||
publicIRI, err := parseURI(pub.PublicActivityPubIRI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new delete.
|
||||
// todo: tc.AccountToASDelete
|
||||
delete := streams.NewActivityStreamsDelete()
|
||||
|
|
@ -121,7 +115,7 @@ func (f *federate) DeleteAccount(ctx context.Context, account *gtsmodel.Account)
|
|||
|
||||
// Address the delete CC public.
|
||||
deleteCC := streams.NewActivityStreamsCcProperty()
|
||||
deleteCC.AppendIRI(publicIRI)
|
||||
deleteCC.AppendIRI(ap.PublicURI())
|
||||
delete.SetActivityStreamsCc(deleteCC)
|
||||
|
||||
// Send the Delete via the Actor's outbox.
|
||||
|
|
@ -877,14 +871,14 @@ func (f *federate) UpdateAccount(ctx context.Context, account *gtsmodel.Account)
|
|||
return err
|
||||
}
|
||||
|
||||
// Convert account to ActivityStreams Person.
|
||||
person, err := f.converter.AccountToAS(ctx, account)
|
||||
// Convert account to Accountable.
|
||||
accountable, err := f.converter.AccountToAS(ctx, account)
|
||||
if err != nil {
|
||||
return gtserror.Newf("error converting account to Person: %w", err)
|
||||
}
|
||||
|
||||
// Use ActivityStreams Person as Object of Update.
|
||||
update, err := f.converter.WrapPersonInUpdate(person, account)
|
||||
// Use Accountable as Object of Update.
|
||||
update, err := f.converter.WrapAccountableInUpdate(accountable)
|
||||
if err != nil {
|
||||
return gtserror.Newf("error wrapping Person in Update: %w", err)
|
||||
}
|
||||
|
|
@ -1089,11 +1083,6 @@ func (f *federate) MoveAccount(ctx context.Context, account *gtsmodel.Account) e
|
|||
return err
|
||||
}
|
||||
|
||||
publicIRI, err := parseURI(pub.PublicActivityPubIRI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new move.
|
||||
move := streams.NewActivityStreamsMove()
|
||||
|
||||
|
|
@ -1115,7 +1104,7 @@ func (f *federate) MoveAccount(ctx context.Context, account *gtsmodel.Account) e
|
|||
ap.AppendTo(move, followersIRI)
|
||||
|
||||
// Address the move CC public.
|
||||
ap.AppendCc(move, publicIRI)
|
||||
ap.AppendCc(move, ap.PublicURI())
|
||||
|
||||
// Send the Move via the Actor's outbox.
|
||||
if _, err := f.FederatingActor().Send(
|
||||
|
|
|
|||
|
|
@ -179,6 +179,28 @@ func (suite *FromClientAPITestSuite) checkStreamed(
|
|||
}
|
||||
}
|
||||
|
||||
// checkWebPushed asserts that the target account got a single Web Push notification with a given type.
|
||||
func (suite *FromClientAPITestSuite) checkWebPushed(
|
||||
sender *testrig.WebPushMockSender,
|
||||
accountID string,
|
||||
notificationType gtsmodel.NotificationType,
|
||||
) {
|
||||
pushedNotifications := sender.Sent[accountID]
|
||||
if suite.Len(pushedNotifications, 1) {
|
||||
pushedNotification := pushedNotifications[0]
|
||||
suite.Equal(notificationType, pushedNotification.NotificationType)
|
||||
}
|
||||
}
|
||||
|
||||
// checkNotWebPushed asserts that the target account got no Web Push notifications.
|
||||
func (suite *FromClientAPITestSuite) checkNotWebPushed(
|
||||
sender *testrig.WebPushMockSender,
|
||||
accountID string,
|
||||
) {
|
||||
pushedNotifications := sender.Sent[accountID]
|
||||
suite.Len(pushedNotifications, 0)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) statusJSON(
|
||||
ctx context.Context,
|
||||
typeConverter *typeutils.Converter,
|
||||
|
|
@ -341,6 +363,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithNotification() {
|
|||
string(notifJSON),
|
||||
stream.EventTypeNotification,
|
||||
)
|
||||
|
||||
// Check for a Web Push status notification.
|
||||
suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationStatus)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusReply() {
|
||||
|
|
@ -409,6 +434,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReply() {
|
|||
statusJSON,
|
||||
stream.EventTypeUpdate,
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyMuted() {
|
||||
|
|
@ -470,6 +498,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyMuted() {
|
|||
|
||||
suite.ErrorIs(err, db.ErrNoEntries)
|
||||
suite.Nil(notif)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostMuted() {
|
||||
|
|
@ -531,6 +562,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostMuted() {
|
|||
|
||||
suite.ErrorIs(err, db.ErrNoEntries)
|
||||
suite.Nil(notif)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyListOnlyOK() {
|
||||
|
|
@ -607,6 +641,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyLis
|
|||
statusJSON,
|
||||
stream.EventTypeUpdate,
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyListOnlyNo() {
|
||||
|
|
@ -689,6 +726,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyLis
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyListRepliesPolicyNone() {
|
||||
|
|
@ -765,6 +805,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyListRepliesPoli
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoost() {
|
||||
|
|
@ -829,6 +872,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoost() {
|
|||
statusJSON,
|
||||
stream.EventTypeUpdate,
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostNoReblogs() {
|
||||
|
|
@ -981,6 +1027,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWhichBeginsConversat
|
|||
conversationJSON,
|
||||
stream.EventTypeConversation,
|
||||
)
|
||||
|
||||
// Check for a Web Push mention notification.
|
||||
suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationMention)
|
||||
}
|
||||
|
||||
// A public message to a local user should not result in a conversation notification.
|
||||
|
|
@ -1050,6 +1099,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWhichShouldNotCreate
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for a Web Push mention notification.
|
||||
suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationMention)
|
||||
}
|
||||
|
||||
// A public status with a hashtag followed by a local user who does not otherwise follow the author
|
||||
|
|
@ -1123,6 +1175,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithFollowedHashtag(
|
|||
"",
|
||||
stream.EventTypeUpdate,
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
// A public status with a hashtag followed by a local user who does not otherwise follow the author
|
||||
|
|
@ -1204,6 +1259,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithFollowedHashtagA
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
// A boost of a public status with a hashtag followed by a local user
|
||||
|
|
@ -1306,6 +1364,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateBoostWithFollowedHashtag()
|
|||
"",
|
||||
stream.EventTypeUpdate,
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
// A boost of a public status with a hashtag followed by a local user
|
||||
|
|
@ -1416,6 +1477,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateBoostWithFollowedHashtagAn
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
// A boost of a public status with a hashtag followed by a local user
|
||||
|
|
@ -1526,6 +1590,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateBoostWithFollowedHashtagAn
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
// A public status with a hashtag followed by a local user who follows the author and has them on an exclusive list
|
||||
|
|
@ -1598,6 +1665,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
// A public status with a hashtag followed by a local user who follows the author and has them on an exclusive list
|
||||
|
|
@ -1712,6 +1782,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
// A public status with a hashtag followed by a local user who follows the author and has them on an exclusive list
|
||||
|
|
@ -1837,6 +1910,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv
|
|||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
// Check for a Web Push status notification.
|
||||
suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationStatus)
|
||||
}
|
||||
|
||||
// Updating a public status with a hashtag followed by a local user who does not otherwise follow the author
|
||||
|
|
@ -1910,6 +1986,9 @@ func (suite *FromClientAPITestSuite) TestProcessUpdateStatusWithFollowedHashtag(
|
|||
"",
|
||||
stream.EventTypeStatusUpdate,
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
}
|
||||
|
||||
func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
|
||||
|
|
@ -1963,6 +2042,9 @@ func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
|
|||
stream.EventTypeDelete,
|
||||
)
|
||||
|
||||
// Check for absence of Web Push notifications.
|
||||
suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
|
||||
|
||||
// Boost should no longer be in the database.
|
||||
if !testrig.WaitFor(func() bool {
|
||||
_, err := testStructs.State.DB.GetStatusByID(ctx, boostOfDeletedStatus.ID)
|
||||
|
|
|
|||
|
|
@ -189,6 +189,14 @@ func (p *Processor) ProcessFromFediAPI(ctx context.Context, fMsg *messages.FromF
|
|||
if fMsg.APObjectType == ap.ActorPerson {
|
||||
return p.fediAPI.MoveAccount(ctx, fMsg)
|
||||
}
|
||||
|
||||
// UNDO SOMETHING
|
||||
case ap.ActivityUndo:
|
||||
|
||||
// UNDO ANNOUNCE
|
||||
if fMsg.APObjectType == ap.ActivityAnnounce {
|
||||
return p.fediAPI.UndoAnnounce(ctx, fMsg)
|
||||
}
|
||||
}
|
||||
|
||||
return gtserror.Newf("unhandled: %s %s", fMsg.APActivityType, fMsg.APObjectType)
|
||||
|
|
@ -1159,3 +1167,34 @@ func (p *fediAPI) RejectAnnounce(ctx context.Context, fMsg *messages.FromFediAPI
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fediAPI) UndoAnnounce(
|
||||
ctx context.Context,
|
||||
fMsg *messages.FromFediAPI,
|
||||
) error {
|
||||
boost, ok := fMsg.GTSModel.(*gtsmodel.Status)
|
||||
if !ok {
|
||||
return gtserror.Newf("%T not parseable as *gtsmodel.Status", fMsg.GTSModel)
|
||||
}
|
||||
|
||||
// Delete the boost wrapper itself.
|
||||
if err := p.state.DB.DeleteStatusByID(ctx, boost.ID); err != nil {
|
||||
return gtserror.Newf("db error deleting boost: %w", err)
|
||||
}
|
||||
|
||||
// Update statuses count for the requesting account.
|
||||
if err := p.utils.decrementStatusesCount(ctx, fMsg.Requesting, boost); err != nil {
|
||||
log.Errorf(ctx, "error updating account stats: %v", err)
|
||||
}
|
||||
|
||||
// Remove the boost wrapper from all timelines.
|
||||
if err := p.surface.deleteStatusFromTimelines(ctx, boost.ID); err != nil {
|
||||
log.Errorf(ctx, "error removing timelined boost: %v", err)
|
||||
}
|
||||
|
||||
// Interaction counts changed on the boosted status;
|
||||
// uncache the prepared version from all timelines.
|
||||
p.surface.invalidateStatusFromTimelines(ctx, boost.BoostOfID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package workers_test
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
|
@ -29,6 +30,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/stream"
|
||||
|
|
@ -240,7 +242,7 @@ func (suite *FromFediAPITestSuite) TestProcessFave() {
|
|||
notif := >smodel.Notification{}
|
||||
err = testStructs.State.DB.GetWhere(context.Background(), where, notif)
|
||||
suite.NoError(err)
|
||||
suite.Equal(gtsmodel.NotificationFave, notif.NotificationType)
|
||||
suite.Equal(gtsmodel.NotificationFavourite, notif.NotificationType)
|
||||
suite.Equal(fave.TargetAccountID, notif.TargetAccountID)
|
||||
suite.Equal(fave.AccountID, notif.OriginAccountID)
|
||||
suite.Equal(fave.StatusID, notif.StatusID)
|
||||
|
|
@ -313,7 +315,7 @@ func (suite *FromFediAPITestSuite) TestProcessFaveWithDifferentReceivingAccount(
|
|||
notif := >smodel.Notification{}
|
||||
err = testStructs.State.DB.GetWhere(context.Background(), where, notif)
|
||||
suite.NoError(err)
|
||||
suite.Equal(gtsmodel.NotificationFave, notif.NotificationType)
|
||||
suite.Equal(gtsmodel.NotificationFavourite, notif.NotificationType)
|
||||
suite.Equal(fave.TargetAccountID, notif.TargetAccountID)
|
||||
suite.Equal(fave.AccountID, notif.OriginAccountID)
|
||||
suite.Equal(fave.StatusID, notif.StatusID)
|
||||
|
|
@ -679,6 +681,60 @@ func (suite *FromFediAPITestSuite) TestMoveAccount() {
|
|||
suite.WithinDuration(time.Now(), move.SucceededAt, 1*time.Minute)
|
||||
}
|
||||
|
||||
func (suite *FromFediAPITestSuite) TestUndoAnnounce() {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
testStructs = testrig.SetupTestStructs(rMediaPath, rTemplatePath)
|
||||
requestingAcct = suite.testAccounts["remote_account_1"]
|
||||
receivingAcct = suite.testAccounts["local_account_1"]
|
||||
boostedStatus = suite.testStatuses["admin_account_status_1"]
|
||||
)
|
||||
defer testrig.TearDownTestStructs(testStructs)
|
||||
|
||||
// Have remote_account_1 boost admin_account.
|
||||
boost, err := testStructs.TypeConverter.StatusToBoost(
|
||||
ctx,
|
||||
boostedStatus,
|
||||
requestingAcct,
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Set the boost URI + URL to
|
||||
// fossbros-anonymous.io.
|
||||
boost.URI = "https://fossbros-anonymous.io/users/foss_satan/" + boost.ID
|
||||
boost.URL = boost.URI
|
||||
|
||||
// Store the boost.
|
||||
if err := testStructs.State.DB.PutStatus(ctx, boost); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Process the Undo.
|
||||
err = testStructs.Processor.Workers().ProcessFromFediAPI(ctx, &messages.FromFediAPI{
|
||||
APObjectType: ap.ActivityAnnounce,
|
||||
APActivityType: ap.ActivityUndo,
|
||||
GTSModel: boost,
|
||||
Receiving: receivingAcct,
|
||||
Requesting: requestingAcct,
|
||||
})
|
||||
suite.NoError(err)
|
||||
|
||||
// Wait for side effects to trigger:
|
||||
// the boost should be deleted.
|
||||
if !testrig.WaitFor(func() bool {
|
||||
_, err := testStructs.State.DB.GetStatusByID(
|
||||
gtscontext.SetBarebones(ctx),
|
||||
boost.ID,
|
||||
)
|
||||
return errors.Is(err, db.ErrNoEntries)
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for boost to be removed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromFederatorTestSuite(t *testing.T) {
|
||||
suite.Run(t, &FromFediAPITestSuite{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/processing/stream"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/webpush"
|
||||
)
|
||||
|
||||
// Surface wraps functions for 'surfacing' the result
|
||||
|
|
@ -38,5 +39,6 @@ type Surface struct {
|
|||
Stream *stream.Processor
|
||||
VisFilter *visibility.Filter
|
||||
EmailSender email.Sender
|
||||
WebPushSender webpush.Sender
|
||||
Conversations *conversations.Processor
|
||||
}
|
||||
|
|
|
|||
|
|
@ -250,7 +250,7 @@ func (s *Surface) notifyFave(
|
|||
// notify status author
|
||||
// of fave by account.
|
||||
if err := s.Notify(ctx,
|
||||
gtsmodel.NotificationFave,
|
||||
gtsmodel.NotificationFavourite,
|
||||
fave.TargetAccount,
|
||||
fave.Account,
|
||||
fave.StatusID,
|
||||
|
|
@ -521,7 +521,7 @@ func (s *Surface) notifySignup(ctx context.Context, newUser *gtsmodel.User) erro
|
|||
var errs gtserror.MultiError
|
||||
for _, mod := range modAccounts {
|
||||
if err := s.Notify(ctx,
|
||||
gtsmodel.NotificationSignup,
|
||||
gtsmodel.NotificationAdminSignup,
|
||||
mod,
|
||||
newUser.Account,
|
||||
"",
|
||||
|
|
@ -647,5 +647,10 @@ func (s *Surface) Notify(
|
|||
}
|
||||
s.Stream.Notify(ctx, targetAccount, apiNotif)
|
||||
|
||||
// Send Web Push notification to the user.
|
||||
if err = s.WebPushSender.Send(ctx, notif, filters, compiledMutes); err != nil {
|
||||
return gtserror.Newf("error sending Web Push notifications: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ func (suite *SurfaceNotifyTestSuite) TestSpamNotifs() {
|
|||
Stream: testStructs.Processor.Stream(),
|
||||
VisFilter: visibility.NewFilter(testStructs.State),
|
||||
EmailSender: testStructs.EmailSender,
|
||||
WebPushSender: testStructs.WebPushSender,
|
||||
Conversations: testStructs.Processor.Conversations(),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/processing/stream"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/webpush"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/workers"
|
||||
)
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ func New(
|
|||
converter *typeutils.Converter,
|
||||
visFilter *visibility.Filter,
|
||||
emailSender email.Sender,
|
||||
webPushSender webpush.Sender,
|
||||
account *account.Processor,
|
||||
media *media.Processor,
|
||||
stream *stream.Processor,
|
||||
|
|
@ -65,6 +67,7 @@ func New(
|
|||
Stream: stream,
|
||||
VisFilter: visFilter,
|
||||
EmailSender: emailSender,
|
||||
WebPushSender: webPushSender,
|
||||
Conversations: conversations,
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue