diff options
author | 2024-09-10 14:34:49 +0200 | |
---|---|---|
committer | 2024-09-10 12:34:49 +0000 | |
commit | 307d98e3862b6e867eea524b81d5428b03e6607c (patch) | |
tree | b990378c5452f5779b85bd0d769db77a78f93600 /internal/processing/workers | |
parent | [chore] status dereferencing improvements (#3255) (diff) | |
download | gotosocial-307d98e3862b6e867eea524b81d5428b03e6607c.tar.xz |
[feature] Process `Reject` of interaction via fedi API, put rejected statuses in the "sin bin" 😈 (#3271)
* [feature] Process `Reject` of interaction via fedi API, put rejected statuses in the "sin bin"
* update test
* move nil check back to `rejectStatusIRI`
Diffstat (limited to 'internal/processing/workers')
-rw-r--r-- | internal/processing/workers/fromclientapi.go | 62 | ||||
-rw-r--r-- | internal/processing/workers/fromfediapi.go | 151 | ||||
-rw-r--r-- | internal/processing/workers/util.go | 99 | ||||
-rw-r--r-- | internal/processing/workers/workers.go | 9 |
4 files changed, 260 insertions, 61 deletions
diff --git a/internal/processing/workers/fromclientapi.go b/internal/processing/workers/fromclientapi.go index c723a6001..c8bc8352f 100644 --- a/internal/processing/workers/fromclientapi.go +++ b/internal/processing/workers/fromclientapi.go @@ -911,11 +911,6 @@ func (p *clientAPI) UndoAnnounce(ctx context.Context, cMsg *messages.FromClientA } func (p *clientAPI) DeleteStatus(ctx context.Context, cMsg *messages.FromClientAPI) error { - // Don't delete attachments, just unattach them: - // this request comes from the client API and the - // poster may want to use attachments again later. - const deleteAttachments = false - status, ok := cMsg.GTSModel.(*gtsmodel.Status) if !ok { return gtserror.Newf("%T not parseable as *gtsmodel.Status", cMsg.GTSModel) @@ -942,8 +937,22 @@ func (p *clientAPI) DeleteStatus(ctx context.Context, cMsg *messages.FromClientA // (stops processing of remote origin data targeting this status). p.state.Workers.Federator.Queue.Delete("TargetURI", status.URI) - // First perform the actual status deletion. - if err := p.utils.wipeStatus(ctx, status, deleteAttachments); err != nil { + // Don't delete attachments, just unattach them: + // this request comes from the client API and the + // poster may want to use attachments again later. + const deleteAttachments = false + + // This is just a deletion, not a Reject, + // we don't need to take a copy of this status. + const copyToSinBin = false + + // Perform the actual status deletion. + if err := p.utils.wipeStatus( + ctx, + status, + deleteAttachments, + copyToSinBin, + ); err != nil { log.Errorf(ctx, "error wiping status: %v", err) } @@ -1275,9 +1284,23 @@ func (p *clientAPI) RejectReply(ctx context.Context, cMsg *messages.FromClientAP return gtserror.Newf("db error getting rejected reply: %w", err) } - // Totally wipe the status. - if err := p.utils.wipeStatus(ctx, status, true); err != nil { - return gtserror.Newf("error wiping status: %w", err) + // Delete attachments from this status. + // It's rejected so there's no possibility + // for the poster to delete + redraft it. + const deleteAttachments = true + + // Keep a copy of the status in + // the sin bin for future review. + const copyToSinBin = true + + // Perform the actual status deletion. + if err := p.utils.wipeStatus( + ctx, + status, + deleteAttachments, + copyToSinBin, + ); err != nil { + log.Errorf(ctx, "error wiping reply: %v", err) } return nil @@ -1306,9 +1329,22 @@ func (p *clientAPI) RejectAnnounce(ctx context.Context, cMsg *messages.FromClien return gtserror.Newf("db error getting rejected announce: %w", err) } - // Totally wipe the status. - if err := p.utils.wipeStatus(ctx, boost, true); err != nil { - return gtserror.Newf("error wiping status: %w", err) + // Boosts don't have attachments anyway + // so it doesn't matter what we set here. + const deleteAttachments = true + + // This is just a boost, don't + // keep a copy in the sin bin. + const copyToSinBin = true + + // Perform the actual status deletion. + if err := p.utils.wipeStatus( + ctx, + boost, + deleteAttachments, + copyToSinBin, + ); err != nil { + log.Errorf(ctx, "error wiping announce: %v", err) } return nil diff --git a/internal/processing/workers/fromfediapi.go b/internal/processing/workers/fromfediapi.go index 42e5e9db2..d8abaa865 100644 --- a/internal/processing/workers/fromfediapi.go +++ b/internal/processing/workers/fromfediapi.go @@ -27,6 +27,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" + "github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/uris" @@ -146,6 +147,23 @@ func (p *Processor) ProcessFromFediAPI(ctx context.Context, fMsg *messages.FromF return p.fediAPI.AcceptAnnounce(ctx, fMsg) } + // REJECT SOMETHING + case ap.ActivityReject: + switch fMsg.APObjectType { + + // REJECT LIKE + case ap.ActivityLike: + return p.fediAPI.RejectLike(ctx, fMsg) + + // REJECT NOTE/STATUS (ie., reject a reply) + case ap.ObjectNote: + return p.fediAPI.RejectReply(ctx, fMsg) + + // REJECT BOOST + case ap.ActivityAnnounce: + return p.fediAPI.RejectAnnounce(ctx, fMsg) + } + // DELETE SOMETHING case ap.ActivityDelete: switch fMsg.APObjectType { @@ -878,11 +896,6 @@ func (p *fediAPI) UpdateStatus(ctx context.Context, fMsg *messages.FromFediAPI) } func (p *fediAPI) DeleteStatus(ctx context.Context, fMsg *messages.FromFediAPI) error { - // Delete attachments from this status, since this request - // comes from the federating API, and there's no way the - // poster can do a delete + redraft for it on our instance. - const deleteAttachments = true - status, ok := fMsg.GTSModel.(*gtsmodel.Status) if !ok { return gtserror.Newf("%T not parseable as *gtsmodel.Status", fMsg.GTSModel) @@ -909,8 +922,22 @@ func (p *fediAPI) DeleteStatus(ctx context.Context, fMsg *messages.FromFediAPI) // (stops processing of remote origin data targeting this status). p.state.Workers.Federator.Queue.Delete("TargetURI", status.URI) - // First perform the actual status deletion. - if err := p.utils.wipeStatus(ctx, status, deleteAttachments); err != nil { + // Delete attachments from this status, since this request + // comes from the federating API, and there's no way the + // poster can do a delete + redraft for it on our instance. + const deleteAttachments = true + + // This is just a deletion, not a Reject, + // we don't need to take a copy of this status. + const copyToSinBin = false + + // Perform the actual status deletion. + if err := p.utils.wipeStatus( + ctx, + status, + deleteAttachments, + copyToSinBin, + ); err != nil { log.Errorf(ctx, "error wiping status: %v", err) } @@ -956,3 +983,113 @@ func (p *fediAPI) DeleteAccount(ctx context.Context, fMsg *messages.FromFediAPI) return nil } + +func (p *fediAPI) RejectLike(ctx context.Context, fMsg *messages.FromFediAPI) error { + req, ok := fMsg.GTSModel.(*gtsmodel.InteractionRequest) + if !ok { + return gtserror.Newf("%T not parseable as *gtsmodel.InteractionRequest", fMsg.GTSModel) + } + + // At this point the InteractionRequest should already + // be in the database, we just need to do side effects. + + // Send out the Reject. + if err := p.federate.RejectInteraction(ctx, req); err != nil { + log.Errorf(ctx, "error federating rejection of like: %v", err) + } + + // Get the rejected fave. + fave, err := p.state.DB.GetStatusFaveByURI( + gtscontext.SetBarebones(ctx), + req.InteractionURI, + ) + if err != nil { + return gtserror.Newf("db error getting rejected fave: %w", err) + } + + // Delete the fave. + if err := p.state.DB.DeleteStatusFaveByID(ctx, fave.ID); err != nil { + return gtserror.Newf("db error deleting fave: %w", err) + } + + return nil +} + +func (p *fediAPI) RejectReply(ctx context.Context, fMsg *messages.FromFediAPI) error { + req, ok := fMsg.GTSModel.(*gtsmodel.InteractionRequest) + if !ok { + return gtserror.Newf("%T not parseable as *gtsmodel.InteractionRequest", fMsg.GTSModel) + } + + // At this point the InteractionRequest should already + // be in the database, we just need to do side effects. + + // Get the rejected status. + status, err := p.state.DB.GetStatusByURI( + gtscontext.SetBarebones(ctx), + req.InteractionURI, + ) + if err != nil { + return gtserror.Newf("db error getting rejected reply: %w", err) + } + + // Delete attachments from this status. + // It's rejected so there's no possibility + // for the poster to delete + redraft it. + const deleteAttachments = true + + // Keep a copy of the status in + // the sin bin for future review. + const copyToSinBin = true + + // Perform the actual status deletion. + if err := p.utils.wipeStatus( + ctx, + status, + deleteAttachments, + copyToSinBin, + ); err != nil { + log.Errorf(ctx, "error wiping reply: %v", err) + } + + return nil +} + +func (p *fediAPI) RejectAnnounce(ctx context.Context, fMsg *messages.FromFediAPI) error { + req, ok := fMsg.GTSModel.(*gtsmodel.InteractionRequest) + if !ok { + return gtserror.Newf("%T not parseable as *gtsmodel.InteractionRequest", fMsg.GTSModel) + } + + // At this point the InteractionRequest should already + // be in the database, we just need to do side effects. + + // Get the rejected boost. + boost, err := p.state.DB.GetStatusByURI( + gtscontext.SetBarebones(ctx), + req.InteractionURI, + ) + if err != nil { + return gtserror.Newf("db error getting rejected announce: %w", err) + } + + // Boosts don't have attachments anyway + // so it doesn't matter what we set here. + const deleteAttachments = true + + // This is just a boost, don't + // keep a copy in the sin bin. + const copyToSinBin = true + + // Perform the actual status deletion. + if err := p.utils.wipeStatus( + ctx, + boost, + deleteAttachments, + copyToSinBin, + ); err != nil { + log.Errorf(ctx, "error wiping announce: %v", err) + } + + return nil +} diff --git a/internal/processing/workers/util.go b/internal/processing/workers/util.go index bb7faffbf..042f4827c 100644 --- a/internal/processing/workers/util.go +++ b/internal/processing/workers/util.go @@ -37,69 +37,90 @@ import ( // util provides util functions used by both // the fromClientAPI and fromFediAPI functions. type utils struct { - state *state.State - media *media.Processor - account *account.Processor - surface *Surface + state *state.State + media *media.Processor + account *account.Processor + surface *Surface + converter *typeutils.Converter } -// wipeStatus encapsulates common logic -// used to totally delete a status + all -// its attachments, notifications, boosts, -// and timeline entries. +// wipeStatus encapsulates common logic used to +// totally delete a status + all its attachments, +// notifications, boosts, and timeline entries. +// +// If deleteAttachments is true, then any status +// attachments will also be deleted, else they +// will just be detached. +// +// If copyToSinBin is true, then a version of the +// status will be put in the `sin_bin_statuses` +// table prior to deletion. func (u *utils) wipeStatus( ctx context.Context, - statusToDelete *gtsmodel.Status, + status *gtsmodel.Status, deleteAttachments bool, + copyToSinBin bool, ) error { var errs gtserror.MultiError + if copyToSinBin { + // Copy this status to the sin bin before we delete it. + sbStatus, err := u.converter.StatusToSinBinStatus(ctx, status) + if err != nil { + errs.Appendf("error converting status to sinBinStatus: %w", err) + } else { + if err := u.state.DB.PutSinBinStatus(ctx, sbStatus); err != nil { + errs.Appendf("db error storing sinBinStatus: %w", err) + } + } + } + // Either delete all attachments for this status, - // or simply unattach + clean them separately later. + // or simply detach + clean them separately later. // - // Reason to unattach rather than delete is that - // the poster might want to reattach them to another - // status immediately (in case of delete + redraft) + // Reason to detach rather than delete is that + // the author might want to reattach them to another + // status immediately (in case of delete + redraft). if deleteAttachments { // todo:u.state.DB.DeleteAttachmentsForStatus - for _, id := range statusToDelete.AttachmentIDs { + for _, id := range status.AttachmentIDs { if err := u.media.Delete(ctx, id); err != nil { errs.Appendf("error deleting media: %w", err) } } } else { // todo:u.state.DB.UnattachAttachmentsForStatus - for _, id := range statusToDelete.AttachmentIDs { - if _, err := u.media.Unattach(ctx, statusToDelete.Account, id); err != nil { + for _, id := range status.AttachmentIDs { + if _, err := u.media.Unattach(ctx, status.Account, id); err != nil { errs.Appendf("error unattaching media: %w", err) } } } - // delete all mention entries generated by this status + // Delete all mentions generated by this status. // todo:u.state.DB.DeleteMentionsForStatus - for _, id := range statusToDelete.MentionIDs { + for _, id := range status.MentionIDs { if err := u.state.DB.DeleteMentionByID(ctx, id); err != nil { errs.Appendf("error deleting status mention: %w", err) } } - // delete all notification entries generated by this status - if err := u.state.DB.DeleteNotificationsForStatus(ctx, statusToDelete.ID); err != nil { + // Delete all notifications generated by this status. + if err := u.state.DB.DeleteNotificationsForStatus(ctx, status.ID); err != nil { errs.Appendf("error deleting status notifications: %w", err) } - // delete all bookmarks that point to this status - if err := u.state.DB.DeleteStatusBookmarksForStatus(ctx, statusToDelete.ID); err != nil { + // Delete all bookmarks of this status. + if err := u.state.DB.DeleteStatusBookmarksForStatus(ctx, status.ID); err != nil { errs.Appendf("error deleting status bookmarks: %w", err) } - // delete all faves of this status - if err := u.state.DB.DeleteStatusFavesForStatus(ctx, statusToDelete.ID); err != nil { + // Delete all faves of this status. + if err := u.state.DB.DeleteStatusFavesForStatus(ctx, status.ID); err != nil { errs.Appendf("error deleting status faves: %w", err) } - if pollID := statusToDelete.PollID; pollID != "" { + if pollID := status.PollID; pollID != "" { // Delete this poll by ID from the database. if err := u.state.DB.DeletePollByID(ctx, pollID); err != nil { errs.Appendf("error deleting status poll: %w", err) @@ -114,38 +135,42 @@ func (u *utils) wipeStatus( _ = u.state.Workers.Scheduler.Cancel(pollID) } - // delete all boosts for this status + remove them from timelines + // Get all boost of this status so that we can + // delete those boosts + remove them from timelines. boosts, err := u.state.DB.GetStatusBoosts( - // we MUST set a barebones context here, + // We MUST set a barebones context here, // as depending on where it came from the // original BoostOf may already be gone. gtscontext.SetBarebones(ctx), - statusToDelete.ID) + status.ID) if err != nil { errs.Appendf("error fetching status boosts: %w", err) } for _, boost := range boosts { - if err := u.surface.deleteStatusFromTimelines(ctx, boost.ID); err != nil { - errs.Appendf("error deleting boost from timelines: %w", err) - } + // Delete the boost itself. if err := u.state.DB.DeleteStatusByID(ctx, boost.ID); err != nil { errs.Appendf("error deleting boost: %w", err) } + + // Remove the boost from any and all timelines. + if err := u.surface.deleteStatusFromTimelines(ctx, boost.ID); err != nil { + errs.Appendf("error deleting boost from timelines: %w", err) + } } - // delete this status from any and all timelines - if err := u.surface.deleteStatusFromTimelines(ctx, statusToDelete.ID); err != nil { + // Delete the status itself from any and all timelines. + if err := u.surface.deleteStatusFromTimelines(ctx, status.ID); err != nil { errs.Appendf("error deleting status from timelines: %w", err) } - // delete this status from any conversations that it's part of - if err := u.state.DB.DeleteStatusFromConversations(ctx, statusToDelete.ID); err != nil { + // Delete this status from any conversations it's part of. + if err := u.state.DB.DeleteStatusFromConversations(ctx, status.ID); err != nil { errs.Appendf("error deleting status from conversations: %w", err) } - // finally, delete the status itself - if err := u.state.DB.DeleteStatusByID(ctx, statusToDelete.ID); err != nil { + // Finally delete the status itself. + if err := u.state.DB.DeleteStatusByID(ctx, status.ID); err != nil { errs.Appendf("error deleting status: %w", err) } diff --git a/internal/processing/workers/workers.go b/internal/processing/workers/workers.go index d4b525783..ad673481b 100644 --- a/internal/processing/workers/workers.go +++ b/internal/processing/workers/workers.go @@ -70,10 +70,11 @@ func New( // Init shared util funcs. utils := &utils{ - state: state, - media: media, - account: account, - surface: surface, + state: state, + media: media, + account: account, + surface: surface, + converter: converter, } return Processor{ |