Skip to content

Commit

Permalink
Refactor and update API
Browse files Browse the repository at this point in the history
  • Loading branch information
crazytaxii committed May 24, 2024
1 parent 1414237 commit 98d19ba
Show file tree
Hide file tree
Showing 9 changed files with 325 additions and 309 deletions.
43 changes: 32 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,24 @@

**需要先在友盟官网创建应用获得 App Key 和 App Secret!**

+ `Push()` 单播
+ `Listcast()` 多播
+ `Broadcast()`广播(默认每天可推送10次)
- `Unicast()` 单播
- `Listcast()` 多播
- `Broadcast()`广播(默认每天可推送10次)

```go
import (
umeng "github.com/crazytaxii/gomeng"
)

func main() {
client := umeng.NewClient(false, "app_key", "app_master_secret", 10 * time.Second)
payload := map[string]interface{}{
cfg := &umeng.Config{
AppKey: "app_key",
AppSecret: "app_master_secret",
ProductionMode: false,
}
client := umeng.NewClient(cfg)

demo := umeng.Payload{
"display_type": "notification",
"body": map[string]interface{}{
"ticker": "test_ticker",
Expand All @@ -35,16 +41,31 @@ func main() {
},
}

if err := client.Push(payload, "device_token"); err != nil {
log.Fatalf("err: %v", err)
retMsg, err := client.Unicast(context.Background() ,demo, "device_token")
if err != nil {
log.Fatalf("failed to unicast: %v", err)
}
if err := rm.Error(); err != nil {
// business error
log.Fatal(err)
}

if err := client.Listcast(payload, "device_token1", "device_token2", "device_token3"); err != nil {
log.Fatalf("err: %v", err)
retMsg, err := client.Listcast(context.Background(), demo, "device_token1", "device_token2", "device_token3")
if err != nil {
log.Fatalf("failed to listcast: %v", err)
}
if err := rm.Error(); err != nil {
// business error
log.Fatal(err)
}

if err := client.Broadcast(payload); err != nil {
fmt.Fatalf("err: %v", err)
retMsg, err := client.Broadcast(context.Background(), demo)
if err != nil {
fmt.Fatalf("failed to broadcast: %v", err)
}
if err := rm.Error(); err != nil {
// business error
log.Fatal(err)
}
}
```
55 changes: 31 additions & 24 deletions api.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
package gomeng

import (
"encoding/json"
"fmt"
import "fmt"

const (
baseURL = "https://msgapi.umeng.com"

apiPush = "api/send"
apiBroadcast = "api/send"
)

type requestType string

const (
BaseURL = "https://msgapi.umeng.com/api/"
unicastRequest requestType = "unicast"
listcastRequest requestType = "listcast"
broadcastRequest requestType = "broadcast"
)

type ReturnState string

APIPush = "send"
APIBroadcast = "send"
const (
SuccessState ReturnState = "SUCCESS"
FailState ReturnState = "FAIL"
)

type ResponseMessage struct {
Ret string `json:"ret"`
type (
Data struct {
MsgID string `json:"msg_id"`
TaskID string `json:"task_id"`
ErrMsg string `json:"error_msg"`
ErrCode string `json:"error_code"`
} `json:"data"`
}

func (rm *ResponseMessage) Unmarshal(data []byte) error {
if err := json.Unmarshal(data, rm); err != nil {
return err
MessageID string `json:"msg_id"`
TaskID string `json:"task_id"`
ErrMessage string `json:"error_msg"`
ErrCode string `json:"error_code"`
}
return nil
}
ResponseMessage struct {
Ret ReturnState `json:"ret"`
Data `json:"data"`
}
)

func (rm *ResponseMessage) Error() error {
if rm.Ret != "SUCCESS" {
return fmt.Errorf("Umeng push failed, error message: %s, error code: %s",
rm.Data.ErrMsg, rm.Data.ErrCode)
if rm.Ret == SuccessState {
return nil
}
return nil
return fmt.Errorf("error %s: %s", rm.ErrCode, rm.ErrMessage)
}
296 changes: 148 additions & 148 deletions docs/umeng_api.md

Large diffs are not rendered by default.

25 changes: 14 additions & 11 deletions gomeng.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,25 @@ import (
"time"
)

const DefaultTimeout = 10 * time.Second
const defaultTimeout = 10 * time.Second

type Config struct {
AppKey string `json:"app_key" yaml:"appKey"`
AppSecret string `json:"app_secret" yaml:"appSecret"`
ProductionMode bool `json:"production_mode" yaml:"productionMode"`
Timeout time.Duration `json:"timeout" yaml:"timeout"`
}

type Client struct {
productMode bool
key string
secret string
*http.Client
cfg *Config
rawhttp *http.Client
}

func NewClient(productMode bool, key, secret string, timeout time.Duration) *Client {
func NewClient(cfg *Config) *Client {
return &Client{
productMode: productMode,
key: key,
secret: secret,
Client: &http.Client{
Timeout: timeout,
cfg: cfg,
rawhttp: &http.Client{
Timeout: fallback2DefaultIfZero(cfg.Timeout),
},
}
}
33 changes: 33 additions & 0 deletions helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package gomeng

import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"time"
)

func sign(method, url, secret string, raw []byte) (string, error) {
buf := bytes.NewBufferString(method)
buf.WriteString(url)
buf.Write(raw)
buf.WriteString(secret)
hasher := md5.New()
if _, err := hasher.Write(buf.Bytes()); err != nil {
return "", err
}
return hex.EncodeToString(hasher.Sum(nil)), nil
}

func joinSign(url, sign string) string {
// e.g. https://msgapi.umeng.com/api/send?sign=xxx
return fmt.Sprintf("%s?sign=%s", url, sign)
}

func fallback2DefaultIfZero(timeout time.Duration) time.Duration {
if timeout > 0 {
return timeout
}
return defaultTimeout
}
35 changes: 8 additions & 27 deletions push.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,25 @@
package gomeng

import "context"

/**
* 推送给单用户(单播)
*/
func (c *Client) Push(payload map[string]interface{}, deviceToken string) error {
resp, err := c.doPost(c.genReqParams(payload, "unicast", deviceToken), APIPush)
if err != nil {
return err
}
if err := resp.Error(); err != nil {
return err
}
return nil
func (c *Client) Unicast(ctx context.Context, payload Payload, deviceToken string) (resp *ResponseMessage, err error) {
return c.doPost(ctx, c.genRequestParams(payload, unicastRequest, deviceToken), apiPush)
}

/**
* 推送给多用户(列播)
*/
func (c *Client) ListCast(payload map[string]interface{}, deviceTokens ...string) error {
resp, err := c.doPost(c.genReqParams(payload, "listcast", deviceTokens...), APIPush)
if err != nil {
return err
}
if err := resp.Error(); err != nil {
return err
}
return nil
func (c *Client) ListCast(ctx context.Context, payload Payload, deviceTokens ...string) (resp *ResponseMessage, err error) {
return c.doPost(ctx, c.genRequestParams(payload, listcastRequest, deviceTokens...), apiPush)
}

/**
* 推送给所有用户(广播)
* 默认每天可推送10次
*/
func (c *Client) Broadcast(payload map[string]interface{}) error {
resp, err := c.doPost(c.genReqParams(payload, "broadcast"), APIBroadcast)
if err != nil {
return err
}
if err := resp.Error(); err != nil {
return err
}
return nil
func (c *Client) Broadcast(ctx context.Context, payload Payload) (resp *ResponseMessage, err error) {
return c.doPost(ctx, c.genRequestParams(payload, broadcastRequest), apiBroadcast)
}
85 changes: 34 additions & 51 deletions push_test.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package gomeng

import (
"context"
"os"
"testing"
)

const (
TestAppKey = "app_key"
TestAppSecret = "app_secret"
TestProductMode = false
TestDeviceToken = "AtOAal-11NoRhG1KJv_aq1aij5O_aWwMlvvklGNu1LmG"
)

func TestPush2SingleUser(t *testing.T) {
c := NewClient(TestProductMode, TestAppKey, TestAppSecret, DefaultTimeout)
payload := map[string]interface{}{
func newTest() (client *Client, payload Payload, token string) {
cfg := &Config{
AppKey: os.Getenv("APP_KEY"),
AppSecret: os.Getenv("APP_SECRET"),
Timeout: defaultTimeout,
}
return NewClient(cfg), Payload{
"display_type": "notification",
"body": map[string]interface{}{
"ticker": "test_ticker",
Expand All @@ -28,57 +27,41 @@ func TestPush2SingleUser(t *testing.T) {
"after_open": "go_app",
"play_sound": "true",
},
}
}, os.Getenv("DEVICE_TOKEN")
}

if err := c.Push(payload, TestDeviceToken); err != nil {
t.Fatalf("err: %v", err)
func TestPush2SingleUser(t *testing.T) {
c, payload, token := newTest()
rm, err := c.Unicast(context.Background(), payload, token)
if err != nil {
t.Errorf("err: %v", err)
return
}
if err := rm.Error(); err != nil {
t.Errorf("%s failed: %v", t.Name(), err)
}
}

func TestPush2MultiUsers(t *testing.T) {
c := NewClient(TestProductMode, TestAppKey, TestAppSecret, DefaultTimeout)
payload := map[string]interface{}{
"display_type": "notification",
"body": map[string]interface{}{
"ticker": "test_ticker",
"title": "test_title",
"text": "test_text",
"builder_id:": 1,
"custom": map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
"after_open": "go_app",
"play_sound": "true",
},
c, payload, token := newTest()
rm, err := c.ListCast(context.Background(), payload, token)
if err != nil {
t.Errorf("err: %v", err)
return
}

if err := c.ListCast(payload, "AtOAal-11NoRhG1KJv_aq1aij5O_aWwMlvvklGNu1LmG"); err != nil {
t.Fatalf("err: %v", err)
if err := rm.Error(); err != nil {
t.Errorf("%s failed: %v", t.Name(), err)
}
}

func TestPush2AllUsers(t *testing.T) {
c := NewClient(TestProductMode, TestAppKey, TestAppSecret, DefaultTimeout)
payload := map[string]interface{}{
"display_type": "notification",
"body": map[string]interface{}{
"ticker": "test_ticker",
"title": "test_title",
"text": "test_text",
"builder_id:": 1,
"custom": map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
"after_open": "go_app",
"play_sound": "true",
},
c, payload, _ := newTest()
rm, err := c.Broadcast(context.Background(), payload)
if err != nil {
t.Errorf("err: %v", err)
return
}

if err := c.Broadcast(payload); err != nil {
t.Fatalf("err: %v", err)
if err := rm.Error(); err != nil {
t.Errorf("%s failed: %v", t.Name(), err)
}
}
Loading

0 comments on commit 98d19ba

Please sign in to comment.