mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-05-11 20:45:57 +08:00
proto modify
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
package fcm
|
||||
|
||||
import (
|
||||
"OpenIM/internal/push/offlinepush"
|
||||
"OpenIM/pkg/common/config"
|
||||
"OpenIM/pkg/common/constant"
|
||||
"OpenIM/pkg/common/db/cache"
|
||||
"context"
|
||||
firebase "firebase.google.com/go"
|
||||
"firebase.google.com/go/messaging"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"google.golang.org/api/option"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const SinglePushCountLimit = 400
|
||||
|
||||
var Terminal = []int{constant.IOSPlatformID, constant.AndroidPlatformID, constant.WebPlatformID}
|
||||
|
||||
type Fcm struct {
|
||||
fcmMsgCli *messaging.Client
|
||||
cache cache.Model
|
||||
}
|
||||
|
||||
func NewClient(cache cache.Model) *Fcm {
|
||||
opt := option.WithCredentialsFile(filepath.Join(config.Root, "config", config.Config.Push.Fcm.ServiceAccount))
|
||||
fcmApp, err := firebase.NewApp(context.Background(), nil, opt)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// auth
|
||||
// fcmClient, err := fcmApp.Auth(context.Background())
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
ctx := context.Background()
|
||||
fcmMsgClient, err := fcmApp.Messaging(ctx)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
return nil
|
||||
}
|
||||
return &Fcm{fcmMsgCli: fcmMsgClient}
|
||||
}
|
||||
|
||||
func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
// accounts->registrationToken
|
||||
allTokens := make(map[string][]string, 0)
|
||||
for _, account := range userIDs {
|
||||
var personTokens []string
|
||||
for _, v := range Terminal {
|
||||
Token, err := f.cache.GetFcmToken(ctx, account, v)
|
||||
if err == nil {
|
||||
personTokens = append(personTokens, Token)
|
||||
}
|
||||
}
|
||||
allTokens[account] = personTokens
|
||||
}
|
||||
Success := 0
|
||||
Fail := 0
|
||||
notification := &messaging.Notification{}
|
||||
notification.Body = content
|
||||
notification.Title = title
|
||||
var messages []*messaging.Message
|
||||
for userID, personTokens := range allTokens {
|
||||
apns := &messaging.APNSConfig{Payload: &messaging.APNSPayload{Aps: &messaging.Aps{Sound: opts.IOSPushSound}}}
|
||||
messageCount := len(messages)
|
||||
if messageCount >= SinglePushCountLimit {
|
||||
response, err := f.fcmMsgCli.SendAll(ctx, messages)
|
||||
if err != nil {
|
||||
Fail = Fail + messageCount
|
||||
} else {
|
||||
Success = Success + response.SuccessCount
|
||||
Fail = Fail + response.FailureCount
|
||||
}
|
||||
messages = messages[0:0]
|
||||
}
|
||||
if opts.IOSBadgeCount {
|
||||
unreadCountSum, err := f.cache.IncrUserBadgeUnreadCountSum(ctx, userID)
|
||||
if err == nil {
|
||||
apns.Payload.Aps.Badge = &unreadCountSum
|
||||
} else {
|
||||
//log.Error(operationID, "IncrUserBadgeUnreadCountSum redis err", err.Error(), uid)
|
||||
Fail++
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
unreadCountSum, err := f.cache.GetUserBadgeUnreadCountSum(ctx, userID)
|
||||
if err == nil && unreadCountSum != 0 {
|
||||
apns.Payload.Aps.Badge = &unreadCountSum
|
||||
} else if err == redis.Nil || unreadCountSum == 0 {
|
||||
zero := 1
|
||||
apns.Payload.Aps.Badge = &zero
|
||||
} else {
|
||||
//log.Error(operationID, "GetUserBadgeUnreadCountSum redis err", err.Error(), uid)
|
||||
Fail++
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, token := range personTokens {
|
||||
temp := &messaging.Message{
|
||||
Data: map[string]string{"ex": opts.Ex},
|
||||
Token: token,
|
||||
Notification: notification,
|
||||
APNS: apns,
|
||||
}
|
||||
messages = append(messages, temp)
|
||||
}
|
||||
}
|
||||
messageCount := len(messages)
|
||||
if messageCount > 0 {
|
||||
response, err := f.fcmMsgCli.SendAll(ctx, messages)
|
||||
if err != nil {
|
||||
Fail = Fail + messageCount
|
||||
//log.Info(operationID, "some token push err", err.Error(), messageCount)
|
||||
} else {
|
||||
Success = Success + response.SuccessCount
|
||||
Fail = Fail + response.FailureCount
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package fcm
|
||||
|
||||
import (
|
||||
"OpenIM/internal/push/offlinepush"
|
||||
"OpenIM/pkg/common/db/cache"
|
||||
"context"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Push(t *testing.T) {
|
||||
var redis cache.Model
|
||||
offlinePusher := NewClient(redis)
|
||||
err := offlinePusher.Push(context.Background(), []string{"userID1"}, "test", "test", &offlinepush.Opts{})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package getui
|
||||
|
||||
import (
|
||||
"OpenIM/pkg/common/config"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Resp struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func (r *Resp) parseError() (err error) {
|
||||
switch r.Code {
|
||||
case tokenExpireCode:
|
||||
err = TokenExpireError
|
||||
case 0:
|
||||
err = nil
|
||||
default:
|
||||
err = fmt.Errorf("code %d, msg %s", r.Code, r.Msg)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type RespI interface {
|
||||
parseError() error
|
||||
}
|
||||
|
||||
type AuthReq struct {
|
||||
Sign string `json:"sign"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
AppKey string `json:"appkey"`
|
||||
}
|
||||
|
||||
type AuthResp struct {
|
||||
ExpireTime string `json:"expire_time"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type TaskResp struct {
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
TTL *int64 `json:"ttl"`
|
||||
}
|
||||
|
||||
type Audience struct {
|
||||
Alias []string `json:"alias"`
|
||||
}
|
||||
|
||||
type PushMessage struct {
|
||||
Notification *Notification `json:"notification,omitempty"`
|
||||
Transmission *string `json:"transmission,omitempty"`
|
||||
}
|
||||
|
||||
type PushChannel struct {
|
||||
Ios *Ios `json:"ios"`
|
||||
Android *Android `json:"android"`
|
||||
}
|
||||
|
||||
type PushReq struct {
|
||||
RequestID *string `json:"request_id"`
|
||||
Settings *Settings `json:"settings"`
|
||||
Audience *Audience `json:"audience"`
|
||||
PushMessage *PushMessage `json:"push_message"`
|
||||
PushChannel *PushChannel `json:"push_channel"`
|
||||
IsAsync *bool `json:"is_async"`
|
||||
TaskID *string `json:"taskid"`
|
||||
}
|
||||
|
||||
type Ios struct {
|
||||
NotificationType *string `json:"type"`
|
||||
AutoBadge *string `json:"auto_badge"`
|
||||
Aps struct {
|
||||
Sound string `json:"sound"`
|
||||
Alert Alert `json:"alert"`
|
||||
} `json:"aps"`
|
||||
}
|
||||
|
||||
type Alert struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
type Android struct {
|
||||
Ups struct {
|
||||
Notification Notification `json:"notification"`
|
||||
Options Options `json:"options"`
|
||||
} `json:"ups"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
ChannelID string `json:"channelID"`
|
||||
ChannelName string `json:"ChannelName"`
|
||||
ClickType string `json:"click_type"`
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
HW struct {
|
||||
DefaultSound bool `json:"/message/android/notification/default_sound"`
|
||||
ChannelID string `json:"/message/android/notification/channel_id"`
|
||||
Sound string `json:"/message/android/notification/sound"`
|
||||
Importance string `json:"/message/android/notification/importance"`
|
||||
} `json:"HW"`
|
||||
XM struct {
|
||||
ChannelID string `json:"/extra.channel_id"`
|
||||
} `json:"XM"`
|
||||
VV struct {
|
||||
Classification int `json:"/classification"`
|
||||
} `json:"VV"`
|
||||
}
|
||||
|
||||
type Payload struct {
|
||||
IsSignal bool `json:"isSignal"`
|
||||
}
|
||||
|
||||
func newPushReq(title, content string) PushReq {
|
||||
pushReq := PushReq{PushMessage: &PushMessage{Notification: &Notification{
|
||||
Title: title,
|
||||
Body: content,
|
||||
ClickType: "startapp",
|
||||
ChannelID: config.Config.Push.Getui.ChannelID,
|
||||
ChannelName: config.Config.Push.Getui.ChannelName,
|
||||
}}}
|
||||
return pushReq
|
||||
}
|
||||
|
||||
func newBatchPushReq(userIDs []string, taskID string) PushReq {
|
||||
var IsAsync = true
|
||||
return PushReq{Audience: &Audience{Alias: userIDs}, IsAsync: &IsAsync, TaskID: &taskID}
|
||||
}
|
||||
|
||||
func (pushReq *PushReq) setPushChannel(title string, body string) {
|
||||
pushReq.PushChannel = &PushChannel{}
|
||||
// autoBadge := "+1"
|
||||
pushReq.PushChannel.Ios = &Ios{}
|
||||
notify := "notify"
|
||||
pushReq.PushChannel.Ios.NotificationType = ¬ify
|
||||
pushReq.PushChannel.Ios.Aps.Sound = "default"
|
||||
pushReq.PushChannel.Ios.Aps.Alert = Alert{
|
||||
Title: title,
|
||||
Body: body,
|
||||
}
|
||||
pushReq.PushChannel.Android = &Android{}
|
||||
pushReq.PushChannel.Android.Ups.Notification = Notification{
|
||||
Title: title,
|
||||
Body: body,
|
||||
ClickType: "startapp",
|
||||
}
|
||||
pushReq.PushChannel.Android.Ups.Options = Options{
|
||||
HW: struct {
|
||||
DefaultSound bool `json:"/message/android/notification/default_sound"`
|
||||
ChannelID string `json:"/message/android/notification/channel_id"`
|
||||
Sound string `json:"/message/android/notification/sound"`
|
||||
Importance string `json:"/message/android/notification/importance"`
|
||||
}{ChannelID: "RingRing4", Sound: "/raw/ring001", Importance: "NORMAL"},
|
||||
XM: struct {
|
||||
ChannelID string `json:"/extra.channel_id"`
|
||||
}{ChannelID: "high_system"},
|
||||
VV: struct {
|
||||
Classification int "json:\"/classification\""
|
||||
}{
|
||||
Classification: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package getui
|
||||
|
||||
import (
|
||||
"OpenIM/internal/push/offlinepush"
|
||||
"OpenIM/pkg/common/config"
|
||||
"OpenIM/pkg/common/db/cache"
|
||||
http2 "OpenIM/pkg/common/http"
|
||||
"OpenIM/pkg/common/log"
|
||||
"OpenIM/pkg/common/tracelog"
|
||||
"OpenIM/pkg/utils/splitter"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"sync"
|
||||
|
||||
"OpenIM/pkg/utils"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
TokenExpireError = errors.New("token expire")
|
||||
UserIDEmptyError = errors.New("userIDs is empty")
|
||||
)
|
||||
|
||||
const (
|
||||
pushURL = "/push/single/alias"
|
||||
authURL = "/auth"
|
||||
taskURL = "/push/list/message"
|
||||
batchPushURL = "/push/list/alias"
|
||||
|
||||
// codes
|
||||
tokenExpireCode = 10001
|
||||
tokenExpireTime = 60 * 60 * 23
|
||||
taskIDTTL = 1000 * 60 * 60 * 24
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
cache cache.Model
|
||||
tokenExpireTime int64
|
||||
taskIDTTL int64
|
||||
}
|
||||
|
||||
func NewClient(cache cache.Model) *Client {
|
||||
return &Client{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL}
|
||||
}
|
||||
|
||||
func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
token, err := g.cache.GetGetuiToken(ctx)
|
||||
if err != nil {
|
||||
if err == redis.Nil {
|
||||
token, err = g.getTokenAndSave2Redis(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
pushReq := newPushReq(title, content)
|
||||
pushReq.setPushChannel(title, content)
|
||||
if len(userIDs) > 1 {
|
||||
maxNum := 999
|
||||
if len(userIDs) > maxNum {
|
||||
s := splitter.NewSplitter(maxNum, userIDs)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(s.GetSplitResult()))
|
||||
for i, v := range s.GetSplitResult() {
|
||||
go func(index int, userIDs []string) {
|
||||
defer wg.Done()
|
||||
if err2 := g.batchPush(ctx, token, userIDs, pushReq); err2 != nil {
|
||||
log.NewError(tracelog.GetOperationID(ctx), "batchPush failed", i, token, pushReq)
|
||||
err = err2
|
||||
}
|
||||
}(i, v.Item)
|
||||
}
|
||||
wg.Wait()
|
||||
} else {
|
||||
err = g.batchPush(ctx, token, userIDs, pushReq)
|
||||
}
|
||||
} else if len(userIDs) == 1 {
|
||||
err = g.singlePush(ctx, token, userIDs[0], pushReq)
|
||||
} else {
|
||||
return UserIDEmptyError
|
||||
}
|
||||
switch err {
|
||||
case TokenExpireError:
|
||||
token, err = g.getTokenAndSave2Redis(ctx)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(config.Config.Push.Getui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.Getui.MasterSecret))
|
||||
sign := hex.EncodeToString(h.Sum(nil))
|
||||
reqAuth := AuthReq{
|
||||
Sign: sign,
|
||||
Timestamp: strconv.Itoa(int(timeStamp)),
|
||||
AppKey: config.Config.Push.Getui.AppKey,
|
||||
}
|
||||
respAuth := AuthResp{}
|
||||
err = g.request(ctx, authURL, reqAuth, "", &respAuth)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
expire, err := strconv.Atoi(respAuth.ExpireTime)
|
||||
return respAuth.Token, int64(expire), err
|
||||
}
|
||||
|
||||
func (g *Client) GetTaskID(ctx context.Context, token string, pushReq PushReq) (string, error) {
|
||||
respTask := TaskResp{}
|
||||
ttl := int64(1000 * 60 * 5)
|
||||
pushReq.Settings = &Settings{TTL: &ttl}
|
||||
err := g.request(ctx, taskURL, pushReq, token, &respTask)
|
||||
if err != nil {
|
||||
return "", utils.Wrap(err, "")
|
||||
}
|
||||
return respTask.TaskID, nil
|
||||
}
|
||||
|
||||
// max num is 999
|
||||
func (g *Client) batchPush(ctx context.Context, token string, userIDs []string, pushReq PushReq) error {
|
||||
taskID, err := g.GetTaskID(ctx, token, pushReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pushReq = newBatchPushReq(userIDs, taskID)
|
||||
return g.request(ctx, batchPushURL, pushReq, token, nil)
|
||||
}
|
||||
|
||||
func (g *Client) singlePush(ctx context.Context, token, userID string, pushReq PushReq) error {
|
||||
operationID := tracelog.GetOperationID(ctx)
|
||||
pushReq.RequestID = &operationID
|
||||
pushReq.Audience = &Audience{Alias: []string{userID}}
|
||||
return g.request(ctx, pushURL, pushReq, token, nil)
|
||||
}
|
||||
|
||||
func (g *Client) request(ctx context.Context, url string, input interface{}, token string, output interface{}) error {
|
||||
header := map[string]string{"token": token}
|
||||
resp := &Resp{}
|
||||
resp.Data = output
|
||||
return g.postReturn(config.Config.Push.Getui.PushUrl+url, header, input, resp, 3)
|
||||
}
|
||||
|
||||
func (g *Client) postReturn(url string, header map[string]string, input interface{}, output RespI, timeout int) error {
|
||||
err := http2.PostReturn(url, header, input, output, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return parseError()
|
||||
}
|
||||
|
||||
func (g *Client) getTokenAndSave2Redis(ctx context.Context) (token string, err error) {
|
||||
token, _, err = g.Auth(ctx, time.Now().UnixNano()/1e6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.cache.SetGetuiToken(ctx, token, 60*60*23)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (g *Client) GetTaskIDAndSave2Redis(ctx context.Context, token string, pushReq PushReq) (taskID string, err error) {
|
||||
pushReq.Settings = &Settings{TTL: &g.taskIDTTL}
|
||||
taskID, err = g.GetTaskID(ctx, token, pushReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = g.cache.SetGetuiTaskID(ctx, taskID, g.tokenExpireTime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package body
|
||||
|
||||
const (
|
||||
TAG = "tag"
|
||||
TAGAND = "tag_and"
|
||||
TAGNOT = "tag_not"
|
||||
ALIAS = "alias"
|
||||
REGISTRATIONID = "registration_id"
|
||||
)
|
||||
|
||||
type Audience struct {
|
||||
Object interface{}
|
||||
audience map[string][]string
|
||||
}
|
||||
|
||||
func (a *Audience) set(key string, v []string) {
|
||||
if a.audience == nil {
|
||||
a.audience = make(map[string][]string)
|
||||
a.Object = a.audience
|
||||
}
|
||||
//v, ok = this.audience[key]
|
||||
//if ok {
|
||||
// return
|
||||
//}
|
||||
a.audience[key] = v
|
||||
}
|
||||
|
||||
func (a *Audience) SetTag(tags []string) {
|
||||
a.set(TAG, tags)
|
||||
}
|
||||
|
||||
func (a *Audience) SetTagAnd(tags []string) {
|
||||
a.set(TAGAND, tags)
|
||||
}
|
||||
|
||||
func (a *Audience) SetTagNot(tags []string) {
|
||||
a.set(TAGNOT, tags)
|
||||
}
|
||||
|
||||
func (a *Audience) SetAlias(alias []string) {
|
||||
a.set(ALIAS, alias)
|
||||
}
|
||||
|
||||
func (a *Audience) SetRegistrationId(ids []string) {
|
||||
a.set(REGISTRATIONID, ids)
|
||||
}
|
||||
|
||||
func (a *Audience) SetAll() {
|
||||
a.Object = "all"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package body
|
||||
|
||||
type Message struct {
|
||||
MsgContent string `json:"msg_content"`
|
||||
Title string `json:"title,omitempty"`
|
||||
ContentType string `json:"content_type,omitempty"`
|
||||
Extras map[string]interface{} `json:"extras,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Message) SetMsgContent(c string) {
|
||||
m.MsgContent = c
|
||||
}
|
||||
|
||||
func (m *Message) SetTitle(t string) {
|
||||
m.Title = t
|
||||
}
|
||||
|
||||
func (m *Message) SetContentType(c string) {
|
||||
m.ContentType = c
|
||||
}
|
||||
|
||||
func (m *Message) SetExtras(key string, value interface{}) {
|
||||
if m.Extras == nil {
|
||||
m.Extras = make(map[string]interface{})
|
||||
}
|
||||
m.Extras[key] = value
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package body
|
||||
|
||||
import (
|
||||
"OpenIM/pkg/common/config"
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
Alert string `json:"alert,omitempty"`
|
||||
Android Android `json:"android,omitempty"`
|
||||
IOS Ios `json:"ios,omitempty"`
|
||||
}
|
||||
|
||||
type Android struct {
|
||||
Alert string `json:"alert,omitempty"`
|
||||
Intent struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
} `json:"intent,omitempty"`
|
||||
Extras Extras `json:"extras"`
|
||||
}
|
||||
type Ios struct {
|
||||
Alert string `json:"alert,omitempty"`
|
||||
Sound string `json:"sound,omitempty"`
|
||||
Badge string `json:"badge,omitempty"`
|
||||
Extras Extras `json:"extras"`
|
||||
MutableContent bool `json:"mutable-content"`
|
||||
}
|
||||
|
||||
type Extras struct {
|
||||
ClientMsgID string `json:"clientMsgID"`
|
||||
}
|
||||
|
||||
func (n *Notification) SetAlert(alert string) {
|
||||
n.Alert = alert
|
||||
n.Android.Alert = alert
|
||||
n.SetAndroidIntent()
|
||||
n.IOS.Alert = alert
|
||||
n.IOS.Sound = "default"
|
||||
n.IOS.Badge = "+1"
|
||||
}
|
||||
|
||||
func (n *Notification) SetExtras(extras Extras) {
|
||||
n.IOS.Extras = extras
|
||||
n.Android.Extras = extras
|
||||
}
|
||||
|
||||
func (n *Notification) SetAndroidIntent() {
|
||||
n.Android.Intent.URL = config.Config.Push.Jpns.PushIntent
|
||||
}
|
||||
|
||||
func (n *Notification) IOSEnableMutableContent() {
|
||||
n.IOS.MutableContent = true
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package body
|
||||
|
||||
type Options struct {
|
||||
ApnsProduction bool `json:"apns_production"`
|
||||
}
|
||||
|
||||
func (o *Options) SetApnsProduction(c bool) {
|
||||
o.ApnsProduction = c
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package body
|
||||
|
||||
import (
|
||||
"OpenIM/pkg/common/constant"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
ANDROID = "android"
|
||||
IOS = "ios"
|
||||
QUICKAPP = "quickapp"
|
||||
WINDOWSPHONE = "winphone"
|
||||
ALL = "all"
|
||||
)
|
||||
|
||||
type Platform struct {
|
||||
Os interface{}
|
||||
osArry []string
|
||||
}
|
||||
|
||||
func (p *Platform) Set(os string) error {
|
||||
if p.Os == nil {
|
||||
p.osArry = make([]string, 0, 4)
|
||||
} else {
|
||||
switch p.Os.(type) {
|
||||
case string:
|
||||
return errors.New("platform is all")
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
for _, value := range p.osArry {
|
||||
if os == value {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
switch os {
|
||||
case IOS:
|
||||
fallthrough
|
||||
case ANDROID:
|
||||
fallthrough
|
||||
case QUICKAPP:
|
||||
fallthrough
|
||||
case WINDOWSPHONE:
|
||||
p.osArry = append(p.osArry, os)
|
||||
p.Os = p.osArry
|
||||
default:
|
||||
return errors.New("unknow platform")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func (p *Platform) SetPlatform(platform string) error {
|
||||
switch platform {
|
||||
case constant.AndroidPlatformStr:
|
||||
return p.SetAndroid()
|
||||
case constant.IOSPlatformStr:
|
||||
return p.SetIOS()
|
||||
default:
|
||||
return errors.New("platform err")
|
||||
}
|
||||
|
||||
}
|
||||
func (p *Platform) SetIOS() error {
|
||||
return p.Set(IOS)
|
||||
}
|
||||
|
||||
func (p *Platform) SetAndroid() error {
|
||||
return p.Set(ANDROID)
|
||||
}
|
||||
|
||||
func (p *Platform) SetQuickApp() error {
|
||||
return p.Set(QUICKAPP)
|
||||
}
|
||||
|
||||
func (p *Platform) SetWindowsPhone() error {
|
||||
return p.Set(WINDOWSPHONE)
|
||||
}
|
||||
|
||||
func (p *Platform) SetAll() {
|
||||
p.Os = ALL
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package body
|
||||
|
||||
type PushObj struct {
|
||||
Platform interface{} `json:"platform"`
|
||||
Audience interface{} `json:"audience"`
|
||||
Notification interface{} `json:"notification,omitempty"`
|
||||
Message interface{} `json:"message,omitempty"`
|
||||
Options interface{} `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PushObj) SetPlatform(pf *Platform) {
|
||||
p.Platform = pf.Os
|
||||
}
|
||||
|
||||
func (p *PushObj) SetAudience(ad *Audience) {
|
||||
p.Audience = ad.Object
|
||||
}
|
||||
|
||||
func (p *PushObj) SetNotification(no *Notification) {
|
||||
p.Notification = no
|
||||
}
|
||||
|
||||
func (p *PushObj) SetMessage(m *Message) {
|
||||
p.Message = m
|
||||
}
|
||||
func (p *PushObj) SetOptions(o *Options) {
|
||||
p.Options = o
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package jpush
|
||||
|
||||
import (
|
||||
"OpenIM/internal/push/offlinepush"
|
||||
"OpenIM/internal/push/offlinepush/jpush/body"
|
||||
"OpenIM/pkg/common/config"
|
||||
http2 "OpenIM/pkg/common/http"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type JPush struct{}
|
||||
|
||||
func NewClient() *JPush {
|
||||
return &JPush{}
|
||||
}
|
||||
|
||||
func (j *JPush) Auth(apiKey, secretKey string, timeStamp int64) (token string, err error) {
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (j *JPush) SetAlias(cid, alias string) (resp string, err error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (j *JPush) getAuthorization(appKey string, masterSecret string) string {
|
||||
str := fmt.Sprintf("%s:%s", appKey, masterSecret)
|
||||
buf := []byte(str)
|
||||
Authorization := fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString(buf))
|
||||
return Authorization
|
||||
}
|
||||
|
||||
func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
var pf body.Platform
|
||||
pf.SetAll()
|
||||
var au body.Audience
|
||||
au.SetAlias(userIDs)
|
||||
var no body.Notification
|
||||
var extras body.Extras
|
||||
if opts.Signal.ClientMsgID != "" {
|
||||
extras.ClientMsgID = opts.Signal.ClientMsgID
|
||||
}
|
||||
no.IOSEnableMutableContent()
|
||||
no.SetExtras(extras)
|
||||
no.SetAlert(title)
|
||||
var msg body.Message
|
||||
msg.SetMsgContent(content)
|
||||
var opt body.Options
|
||||
opt.SetApnsProduction(config.Config.IOSPush.Production)
|
||||
var pushObj body.PushObj
|
||||
pushObj.SetPlatform(&pf)
|
||||
pushObj.SetAudience(&au)
|
||||
pushObj.SetNotification(&no)
|
||||
pushObj.SetMessage(&msg)
|
||||
pushObj.SetOptions(&opt)
|
||||
var resp interface{}
|
||||
return j.request(pushObj, resp, 5)
|
||||
}
|
||||
|
||||
func (j *JPush) request(po body.PushObj, resp interface{}, timeout int) error {
|
||||
return http2.PostReturn(config.Config.Push.Jpns.PushUrl, map[string]string{"Authorization": j.getAuthorization(config.Config.Push.Jpns.AppKey, config.Config.Push.Jpns.MasterSecret)}, po, resp, timeout)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package offlinepush
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type OfflinePusher interface {
|
||||
Push(ctx context.Context, userIDs []string, title, content string, opts *Opts) error
|
||||
}
|
||||
|
||||
type Opts struct {
|
||||
Signal *Signal
|
||||
IOSPushSound string
|
||||
IOSBadgeCount bool
|
||||
Ex string
|
||||
}
|
||||
|
||||
type Signal struct {
|
||||
ClientMsgID string
|
||||
}
|
||||
Reference in New Issue
Block a user