mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-27 03:23:31 -06:00
start converting statuses to AS
This commit is contained in:
parent
3623205fd7
commit
cc0262055a
3 changed files with 198 additions and 64 deletions
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/go-fed/activity/pub"
|
|
||||||
"github.com/go-fed/activity/streams"
|
"github.com/go-fed/activity/streams"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
@ -66,16 +65,20 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error
|
||||||
// UPDATE
|
// UPDATE
|
||||||
case gtsmodel.ActivityStreamsAccept:
|
case gtsmodel.ActivityStreamsAccept:
|
||||||
// ACCEPT
|
// ACCEPT
|
||||||
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
|
switch clientMsg.APObjectType {
|
||||||
if !ok {
|
case gtsmodel.ActivityStreamsFollow:
|
||||||
return errors.New("accept was not parseable as *gtsmodel.Follow")
|
// ACCEPT FOLLOW
|
||||||
|
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("accept was not parseable as *gtsmodel.Follow")
|
||||||
|
}
|
||||||
|
return p.federateAcceptFollowRequest(follow, clientMsg.OriginAccount, clientMsg.TargetAccount)
|
||||||
}
|
}
|
||||||
return p.federateAcceptFollowRequest(follow)
|
|
||||||
case gtsmodel.ActivityStreamsUndo:
|
case gtsmodel.ActivityStreamsUndo:
|
||||||
// UNDO
|
// UNDO
|
||||||
switch clientMsg.APObjectType {
|
switch clientMsg.APObjectType {
|
||||||
// UNDO FOLLOW
|
|
||||||
case gtsmodel.ActivityStreamsFollow:
|
case gtsmodel.ActivityStreamsFollow:
|
||||||
|
// UNDO FOLLOW
|
||||||
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
|
follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("undo was not parseable as *gtsmodel.Follow")
|
return errors.New("undo was not parseable as *gtsmodel.Follow")
|
||||||
|
|
@ -114,6 +117,11 @@ func (p *processor) federateStatus(status *gtsmodel.Status) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) federateFollow(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
func (p *processor) federateFollow(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
||||||
|
// if both accounts are local there's nothing to do here
|
||||||
|
if originAccount.Domain == "" && targetAccount.Domain == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount)
|
asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("federateFollow: error converting follow to as format: %s", err)
|
return fmt.Errorf("federateFollow: error converting follow to as format: %s", err)
|
||||||
|
|
@ -129,6 +137,11 @@ func (p *processor) federateFollow(follow *gtsmodel.Follow, originAccount *gtsmo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) federateUnfollow(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
func (p *processor) federateUnfollow(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
||||||
|
// if both accounts are local there's nothing to do here
|
||||||
|
if originAccount.Domain == "" && targetAccount.Domain == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// recreate the follow
|
// recreate the follow
|
||||||
asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount)
|
asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -164,75 +177,52 @@ func (p *processor) federateUnfollow(follow *gtsmodel.Follow, originAccount *gts
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) federateAcceptFollowRequest(follow *gtsmodel.Follow) error {
|
func (p *processor) federateAcceptFollowRequest(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
||||||
|
// if both accounts are local there's nothing to do here
|
||||||
// TODO: tidy up this whole function -- move most of the logic for the conversion to the type converter because this is just a mess! Shame on me!
|
if originAccount.Domain == "" && targetAccount.Domain == "" {
|
||||||
|
return nil
|
||||||
followAccepter := >smodel.Account{}
|
|
||||||
if err := p.db.GetByID(follow.TargetAccountID, followAccepter); err != nil {
|
|
||||||
return fmt.Errorf("error federating follow accept: %s", err)
|
|
||||||
}
|
}
|
||||||
followAccepterIRI, err := url.Parse(followAccepter.URI)
|
|
||||||
|
// recreate the AS follow
|
||||||
|
asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error parsing URL: %s", err)
|
return fmt.Errorf("federateUnfollow: error converting follow to as format: %s", err)
|
||||||
}
|
}
|
||||||
followAccepterOutboxIRI, err := url.Parse(followAccepter.OutboxURI)
|
|
||||||
|
acceptingAccountURI, err := url.Parse(targetAccount.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error parsing URL: %s", err)
|
return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
|
||||||
}
|
}
|
||||||
me := streams.NewActivityStreamsActorProperty()
|
|
||||||
me.AppendIRI(followAccepterIRI)
|
|
||||||
|
|
||||||
followRequester := >smodel.Account{}
|
requestingAccountURI, err := url.Parse(originAccount.URI)
|
||||||
if err := p.db.GetByID(follow.AccountID, followRequester); err != nil {
|
|
||||||
return fmt.Errorf("error federating follow accept: %s", err)
|
|
||||||
}
|
|
||||||
requesterIRI, err := url.Parse(followRequester.URI)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error parsing URL: %s", err)
|
return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
|
||||||
}
|
}
|
||||||
them := streams.NewActivityStreamsActorProperty()
|
|
||||||
them.AppendIRI(requesterIRI)
|
|
||||||
|
|
||||||
// prepare the follow
|
// create an Accept
|
||||||
ASFollow := streams.NewActivityStreamsFollow()
|
accept := streams.NewActivityStreamsAccept()
|
||||||
// set the follow requester as the actor
|
|
||||||
ASFollow.SetActivityStreamsActor(them)
|
// set the accepting actor on it
|
||||||
// set the ID from the follow
|
acceptActorProp := streams.NewActivityStreamsActorProperty()
|
||||||
ASFollowURI, err := url.Parse(follow.URI)
|
acceptActorProp.AppendIRI(acceptingAccountURI)
|
||||||
|
accept.SetActivityStreamsActor(acceptActorProp)
|
||||||
|
|
||||||
|
// Set the recreated follow as the 'object' property.
|
||||||
|
acceptObject := streams.NewActivityStreamsObjectProperty()
|
||||||
|
acceptObject.AppendActivityStreamsFollow(asFollow)
|
||||||
|
accept.SetActivityStreamsObject(acceptObject)
|
||||||
|
|
||||||
|
// Set the To of the accept as the originator of the follow
|
||||||
|
acceptTo := streams.NewActivityStreamsToProperty()
|
||||||
|
acceptTo.AppendIRI(requestingAccountURI)
|
||||||
|
accept.SetActivityStreamsTo(acceptTo)
|
||||||
|
|
||||||
|
outboxIRI, err := url.Parse(targetAccount.OutboxURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error parsing URL: %s", err)
|
return fmt.Errorf("federateAcceptFollowRequest: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
|
||||||
}
|
}
|
||||||
ASFollowIDProp := streams.NewJSONLDIdProperty()
|
|
||||||
ASFollowIDProp.SetIRI(ASFollowURI)
|
|
||||||
ASFollow.SetJSONLDId(ASFollowIDProp)
|
|
||||||
|
|
||||||
// set the object as the accepter URI
|
// send off the accept using the accepter's outbox
|
||||||
ASFollowObjectProp := streams.NewActivityStreamsObjectProperty()
|
_, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, accept)
|
||||||
ASFollowObjectProp.AppendIRI(followAccepterIRI)
|
|
||||||
|
|
||||||
// Prepare the response.
|
|
||||||
ASAccept := streams.NewActivityStreamsAccept()
|
|
||||||
// Set us as the 'actor'.
|
|
||||||
ASAccept.SetActivityStreamsActor(me)
|
|
||||||
|
|
||||||
// Set the Follow as the 'object' property.
|
|
||||||
ASAcceptObject := streams.NewActivityStreamsObjectProperty()
|
|
||||||
ASAcceptObject.AppendActivityStreamsFollow(ASFollow)
|
|
||||||
ASAccept.SetActivityStreamsObject(ASAcceptObject)
|
|
||||||
|
|
||||||
// Add all actors on the original Follow to the 'to' property.
|
|
||||||
ASAcceptTo := streams.NewActivityStreamsToProperty()
|
|
||||||
followActors := ASFollow.GetActivityStreamsActor()
|
|
||||||
for iter := followActors.Begin(); iter != followActors.End(); iter = iter.Next() {
|
|
||||||
id, err := pub.ToId(iter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ASAcceptTo.AppendIRI(id)
|
|
||||||
}
|
|
||||||
ASAccept.SetActivityStreamsTo(ASAcceptTo)
|
|
||||||
|
|
||||||
_, err = p.federator.FederatingActor().Send(context.Background(), followAccepterOutboxIRI, ASAccept)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
asPublicURI = "https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
)
|
||||||
|
|
||||||
// TypeConverter is an interface for the common action of converting between apimodule (frontend, serializable) models,
|
// TypeConverter is an interface for the common action of converting between apimodule (frontend, serializable) models,
|
||||||
// internal gts models used in the database, and activitypub models used in federation.
|
// internal gts models used in the database, and activitypub models used in federation.
|
||||||
//
|
//
|
||||||
|
|
@ -112,6 +116,8 @@ type TypeConverter interface {
|
||||||
|
|
||||||
// FollowToASFollow converts a gts model Follow into an activity streams Follow, suitable for federation
|
// FollowToASFollow converts a gts model Follow into an activity streams Follow, suitable for federation
|
||||||
FollowToAS(f *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (vocab.ActivityStreamsFollow, error)
|
FollowToAS(f *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) (vocab.ActivityStreamsFollow, error)
|
||||||
|
|
||||||
|
MentionToAS(m *gtsmodel.Mention) (vocab.ActivityStreamsMention, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type converter struct {
|
type converter struct {
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,134 @@ func (c *converter) AccountToAS(a *gtsmodel.Account) (vocab.ActivityStreamsPerso
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, error) {
|
func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, error) {
|
||||||
|
// ensure prerequisites here before we get stuck in
|
||||||
|
|
||||||
|
// check if author account is already attached to status and attach it if not
|
||||||
|
// if we can't retrieve this, bail here already because we can't attribute the status to anyone
|
||||||
|
if s.GTSAccount == nil {
|
||||||
|
a := >smodel.Account{}
|
||||||
|
if err := c.db.GetByID(s.AccountID, a); err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error retrieving author account from db: %s", err)
|
||||||
|
}
|
||||||
|
s.GTSAccount = a
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the Note!
|
||||||
|
status := streams.NewActivityStreamsNote()
|
||||||
|
|
||||||
|
// id
|
||||||
|
statusURI, err := url.Parse(s.URI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error parsing url %s: %s", s.URI, err)
|
||||||
|
}
|
||||||
|
statusIDProp := streams.NewJSONLDIdProperty()
|
||||||
|
statusIDProp.SetIRI(statusURI)
|
||||||
|
status.SetJSONLDId(statusIDProp)
|
||||||
|
|
||||||
|
// type
|
||||||
|
// will be set automatically by go-fed
|
||||||
|
|
||||||
|
// summary aka cw
|
||||||
|
statusSummaryProp := streams.NewActivityStreamsSummaryProperty()
|
||||||
|
statusSummaryProp.AppendXMLSchemaString(s.ContentWarning)
|
||||||
|
status.SetActivityStreamsSummary(statusSummaryProp)
|
||||||
|
|
||||||
|
// inReplyTo
|
||||||
|
if s.InReplyToID != "" {
|
||||||
|
// fetch the replied status if we don't have it on hand already
|
||||||
|
if s.GTSReplyToStatus == nil {
|
||||||
|
rs := >smodel.Status{}
|
||||||
|
if err := c.db.GetByID(s.InReplyToID, rs); err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error retrieving replied-to status from db: %s", err)
|
||||||
|
}
|
||||||
|
s.GTSReplyToStatus = rs
|
||||||
|
}
|
||||||
|
rURI, err := url.Parse(s.GTSReplyToStatus.URI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error parsing url %s: %s", s.GTSReplyToStatus.URI, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
inReplyToProp := streams.NewActivityStreamsInReplyToProperty()
|
||||||
|
inReplyToProp.AppendIRI(rURI)
|
||||||
|
status.SetActivityStreamsInReplyTo(inReplyToProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// published
|
||||||
|
publishedProp := streams.NewActivityStreamsPublishedProperty()
|
||||||
|
publishedProp.Set(s.CreatedAt)
|
||||||
|
status.SetActivityStreamsPublished(publishedProp)
|
||||||
|
|
||||||
|
// url
|
||||||
|
if s.URL != "" {
|
||||||
|
sURL, err := url.Parse(s.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error parsing url %s: %s", s.URL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
urlProp := streams.NewActivityStreamsUrlProperty()
|
||||||
|
urlProp.AppendIRI(sURL)
|
||||||
|
status.SetActivityStreamsUrl(urlProp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// attributedTo
|
||||||
|
authorAccountURI, err := url.Parse(s.GTSAccount.URI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error parsing url %s: %s", s.GTSAccount.URI, err)
|
||||||
|
}
|
||||||
|
attributedToProp := streams.NewActivityStreamsAttributedToProperty()
|
||||||
|
attributedToProp.AppendIRI(authorAccountURI)
|
||||||
|
status.SetActivityStreamsAttributedTo(attributedToProp)
|
||||||
|
|
||||||
|
// tags
|
||||||
|
tagProp := streams.NewActivityStreamsTagProperty()
|
||||||
|
|
||||||
|
// tag -- mentions
|
||||||
|
for _, m := range s.GTSMentions {
|
||||||
|
asMention, err := c.MentionToAS(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error converting mention to AS mention: %s", err)
|
||||||
|
}
|
||||||
|
tagProp.AppendActivityStreamsMention(asMention)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag -- emojis
|
||||||
|
|
||||||
|
// tag -- hashtags
|
||||||
|
|
||||||
|
|
||||||
|
status.SetActivityStreamsTag(tagProp)
|
||||||
|
|
||||||
|
// parse out some URIs we need here
|
||||||
|
authorFollowersURI, err := url.Parse(s.GTSAccount.FollowersURI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error parsing url %s: %s", s.GTSAccount.FollowersURI, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicURI, err := url.Parse(asPublicURI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("StatusToAS: error parsing url %s: %s", asPublicURI, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// to
|
||||||
|
switch s.Visibility {
|
||||||
|
case gtsmodel.VisibilityDirect:
|
||||||
|
}
|
||||||
|
|
||||||
|
// cc
|
||||||
|
|
||||||
|
// conversation
|
||||||
|
|
||||||
|
// content
|
||||||
|
|
||||||
|
// attachment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// replies
|
||||||
|
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,3 +433,13 @@ func (c *converter) FollowToAS(f *gtsmodel.Follow, originAccount *gtsmodel.Accou
|
||||||
|
|
||||||
return follow, nil
|
return follow, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *converter) MentionToAS(m *gtsmodel.Mention) (vocab.ActivityStreamsMention, error) {
|
||||||
|
mention := streams.NewActivityStreamsMention()
|
||||||
|
|
||||||
|
if m.NameString == "" {
|
||||||
|
m.NameString =
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue