/
apns.go
120 lines (99 loc) · 2.74 KB
/
apns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package delivery
import (
"context"
"errors"
"os"
"github.com/sideshow/apns2"
"github.com/sideshow/apns2/payload"
"github.com/sideshow/apns2/token"
"github.com/xmtp/example-notification-server-go/pkg/interfaces"
"github.com/xmtp/example-notification-server-go/pkg/options"
"go.uber.org/zap"
)
type ApnsDelivery struct {
logger *zap.Logger
apnsClient *apns2.Client
opts options.ApnsOptions
}
func NewApnsDelivery(logger *zap.Logger, opts options.ApnsOptions) (*ApnsDelivery, error) {
var bytes []byte
var err error
if opts.P8Certificate == "" {
bytes, err = os.ReadFile(opts.P8CertificateFilePath)
if err != nil {
return nil, err
}
} else {
bytes = []byte(opts.P8Certificate)
}
client, err := getApnsClient(bytes, opts.KeyId, opts.TeamId)
if opts.Mode == "production" {
client.Production()
} else if opts.Mode == "development" {
client.Development()
} else {
return nil, errors.New("invalid APNS mode")
}
if err != nil {
return nil, err
}
return &ApnsDelivery{
logger: logger.Named("delivery-service"),
apnsClient: client,
opts: opts,
}, nil
}
func (a ApnsDelivery) CanDeliver(req interfaces.SendRequest) bool {
return req.Installation.DeliveryMechanism.Kind == interfaces.APNS
}
func (a ApnsDelivery) Send(ctx context.Context, req interfaces.SendRequest) error {
if req.Message == nil {
return errors.New("missing message")
}
notification := a.buildNotification(req.Subscription.IsSilent,
req.Installation.DeliveryMechanism.Token,
req.Message.ContentTopic,
string(req.MessageContext.MessageType),
req.Message.Message,
)
res, err := a.apnsClient.PushWithContext(ctx, notification)
if res != nil {
a.logger.Info(
"Sent notification",
zap.String("apns_id", res.ApnsID),
zap.Int("status_code", res.StatusCode),
zap.String("reason", res.Reason),
)
}
return err
}
func (a ApnsDelivery) buildNotification(isSilent bool, token string, contentTopic string, messageKind string, messageBytes []byte) *apns2.Notification {
notificationPayload := payload.NewPayload().
Custom("topic", contentTopic).
Custom("encryptedMessage", messageBytes).
Custom("messageKind", messageKind)
if isSilent {
notificationPayload = notificationPayload.ContentAvailable()
} else {
notificationPayload = notificationPayload.
Alert("New message from XMTP").
MutableContent()
}
return &apns2.Notification{
DeviceToken: token,
Topic: a.opts.Topic,
Payload: notificationPayload,
}
}
func getApnsClient(authKey []byte, keyId, teamId string) (*apns2.Client, error) {
key, err := token.AuthKeyFromBytes(authKey)
if err != nil {
return nil, err
}
authToken := &token.Token{
AuthKey: key,
KeyID: keyId,
TeamID: teamId,
}
return apns2.NewTokenClient(authToken), nil
}