mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 07:12:25 -05:00
[feature] tentatively start adding polls support (#2249)
This commit is contained in:
parent
297b6eeaaa
commit
c6e00afc7c
36 changed files with 657 additions and 393 deletions
|
|
@ -46,28 +46,29 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
|
|||
return nil // Already processed.
|
||||
}
|
||||
|
||||
acceptObject := accept.GetActivityStreamsObject()
|
||||
if acceptObject == nil {
|
||||
return errors.New("ACCEPT: no object set on vocab.ActivityStreamsAccept")
|
||||
}
|
||||
// Iterate all provided objects in the activity.
|
||||
for _, object := range ap.ExtractObjects(accept) {
|
||||
|
||||
for iter := acceptObject.Begin(); iter != acceptObject.End(); iter = iter.Next() {
|
||||
// check if the object is an IRI
|
||||
if iter.IsIRI() {
|
||||
// we have just the URI of whatever is being accepted, so we need to find out what it is
|
||||
acceptedObjectIRI := iter.GetIRI()
|
||||
if uris.IsFollowPath(acceptedObjectIRI) {
|
||||
// ACCEPT FOLLOW
|
||||
followReq, err := f.state.DB.GetFollowRequestByURI(ctx, acceptedObjectIRI.String())
|
||||
// Check and handle any vocab.Type objects.
|
||||
if objType := object.GetType(); objType != nil {
|
||||
switch objType.GetTypeName() { //nolint:gocritic
|
||||
|
||||
case ap.ActivityFollow:
|
||||
// Cast the vocab.Type object to known AS type.
|
||||
asFollow := objType.(vocab.ActivityStreamsFollow)
|
||||
|
||||
// convert the follow to something we can understand
|
||||
gtsFollow, err := f.converter.ASFollowToFollow(ctx, asFollow)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ACCEPT: couldn't get follow request with id %s from the database: %s", acceptedObjectIRI.String(), err)
|
||||
return fmt.Errorf("ACCEPT: error converting asfollow to gtsfollow: %s", err)
|
||||
}
|
||||
|
||||
// make sure the addressee of the original follow is the same as whatever inbox this landed in
|
||||
if followReq.AccountID != receivingAccount.ID {
|
||||
if gtsFollow.AccountID != receivingAccount.ID {
|
||||
return errors.New("ACCEPT: follow object account and inbox account were not the same")
|
||||
}
|
||||
follow, err := f.state.DB.AcceptFollowRequest(ctx, followReq.AccountID, followReq.TargetAccountID)
|
||||
|
||||
follow, err := f.state.DB.AcceptFollowRequest(ctx, gtsFollow.AccountID, gtsFollow.TargetAccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -78,31 +79,36 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
|
|||
GTSModel: follow,
|
||||
ReceivingAccount: receivingAccount,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// check if iter is an AP object / type
|
||||
if iter.GetType() == nil {
|
||||
continue
|
||||
}
|
||||
if iter.GetType().GetTypeName() == ap.ActivityFollow {
|
||||
|
||||
// Check and handle any
|
||||
// IRI type objects.
|
||||
if object.IsIRI() {
|
||||
|
||||
// Extract IRI from object.
|
||||
iri := object.GetIRI()
|
||||
if !uris.IsFollowPath(iri) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Serialize IRI.
|
||||
iriStr := iri.String()
|
||||
|
||||
// ACCEPT FOLLOW
|
||||
asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
|
||||
if !ok {
|
||||
return errors.New("ACCEPT: couldn't parse follow into vocab.ActivityStreamsFollow")
|
||||
}
|
||||
// convert the follow to something we can understand
|
||||
gtsFollow, err := f.converter.ASFollowToFollow(ctx, asFollow)
|
||||
followReq, err := f.state.DB.GetFollowRequestByURI(ctx, iriStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ACCEPT: error converting asfollow to gtsfollow: %s", err)
|
||||
return fmt.Errorf("ACCEPT: couldn't get follow request with id %s from the database: %s", iriStr, err)
|
||||
}
|
||||
|
||||
// make sure the addressee of the original follow is the same as whatever inbox this landed in
|
||||
if gtsFollow.AccountID != receivingAccount.ID {
|
||||
if followReq.AccountID != receivingAccount.ID {
|
||||
return errors.New("ACCEPT: follow object account and inbox account were not the same")
|
||||
}
|
||||
follow, err := f.state.DB.AcceptFollowRequest(ctx, gtsFollow.AccountID, gtsFollow.TargetAccountID)
|
||||
|
||||
follow, err := f.state.DB.AcceptFollowRequest(ctx, followReq.AccountID, followReq.TargetAccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -114,8 +120,9 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
|
|||
ReceivingAccount: receivingAccount,
|
||||
})
|
||||
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
|||
// FLAG / REPORT SOMETHING
|
||||
return f.activityFlag(ctx, asType, receivingAccount, requestingAccount)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +112,7 @@ func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, rec
|
|||
GTSModel: block,
|
||||
ReceivingAccount: receiving,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -132,37 +134,19 @@ func (f *federatingDB) activityCreate(
|
|||
return gtserror.Newf("could not convert asType %T to ActivityStreamsCreate", asType)
|
||||
}
|
||||
|
||||
// Create must have an Object.
|
||||
objectProp := create.GetActivityStreamsObject()
|
||||
if objectProp == nil {
|
||||
return gtserror.New("create had no Object")
|
||||
}
|
||||
|
||||
// Iterate through the Object property and process FIRST provided statusable.
|
||||
// todo: https://github.com/superseriousbusiness/gotosocial/issues/1905
|
||||
for iter := objectProp.Begin(); iter != objectProp.End(); iter = iter.Next() {
|
||||
object := iter.GetType()
|
||||
if object == nil {
|
||||
// Can't do Create with Object that's just a URI.
|
||||
// Warn log this because it's an AP error.
|
||||
log.Warn(ctx, "object entry was not a type: %[1]T%[1]+v", iter)
|
||||
for _, object := range ap.ExtractObjects(create) {
|
||||
// Try to get object as vocab.Type,
|
||||
// else skip handling (likely) IRI.
|
||||
objType := object.GetType()
|
||||
if objType == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure given object type is a statusable.
|
||||
statusable, ok := object.(ap.Statusable)
|
||||
if !ok {
|
||||
// Can't (currently) Create anything other than a Statusable. ([1] is a format arg index)
|
||||
log.Debugf(ctx, "object entry type (currently) unsupported: %[1]T%[1]+v", object)
|
||||
continue
|
||||
if statusable, ok := ap.ToStatusable(objType); ok {
|
||||
return f.createStatusable(ctx, statusable, receivingAccount, requestingAccount)
|
||||
}
|
||||
|
||||
// Handle creation of statusable.
|
||||
return f.createStatusable(ctx,
|
||||
statusable,
|
||||
receivingAccount,
|
||||
requestingAccount,
|
||||
)
|
||||
// TODO: handle CREATE of other types?
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ type DB interface {
|
|||
Accept(ctx context.Context, accept vocab.ActivityStreamsAccept) error
|
||||
Reject(ctx context.Context, reject vocab.ActivityStreamsReject) error
|
||||
Announce(ctx context.Context, announce vocab.ActivityStreamsAnnounce) error
|
||||
Question(ctx context.Context, question vocab.ActivityStreamsQuestion) error
|
||||
}
|
||||
|
||||
// FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface.
|
||||
|
|
|
|||
32
internal/federation/federatingdb/question.go
Normal file
32
internal/federation/federatingdb/question.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// 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 federatingdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
)
|
||||
|
||||
func (f *federatingDB) Question(ctx context.Context, question vocab.ActivityStreamsQuestion) error {
|
||||
receivingAccount, requestingAccount, internal := extractFromCtx(ctx)
|
||||
if internal {
|
||||
return nil // Already processed.
|
||||
}
|
||||
return f.createStatusable(ctx, question, receivingAccount, requestingAccount)
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
)
|
||||
|
|
@ -48,31 +49,31 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo)
|
|||
return nil // Already processed.
|
||||
}
|
||||
|
||||
undoObject := undo.GetActivityStreamsObject()
|
||||
if undoObject == nil {
|
||||
return errors.New("UNDO: no object set on vocab.ActivityStreamsUndo")
|
||||
}
|
||||
var errs gtserror.MultiError
|
||||
|
||||
for iter := undoObject.Begin(); iter != undoObject.End(); iter = iter.Next() {
|
||||
t := iter.GetType()
|
||||
if t == nil {
|
||||
for _, object := range ap.ExtractObjects(undo) {
|
||||
// Try to get object as vocab.Type,
|
||||
// else skip handling (likely) IRI.
|
||||
objType := object.GetType()
|
||||
if objType == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
switch t.GetTypeName() {
|
||||
switch objType.GetTypeName() {
|
||||
case ap.ActivityFollow:
|
||||
if err := f.undoFollow(ctx, receivingAccount, undo, t); err != nil {
|
||||
return err
|
||||
if err := f.undoFollow(ctx, receivingAccount, undo, objType); err != nil {
|
||||
errs.Appendf("error undoing follow: %w", err)
|
||||
}
|
||||
case ap.ActivityLike:
|
||||
if err := f.undoLike(ctx, receivingAccount, undo, t); err != nil {
|
||||
return err
|
||||
if err := f.undoLike(ctx, receivingAccount, undo, objType); err != nil {
|
||||
errs.Appendf("error undoing like: %w", err)
|
||||
}
|
||||
case ap.ActivityAnnounce:
|
||||
// todo: undo boost / reblog / announce
|
||||
// TODO: actually handle this !
|
||||
log.Warn(ctx, "skipped undo announce")
|
||||
case ap.ActivityBlock:
|
||||
if err := f.undoBlock(ctx, receivingAccount, undo, t); err != nil {
|
||||
return err
|
||||
if err := f.undoBlock(ctx, receivingAccount, undo, objType); err != nil {
|
||||
errs.Appendf("error undoing block: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,21 +56,18 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
|
|||
return nil // Already processed.
|
||||
}
|
||||
|
||||
switch asType.GetTypeName() {
|
||||
case ap.ActorApplication, ap.ActorGroup, ap.ActorOrganization, ap.ActorPerson, ap.ActorService:
|
||||
return f.updateAccountable(ctx, receivingAccount, requestingAccount, asType)
|
||||
if accountable, ok := ap.ToAccountable(asType); ok {
|
||||
return f.updateAccountable(ctx, receivingAccount, requestingAccount, accountable)
|
||||
}
|
||||
|
||||
if statusable, ok := ap.ToStatusable(asType); ok {
|
||||
return f.updateStatusable(ctx, receivingAccount, requestingAccount, statusable)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *federatingDB) updateAccountable(ctx context.Context, receivingAcct *gtsmodel.Account, requestingAcct *gtsmodel.Account, asType vocab.Type) error {
|
||||
// Ensure delivered asType is a valid Accountable model.
|
||||
accountable, ok := asType.(ap.Accountable)
|
||||
if !ok {
|
||||
return gtserror.Newf("could not convert vocab.Type %T to Accountable", asType)
|
||||
}
|
||||
|
||||
func (f *federatingDB) updateAccountable(ctx context.Context, receivingAcct *gtsmodel.Account, requestingAcct *gtsmodel.Account, accountable ap.Accountable) error {
|
||||
// Extract AP URI of the updated Accountable model.
|
||||
idProp := accountable.GetJSONLDId()
|
||||
if idProp == nil || !idProp.IsIRI() {
|
||||
|
|
@ -103,3 +100,43 @@ func (f *federatingDB) updateAccountable(ctx context.Context, receivingAcct *gts
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *federatingDB) updateStatusable(ctx context.Context, receivingAcct *gtsmodel.Account, requestingAcct *gtsmodel.Account, statusable ap.Statusable) error {
|
||||
// Extract AP URI of the updated model.
|
||||
idProp := statusable.GetJSONLDId()
|
||||
if idProp == nil || !idProp.IsIRI() {
|
||||
return gtserror.New("invalid id prop")
|
||||
}
|
||||
|
||||
// Get the status URI string for lookups.
|
||||
statusURI := idProp.GetIRI()
|
||||
statusURIStr := statusURI.String()
|
||||
|
||||
// Don't try to update local statuses.
|
||||
if statusURI.Host == config.GetHost() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the status we have on file for this URI string.
|
||||
status, err := f.state.DB.GetStatusByURI(ctx, statusURIStr)
|
||||
if err != nil {
|
||||
return gtserror.Newf("error fetching status from db: %w", err)
|
||||
}
|
||||
|
||||
// Check that update was by the status author.
|
||||
if status.AccountID != requestingAcct.ID {
|
||||
return gtserror.Newf("update for %s was not requested by author", statusURIStr)
|
||||
}
|
||||
|
||||
// Queue an UPDATE NOTE activity to our fedi API worker,
|
||||
// this will handle necessary database insertions, etc.
|
||||
f.state.Workers.EnqueueFediAPI(ctx, messages.FromFediAPI{
|
||||
APObjectType: ap.ObjectNote,
|
||||
APActivityType: ap.ActivityUpdate,
|
||||
GTSModel: status, // original status
|
||||
APObjectModel: statusable,
|
||||
ReceivingAccount: receivingAcct,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue