|
|
|
@ -16,8 +16,11 @@ package fcm
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
|
|
|
|
"github.com/openimsdk/tools/utils/httputil"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
firebase "firebase.google.com/go"
|
|
|
|
|
"firebase.google.com/go/messaging"
|
|
|
|
@ -40,13 +43,25 @@ type Fcm struct {
|
|
|
|
|
|
|
|
|
|
// NewClient initializes a new FCM client using the Firebase Admin SDK.
|
|
|
|
|
// It requires the FCM service account credentials file located within the project's configuration directory.
|
|
|
|
|
func NewClient(pushConf *config.Push, cache cache.ThirdCache) (*Fcm, error) {
|
|
|
|
|
projectRoot, err := config.GetProjectRoot()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
func NewClient(pushConf *config.Push, cache cache.ThirdCache, fcmConfigPath string) (*Fcm, error) {
|
|
|
|
|
var opt option.ClientOption
|
|
|
|
|
switch {
|
|
|
|
|
case len(pushConf.FCM.FilePath) != 0:
|
|
|
|
|
// with file path
|
|
|
|
|
credentialsFilePath := filepath.Join(fcmConfigPath, pushConf.FCM.FilePath)
|
|
|
|
|
opt = option.WithCredentialsFile(credentialsFilePath)
|
|
|
|
|
case len(pushConf.FCM.AuthURL) != 0:
|
|
|
|
|
// with authentication URL
|
|
|
|
|
client := httputil.NewHTTPClient(httputil.NewClientConfig())
|
|
|
|
|
resp, err := client.Get(pushConf.FCM.AuthURL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
opt = option.WithCredentialsJSON(resp)
|
|
|
|
|
default:
|
|
|
|
|
return nil, errs.New("no FCM config").Wrap()
|
|
|
|
|
}
|
|
|
|
|
credentialsFilePath := filepath.Join(projectRoot, "config", pushConf.FCM.ServiceAccount)
|
|
|
|
|
opt := option.WithCredentialsFile(credentialsFilePath)
|
|
|
|
|
|
|
|
|
|
fcmApp, err := firebase.NewApp(context.Background(), nil, opt)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
@ -56,7 +71,6 @@ func NewClient(pushConf *config.Push, cache cache.ThirdCache) (*Fcm, error) {
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errs.Wrap(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -79,6 +93,8 @@ func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string,
|
|
|
|
|
notification.Body = content
|
|
|
|
|
notification.Title = title
|
|
|
|
|
var messages []*messaging.Message
|
|
|
|
|
var sendErrBuilder strings.Builder
|
|
|
|
|
var msgErrBuilder strings.Builder
|
|
|
|
|
for userID, personTokens := range allTokens {
|
|
|
|
|
apns := &messaging.APNSConfig{Payload: &messaging.APNSPayload{Aps: &messaging.Aps{Sound: opts.IOSPushSound}}}
|
|
|
|
|
messageCount := len(messages)
|
|
|
|
@ -86,9 +102,21 @@ func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string,
|
|
|
|
|
response, err := f.fcmMsgCli.SendAll(ctx, messages)
|
|
|
|
|
if err != nil {
|
|
|
|
|
Fail = Fail + messageCount
|
|
|
|
|
// Record push error
|
|
|
|
|
sendErrBuilder.WriteString(err.Error())
|
|
|
|
|
sendErrBuilder.WriteByte('.')
|
|
|
|
|
} else {
|
|
|
|
|
Success = Success + response.SuccessCount
|
|
|
|
|
Fail = Fail + response.FailureCount
|
|
|
|
|
if response.FailureCount != 0 {
|
|
|
|
|
// Record message error
|
|
|
|
|
for i := range response.Responses {
|
|
|
|
|
if !response.Responses[i].Success {
|
|
|
|
|
msgErrBuilder.WriteString(response.Responses[i].Error.Error())
|
|
|
|
|
msgErrBuilder.WriteByte('.')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
messages = messages[0:0]
|
|
|
|
|
}
|
|
|
|
@ -134,5 +162,9 @@ func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string,
|
|
|
|
|
Fail = Fail + response.FailureCount
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if Fail != 0 {
|
|
|
|
|
return errs.New(fmt.Sprintf("%d message send failed;send err:%s;message err:%s",
|
|
|
|
|
Fail, sendErrBuilder.String(), msgErrBuilder.String())).Wrap()
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|