Skip to content

Commit

Permalink
feat: add endpoint for registering alby go 'notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya committed Nov 7, 2024
1 parent 12d12b1 commit 8fc78f1
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 1 deletion.
1 change: 1 addition & 0 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func main() {
e.POST("/nip47", svc.NIP47Handler)
e.POST("/nip47/webhook", svc.NIP47WebhookHandler)
e.POST("/nip47/notifications", svc.NIP47NotificationHandler)
e.POST("/nip47/notifications/go", svc.NIP47ExpoNotificationHandler)
e.POST("/publish", svc.PublishHandler)
e.POST("/subscriptions", svc.SubscriptionHandler)
e.DELETE("/subscriptions/:id", svc.StopSubscriptionHandler)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/oliveroneill/exponent-server-sdk-golang v0.0.0-20210823140141-d050598be512 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/puzpuzpuz/xsync/v3 v3.1.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nbd-wtf/go-nostr v0.31.4 h1:Qq+PHyKixRZR6Tn+omF7LykK/IR6qcdmThNY132pKsA=
github.com/nbd-wtf/go-nostr v0.31.4/go.mod h1:vHKtHyLXDXzYBN0fi/9Y/Q5AD0p+hk8TQVKlldAi0gI=
github.com/oliveroneill/exponent-server-sdk-golang v0.0.0-20210823140141-d050598be512 h1:/ZSmjwl1inqsiHMhn+sPlEtSHdVTf+TH3LNGGdMQ/vA=
github.com/oliveroneill/exponent-server-sdk-golang v0.0.0-20210823140141-d050598be512/go.mod h1:Isv/48UnAjtxS8FD80Bito3ZJqZRyIMxKARIEITfW4k=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0=
Expand Down
126 changes: 126 additions & 0 deletions internal/nostr/expo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package nostr

import (
"net/http"
"time"

"github.com/labstack/echo/v4"
"github.com/nbd-wtf/go-nostr"
expo "github.com/oliveroneill/exponent-server-sdk-golang/sdk"
"github.com/sirupsen/logrus"
)

func (svc *Service) NIP47ExpoNotificationHandler(c echo.Context) error {
var requestData NIP47ExpoNotificationRequest
// send in a pubkey and authenticate by signing
if err := c.Bind(&requestData); err != nil {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "Error decoding notification request",
Error: err.Error(),
})
}

if (requestData.PushToken == "") {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "push token is empty",
Error: "no push token in request data",
})
}

_, err := expo.NewExponentPushToken(requestData.PushToken)
if err != nil {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "invalid push token",
Error: "invalid push token in request data",
})
}

if (requestData.WalletPubkey == "") {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "wallet pubkey is empty",
Error: "no wallet pubkey in request data",
})
}

if (requestData.ConnPubkey == "") {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "connection pubkey is empty",
Error: "no connection pubkey in request data",
})
}

svc.Logger.WithFields(logrus.Fields{
"wallet_pubkey": requestData.WalletPubkey,
"relay_url": requestData.RelayUrl,
"push_token": requestData.PushToken,
}).Debug("Subscribing to send push notifications")

subscription := Subscription{
RelayUrl: requestData.RelayUrl,
PushToken: requestData.PushToken,
Open: true,
Since: time.Now(),
Authors: &[]string{requestData.WalletPubkey},
Kinds: &[]int{NIP_47_NOTIFICATION_KIND},
}

tags := make(nostr.TagMap)
(tags)["p"] = []string{requestData.ConnPubkey}

subscription.Tags = &tags

err = svc.db.Create(&subscription).Error

if err != nil {
svc.Logger.WithError(err).WithFields(logrus.Fields{
"wallet_pubkey": requestData.WalletPubkey,
"relay_url": requestData.RelayUrl,
"push_token": requestData.PushToken,
}).Error("Failed to store subscription")
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: "Failed to store subscription",
Error: err.Error(),
})
}

go svc.startSubscription(svc.Ctx, &subscription, nil, svc.handleSubscribedExpoNotification)

return c.NoContent(http.StatusOK)
}

func (svc *Service) handleSubscribedExpoNotification(event *nostr.Event, subscription *Subscription) {
svc.Logger.WithFields(logrus.Fields{
"event_id": event.ID,
"event_kind": event.Kind,
"subscription_id": subscription.ID,
"relay_url": subscription.RelayUrl,
}).Debug("Received subscribed notification")

pushToken, _ := expo.NewExponentPushToken(subscription.PushToken)

response, err := svc.client.Publish(
&expo.PushMessage{
To: []expo.ExponentPushToken{pushToken},
Title: "New event",
Body: "",
Data: map[string]string{
"content": event.Content,
"appPubkey": event.Tags.GetFirst([]string{"p", ""}).Value(),
},
Priority: expo.DefaultPriority,
},
)

someerr := response.ValidateResponse()
if err != nil || someerr != nil {
svc.Logger.WithFields(logrus.Fields{
"push_token": subscription.PushToken,
}).Error("Failed to send expo notification")
return
}

svc.Logger.WithFields(logrus.Fields{
"event_id": event.ID,
"push_token": subscription.PushToken,
}).Debug("Push notification sent successfully")
}
8 changes: 8 additions & 0 deletions internal/nostr/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Subscription struct {
ID uint
RelayUrl string
WebhookUrl string
PushToken string
Open bool
Ids *[]string `gorm:"-"`
Kinds *[]int `gorm:"-"`
Expand Down Expand Up @@ -185,6 +186,13 @@ type NIP47NotificationRequest struct {
ConnPubkey string `json:"connectionPubkey"`
}

type NIP47ExpoNotificationRequest struct {
RelayUrl string `json:"relayUrl"`
PushToken string `json:"pushToken"`
WalletPubkey string `json:"walletPubkey"`
ConnPubkey string `json:"connectionPubkey"`
}

type NIP47Response struct {
Event *nostr.Event `json:"event,omitempty"`
State string `json:"state"`
Expand Down
16 changes: 15 additions & 1 deletion internal/nostr/nostr.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"gorm.io/gorm"

"github.com/jackc/pgx/v5/stdlib"
expo "github.com/oliveroneill/exponent-server-sdk-golang/sdk"
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
gormtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1"
)
Expand All @@ -49,6 +50,7 @@ type Service struct {
subscriptions map[string]*nostr.Subscription
subscriptionsMutex sync.Mutex
relayMutex sync.Mutex
client *expo.PushClient
}

func NewService(ctx context.Context) (*Service, error) {
Expand Down Expand Up @@ -116,6 +118,12 @@ func NewService(ctx context.Context) (*Service, error) {

subscriptions := make(map[string]*nostr.Subscription)

// TODO: Check limits
client := expo.NewPushClient(&expo.ClientConfig{
Host: "https://api.expo.dev",
APIURL: "/v2",
})

var wg sync.WaitGroup
svc := &Service{
Cfg: cfg,
Expand All @@ -125,6 +133,7 @@ func NewService(ctx context.Context) (*Service, error) {
Logger: logger,
Relay: relay,
subscriptions: subscriptions,
client: client,
}

logger.Info("Starting all open subscriptions...")
Expand All @@ -139,7 +148,11 @@ func NewService(ctx context.Context) (*Service, error) {
// Create a copy of the loop variable to
// avoid passing address of the same variable
subscription := sub
go svc.startSubscription(svc.Ctx, &subscription, nil, svc.handleSubscribedEvent)
handleEvent := svc.handleSubscribedEvent
if sub.PushToken != "" {
handleEvent = svc.handleSubscribedExpoNotification
}
go svc.startSubscription(svc.Ctx, &subscription, nil, handleEvent)
}

return svc, nil
Expand Down Expand Up @@ -928,6 +941,7 @@ func (svc *Service) postEventToWebhook(event *nostr.Event, webhookURL string) {
"event_kind": event.Kind,
"webhook_url": webhookURL,
}).Error("Failed to post event to webhook")
return
}

svc.Logger.WithFields(logrus.Fields{
Expand Down
23 changes: 23 additions & 0 deletions migrations/202411071013_add_push_token_to_subscriptions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package migrations

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

// Add push_token column to subscriptions table
var _202411071013_add_push_token_to_subscriptions = &gormigrate.Migration{
ID: "202411071013_add_push_token_to_subscriptions",
Migrate: func(tx *gorm.DB) error {
if err := tx.Exec("ALTER TABLE subscriptions ADD COLUMN push_token TEXT").Error; err != nil {
return err
}
return nil
},
Rollback: func(tx *gorm.DB) error {
if err := tx.Exec("ALTER TABLE subscriptions DROP COLUMN push_token").Error; err != nil {
return err
}
return nil
},
}
1 change: 1 addition & 0 deletions migrations/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func Migrate(db *gorm.DB) error {
_202404021628_add_uuid_to_subscriptions,
_202404031539_add_indexes,
_202407171220_add_response_received_at_to_request_events,
_202411071013_add_push_token_to_subscriptions,
})

return m.Migrate()
Expand Down

0 comments on commit 8fc78f1

Please sign in to comment.