feat: use robot to migrate code
Signed-off-by: kubbot & kubecub <3293172751ysy@gmail.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/callbackstruct"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/http"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
func url() string {
|
||||
return config.Config.Callback.CallbackUrl
|
||||
}
|
||||
|
||||
func callbackOfflinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error {
|
||||
if !config.Config.Callback.CallbackOfflinePush.Enable {
|
||||
return nil
|
||||
}
|
||||
req := &callbackstruct.CallbackBeforePushReq{
|
||||
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: constant.CallbackOfflinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
},
|
||||
UserIDList: userIDs,
|
||||
},
|
||||
OfflinePushInfo: msg.OfflinePushInfo,
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: msg.GroupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: utils.GetContent(msg),
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforePushResp{}
|
||||
if err := http.CallBackPostReturn(ctx, url(), req, resp, config.Config.Callback.CallbackOfflinePush); err != nil {
|
||||
if err == errs.ErrCallbackContinue {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if len(resp.UserIDs) != 0 {
|
||||
*offlinePushUserIDs = resp.UserIDs
|
||||
}
|
||||
if resp.OfflinePushInfo != nil {
|
||||
msg.OfflinePushInfo = resp.OfflinePushInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func callbackOnlinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
|
||||
if !config.Config.Callback.CallbackOnlinePush.Enable || utils.Contain(msg.SendID, userIDs...) {
|
||||
return nil
|
||||
}
|
||||
req := callbackstruct.CallbackBeforePushReq{
|
||||
UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: constant.CallbackOnlinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
},
|
||||
UserIDList: userIDs,
|
||||
},
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: msg.GroupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: utils.GetContent(msg),
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforePushResp{}
|
||||
return http.CallBackPostReturn(ctx, url(), req, resp, config.Config.Callback.CallbackOnlinePush)
|
||||
}
|
||||
|
||||
func callbackBeforeSuperGroupOnlinePush(ctx context.Context, groupID string, msg *sdkws.MsgData, pushToUserIDs *[]string) error {
|
||||
if !config.Config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable {
|
||||
return nil
|
||||
}
|
||||
req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
|
||||
UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
|
||||
CallbackCommand: constant.CallbackSuperGroupOnlinePushCommand,
|
||||
OperationID: mcontext.GetOperationID(ctx),
|
||||
PlatformID: int(msg.SenderPlatformID),
|
||||
Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
|
||||
},
|
||||
ClientMsgID: msg.ClientMsgID,
|
||||
SendID: msg.SendID,
|
||||
GroupID: groupID,
|
||||
ContentType: msg.ContentType,
|
||||
SessionType: msg.SessionType,
|
||||
AtUserIDs: msg.AtUserIDList,
|
||||
Content: utils.GetContent(msg),
|
||||
Seq: msg.Seq,
|
||||
}
|
||||
resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{}
|
||||
if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, req, resp, config.Config.Callback.CallbackBeforeSuperGroupOnlinePush); err != nil {
|
||||
if err == errs.ErrCallbackContinue {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if len(resp.UserIDs) != 0 {
|
||||
*pushToUserIDs = resp.UserIDs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/prome"
|
||||
)
|
||||
|
||||
type Consumer struct {
|
||||
pushCh ConsumerHandler
|
||||
successCount uint64
|
||||
}
|
||||
|
||||
func NewConsumer(pusher *Pusher) *Consumer {
|
||||
return &Consumer{
|
||||
pushCh: *NewConsumerHandler(pusher),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Consumer) initPrometheus() {
|
||||
prome.NewMsgOfflinePushSuccessCounter()
|
||||
prome.NewMsgOfflinePushFailedCounter()
|
||||
}
|
||||
|
||||
func (c *Consumer) Start() {
|
||||
//statistics.NewStatistics(&c.successCount, config.Config.ModuleName.PushName, fmt.Sprintf("%d second push to msg_gateway count", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval)
|
||||
go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(&c.pushCh)
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
** description("").
|
||||
** copyright('open-im,www.open-im.io').
|
||||
** author("fg,Gordon@tuoyun.net").
|
||||
** time(2021/5/27 11:24).
|
||||
*/
|
||||
package content_struct
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Content struct {
|
||||
IsDisplay int32 `json:"isDisplay"`
|
||||
ID string `json:"id"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func NewContentStructString(isDisplay int32, ID string, text string) string {
|
||||
c := Content{IsDisplay: isDisplay, ID: ID, Text: text}
|
||||
return c.contentToString()
|
||||
}
|
||||
|
||||
func (c *Content) contentToString() string {
|
||||
data, _ := json.Marshal(c)
|
||||
dataString := string(data)
|
||||
return dataString
|
||||
}
|
||||
|
||||
type groupMemberFullInfo struct {
|
||||
GroupId string `json:"groupID"`
|
||||
UserId string `json:"userId"`
|
||||
Role int `json:"role"`
|
||||
JoinTime uint64 `json:"joinTime"`
|
||||
NickName string `json:"nickName"`
|
||||
FaceUrl string `json:"faceUrl"`
|
||||
}
|
||||
|
||||
type AgreeOrRejectGroupMember struct {
|
||||
GroupId string `json:"groupID"`
|
||||
UserId string `json:"userId"`
|
||||
Role int `json:"role"`
|
||||
JoinTime uint64 `json:"joinTime"`
|
||||
NickName string `json:"nickName"`
|
||||
FaceUrl string `json:"faceUrl"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
type AtTextContent struct {
|
||||
Text string `json:"text"`
|
||||
AtUserList []string `json:"atUserList"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
|
||||
type CreateGroupSysMsg struct {
|
||||
uIdCreator string `creatorUid`
|
||||
initMemberList []groupMemberFullInfo `json: initMemberList`
|
||||
CreateTime uint64 `json:"CreateTime"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type NotificationContent struct {
|
||||
IsDisplay int32 `json:"isDisplay"`
|
||||
DefaultTips string `json:"defaultTips"`
|
||||
Detail string `json:"detail"`
|
||||
}
|
||||
|
||||
func (c *NotificationContent) ContentToString() string {
|
||||
data, _ := json.Marshal(c)
|
||||
dataString := string(data)
|
||||
return dataString
|
||||
}
|
||||
|
||||
type KickGroupMemberApiReq struct {
|
||||
GroupID string `json:"groupID"`
|
||||
UidList []string `json:"uidList"`
|
||||
Reason string `json:"reason"`
|
||||
OperationID string `json:"operationID"`
|
||||
}
|
||||
|
||||
func NewCreateGroupSysMsgString(create *CreateGroupSysMsg, text string) string {
|
||||
create.Text = text
|
||||
jstring, _ := json.Marshal(create)
|
||||
|
||||
return string(jstring)
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
package getui
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/config"
|
||||
"Open_IM/pkg/common/db"
|
||||
"Open_IM/pkg/common/log"
|
||||
"Open_IM/pkg/utils"
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
|
||||
//"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
GetuiClient *Getui
|
||||
|
||||
TokenExpireError = errors.New("token expire")
|
||||
)
|
||||
|
||||
const (
|
||||
PushURL = "/push/single/alias"
|
||||
AuthURL = "/auth"
|
||||
)
|
||||
|
||||
func init() {
|
||||
GetuiClient = newGetuiClient()
|
||||
}
|
||||
|
||||
type Getui struct{}
|
||||
|
||||
type GetuiCommonResp struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
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 PushReq struct {
|
||||
RequestID string `json:"request_id"`
|
||||
Audience struct {
|
||||
Alias []string `json:"alias"`
|
||||
} `json:"audience"`
|
||||
PushMessage struct {
|
||||
Notification Notification `json:"notification,omitempty"`
|
||||
Transmission string `json:"transmission,omitempty"`
|
||||
} `json:"push_message"`
|
||||
PushChannel struct {
|
||||
Ios Ios `json:"ios"`
|
||||
Android Android `json:"android"`
|
||||
} `json:"push_channel"`
|
||||
}
|
||||
|
||||
type Ios struct {
|
||||
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"`
|
||||
} `json:"ups"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Title string `json:"title"`
|
||||
Body string `json:"body"`
|
||||
ClickType string `json:"click_type"`
|
||||
}
|
||||
|
||||
type PushResp struct {
|
||||
}
|
||||
|
||||
func newGetuiClient() *Getui {
|
||||
return &Getui{}
|
||||
}
|
||||
|
||||
func (g *Getui) Push(userIDList []string, alert, detailContent, operationID string) (resp string, err error) {
|
||||
token, err := db.DB.GetGetuiToken()
|
||||
log.NewDebug(operationID, utils.GetSelfFuncName(), "token:", token)
|
||||
if err != nil {
|
||||
log.NewError(operationID, utils.OperationIDGenerator(), "GetGetuiToken failed", err.Error())
|
||||
}
|
||||
if token == "" || err != nil {
|
||||
token, err = g.getTokenAndSave2Redis(operationID)
|
||||
if err != nil {
|
||||
log.NewError(operationID, utils.GetSelfFuncName(), "getTokenAndSave2Redis failed", err.Error())
|
||||
return "", utils.Wrap(err, "")
|
||||
}
|
||||
}
|
||||
pushReq := PushReq{
|
||||
RequestID: utils.OperationIDGenerator(),
|
||||
Audience: struct {
|
||||
Alias []string `json:"alias"`
|
||||
}{Alias: []string{userIDList[0]}},
|
||||
}
|
||||
pushReq.PushMessage.Notification = Notification{
|
||||
Title: alert,
|
||||
Body: alert,
|
||||
ClickType: "startapp",
|
||||
}
|
||||
pushReq.PushChannel.Ios.Aps.Sound = "default"
|
||||
pushReq.PushChannel.Ios.Aps.Alert = Alert{
|
||||
Title: alert,
|
||||
Body: alert,
|
||||
}
|
||||
pushReq.PushChannel.Android.Ups.Notification = Notification{
|
||||
Title: alert,
|
||||
Body: alert,
|
||||
ClickType: "startapp",
|
||||
}
|
||||
pushResp := PushResp{}
|
||||
err = g.request(PushURL, pushReq, token, &pushResp, operationID)
|
||||
switch err {
|
||||
case TokenExpireError:
|
||||
token, err = g.getTokenAndSave2Redis(operationID)
|
||||
if err != nil {
|
||||
log.NewError(operationID, utils.GetSelfFuncName(), "getTokenAndSave2Redis failed, ", err.Error())
|
||||
} else {
|
||||
log.NewInfo(operationID, utils.GetSelfFuncName(), "getTokenAndSave2Redis: ", token)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return "", utils.Wrap(err, "push failed")
|
||||
}
|
||||
respBytes, err := json.Marshal(pushResp)
|
||||
return string(respBytes), utils.Wrap(err, "")
|
||||
}
|
||||
|
||||
func (g *Getui) Auth(operationID string, timeStamp int64) (token string, expireTime int64, err error) {
|
||||
log.NewInfo(operationID, utils.GetSelfFuncName(), config.Config.Push.Getui.AppKey, timeStamp, config.Config.Push.Getui.MasterSecret)
|
||||
h := sha256.New()
|
||||
h.Write([]byte(config.Config.Push.Getui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.Getui.MasterSecret))
|
||||
sum := h.Sum(nil)
|
||||
sign := hex.EncodeToString(sum)
|
||||
log.NewInfo(operationID, utils.GetSelfFuncName(), "sha256 result", sign)
|
||||
reqAuth := AuthReq{
|
||||
Sign: sign,
|
||||
Timestamp: strconv.Itoa(int(timeStamp)),
|
||||
Appkey: config.Config.Push.Getui.AppKey,
|
||||
}
|
||||
respAuth := AuthResp{}
|
||||
err = g.request(AuthURL, reqAuth, "", &respAuth, operationID)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
log.NewInfo(operationID, utils.GetSelfFuncName(), "result: ", respAuth)
|
||||
expire, err := strconv.Atoi(respAuth.ExpireTime)
|
||||
return respAuth.Token, int64(expire), err
|
||||
}
|
||||
|
||||
func (g *Getui) request(url string, content interface{}, token string, returnStruct interface{}, operationID string) error {
|
||||
con, err := json.Marshal(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client := &http.Client{}
|
||||
log.Debug(operationID, utils.GetSelfFuncName(), "json:", string(con), "token:", token)
|
||||
req, err := http.NewRequest("POST", config.Config.Push.Getui.PushUrl+url, bytes.NewBuffer(con))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if token != "" {
|
||||
req.Header.Set("token", token)
|
||||
}
|
||||
req.Header.Set("content-type", "application/json")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
result, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.NewDebug(operationID, "getui", utils.GetSelfFuncName(), "resp, ", string(result))
|
||||
commonResp := GetuiCommonResp{}
|
||||
commonResp.Data = returnStruct
|
||||
if err := json.Unmarshal(result, &commonResp); err != nil {
|
||||
return err
|
||||
}
|
||||
if commonResp.Code == 10001 {
|
||||
return TokenExpireError
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Getui) getTokenAndSave2Redis(operationID string) (token string, err error) {
|
||||
token, expireTime, err := g.Auth(operationID, time.Now().UnixNano()/1e6)
|
||||
if err != nil {
|
||||
return "", utils.Wrap(err, "Auth failed")
|
||||
}
|
||||
log.NewDebug(operationID, "getui", utils.GetSelfFuncName(), token, expireTime, err)
|
||||
err = db.DB.SetGetuiToken(token, 60*60*23)
|
||||
if err != nil {
|
||||
return "", utils.Wrap(err, "Auth failed")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func 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
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"Open_IM/internal/push/jpush/common"
|
||||
"Open_IM/internal/push/jpush/requestBody"
|
||||
"Open_IM/pkg/common/config"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var (
|
||||
JPushClient *JPush
|
||||
)
|
||||
|
||||
func init() {
|
||||
JPushClient = newGetuiClient()
|
||||
}
|
||||
|
||||
type JPush struct{}
|
||||
|
||||
func newGetuiClient() *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) Push(accounts []string, alert, detailContent, operationID string) (string, error) {
|
||||
var pf requestBody.Platform
|
||||
pf.SetAll()
|
||||
var au requestBody.Audience
|
||||
au.SetAlias(accounts)
|
||||
var no requestBody.Notification
|
||||
no.SetAlert(alert)
|
||||
var me requestBody.Message
|
||||
me.SetMsgContent(detailContent)
|
||||
var o requestBody.Options
|
||||
o.SetApnsProduction(config.Config.IOSPush.Production)
|
||||
var po requestBody.PushObj
|
||||
po.SetPlatform(&pf)
|
||||
po.SetAudience(&au)
|
||||
po.SetNotification(&no)
|
||||
po.SetMessage(&me)
|
||||
po.SetOptions(&o)
|
||||
|
||||
con, err := json.Marshal(po)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest("POST", config.Config.Push.Jpns.PushUrl, bytes.NewBuffer(con))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("Authorization", common.GetAuthorization(config.Config.Push.Jpns.AppKey, config.Config.Push.Jpns.MasterSecret))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
result, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
** description("").
|
||||
** copyright('open-im,www.open-im.io').
|
||||
** author("fg,Gordon@open-im.io").
|
||||
** time(2021/3/22 15:33).
|
||||
*/
|
||||
package logic
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/config"
|
||||
"Open_IM/pkg/common/constant"
|
||||
"Open_IM/pkg/common/kafka"
|
||||
"Open_IM/pkg/statistics"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
rpcServer RPCServer
|
||||
pushCh PushConsumerHandler
|
||||
pushTerminal []int32
|
||||
producer *kafka.Producer
|
||||
count uint64
|
||||
)
|
||||
|
||||
func Init(rpcPort int) {
|
||||
|
||||
rpcServer.Init(rpcPort)
|
||||
pushCh.Init()
|
||||
pushTerminal = []int32{constant.IOSPlatformID, constant.AndroidPlatformID}
|
||||
}
|
||||
func init() {
|
||||
producer = kafka.NewKafkaProducer(config.Config.Kafka.Ws2mschat.Addr, config.Config.Kafka.Ws2mschat.Topic)
|
||||
statistics.NewStatistics(&count, config.Config.ModuleName.PushName, fmt.Sprintf("%d second push to msg_gateway count", 300), 300)
|
||||
}
|
||||
|
||||
func Run() {
|
||||
go rpcServer.run()
|
||||
go pushCh.pushConsumerGroup.RegisterHandleAndConsumer(&pushCh)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
** description("").
|
||||
** copyright('Open_IM,www.Open_IM.io').
|
||||
** author("fg,Gordon@tuoyun.net").
|
||||
** time(2021/5/13 10:33).
|
||||
*/
|
||||
package logic
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/config"
|
||||
"Open_IM/pkg/common/constant"
|
||||
kfk "Open_IM/pkg/common/kafka"
|
||||
"Open_IM/pkg/common/log"
|
||||
pbChat "Open_IM/pkg/proto/msg"
|
||||
pbPush "Open_IM/pkg/proto/push"
|
||||
"Open_IM/pkg/utils"
|
||||
"github.com/Shopify/sarama"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
type fcb func(msg []byte)
|
||||
|
||||
type PushConsumerHandler struct {
|
||||
msgHandle map[string]fcb
|
||||
pushConsumerGroup *kfk.MConsumerGroup
|
||||
}
|
||||
|
||||
func (ms *PushConsumerHandler) Init() {
|
||||
ms.msgHandle = make(map[string]fcb)
|
||||
ms.msgHandle[config.Config.Kafka.Ms2pschat.Topic] = ms.handleMs2PsChat
|
||||
ms.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{KafkaVersion: sarama.V0_10_2_0,
|
||||
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false}, []string{config.Config.Kafka.Ms2pschat.Topic}, config.Config.Kafka.Ms2pschat.Addr,
|
||||
config.Config.Kafka.ConsumerGroupID.MsgToPush)
|
||||
}
|
||||
func (ms *PushConsumerHandler) handleMs2PsChat(msg []byte) {
|
||||
log.InfoByKv("msg come from kafka And push!!!", "", "msg", string(msg))
|
||||
msgFromMQ := pbChat.PushMsgDataToMQ{}
|
||||
if err := proto.Unmarshal(msg, &msgFromMQ); err != nil {
|
||||
log.ErrorByKv("push Unmarshal msg err", "", "msg", string(msg), "err", err.Error())
|
||||
return
|
||||
}
|
||||
pbData := &pbPush.PushMsgReq{
|
||||
OperationID: msgFromMQ.OperationID,
|
||||
MsgData: msgFromMQ.MsgData,
|
||||
PushToUserID: msgFromMQ.PushToUserID,
|
||||
}
|
||||
sec := msgFromMQ.MsgData.SendTime / 1000
|
||||
nowSec := utils.GetCurrentTimestampBySecond()
|
||||
if nowSec-sec > 10 {
|
||||
return
|
||||
}
|
||||
switch msgFromMQ.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
MsgToSuperGroupUser(pbData)
|
||||
default:
|
||||
MsgToUser(pbData)
|
||||
}
|
||||
//Call push module to send message to the user
|
||||
//MsgToUser((*pbPush.PushMsgReq)(&msgFromMQ))
|
||||
}
|
||||
func (PushConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (PushConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (ms *PushConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
||||
claim sarama.ConsumerGroupClaim) error {
|
||||
for msg := range claim.Messages() {
|
||||
log.InfoByKv("kafka get info to mysql", "", "msgTopic", msg.Topic, "msgPartition", msg.Partition, "msg", string(msg.Value))
|
||||
ms.msgHandle[msg.Topic](msg.Value)
|
||||
sess.MarkMessage(msg, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/config"
|
||||
"Open_IM/pkg/common/constant"
|
||||
"Open_IM/pkg/common/log"
|
||||
"Open_IM/pkg/grpc-etcdv3/getcdv3"
|
||||
"Open_IM/pkg/proto/push"
|
||||
"Open_IM/pkg/utils"
|
||||
"context"
|
||||
"google.golang.org/grpc"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RPCServer struct {
|
||||
rpcPort int
|
||||
rpcRegisterName string
|
||||
etcdSchema string
|
||||
etcdAddr []string
|
||||
}
|
||||
|
||||
func (r *RPCServer) Init(rpcPort int) {
|
||||
r.rpcPort = rpcPort
|
||||
r.rpcRegisterName = config.Config.RpcRegisterName.OpenImPushName
|
||||
r.etcdSchema = config.Config.Etcd.EtcdSchema
|
||||
r.etcdAddr = config.Config.Etcd.EtcdAddr
|
||||
}
|
||||
func (r *RPCServer) run() {
|
||||
ip := utils.ServerIP
|
||||
registerAddress := ip + ":" + utils.IntToString(r.rpcPort)
|
||||
listener, err := net.Listen("tcp", registerAddress)
|
||||
if err != nil {
|
||||
log.ErrorByKv("push module rpc listening port err", "", "err", err.Error())
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
srv := grpc.NewServer()
|
||||
defer srv.GracefulStop()
|
||||
pbPush.RegisterPushMsgServiceServer(srv, r)
|
||||
err = getcdv3.RegisterEtcd(r.etcdSchema, strings.Join(r.etcdAddr, ","), ip, r.rpcPort, r.rpcRegisterName, 10)
|
||||
if err != nil {
|
||||
log.ErrorByKv("register push module rpc to etcd err", "", "err", err.Error())
|
||||
}
|
||||
err = srv.Serve(listener)
|
||||
if err != nil {
|
||||
log.ErrorByKv("push module rpc start err", "", "err", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
func (r *RPCServer) PushMsg(_ context.Context, pbData *pbPush.PushMsgReq) (*pbPush.PushMsgResp, error) {
|
||||
//Call push module to send message to the user
|
||||
switch pbData.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
MsgToSuperGroupUser(pbData)
|
||||
default:
|
||||
MsgToUser(pbData)
|
||||
}
|
||||
return &pbPush.PushMsgResp{
|
||||
ResultCode: 0,
|
||||
}, nil
|
||||
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
/*
|
||||
** description("").
|
||||
** copyright('open-im,www.open-im.io').
|
||||
** author("fg,Gordon@open-im.io").
|
||||
** time(2021/3/5 14:31).
|
||||
*/
|
||||
package logic
|
||||
|
||||
import (
|
||||
pusher "Open_IM/internal/push"
|
||||
"Open_IM/internal/push/getui"
|
||||
jpush "Open_IM/internal/push/jpush"
|
||||
"Open_IM/pkg/common/config"
|
||||
"Open_IM/pkg/common/constant"
|
||||
"Open_IM/pkg/common/log"
|
||||
"Open_IM/pkg/grpc-etcdv3/getcdv3"
|
||||
pbCache "Open_IM/pkg/proto/cache"
|
||||
pbPush "Open_IM/pkg/proto/push"
|
||||
pbRelay "Open_IM/pkg/proto/relay"
|
||||
"Open_IM/pkg/utils"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"google.golang.org/grpc"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type OpenIMContent struct {
|
||||
SessionType int `json:"sessionType"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Seq uint32 `json:"seq"`
|
||||
}
|
||||
type AtContent struct {
|
||||
Text string `json:"text"`
|
||||
AtUserList []string `json:"atUserList"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
|
||||
var grpcCons []*grpc.ClientConn
|
||||
|
||||
func MsgToUser(pushMsg *pbPush.PushMsgReq) {
|
||||
var wsResult []*pbRelay.SingleMsgToUserPlatform
|
||||
isOfflinePush := utils.GetSwitchFromOptions(pushMsg.MsgData.Options, constant.IsOfflinePush)
|
||||
log.Debug(pushMsg.OperationID, "Get msg from msg_transfer And push msg", pushMsg.String())
|
||||
if len(grpcCons) == 0 {
|
||||
log.NewWarn(pushMsg.OperationID, "first GetConn4Unique ")
|
||||
grpcCons = getcdv3.GetConn4Unique(config.Config.Etcd.EtcdSchema, strings.Join(config.Config.Etcd.EtcdAddr, ","), config.Config.RpcRegisterName.OpenImOnlineMessageRelayName)
|
||||
}
|
||||
//Online push message
|
||||
log.Debug(pushMsg.OperationID, "len grpc", len(grpcCons), "data", pushMsg.String())
|
||||
for _, v := range grpcCons {
|
||||
msgClient := pbRelay.NewOnlineMessageRelayServiceClient(v)
|
||||
reply, err := msgClient.OnlinePushMsg(context.Background(), &pbRelay.OnlinePushMsgReq{OperationID: pushMsg.OperationID, MsgData: pushMsg.MsgData, PushToUserID: pushMsg.PushToUserID})
|
||||
if err != nil {
|
||||
log.NewError("SuperGroupOnlineBatchPushOneMsg push data to client rpc err", pushMsg.OperationID, "err", err)
|
||||
continue
|
||||
}
|
||||
if reply != nil && reply.Resp != nil {
|
||||
wsResult = append(wsResult, reply.Resp...)
|
||||
}
|
||||
}
|
||||
log.NewInfo(pushMsg.OperationID, "push_result", wsResult, "sendData", pushMsg.MsgData)
|
||||
count++
|
||||
if isOfflinePush && pushMsg.PushToUserID != pushMsg.MsgData.SendID {
|
||||
for _, v := range wsResult {
|
||||
if v.ResultCode == 0 {
|
||||
if utils.IsContainInt32(v.RecvPlatFormID, pushTerminal) {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
if utils.IsContainInt32(v.RecvPlatFormID, pushTerminal) {
|
||||
//Use offline push messaging
|
||||
var UIDList []string
|
||||
UIDList = append(UIDList, v.RecvID)
|
||||
customContent := OpenIMContent{
|
||||
SessionType: int(pushMsg.MsgData.SessionType),
|
||||
From: pushMsg.MsgData.SendID,
|
||||
To: pushMsg.MsgData.RecvID,
|
||||
Seq: pushMsg.MsgData.Seq,
|
||||
}
|
||||
bCustomContent, _ := json.Marshal(customContent)
|
||||
jsonCustomContent := string(bCustomContent)
|
||||
var content string
|
||||
if pushMsg.MsgData.OfflinePushInfo != nil {
|
||||
content = pushMsg.MsgData.OfflinePushInfo.Title
|
||||
log.NewDebug(pushMsg.OperationID, utils.GetSelfFuncName(), "xxxx OfflinePushInfo", content)
|
||||
} else {
|
||||
log.NewDebug(pushMsg.OperationID, utils.GetSelfFuncName(), "xxxx2222 OfflinePushInfo", content)
|
||||
switch pushMsg.MsgData.ContentType {
|
||||
case constant.Text:
|
||||
content = constant.ContentType2PushContent[constant.Text]
|
||||
case constant.Picture:
|
||||
content = constant.ContentType2PushContent[constant.Picture]
|
||||
case constant.Voice:
|
||||
content = constant.ContentType2PushContent[constant.Voice]
|
||||
case constant.Video:
|
||||
content = constant.ContentType2PushContent[constant.Video]
|
||||
case constant.File:
|
||||
content = constant.ContentType2PushContent[constant.File]
|
||||
case constant.AtText:
|
||||
a := AtContent{}
|
||||
_ = utils.JsonStringToStruct(string(pushMsg.MsgData.Content), &a)
|
||||
if utils.IsContain(v.RecvID, a.AtUserList) {
|
||||
content = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common]
|
||||
} else {
|
||||
content = constant.ContentType2PushContent[constant.GroupMsg]
|
||||
}
|
||||
default:
|
||||
content = constant.ContentType2PushContent[constant.Common]
|
||||
}
|
||||
}
|
||||
var offlinePusher pusher.OfflinePusher
|
||||
if config.Config.Push.Getui.Enable {
|
||||
log.NewInfo(pushMsg.OperationID, utils.GetSelfFuncName(), config.Config.Push.Getui)
|
||||
offlinePusher = getui.GetuiClient
|
||||
}
|
||||
if config.Config.Push.Jpns.Enable {
|
||||
offlinePusher = jpush.JPushClient
|
||||
}
|
||||
if offlinePusher == nil {
|
||||
offlinePusher = jpush.JPushClient
|
||||
}
|
||||
pushResult, err := offlinePusher.Push(UIDList, content, jsonCustomContent, pushMsg.OperationID)
|
||||
if err != nil {
|
||||
log.NewError(pushMsg.OperationID, "offline push error", pushMsg.String(), err.Error())
|
||||
} else {
|
||||
log.NewDebug(pushMsg.OperationID, "offline push return result is ", pushResult, pushMsg.MsgData)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func MsgToSuperGroupUser(pushMsg *pbPush.PushMsgReq) {
|
||||
return
|
||||
//var wsResult []*pbRelay.SingelMsgToUserResultList
|
||||
//isOfflinePush := utils.GetSwitchFromOptions(pushMsg.MsgData.Options, constant.IsOfflinePush)
|
||||
//log.Debug(pushMsg.OperationID, "Get msg from msg_transfer And push msg", pushMsg.String())
|
||||
//if len(grpcCons) == 0 {
|
||||
// log.NewWarn(pushMsg.OperationID, "first GetConn4Unique ")
|
||||
// grpcCons = getcdv3.GetConn4Unique(config.Config.Etcd.EtcdSchema, strings.Join(config.Config.Etcd.EtcdAddr, ","), config.Config.RpcRegisterName.OpenImOnlineMessageRelayName)
|
||||
//}
|
||||
////Online push message
|
||||
//log.Debug("test", pushMsg.OperationID, "len grpc", len(grpcCons), "data", pushMsg.String())
|
||||
//for _, v := range grpcCons {
|
||||
// msgClient := pbRelay.NewOnlineMessageRelayServiceClient(v)
|
||||
// reply, err := msgClient.OnlineBatchPushOneMsg(context.Background(), &pbRelay.OnlineBatchPushOneMsgReq{OperationID: pushMsg.OperationID, MsgData: pushMsg.MsgData, PushToUserIDList: pushMsg.PushToUserID})
|
||||
// if err != nil {
|
||||
// log.NewError("push data to client rpc err", pushMsg.OperationID, "err", err)
|
||||
// continue
|
||||
// }
|
||||
// if reply != nil && reply.SinglePushResult != nil {
|
||||
// wsResult = append(wsResult, reply.SinglePushResult...)
|
||||
// }
|
||||
//}
|
||||
//log.NewInfo(pushMsg.OperationID, "push_result", wsResult, "sendData", pushMsg.MsgData)
|
||||
//successCount++
|
||||
//if isOfflinePush && pushMsg.PushToUserID != pushMsg.MsgData.SendID {
|
||||
// for _, v := range wsResult {
|
||||
// if v.ResultCode == 0 {
|
||||
// if utils.IsContainInt32(v.RecvPlatFormID, pushTerminal) {
|
||||
// break
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// if utils.IsContainInt32(v.RecvPlatFormID, pushTerminal) {
|
||||
// //Use offline push messaging
|
||||
// var UIDList []string
|
||||
// UIDList = append(UIDList, v.RecvID)
|
||||
// customContent := OpenIMContent{
|
||||
// SessionType: int(pushMsg.MsgData.SessionType),
|
||||
// From: pushMsg.MsgData.SendID,
|
||||
// To: pushMsg.MsgData.RecvID,
|
||||
// Seq: pushMsg.MsgData.Seq,
|
||||
// }
|
||||
// bCustomContent, _ := json.Marshal(customContent)
|
||||
// jsonCustomContent := string(bCustomContent)
|
||||
// var content string
|
||||
// if pushMsg.MsgData.OfflinePushInfo != nil {
|
||||
// content = pushMsg.MsgData.OfflinePushInfo.Title
|
||||
//
|
||||
// } else {
|
||||
// switch pushMsg.MsgData.ContentType {
|
||||
// case constant.Text:
|
||||
// content = constant.ContentType2PushContent[constant.Text]
|
||||
// case constant.Picture:
|
||||
// content = constant.ContentType2PushContent[constant.Picture]
|
||||
// case constant.Voice:
|
||||
// content = constant.ContentType2PushContent[constant.Voice]
|
||||
// case constant.Video:
|
||||
// content = constant.ContentType2PushContent[constant.Video]
|
||||
// case constant.File:
|
||||
// content = constant.ContentType2PushContent[constant.File]
|
||||
// case constant.AtText:
|
||||
// a := AtContent{}
|
||||
// _ = utils.JsonStringToStruct(string(pushMsg.MsgData.Content), &a)
|
||||
// if utils.IsContain(v.RecvID, a.AtUserList) {
|
||||
// content = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common]
|
||||
// } else {
|
||||
// content = constant.ContentType2PushContent[constant.GroupMsg]
|
||||
// }
|
||||
// default:
|
||||
// content = constant.ContentType2PushContent[constant.Common]
|
||||
// }
|
||||
// }
|
||||
// callbackResp := callbackOfflinePush(pushMsg.OperationID, UIDList[0], pushMsg.MsgData.OfflinePushInfo, v.RecvPlatFormID)
|
||||
// log.NewDebug(pushMsg.OperationID, utils.GetSelfFuncName(), "offline callback Resp")
|
||||
// if callbackResp.ErrCode != 0 {
|
||||
// log.NewError(pushMsg.OperationID, utils.GetSelfFuncName(), "callbackOfflinePush result: ", callbackResp)
|
||||
// }
|
||||
// if callbackResp.ActionCode != constant.ActionAllow {
|
||||
// log.NewDebug(pushMsg.OperationID, utils.GetSelfFuncName(), "offlinePush stop")
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// if offlinePusher == nil {
|
||||
// offlinePusher = jpush.JPushClient
|
||||
// }
|
||||
// pushResult, err := offlinePusher.Push(UIDList, content, jsonCustomContent, pushMsg.OperationID)
|
||||
// if err != nil {
|
||||
// log.NewError(pushMsg.OperationID, "offline push error", pushMsg.String(), err.Error())
|
||||
// } else {
|
||||
// log.NewDebug(pushMsg.OperationID, "offline push return result is ", pushResult, pushMsg.MsgData)
|
||||
// }
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
//}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
tpns "Open_IM/internal/push/sdk/tpns-server-sdk-go/go"
|
||||
"Open_IM/internal/push/sdk/tpns-server-sdk-go/go/auth"
|
||||
"Open_IM/internal/push/sdk/tpns-server-sdk-go/go/common"
|
||||
"Open_IM/internal/push/sdk/tpns-server-sdk-go/go/req"
|
||||
"Open_IM/pkg/common/config"
|
||||
)
|
||||
|
||||
var badgeType = -2
|
||||
var iosAcceptId = auth.Auther{AccessID: config.Config.Push.Tpns.Ios.AccessID, SecretKey: config.Config.Push.Tpns.Ios.SecretKey}
|
||||
|
||||
func IOSAccountListPush(accounts []string, title, content, jsonCustomContent string) {
|
||||
var iosMessage = tpns.Message{
|
||||
Title: title,
|
||||
Content: content,
|
||||
IOS: &tpns.IOSParams{
|
||||
Aps: &tpns.Aps{
|
||||
BadgeType: &badgeType,
|
||||
Sound: "default",
|
||||
Category: "INVITE_CATEGORY",
|
||||
},
|
||||
CustomContent: jsonCustomContent,
|
||||
//CustomContent: `"{"key\":\"value\"}"`,
|
||||
},
|
||||
}
|
||||
pushReq, reqBody, err := req.NewListAccountPush(accounts, iosMessage)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iosAcceptId.Auth(pushReq, auth.UseSignAuthored, iosAcceptId, reqBody)
|
||||
common.PushAndGetResult(pushReq)
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package fcm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
|
||||
firebase "firebase.google.com/go"
|
||||
"firebase.google.com/go/messaging"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"google.golang.org/api/option"
|
||||
)
|
||||
|
||||
const SinglePushCountLimit = 400
|
||||
|
||||
var Terminal = []int{constant.IOSPlatformID, constant.AndroidPlatformID, constant.WebPlatformID}
|
||||
|
||||
type Fcm struct {
|
||||
fcmMsgCli *messaging.Client
|
||||
cache cache.MsgModel
|
||||
}
|
||||
|
||||
func NewClient(cache cache.MsgModel) *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, cache: cache}
|
||||
}
|
||||
|
||||
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 (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Push(t *testing.T) {
|
||||
var redis cache.MsgModel
|
||||
offlinePusher := NewClient(redis)
|
||||
err := offlinePusher.Push(context.Background(), []string{"userID1"}, "test", "test", &offlinepush.Opts{})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package getui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
)
|
||||
|
||||
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 = ErrTokenExpire
|
||||
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,182 @@
|
||||
package getui
|
||||
|
||||
import (
|
||||
"github.com/go-redis/redis"
|
||||
"sync"
|
||||
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
http2 "github.com/OpenIMSDK/Open-IM-Server/pkg/common/http"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/errs"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils/splitter"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrTokenExpire = errors.New("token expire")
|
||||
ErrUserIDEmpty = 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.MsgModel
|
||||
tokenExpireTime int64
|
||||
taskIDTTL int64
|
||||
}
|
||||
|
||||
func NewClient(cache cache.MsgModel) *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 errs.Unwrap(err) == redis.Nil {
|
||||
log.ZInfo(ctx, "getui token not exist in redis")
|
||||
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.ZError(ctx, "batchPush failed", err2, "index", index, "token", token, "req", 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 ErrUserIDEmpty
|
||||
}
|
||||
switch err {
|
||||
case ErrTokenExpire:
|
||||
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 := mcontext.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(ctx, config.Config.Push.GeTui.PushUrl+url, header, input, resp, 3)
|
||||
}
|
||||
|
||||
func (g *Client) postReturn(ctx context.Context, url string, header map[string]string, input interface{}, output RespI, timeout int) error {
|
||||
err := http2.PostReturn(ctx, url, header, input, output, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return output.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
|
||||
}
|
||||
+9
-12
@@ -1,13 +1,11 @@
|
||||
package requestBody
|
||||
package body
|
||||
|
||||
const (
|
||||
TAG = "tag"
|
||||
TAG_AND = "tag_and"
|
||||
TAG_NOT = "tag_not"
|
||||
ALIAS = "alias"
|
||||
REGISTRATION_ID = "registration_id"
|
||||
SEGMENT = "segment"
|
||||
ABTEST = "abtest"
|
||||
TAG = "tag"
|
||||
TAGAND = "tag_and"
|
||||
TAGNOT = "tag_not"
|
||||
ALIAS = "alias"
|
||||
REGISTRATIONID = "registration_id"
|
||||
)
|
||||
|
||||
type Audience struct {
|
||||
@@ -20,7 +18,6 @@ func (a *Audience) set(key string, v []string) {
|
||||
a.audience = make(map[string][]string)
|
||||
a.Object = a.audience
|
||||
}
|
||||
|
||||
//v, ok = this.audience[key]
|
||||
//if ok {
|
||||
// return
|
||||
@@ -33,11 +30,11 @@ func (a *Audience) SetTag(tags []string) {
|
||||
}
|
||||
|
||||
func (a *Audience) SetTagAnd(tags []string) {
|
||||
a.set(TAG_AND, tags)
|
||||
a.set(TAGAND, tags)
|
||||
}
|
||||
|
||||
func (a *Audience) SetTagNot(tags []string) {
|
||||
a.set(TAG_NOT, tags)
|
||||
a.set(TAGNOT, tags)
|
||||
}
|
||||
|
||||
func (a *Audience) SetAlias(alias []string) {
|
||||
@@ -45,7 +42,7 @@ func (a *Audience) SetAlias(alias []string) {
|
||||
}
|
||||
|
||||
func (a *Audience) SetRegistrationId(ids []string) {
|
||||
a.set(REGISTRATION_ID, ids)
|
||||
a.set(REGISTRATIONID, ids)
|
||||
}
|
||||
|
||||
func (a *Audience) SetAll() {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package requestBody
|
||||
package body
|
||||
|
||||
type Message struct {
|
||||
MsgContent string `json:"msg_content"`
|
||||
+22
-6
@@ -1,7 +1,7 @@
|
||||
package requestBody
|
||||
package body
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
@@ -15,11 +15,18 @@ type Android struct {
|
||||
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"`
|
||||
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) {
|
||||
@@ -29,8 +36,17 @@ func (n *Notification) SetAlert(alert string) {
|
||||
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
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package requestBody
|
||||
package body
|
||||
|
||||
type Options struct {
|
||||
ApnsProduction bool `json:"apns_production"`
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package requestBody
|
||||
package body
|
||||
|
||||
import (
|
||||
"Open_IM/pkg/common/constant"
|
||||
"errors"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
)
|
||||
|
||||
const (
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package requestBody
|
||||
package body
|
||||
|
||||
type PushObj struct {
|
||||
Platform interface{} `json:"platform"`
|
||||
@@ -0,0 +1,63 @@
|
||||
package jpush
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush/jpush/body"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
http2 "github.com/OpenIMSDK/Open-IM-Server/pkg/common/http"
|
||||
)
|
||||
|
||||
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(ctx, pushObj, resp, 5)
|
||||
}
|
||||
|
||||
func (j *JPush) request(ctx context.Context, po body.PushObj, resp interface{}, timeout int) error {
|
||||
return http2.PostReturn(ctx, 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
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
kfk "github.com/OpenIMSDK/Open-IM-Server/pkg/common/kafka"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
pbChat "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msg"
|
||||
pbPush "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/push"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
"github.com/Shopify/sarama"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type ConsumerHandler struct {
|
||||
pushConsumerGroup *kfk.MConsumerGroup
|
||||
pusher *Pusher
|
||||
}
|
||||
|
||||
func NewConsumerHandler(pusher *Pusher) *ConsumerHandler {
|
||||
var consumerHandler ConsumerHandler
|
||||
consumerHandler.pusher = pusher
|
||||
consumerHandler.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{KafkaVersion: sarama.V2_0_0_0,
|
||||
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false}, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr,
|
||||
config.Config.Kafka.ConsumerGroupID.MsgToPush)
|
||||
return &consumerHandler
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
|
||||
msgFromMQ := pbChat.PushMsgDataToMQ{}
|
||||
if err := proto.Unmarshal(msg, &msgFromMQ); err != nil {
|
||||
log.ZError(ctx, "push Unmarshal msg err", err, "msg", string(msg))
|
||||
return
|
||||
}
|
||||
pbData := &pbPush.PushMsgReq{
|
||||
MsgData: msgFromMQ.MsgData,
|
||||
ConversationID: msgFromMQ.ConversationID,
|
||||
}
|
||||
sec := msgFromMQ.MsgData.SendTime / 1000
|
||||
nowSec := utils.GetCurrentTimestampBySecond()
|
||||
if nowSec-sec > 10 {
|
||||
return
|
||||
}
|
||||
var err error
|
||||
switch msgFromMQ.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
default:
|
||||
err = c.pusher.Push2User(ctx, []string{pbData.MsgData.SendID, pbData.MsgData.RecvID}, pbData.MsgData)
|
||||
}
|
||||
if err != nil {
|
||||
if err == errNoOfflinePusher {
|
||||
log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
|
||||
} else {
|
||||
log.ZError(ctx, "push failed", err, "msg", pbData.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
func (ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
||||
claim sarama.ConsumerGroupClaim) error {
|
||||
for msg := range claim.Messages() {
|
||||
ctx := c.pushConsumerGroup.GetContextFromMsg(msg)
|
||||
c.handleMs2PsChat(ctx, msg.Value)
|
||||
sess.MarkMessage(msg, "")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package push
|
||||
|
||||
type OfflinePusher interface {
|
||||
Push(userIDList []string, alert, detailContent, operationID string) (resp string, err error)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/localcache"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry"
|
||||
pbPush "github.com/OpenIMSDK/Open-IM-Server/pkg/proto/push"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type pushServer struct {
|
||||
pusher *Pusher
|
||||
}
|
||||
|
||||
func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheModel := cache.NewMsgCacheModel(rdb)
|
||||
offlinePusher := NewOfflinePusher(cacheModel)
|
||||
database := controller.NewPushDatabase(cacheModel)
|
||||
pusher := NewPusher(client, offlinePusher, database, localcache.NewGroupLocalCache(client), localcache.NewConversationLocalCache(client))
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
pbPush.RegisterPushMsgServiceServer(server, &pushServer{
|
||||
pusher: pusher,
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
consumer := NewConsumer(pusher)
|
||||
consumer.initPrometheus()
|
||||
consumer.Start()
|
||||
}()
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pushServer) PushMsg(ctx context.Context, pbData *pbPush.PushMsgReq) (resp *pbPush.PushMsgResp, err error) {
|
||||
switch pbData.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
err = r.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
default:
|
||||
err = r.pusher.Push2User(ctx, []string{pbData.MsgData.RecvID, pbData.MsgData.SendID}, pbData.MsgData)
|
||||
}
|
||||
if err != nil {
|
||||
if err != errNoOfflinePusher {
|
||||
return nil, err
|
||||
} else {
|
||||
log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
|
||||
}
|
||||
}
|
||||
return &pbPush.PushMsgResp{}, nil
|
||||
}
|
||||
|
||||
func (r *pushServer) DelUserPushToken(ctx context.Context, req *pbPush.DelUserPushTokenReq) (resp *pbPush.DelUserPushTokenResp, err error) {
|
||||
if err = r.pusher.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbPush.DelUserPushTokenResp{}, nil
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush/fcm"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush/getui"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/internal/push/offlinepush/jpush"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/constant"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/cache"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/localcache"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/log"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/mcontext"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/prome"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/discoveryregistry"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/msggateway"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/proto/sdkws"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/utils"
|
||||
)
|
||||
|
||||
type Pusher struct {
|
||||
database controller.PushDatabase
|
||||
discov discoveryregistry.SvcDiscoveryRegistry
|
||||
offlinePusher offlinepush.OfflinePusher
|
||||
groupLocalCache *localcache.GroupLocalCache
|
||||
conversationLocalCache *localcache.ConversationLocalCache
|
||||
msgClient *rpcclient.MessageRpcClient
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient
|
||||
groupRpcClient *rpcclient.GroupRpcClient
|
||||
successCount int
|
||||
}
|
||||
|
||||
var errNoOfflinePusher = errors.New("no offlinePusher is configured")
|
||||
|
||||
func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase,
|
||||
groupLocalCache *localcache.GroupLocalCache, conversationLocalCache *localcache.ConversationLocalCache) *Pusher {
|
||||
msgClient := rpcclient.NewMessageRpcClient(discov)
|
||||
conversationRpcClient := rpcclient.NewConversationRpcClient(discov)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(discov)
|
||||
return &Pusher{
|
||||
discov: discov,
|
||||
database: database,
|
||||
offlinePusher: offlinePusher,
|
||||
groupLocalCache: groupLocalCache,
|
||||
conversationLocalCache: conversationLocalCache,
|
||||
msgClient: &msgClient,
|
||||
conversationRpcClient: &conversationRpcClient,
|
||||
groupRpcClient: &groupRpcClient,
|
||||
}
|
||||
}
|
||||
|
||||
func NewOfflinePusher(cache cache.MsgModel) offlinepush.OfflinePusher {
|
||||
var offlinePusher offlinepush.OfflinePusher
|
||||
switch config.Config.Push.Enable {
|
||||
case "getui":
|
||||
offlinePusher = getui.NewClient(cache)
|
||||
case "fcm":
|
||||
offlinePusher = fcm.NewClient(cache)
|
||||
case "jpush":
|
||||
offlinePusher = jpush.NewClient()
|
||||
}
|
||||
return offlinePusher
|
||||
}
|
||||
|
||||
func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
|
||||
conevrsationID := utils.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID)
|
||||
maxSeq, err := p.msgClient.GetConversationMaxSeq(ctx, conevrsationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq)
|
||||
}
|
||||
|
||||
func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
|
||||
log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String())
|
||||
// callback
|
||||
if err := callbackOnlinePush(ctx, userIDs, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
// push
|
||||
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs)
|
||||
p.successCount++
|
||||
for _, userID := range userIDs {
|
||||
if isOfflinePush && userID != msg.SendID {
|
||||
// save invitation info for offline push
|
||||
for _, v := range wsResults {
|
||||
if v.OnlinePush {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := callbackOfflinePush(ctx, userIDs, msg, &[]string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.offlinePushMsg(ctx, userID, msg, userIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t interface{}) error {
|
||||
var notificationElem struct {
|
||||
Detail string `json:"detail,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal(bytes, ¬ificationElem); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal([]byte(notificationElem.Detail), t)
|
||||
}
|
||||
|
||||
func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
|
||||
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
|
||||
var pushToUserIDs []string
|
||||
if err := callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(pushToUserIDs) == 0 {
|
||||
pushToUserIDs, err = p.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch msg.ContentType {
|
||||
case constant.MemberQuitNotification:
|
||||
var tips sdkws.MemberQuitTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, []string{tips.QuitUser.UserID})
|
||||
pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID)
|
||||
case constant.MemberKickedNotification:
|
||||
var tips sdkws.MemberKickedTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err := p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, kickedUsers)
|
||||
pushToUserIDs = append(pushToUserIDs, kickedUsers...)
|
||||
case constant.GroupDismissedNotification:
|
||||
if utils.IsNotification(utils.GetConversationIDByMsg(msg)) { // 消息先到,通知后到
|
||||
var tips sdkws.GroupDismissedTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs)
|
||||
if len(config.Config.Manager.UserID) > 0 {
|
||||
ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0])
|
||||
}
|
||||
defer func(groupID string) {
|
||||
if err := p.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
|
||||
log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
|
||||
}
|
||||
}(groupID)
|
||||
}
|
||||
}
|
||||
}
|
||||
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg)
|
||||
p.successCount++
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
if isOfflinePush {
|
||||
var onlineSuccessUserIDs []string
|
||||
var WebAndPcBackgroundUserIDs []string
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, msg.SendID)
|
||||
for _, v := range wsResults {
|
||||
if v.OnlinePush && v.UserID != msg.SendID {
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
|
||||
}
|
||||
if !v.OnlinePush {
|
||||
if len(v.Resp) != 0 {
|
||||
for _, singleResult := range v.Resp {
|
||||
if singleResult.ResultCode == -2 {
|
||||
if constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC ||
|
||||
singleResult.RecvPlatFormID == constant.WebPlatformID {
|
||||
WebAndPcBackgroundUserIDs = append(WebAndPcBackgroundUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs)
|
||||
if msg.ContentType != constant.SignalingNotification {
|
||||
notNotificationUserIDs, err := p.conversationLocalCache.GetRecvMsgNotNotifyUserIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
// log.ZError(ctx, "GetRecvMsgNotNotifyUserIDs failed", err, "groupID", groupID)
|
||||
return err
|
||||
}
|
||||
needOfflinePushUserIDs = utils.DifferenceString(notNotificationUserIDs, needOfflinePushUserIDs)
|
||||
}
|
||||
//Use offline push messaging
|
||||
if len(needOfflinePushUserIDs) > 0 {
|
||||
var offlinePushUserIDs []string
|
||||
err = callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
err = p.offlinePushMsg(ctx, groupID, msg, offlinePushUserIDs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
_, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs))
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, WebAndPcBackgroundUserIDs))
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//Online push message
|
||||
for _, v := range conns {
|
||||
msgClient := msggateway.NewMsgGatewayClient(v)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs})
|
||||
p.discov.CloseConn(v)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
}
|
||||
|
||||
}
|
||||
return wsResults, nil
|
||||
}
|
||||
|
||||
func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error {
|
||||
title, content, opts, err := p.getOfflinePushInfos(conversationID, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
|
||||
if err != nil {
|
||||
prome.Inc(prome.MsgOfflinePushFailedCounter)
|
||||
return err
|
||||
}
|
||||
prome.Inc(prome.MsgOfflinePushSuccessCounter)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, err error) {
|
||||
opts = &offlinepush.Opts{}
|
||||
// if msg.ContentType > constant.SignalingNotificationBegin && msg.ContentType < constant.SignalingNotificationEnd {
|
||||
// req := &sdkws.SignalReq{}
|
||||
// if err := proto.Unmarshal(msg.Content, req); err != nil {
|
||||
// return nil, utils.Wrap(err, "")
|
||||
// }
|
||||
// switch req.Payload.(type) {
|
||||
// case *sdkws.SignalReq_Invite, *sdkws.SignalReq_InviteInGroup:
|
||||
// opts.Signal = &offlinepush.Signal{ClientMsgID: msg.ClientMsgID}
|
||||
// }
|
||||
// }
|
||||
if msg.OfflinePushInfo != nil {
|
||||
opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount
|
||||
opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound
|
||||
opts.Ex = msg.OfflinePushInfo.Ex
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) {
|
||||
if p.offlinePusher == nil {
|
||||
err = errNoOfflinePusher
|
||||
return
|
||||
}
|
||||
type AtContent struct {
|
||||
Text string `json:"text"`
|
||||
AtUserList []string `json:"atUserList"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
opts, err = p.GetOfflinePushOpts(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if msg.OfflinePushInfo != nil {
|
||||
title = msg.OfflinePushInfo.Title
|
||||
content = msg.OfflinePushInfo.Desc
|
||||
}
|
||||
if title == "" {
|
||||
switch msg.ContentType {
|
||||
case constant.Text:
|
||||
fallthrough
|
||||
case constant.Picture:
|
||||
fallthrough
|
||||
case constant.Voice:
|
||||
fallthrough
|
||||
case constant.Video:
|
||||
fallthrough
|
||||
case constant.File:
|
||||
title = constant.ContentType2PushContent[int64(msg.ContentType)]
|
||||
case constant.AtText:
|
||||
a := AtContent{}
|
||||
_ = utils.JsonStringToStruct(string(msg.Content), &a)
|
||||
if utils.IsContain(conversationID, a.AtUserList) {
|
||||
title = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common]
|
||||
} else {
|
||||
title = constant.ContentType2PushContent[constant.GroupMsg]
|
||||
}
|
||||
case constant.SignalingNotification:
|
||||
title = constant.ContentType2PushContent[constant.SignalMsg]
|
||||
default:
|
||||
title = constant.ContentType2PushContent[constant.Common]
|
||||
}
|
||||
}
|
||||
if content == "" {
|
||||
content = title
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
b64 "encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Auther struct {
|
||||
AccessID string
|
||||
SecretKey string
|
||||
}
|
||||
|
||||
var UseSignAuthored = true
|
||||
|
||||
func (a *Auther) Auth(req *http.Request, useSignAuthored bool, auth Auther, reqBody string) {
|
||||
|
||||
if useSignAuthored {
|
||||
now := time.Now()
|
||||
timeStamp := now.Unix()
|
||||
req.Header.Add("AccessId", auth.AccessID)
|
||||
req.Header.Add("TimeStamp", strconv.Itoa(int(timeStamp)))
|
||||
sign := GenSign(uint64(timeStamp), auth.AccessID, auth.SecretKey, reqBody)
|
||||
req.Header.Add("Sign", sign)
|
||||
} else {
|
||||
author := makeAuthHeader(a.AccessID, a.SecretKey)
|
||||
//log.Printf("author string:%v", author)
|
||||
req.Header.Add("Authorization", author)
|
||||
}
|
||||
//req.Header.Add("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
func makeAuthHeader(appID, secretKey string) string {
|
||||
base64Str := base64.StdEncoding.EncodeToString(
|
||||
[]byte(
|
||||
fmt.Sprintf("%s:%s", appID, secretKey),
|
||||
),
|
||||
)
|
||||
return fmt.Sprintf("Basic %s", base64Str)
|
||||
}
|
||||
|
||||
func GenSign(timeStamp uint64, accessId string, secretKey, requestBody string) string {
|
||||
signBody := strconv.Itoa(int(timeStamp)) + accessId + requestBody
|
||||
// Create a new HMAC by defining the hash type and the key (as byte array)
|
||||
h := hmac.New(sha256.New, []byte(secretKey))
|
||||
// Write Data to it
|
||||
h.Write([]byte(signBody))
|
||||
|
||||
// Get result and encode as hexadecimal string
|
||||
sha := hex.EncodeToString(h.Sum(nil))
|
||||
//fmt.Println()
|
||||
//fmt.Println("timeStamp: " + strconv.Itoa(int(timeStamp)) + " accessID:" + accessId + " body:" + requestBody)
|
||||
sEnc := b64.StdEncoding.EncodeToString([]byte(sha))
|
||||
//fmt.Println("final Result " + sEnc)
|
||||
return sEnc
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func New() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
MaxIdleConns: 100,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
IdleConnTimeout: 30 * time.Second,
|
||||
DisableCompression: false,
|
||||
DisableKeepAlives: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
tpns "Open_IM/internal/push/sdk/tpns-server-sdk-go/go"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func PushAndGetResult(pushReq *http.Request) {
|
||||
c := &http.Client{}
|
||||
rsp, err := c.Do(pushReq)
|
||||
fmt.Println()
|
||||
if err != nil {
|
||||
//fmt.Printf("http err:%v", err)
|
||||
return
|
||||
}
|
||||
defer rsp.Body.Close()
|
||||
body, err := ioutil.ReadAll(rsp.Body)
|
||||
//fmt.Printf("http ReadAll err:%v, body:%v ", err, string(body))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r := &tpns.CommonRsp{}
|
||||
json.Unmarshal(body, r)
|
||||
//fmt.Printf("push result: %+v", r)
|
||||
}
|
||||
|
||||
func UploadFile(req *http.Request) (int, error) {
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return 0, fmt.Errorf("response error, status: %s, body: %s", resp.Status, string(body))
|
||||
}
|
||||
|
||||
type uploadResponse struct {
|
||||
RetCode int `json:"retCode"`
|
||||
ErrMsg string `json:"errMsg"`
|
||||
UploadId int `json:"uploadId"`
|
||||
}
|
||||
|
||||
var ur uploadResponse
|
||||
if err := json.Unmarshal(body, &ur); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if ur.RetCode != 0 {
|
||||
return 0, fmt.Errorf("response with %d:%s", ur.RetCode, ur.ErrMsg)
|
||||
}
|
||||
return ur.UploadId, nil
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package common
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
func ToJson(v interface{}) string {
|
||||
bs, _ := json.Marshal(v)
|
||||
return string(bs)
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
package tpns
|
||||
|
||||
type CommonRspEnv string
|
||||
|
||||
const (
|
||||
// EnvProd
|
||||
EnvProd CommonRspEnv = "product"
|
||||
// EnvDev
|
||||
EnvDev CommonRspEnv = "dev"
|
||||
)
|
||||
|
||||
type CommonRsp struct {
|
||||
// TODO: doc this
|
||||
Seq int64 `json:"seq"`
|
||||
|
||||
PushID string `json:"push_id"`
|
||||
|
||||
RetCode int `json:"ret_code"`
|
||||
|
||||
Environment CommonRspEnv `json:"environment"`
|
||||
|
||||
ErrMsg string `json:"err_msg,omitempty"`
|
||||
|
||||
Result map[string]string `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
type AudienceType string
|
||||
|
||||
const (
|
||||
AdAll AudienceType = "all"
|
||||
|
||||
AdTag AudienceType = "tag"
|
||||
|
||||
AdToken AudienceType = "token"
|
||||
|
||||
AdTokenList AudienceType = "token_list"
|
||||
|
||||
AdAccount AudienceType = "account"
|
||||
|
||||
AdAccountList AudienceType = "account_list"
|
||||
|
||||
AdPackageAccount AudienceType = "package_account_push"
|
||||
|
||||
AdPackageToken AudienceType = "package_token_push"
|
||||
)
|
||||
|
||||
// MessageType push API message_type
|
||||
type MessageType string
|
||||
|
||||
const (
|
||||
MsgTypeNotify MessageType = "notify"
|
||||
|
||||
MsgTypeMessage MessageType = "message"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
AudienceType AudienceType `json:"audience_type"`
|
||||
|
||||
Message Message `json:"message"`
|
||||
|
||||
MessageType MessageType `json:"message_type"`
|
||||
|
||||
Tag []TagRule `json:"tag_rules,omitempty"`
|
||||
|
||||
TokenList []string `json:"token_list,omitempty"`
|
||||
|
||||
AccountList []string `json:"account_list,omitempty"`
|
||||
|
||||
Environment CommonRspEnv `json:"environment,omitempty"`
|
||||
|
||||
UploadId int `json:"upload_id,omitempty"`
|
||||
|
||||
ExpireTime int `json:"expire_time,omitempty"`
|
||||
|
||||
SendTime string `json:"send_time,omitempty"`
|
||||
|
||||
MultiPkg bool `json:"multi_pkg,omitempty"`
|
||||
|
||||
PlanId string `json:"plan_id,omitempty"`
|
||||
|
||||
AccountPushType int `json:"account_push_type,omitempty"`
|
||||
|
||||
PushSpeed int `json:"push_speed,omitempty"`
|
||||
|
||||
CollapseId int `json:"collapse_id"`
|
||||
|
||||
TPNSOnlinePushType int `json:"tpns_online_push_type"`
|
||||
|
||||
ChannelRules []*ChannelDistributeRule `json:"channel_rules,omitempty"`
|
||||
|
||||
LoopParam *PushLoopParam `json:"loop_param,omitempty"`
|
||||
ForceCollapse bool `json:"force_collapse"`
|
||||
}
|
||||
|
||||
type TagListOperation string
|
||||
|
||||
type ChannelDistributeRule struct {
|
||||
ChannelName string `json:"channel"`
|
||||
Disable bool `json:"disable"`
|
||||
}
|
||||
|
||||
type PushLoopParam struct {
|
||||
StartDate string `json:"startDate"`
|
||||
|
||||
EndDate string `json:"endDate"`
|
||||
|
||||
LoopType PushLoopType `json:"loopType"`
|
||||
|
||||
LoopDayIndexs []uint32 `json:"loopDayIndexs"`
|
||||
|
||||
DayTimes []string `json:"dayTimes"`
|
||||
}
|
||||
|
||||
type PushLoopType int32
|
||||
|
||||
const (
|
||||
TagListOpAnd TagListOperation = "AND"
|
||||
|
||||
TagListOpOr TagListOperation = "OR"
|
||||
)
|
||||
|
||||
type TagType string
|
||||
|
||||
const (
|
||||
XGAutoProvince TagType = "xg_auto_province"
|
||||
XGAutoActive TagType = "xg_auto_active"
|
||||
XGUserDefine TagType = "xg_user_define"
|
||||
XGAutoVersion TagType = "xg_auto_version"
|
||||
XGAutoSdkversion TagType = "xg_auto_sdkversion"
|
||||
XGAutoDevicebrand TagType = "xg_auto_devicebrand"
|
||||
XGAutoDeviceversion TagType = "xg_auto_deviceversion"
|
||||
XGAutoCountry TagType = "xg_auto_country"
|
||||
)
|
||||
|
||||
type TagRule struct {
|
||||
TagItems []TagItem `json:"tag_items"`
|
||||
|
||||
IsNot bool `json:"is_not"`
|
||||
|
||||
Operator TagListOperation `json:"operator"`
|
||||
}
|
||||
|
||||
type TagItem struct {
|
||||
// 标签
|
||||
Tags []string `json:"tags"`
|
||||
IsNot bool `json:"is_not"`
|
||||
TagsOperator TagListOperation `json:"tags_operator"`
|
||||
ItemsOperator TagListOperation `json:"items_operator"`
|
||||
TagType TagType `json:"tag_type"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Title string `json:"title,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
|
||||
AcceptTime []AcceptTimeItem `json:"accept_time,omitempty"`
|
||||
|
||||
Android *AndroidParams `json:"android,omitempty"`
|
||||
|
||||
IOS *IOSParams `json:"ios,omitempty"`
|
||||
|
||||
ThreadId string `json:"thread_id,omitempty"`
|
||||
|
||||
ThreadSumtext string `json:"thread_sumtext,omitempty"`
|
||||
|
||||
XGMediaResources string `json:"xg_media_resources,omitempty"`
|
||||
|
||||
XGMediaAudioResources string `json:"xg_media_audio_resources,omitempty"`
|
||||
}
|
||||
|
||||
type AcceptTimeItem struct {
|
||||
Start HourAndMin `json:"start,omitempty"`
|
||||
End HourAndMin `json:"end,omitempty"`
|
||||
}
|
||||
|
||||
type HourAndMin struct {
|
||||
Hour string `json:"hour,omitempty"`
|
||||
Min string `json:"min,omitempty"`
|
||||
}
|
||||
|
||||
type AndroidParams struct {
|
||||
BuilderId *int `json:"builder_id,omitempty"`
|
||||
|
||||
Ring *int `json:"ring,omitempty"`
|
||||
|
||||
RingRaw string `json:"ring_raw,omitempty"`
|
||||
|
||||
Vibrate *int `json:"vibrate,omitempty"`
|
||||
|
||||
Lights *int `json:"lights,omitempty"`
|
||||
|
||||
Clearable *int `json:"clearable,omitempty"`
|
||||
|
||||
IconType *int `json:"icon_type"`
|
||||
|
||||
IconRes string `json:"icon_res,omitempty"`
|
||||
|
||||
StyleId *int `json:"style_id,omitempty"`
|
||||
|
||||
SmallIcon string `json:"small_icon,omitempty"`
|
||||
|
||||
Action *Action `json:"action,omitempty"`
|
||||
|
||||
CustomContent string `json:"custom_content,omitempty"`
|
||||
|
||||
ShowType *int `json:"show_type,omitempty"`
|
||||
|
||||
NChId string `json:"n_ch_id,omitempty"`
|
||||
|
||||
NChName string `json:"n_ch_name,omitempty"`
|
||||
|
||||
HwChId string `json:"hw_ch_id,omitempty"`
|
||||
|
||||
XmChId string `json:"xm_ch_id,omitempty"`
|
||||
|
||||
OppoChId string `json:"oppo_ch_id,omitempty"`
|
||||
|
||||
VivoChId string `json:"vivo_ch_id,omitempty"`
|
||||
|
||||
BadgeType *int `json:"badge_type,omitempty"`
|
||||
|
||||
IconColor *int `json:"icon_color,omitempty"`
|
||||
}
|
||||
|
||||
type Action struct {
|
||||
ActionType *int `json:"action_type,omitempty"`
|
||||
Activity string `json:"activity"`
|
||||
AtyAttr AtyAttr `json:"aty_attr,omitempty"`
|
||||
Intent string `json:"intent"`
|
||||
Browser Browser `json:"browser,omitempty"`
|
||||
}
|
||||
|
||||
type Browser struct {
|
||||
Url string `json:"url,omitempty"`
|
||||
Confirm *int `json:"confirm,omitempty"`
|
||||
}
|
||||
|
||||
type AtyAttr struct {
|
||||
AttrIf *int `json:"if,omitempty"`
|
||||
Pf *int `json:"pf,omitempty"`
|
||||
}
|
||||
|
||||
type IOSParams struct {
|
||||
Aps *Aps `json:"aps,omitempty"`
|
||||
|
||||
CustomContent string `json:"custom_content,omitempty"`
|
||||
}
|
||||
|
||||
type Aps struct {
|
||||
Alert map[string]string `json:"alert,omitempty"`
|
||||
BadgeType *int `json:"badge_type,omitempty"`
|
||||
Category string `json:"category,omitempty"`
|
||||
ContentAvailableInt *int `json:"content-available,omitempty"`
|
||||
MutableContent *int `json:"mutable-content,omitempty"`
|
||||
Sound string `json:"sound,omitempty"`
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
package req
|
||||
|
||||
import (
|
||||
tpns "Open_IM/internal/push/sdk/tpns-server-sdk-go/go"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var PushURL = "https://api.tpns.tencent.com/v3/push/app"
|
||||
|
||||
//var PushURL = "https://test.api.tpns.tencent.com/v3/push/app"
|
||||
|
||||
func URL(url string) {
|
||||
PushURL = url
|
||||
}
|
||||
|
||||
type ReqOpt func(*tpns.Request)
|
||||
|
||||
func NewPush(req *tpns.Request, opts ...ReqOpt) (*http.Request, string, error) {
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewUploadFileRequest(host string, file string) (*http.Request, error) {
|
||||
fp, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer fp.Close()
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
part, err := writer.CreateFormFile("file", filepath.Base(fp.Name()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
io.Copy(part, fp)
|
||||
writer.Close()
|
||||
url := host + "/v3/push/package/upload"
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Content-Type", writer.FormDataContentType())
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func NewSingleAccountPush(
|
||||
message tpns.Message,
|
||||
account string,
|
||||
opts ...ReqOpt,
|
||||
) (*http.Request, string, error) {
|
||||
req := &tpns.Request{
|
||||
MessageType: tpns.MsgTypeNotify,
|
||||
AudienceType: tpns.AdAccountList,
|
||||
AccountList: []string{account},
|
||||
Message: message,
|
||||
}
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewListAccountPush(
|
||||
accounts []string, message tpns.Message,
|
||||
opts ...ReqOpt,
|
||||
) (*http.Request, string, error) {
|
||||
req := &tpns.Request{
|
||||
MessageType: tpns.MsgTypeNotify,
|
||||
AudienceType: tpns.AdAccountList,
|
||||
AccountList: accounts,
|
||||
Message: message,
|
||||
Environment: tpns.EnvDev,
|
||||
}
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewTokenPush(
|
||||
tokens []string, message tpns.Message,
|
||||
opts ...ReqOpt,
|
||||
) (*http.Request, string, error) {
|
||||
req := &tpns.Request{
|
||||
MessageType: tpns.MsgTypeNotify,
|
||||
AudienceType: tpns.AdTokenList,
|
||||
TokenList: tokens,
|
||||
Message: message,
|
||||
Environment: tpns.EnvProd,
|
||||
}
|
||||
//fmt.Printf("reqBody :%v", common.ToJson(req))
|
||||
//fmt.Println()
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewTagsPush(
|
||||
tagList []tpns.TagRule, message tpns.Message,
|
||||
opts ...ReqOpt,
|
||||
) (*http.Request, string, error) {
|
||||
req := &tpns.Request{
|
||||
MessageType: tpns.MsgTypeNotify,
|
||||
AudienceType: tpns.AdTag,
|
||||
Tag: tagList,
|
||||
Message: message,
|
||||
}
|
||||
//fmt.Printf("reqBody :%v", common.ToJson(req))
|
||||
//fmt.Println()
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewAllPush(
|
||||
message tpns.Message,
|
||||
opts ...ReqOpt,
|
||||
) (*http.Request, string, error) {
|
||||
req := &tpns.Request{
|
||||
MessageType: tpns.MsgTypeNotify,
|
||||
AudienceType: tpns.AdAll,
|
||||
Message: message,
|
||||
}
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewAccountPackagePush(
|
||||
message tpns.Message,
|
||||
opts ...ReqOpt,
|
||||
) (*http.Request, string, error) {
|
||||
req := &tpns.Request{
|
||||
MessageType: tpns.MsgTypeNotify,
|
||||
AudienceType: tpns.AdPackageAccount,
|
||||
Message: message,
|
||||
}
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewTokenPackagePush(
|
||||
message tpns.Message,
|
||||
opts ...ReqOpt,
|
||||
) (*http.Request, string, error) {
|
||||
req := &tpns.Request{
|
||||
MessageType: tpns.MsgTypeNotify,
|
||||
AudienceType: tpns.AdPackageToken,
|
||||
Message: message,
|
||||
}
|
||||
return NewPushReq(req, opts...)
|
||||
}
|
||||
|
||||
func NewPushReq(req *tpns.Request, opts ...ReqOpt) (request *http.Request, reqBody string, err error) {
|
||||
for _, opt := range opts {
|
||||
opt(req)
|
||||
}
|
||||
bodyBytes, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
reqBody = string(bodyBytes)
|
||||
//fmt.Printf("NewPushReq req:%v", reqBody)
|
||||
request, err = http.NewRequest("POST", PushURL, bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
return
|
||||
}
|
||||
|
||||
func EnvProd() ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Environment = tpns.EnvProd
|
||||
}
|
||||
}
|
||||
|
||||
func EnvDev() ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Environment = tpns.EnvDev
|
||||
}
|
||||
}
|
||||
|
||||
func Title(t string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Title = t
|
||||
if r.Message.IOS != nil {
|
||||
if r.Message.IOS.Aps != nil {
|
||||
r.Message.IOS.Aps.Alert["title"] = t
|
||||
} else {
|
||||
r.Message.IOS.Aps = &tpns.Aps{
|
||||
Alert: map[string]string{"title": t},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.Message.IOS = &tpns.IOSParams{
|
||||
Aps: &tpns.Aps{
|
||||
Alert: map[string]string{"title": t},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Content(c string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Content = c
|
||||
if r.Message.IOS != nil {
|
||||
if r.Message.IOS.Aps != nil {
|
||||
r.Message.IOS.Aps.Alert["body"] = c
|
||||
} else {
|
||||
r.Message.IOS.Aps = &tpns.Aps{
|
||||
Alert: map[string]string{"body": c},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.Message.IOS = &tpns.IOSParams{
|
||||
Aps: &tpns.Aps{
|
||||
Alert: map[string]string{"body": c},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Ring(ring *int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.Ring = ring
|
||||
}
|
||||
}
|
||||
|
||||
func RingRaw(rr string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.RingRaw = rr
|
||||
}
|
||||
}
|
||||
|
||||
func Vibrate(v *int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.Vibrate = v
|
||||
}
|
||||
}
|
||||
|
||||
func Lights(l *int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.Lights = l
|
||||
}
|
||||
}
|
||||
|
||||
func Clearable(c *int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.Clearable = c
|
||||
}
|
||||
}
|
||||
|
||||
func IconType(it *int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.IconType = it
|
||||
}
|
||||
}
|
||||
|
||||
func IconRes(ir string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.IconRes = ir
|
||||
}
|
||||
}
|
||||
|
||||
func AndroidCustomContent(ct string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.Android.CustomContent = ct
|
||||
}
|
||||
}
|
||||
|
||||
func Aps(aps *tpns.Aps) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message.IOS.Aps = aps
|
||||
}
|
||||
}
|
||||
|
||||
func AudienceType(at tpns.AudienceType) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.AudienceType = at
|
||||
}
|
||||
}
|
||||
|
||||
func Message(m tpns.Message) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Message = m
|
||||
}
|
||||
}
|
||||
|
||||
func TokenList(tl []string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.TokenList = tl
|
||||
}
|
||||
}
|
||||
|
||||
func TokenListAdd(t string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
if r.TokenList != nil {
|
||||
r.TokenList = append(r.TokenList, t)
|
||||
} else {
|
||||
r.TokenList = []string{t}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func AccountList(al []string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.AccountList = al
|
||||
}
|
||||
}
|
||||
|
||||
//ChannelDistributeRules
|
||||
func AddChannelRules(ChannelRules []*tpns.ChannelDistributeRule) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.ChannelRules = ChannelRules
|
||||
}
|
||||
}
|
||||
|
||||
//ChannelDistributeRules
|
||||
func AddLoopParam(loopParam *tpns.PushLoopParam) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.LoopParam = loopParam
|
||||
}
|
||||
}
|
||||
|
||||
func AccountListAdd(a string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
if r.AccountList != nil {
|
||||
r.AccountList = append(r.AccountList, a)
|
||||
} else {
|
||||
r.AccountList = []string{a}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func MessageType(t tpns.MessageType) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.MessageType = t
|
||||
}
|
||||
}
|
||||
|
||||
func AddMultiPkg(multipPkg bool) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.MultiPkg = multipPkg
|
||||
}
|
||||
}
|
||||
|
||||
func AddForceCollapse(forceCollapse bool) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.ForceCollapse = forceCollapse
|
||||
}
|
||||
}
|
||||
|
||||
func AddTPNSOnlinePushType(onlinePushType int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.TPNSOnlinePushType = onlinePushType
|
||||
}
|
||||
}
|
||||
|
||||
func AddCollapseId(collapseId int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.CollapseId = collapseId
|
||||
}
|
||||
}
|
||||
|
||||
func AddPushSpeed(pushSpeed int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.PushSpeed = pushSpeed
|
||||
}
|
||||
}
|
||||
|
||||
func AddAccountPushType(accountPushType int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.AccountPushType = accountPushType
|
||||
}
|
||||
}
|
||||
|
||||
func AddPlanId(planId string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.PlanId = planId
|
||||
}
|
||||
}
|
||||
|
||||
func AddSendTime(sendTime string) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.SendTime = sendTime
|
||||
}
|
||||
}
|
||||
|
||||
func AddExpireTime(expireTime int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.ExpireTime = expireTime
|
||||
}
|
||||
}
|
||||
|
||||
func AddUploadId(UploadId int) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.UploadId = UploadId
|
||||
}
|
||||
}
|
||||
|
||||
func AddEnvironment(Environment tpns.CommonRspEnv) ReqOpt {
|
||||
return func(r *tpns.Request) {
|
||||
r.Environment = Environment
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user