diff options
Diffstat (limited to 'internal/message')
-rw-r--r-- | internal/message/fromclientapiprocess.go | 25 | ||||
-rw-r--r-- | internal/message/fromcommonprocess.go | 135 | ||||
-rw-r--r-- | internal/message/fromfederatorprocess.go | 38 | ||||
-rw-r--r-- | internal/message/notificationsprocess.go | 24 | ||||
-rw-r--r-- | internal/message/processor.go | 3 | ||||
-rw-r--r-- | internal/message/statusprocess.go | 52 |
6 files changed, 216 insertions, 61 deletions
diff --git a/internal/message/fromclientapiprocess.go b/internal/message/fromclientapiprocess.go index b0112152b..12e4bd3c0 100644 --- a/internal/message/fromclientapiprocess.go +++ b/internal/message/fromclientapiprocess.go @@ -49,17 +49,17 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error } return nil case gtsmodel.ActivityStreamsFollow: - // CREATE FOLLOW (request) - follow, ok := clientMsg.GTSModel.(*gtsmodel.Follow) + // CREATE FOLLOW REQUEST + followRequest, ok := clientMsg.GTSModel.(*gtsmodel.FollowRequest) if !ok { - return errors.New("follow was not parseable as *gtsmodel.Follow") + return errors.New("followrequest was not parseable as *gtsmodel.FollowRequest") } - if err := p.notifyFollow(follow); err != nil { + if err := p.notifyFollowRequest(followRequest, clientMsg.TargetAccount); err != nil { return err } - return p.federateFollow(follow, clientMsg.OriginAccount, clientMsg.TargetAccount) + return p.federateFollow(followRequest, clientMsg.OriginAccount, clientMsg.TargetAccount) case gtsmodel.ActivityStreamsLike: // CREATE LIKE/FAVE fave, ok := clientMsg.GTSModel.(*gtsmodel.StatusFave) @@ -67,7 +67,7 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error return errors.New("fave was not parseable as *gtsmodel.StatusFave") } - if err := p.notifyFave(fave); err != nil { + if err := p.notifyFave(fave, clientMsg.TargetAccount); err != nil { return err } @@ -84,6 +84,11 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error if !ok { return errors.New("accept was not parseable as *gtsmodel.Follow") } + + if err := p.notifyFollow(follow, clientMsg.TargetAccount); err != nil { + return err + } + return p.federateAcceptFollowRequest(follow, clientMsg.OriginAccount, clientMsg.TargetAccount) } case gtsmodel.ActivityStreamsUndo: @@ -107,21 +112,23 @@ func (p *processor) federateStatus(status *gtsmodel.Status) error { return fmt.Errorf("federateStatus: error converting status to as format: %s", err) } - outboxIRI, err := url.Parse(status.GTSAccount.OutboxURI) + outboxIRI, err := url.Parse(status.GTSAuthorAccount.OutboxURI) if err != nil { - return fmt.Errorf("federateStatus: error parsing outboxURI %s: %s", status.GTSAccount.OutboxURI, err) + return fmt.Errorf("federateStatus: error parsing outboxURI %s: %s", status.GTSAuthorAccount.OutboxURI, err) } _, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, asStatus) return err } -func (p *processor) federateFollow(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error { +func (p *processor) federateFollow(followRequest *gtsmodel.FollowRequest, 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 } + follow := p.tc.FollowRequestToFollow(followRequest) + asFollow, err := p.tc.FollowToAS(follow, originAccount, targetAccount) if err != nil { return fmt.Errorf("federateFollow: error converting follow to as format: %s", err) diff --git a/internal/message/fromcommonprocess.go b/internal/message/fromcommonprocess.go index 2403a8b72..73d58f1d1 100644 --- a/internal/message/fromcommonprocess.go +++ b/internal/message/fromcommonprocess.go @@ -18,16 +18,143 @@ package message -import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +import ( + "fmt" + + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +) func (p *processor) notifyStatus(status *gtsmodel.Status) error { + // if there are no mentions in this status then just bail + if len(status.Mentions) == 0 { + return nil + } + + if status.GTSMentions == nil { + // there are mentions but they're not fully populated on the status yet so do this + menchies := []*gtsmodel.Mention{} + for _, m := range status.Mentions { + gtsm := >smodel.Mention{} + if err := p.db.GetByID(m, gtsm); err != nil { + return fmt.Errorf("notifyStatus: error getting mention with id %s from the db: %s", m, err) + } + menchies = append(menchies, gtsm) + } + status.GTSMentions = menchies + } + + // now we have mentions as full gtsmodel.Mention structs on the status we can continue + for _, m := range status.GTSMentions { + // make sure this is a local account, otherwise we don't need to create a notification for it + if m.GTSAccount == nil { + a := >smodel.Account{} + if err := p.db.GetByID(m.TargetAccountID, a); err != nil { + // we don't have the account or there's been an error + return fmt.Errorf("notifyStatus: error getting account with id %s from the db: %s", m.TargetAccountID, err) + } + m.GTSAccount = a + } + if m.GTSAccount.Domain != "" { + // not a local account so skip it + continue + } + + // make sure a notif doesn't already exist for this mention + err := p.db.GetWhere([]db.Where{ + {Key: "notification_type", Value: gtsmodel.NotificationMention}, + {Key: "target_account_id", Value: m.TargetAccountID}, + {Key: "origin_account_id", Value: status.AccountID}, + {Key: "status_id", Value: status.ID}, + }, >smodel.Notification{}) + if err == nil { + // notification exists already so just continue + continue + } + if _, ok := err.(db.ErrNoEntries); !ok { + // there's a real error in the db + return fmt.Errorf("notifyStatus: error checking existence of notification for mention with id %s : %s", m.ID, err) + } + + // if we've reached this point we know the mention is for a local account, and the notification doesn't exist, so create it + notif := >smodel.Notification{ + NotificationType: gtsmodel.NotificationMention, + TargetAccountID: m.TargetAccountID, + OriginAccountID: status.AccountID, + StatusID: status.ID, + } + + if err := p.db.Put(notif); err != nil { + return fmt.Errorf("notifyStatus: error putting notification in database: %s", err) + } + } + + return nil +} + +func (p *processor) notifyFollowRequest(followRequest *gtsmodel.FollowRequest, receivingAccount *gtsmodel.Account) error { + // return if this isn't a local account + if receivingAccount.Domain != "" { + return nil + } + + notif := >smodel.Notification{ + NotificationType: gtsmodel.NotificationFollowRequest, + TargetAccountID: followRequest.TargetAccountID, + OriginAccountID: followRequest.AccountID, + } + + if err := p.db.Put(notif); err != nil { + return fmt.Errorf("notifyFollowRequest: error putting notification in database: %s", err) + } + return nil } -func (p *processor) notifyFollow(follow *gtsmodel.Follow) error { +func (p *processor) notifyFollow(follow *gtsmodel.Follow, receivingAccount *gtsmodel.Account) error { + // return if this isn't a local account + if receivingAccount.Domain != "" { + return nil + } + + // first remove the follow request notification + if err := p.db.DeleteWhere([]db.Where{ + {Key: "notification_type", Value: gtsmodel.NotificationFollowRequest}, + {Key: "target_account_id", Value: follow.TargetAccountID}, + {Key: "origin_account_id", Value: follow.AccountID}, + }, >smodel.Notification{}); err != nil { + return fmt.Errorf("notifyFollow: error removing old follow request notification from database: %s", err) + } + + // now create the new follow notification + notif := >smodel.Notification{ + NotificationType: gtsmodel.NotificationFollow, + TargetAccountID: follow.TargetAccountID, + OriginAccountID: follow.AccountID, + } + if err := p.db.Put(notif); err != nil { + return fmt.Errorf("notifyFollow: error putting notification in database: %s", err) + } + return nil } -func (p *processor) notifyFave(fave *gtsmodel.StatusFave) error { - return nil +func (p *processor) notifyFave(fave *gtsmodel.StatusFave, receivingAccount *gtsmodel.Account) error { + // return if this isn't a local account + if receivingAccount.Domain != "" { + return nil + } + + notif := >smodel.Notification{ + NotificationType: gtsmodel.NotificationFave, + TargetAccountID: fave.TargetAccountID, + OriginAccountID: fave.AccountID, + StatusID: fave.StatusID, + } + + if err := p.db.Put(notif); err != nil { + return fmt.Errorf("notifyFave: error putting notification in database: %s", err) + } + + return nil } diff --git a/internal/message/fromfederatorprocess.go b/internal/message/fromfederatorprocess.go index d3ebce400..10dbfcf6e 100644 --- a/internal/message/fromfederatorprocess.go +++ b/internal/message/fromfederatorprocess.go @@ -74,6 +74,26 @@ func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) er if err := p.db.UpdateByID(incomingAccount.ID, incomingAccount); err != nil { return fmt.Errorf("error updating dereferenced account in the db: %s", err) } + case gtsmodel.ActivityStreamsLike: + // CREATE A FAVE + incomingFave, ok := federatorMsg.GTSModel.(*gtsmodel.StatusFave) + if !ok { + return errors.New("like was not parseable as *gtsmodel.StatusFave") + } + + if err := p.notifyFave(incomingFave, federatorMsg.ReceivingAccount); err != nil { + return err + } + case gtsmodel.ActivityStreamsFollow: + // CREATE A FOLLOW REQUEST + incomingFollowRequest, ok := federatorMsg.GTSModel.(*gtsmodel.FollowRequest) + if !ok { + return errors.New("like was not parseable as *gtsmodel.FollowRequest") + } + + if err := p.notifyFollowRequest(incomingFollowRequest, federatorMsg.ReceivingAccount); err != nil { + return err + } } case gtsmodel.ActivityStreamsUpdate: // UPDATE @@ -106,6 +126,20 @@ func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) er // DELETE A PROFILE/ACCOUNT // TODO: handle side effects of account deletion here: delete all objects, statuses, media etc associated with account } + case gtsmodel.ActivityStreamsAccept: + // ACCEPT + switch federatorMsg.APObjectType { + case gtsmodel.ActivityStreamsFollow: + // ACCEPT A FOLLOW + follow, ok := federatorMsg.GTSModel.(*gtsmodel.Follow) + if !ok { + return errors.New("follow was not parseable as *gtsmodel.Follow") + } + + if err := p.notifyFollow(follow, federatorMsg.ReceivingAccount); err != nil { + return err + } + } } return nil @@ -215,8 +249,8 @@ func (p *processor) dereferenceStatusFields(status *gtsmodel.Status) error { } m.StatusID = status.ID - m.OriginAccountID = status.GTSAccount.ID - m.OriginAccountURI = status.GTSAccount.URI + m.OriginAccountID = status.GTSAuthorAccount.ID + m.OriginAccountURI = status.GTSAuthorAccount.URI targetAccount := >smodel.Account{} if err := p.db.GetWhere([]db.Where{{Key: "uri", Value: uri.String()}}, targetAccount); err != nil { diff --git a/internal/message/notificationsprocess.go b/internal/message/notificationsprocess.go new file mode 100644 index 000000000..64726b75f --- /dev/null +++ b/internal/message/notificationsprocess.go @@ -0,0 +1,24 @@ +package message + +import ( + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + "github.com/superseriousbusiness/gotosocial/internal/oauth" +) + +func (p *processor) NotificationsGet(authed *oauth.Auth, limit int, maxID string) ([]*apimodel.Notification, ErrorWithCode) { + notifs, err := p.db.GetNotificationsForAccount(authed.Account.ID, limit, maxID) + if err != nil { + return nil, NewErrorInternalError(err) + } + + mastoNotifs := []*apimodel.Notification{} + for _, n := range notifs { + mastoNotif, err := p.tc.NotificationToMasto(n) + if err != nil { + return nil, NewErrorInternalError(err) + } + mastoNotifs = append(mastoNotifs, mastoNotif) + } + + return mastoNotifs, nil +} diff --git a/internal/message/processor.go b/internal/message/processor.go index bcd64d47a..49a4f6f05 100644 --- a/internal/message/processor.go +++ b/internal/message/processor.go @@ -106,6 +106,9 @@ type Processor interface { // MediaUpdate handles the PUT of a media attachment with the given ID and form MediaUpdate(authed *oauth.Auth, attachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, ErrorWithCode) + // NotificationsGet + NotificationsGet(authed *oauth.Auth, limit int, maxID string) ([]*apimodel.Notification, ErrorWithCode) + // StatusCreate processes the given form to create a new status, returning the api model representation of that status if it's OK. StatusCreate(authed *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, error) // StatusDelete processes the delete of a given status, returning the deleted status if the delete goes through. diff --git a/internal/message/statusprocess.go b/internal/message/statusprocess.go index 6786b2dab..f64c35948 100644 --- a/internal/message/statusprocess.go +++ b/internal/message/statusprocess.go @@ -278,54 +278,14 @@ func (p *processor) StatusBoost(authed *oauth.Auth, targetStatusID string) (*api } // it's visible! it's boostable! so let's boost the FUCK out of it - // first we create a new status and add some basic info to it -- this will be the wrapper for the boosted status - - // the wrapper won't use the same ID as the boosted status so we generate some new UUIDs - uris := util.GenerateURIsForAccount(authed.Account.Username, p.config.Protocol, p.config.Host) - boostWrapperStatusID := uuid.NewString() - boostWrapperStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, boostWrapperStatusID) - boostWrapperStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, boostWrapperStatusID) - - boostWrapperStatus := >smodel.Status{ - ID: boostWrapperStatusID, - URI: boostWrapperStatusURI, - URL: boostWrapperStatusURL, - - // the boosted status is not created now, but the boost certainly is - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - Local: true, // always local since this is being done through the client API - AccountID: authed.Account.ID, - CreatedWithApplicationID: authed.Application.ID, - - // replies can be boosted, but boosts are never replies - InReplyToID: "", - InReplyToAccountID: "", - - // these will all be wrapped in the boosted status so set them empty here - Attachments: []string{}, - Tags: []string{}, - Mentions: []string{}, - Emojis: []string{}, - - // the below fields will be taken from the target status - Content: util.HTMLFormat(targetStatus.Content), - ContentWarning: targetStatus.ContentWarning, - ActivityStreamsType: targetStatus.ActivityStreamsType, - Sensitive: targetStatus.Sensitive, - Language: targetStatus.Language, - Text: targetStatus.Text, - BoostOfID: targetStatus.ID, - Visibility: targetStatus.Visibility, - VisibilityAdvanced: targetStatus.VisibilityAdvanced, - - // attach these here for convenience -- the boosted status/account won't go in the DB - // but they're needed in the processor and for the frontend. Since we have them, we can - // attach them so we don't need to fetch them again later (save some DB calls) - GTSBoostedStatus: targetStatus, - GTSBoostedAccount: targetAccount, + boostWrapperStatus, err := p.tc.StatusToBoost(targetStatus, authed.Account) + if err != nil { + return nil, NewErrorInternalError(err) } + boostWrapperStatus.CreatedWithApplicationID = authed.Application.ID + boostWrapperStatus.GTSBoostedAccount = targetAccount + // put the boost in the database if err := p.db.Put(boostWrapperStatus); err != nil { return nil, NewErrorInternalError(err) |