Skip to content

Commit

Permalink
Read new aggregated payload from followers server
Browse files Browse the repository at this point in the history
  • Loading branch information
dcadenas committed Sep 2, 2024
1 parent d487ee6 commit 4728744
Show file tree
Hide file tree
Showing 12 changed files with 526 additions and 112 deletions.
7 changes: 7 additions & 0 deletions internal/fixtures/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"

"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip19"
"github.com/planetary-social/go-notification-service/internal"
"github.com/planetary-social/go-notification-service/service/domain"
)
Expand Down Expand Up @@ -72,6 +73,12 @@ func SomeHexBytesOfLen(l int) string {
return hex.EncodeToString(b)
}

func PublicKeyAndNpub() (domain.PublicKey, string) {
pk, _ := SomeKeyPair()
npub, _ := nip19.EncodePublicKey(pk.Hex())
return pk, npub
}

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func randSeq(n int) string {
Expand Down
101 changes: 73 additions & 28 deletions service/adapters/apns/apns.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package apns

import (
"encoding/json"
"fmt"
"strings"

"github.com/boreq/errors"
Expand All @@ -15,6 +16,8 @@ import (
"github.com/sideshow/apns2/certificate"
)

const MAX_TOTAL_NPUBS = 58

type Metrics interface {
ReportCallToAPNS(statusCode int, err error)
}
Expand Down Expand Up @@ -80,7 +83,7 @@ func (a *APNS) SendNotification(notification notifications.Notification) error {
return nil
}

func (a *APNS) SendFollowChangeNotification(followChange domain.FollowChange, apnsToken domain.APNSToken) error {
func (a *APNS) SendFollowChangeNotification(followChange domain.FollowChangeBatch, apnsToken domain.APNSToken) error {
if apnsToken.Hex() == "" {
return errors.New("invalid APNs token")
}
Expand Down Expand Up @@ -113,8 +116,8 @@ func (a *APNS) SendFollowChangeNotification(followChange domain.FollowChange, ap
return nil
}

func (a *APNS) buildFollowChangeNotification(followChange domain.FollowChange, apnsToken domain.APNSToken) (*apns2.Notification, error) {
payload, err := followChangePayload(followChange)
func (a *APNS) buildFollowChangeNotification(followChange domain.FollowChangeBatch, apnsToken domain.APNSToken) (*apns2.Notification, error) {
payload, err := FollowChangePayload(followChange)
if err != nil {
return nil, errors.Wrap(err, "error creating a payload")
}
Expand All @@ -131,49 +134,79 @@ func (a *APNS) buildFollowChangeNotification(followChange domain.FollowChange, a
return n, nil
}

func followChangePayload(followChange domain.FollowChange) ([]byte, error) {
func FollowChangePayload(followChange domain.FollowChangeBatch) ([]byte, error) {
return FollowChangePayloadWithValidation(followChange, true)
}

func FollowChangePayloadWithValidation(followChange domain.FollowChangeBatch, validate bool) ([]byte, error) {
alertMessage := ""
if strings.HasPrefix(followChange.FriendlyFollower, "npub") {
if followChange.ChangeType == "unfollowed" {
alertMessage = "You've been unfollowed!"
totalNpubs := len(followChange.Follows) + len(followChange.Unfollows)
if validate && totalNpubs > MAX_TOTAL_NPUBS {
return nil, errors.New("FollowChangeBatch for followee " + followChange.Followee.Hex() + " has too many npubs (" + fmt.Sprint(totalNpubs) + "). MAX_TOTAL_NPUBS is " + fmt.Sprint(MAX_TOTAL_NPUBS))
}

singleChange := totalNpubs == 1

if singleChange {
isFollow := len(followChange.Follows) == 1
if strings.HasPrefix(followChange.FriendlyFollower, "npub") {
if isFollow {
alertMessage = "You have a new follower!"
} else {
alertMessage = "You've been unfollowed!"
}
} else {
alertMessage = "You have a new follower!"
if isFollow {
alertMessage = followChange.FriendlyFollower + " is a new follower!"
} else {
alertMessage = followChange.FriendlyFollower + " has unfollowed you!"
}
}
} else {
if followChange.ChangeType == "unfollowed" {
alertMessage = followChange.FriendlyFollower + " has unfollowed you!"
} else {
alertMessage = followChange.FriendlyFollower + " is a new follower!"
}
alertMessage = fmt.Sprintf("You have %d new followers and %d unfollows!", len(followChange.Follows), len(followChange.Unfollows))
}

followerNpub, err := nip19.EncodePublicKey(followChange.Follower.Hex())
if err != nil {
return nil, errors.Wrap(err, "error encoding the follower npub")
followeeNpub, error := nip19.EncodePublicKey(followChange.Followee.Hex())
if error != nil {
return nil, errors.Wrap(error, "error encoding followee npub")
}

var follows []string
var unfollows []string
if followChange.ChangeType == "followed" {
follows = append(follows, followerNpub)
} else {
unfollows = append(unfollows, followerNpub)
npubFollows, error := pubkeysToNpubs(followChange.Follows)
if error != nil {
return nil, errors.Wrap(error, "error encoding follow npubs")
}

npubUnfollows, error := pubkeysToNpubs(followChange.Unfollows)
if error != nil {
return nil, errors.Wrap(error, "error encoding unfollow npubs")
}

// See https://developer.apple.com/documentation/usernotifications/generating-a-remote-notification

var data map[string]interface{}

if singleChange {
data = map[string]interface{}{
"follows": npubFollows,
"unfollows": npubUnfollows,
"friendlyFollower": followChange.FriendlyFollower,
}
} else {
data = map[string]interface{}{
"follows": npubFollows,
"unfollows": npubUnfollows,
}
}

payload := map[string]interface{}{
"aps": map[string]interface{}{
"alert": alertMessage,
"sound": "default",
"badge": 1,
"thread-id": followChange.Followee.Hex(),
"thread-id": followeeNpub,
"interruption-level": "passive",
},
"data": map[string]interface{}{
"follows": follows,
"unfollows": unfollows,
"friendlyFollower": followChange.FriendlyFollower,
},
"data": data,
}

payloadBytes, err := json.Marshal(payload)
Expand All @@ -183,3 +216,15 @@ func followChangePayload(followChange domain.FollowChange) ([]byte, error) {

return payloadBytes, nil
}

func pubkeysToNpubs(pubkeys []domain.PublicKey) ([]string, error) {
npubs := make([]string, len(pubkeys))
for i, pubkey := range pubkeys {
npub, err := nip19.EncodePublicKey(pubkey.Hex())
if err != nil {
return nil, errors.Wrap(err, "error encoding a public key")
}
npubs[i] = npub
}
return npubs, nil
}
2 changes: 1 addition & 1 deletion service/adapters/apns/apns_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (a *APNSMock) SendNotification(notification notifications.Notification) err
return nil
}

func (a *APNSMock) SendFollowChangeNotification(followChange domain.FollowChange, token domain.APNSToken) error {
func (a *APNSMock) SendFollowChangeNotification(followChange domain.FollowChangeBatch, token domain.APNSToken) error {
notification := notifications.Notification{}

return a.SendNotification(notification)
Expand Down
Loading

0 comments on commit 4728744

Please sign in to comment.