evan.hong 4 years ago
commit
59dde3768d
14 changed files with 1012 additions and 0 deletions
  1. 22 0
      .gitattributes
  2. 36 0
      .gitignore
  3. 125 0
      README.md
  4. 46 0
      audience.go
  5. 71 0
      httpclient.go
  6. 307 0
      httplib.go
  7. 28 0
      message.go
  8. 48 0
      notice.go
  9. 29 0
      option.go
  10. 49 0
      payload.go
  11. 65 0
      platform.go
  12. 134 0
      pushclient.go
  13. 1 0
      report.go
  14. 51 0
      schedule.go

+ 22 - 0
.gitattributes

@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc	 diff=astextplain
+*.DOC	 diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF	 diff=astextplain
+*.rtf	 diff=astextplain
+*.RTF	 diff=astextplain

+ 36 - 0
.gitignore

@@ -0,0 +1,36 @@
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# =========================
+# Operating System Files
+# =========================
+
+# OSX
+# =========================
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must ends with two \r.
+Icon

+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes

+ 125 - 0
README.md

@@ -0,0 +1,125 @@
+jpush-api-go-client
+===================
+
+概述
+----------------------------------- 
+   这是JPush REST API 的 go 版本封装开发包,仅支持最新的REST API v3功能。
+   REST API 文档:http://docs.jpush.cn/display/dev/Push-API-v3
+  
+
+使用  
+----------------------------------- 
+   go get github.com/ylywyn/jpush-api-go-client
+   
+   
+推送流程  
+----------------------------------- 
+### 1.构建要推送的平台: jpushclient.Platform
+	//Platform
+	var pf jpushclient.Platform
+	pf.Add(jpushclient.ANDROID)
+	pf.Add(jpushclient.IOS)
+	pf.Add(jpushclient.WINPHONE)
+	//pf.All()
+      
+### 2.构建接收听众: jpushclient.Audience
+	//Audience
+	var ad jpushclient.Audience
+	s := []string{"t1", "t2", "t3"}
+	ad.SetTag(s)
+	id := []string{"1", "2", "3"}
+	ad.SetID(id)
+	//ad.All()
+      
+### 3.构建通知 jpushclient.Notice,或者消息: jpushclient.Message
+      
+	//Notice
+	var notice jpushclient.Notice
+	notice.SetAlert("alert_test")
+	notice.SetAndroidNotice(&jpushclient.AndroidNotice{Alert: "AndroidNotice"})
+	notice.SetIOSNotice(&jpushclient.IOSNotice{Alert: "IOSNotice"})
+	notice.SetWinPhoneNotice(&jpushclient.WinPhoneNotice{Alert: "WinPhoneNotice"})
+      
+    //jpushclient.Message
+    var msg jpushclient.Message
+	msg.Title = "Hello"
+	msg.Content = "你是ylywn"
+      
+### 4.构建jpushclient.PayLoad
+    payload := jpushclient.NewPushPayLoad()
+	payload.SetPlatform(&pf)
+	payload.SetAudience(&ad)
+	payload.SetMessage(&msg)
+	payload.SetNotice(&notice)
+      
+      
+### 5.构建PushClient,发出推送
+	c := jpushclient.NewPushClient(secret, appKey)
+	r, err := c.Send(bytes)
+	if err != nil {
+		fmt.Printf("err:%s", err.Error())
+	} else {
+		fmt.Printf("ok:%s", r)
+	}
+
+  
+### 6.完整demo
+    package main
+
+	import (
+		"fmt"
+		"github.com/ylywyn/jpush-api-go-client"
+	)
+
+	const (
+		appKey = "you jpush appkey"
+		secret = "you jpush secret"
+	)
+
+	func main() {
+
+		//Platform
+		var pf jpushclient.Platform
+		pf.Add(jpushclient.ANDROID)
+		pf.Add(jpushclient.IOS)
+		pf.Add(jpushclient.WINPHONE)
+		//pf.All()
+
+		//Audience
+		var ad jpushclient.Audience
+		s := []string{"1", "2", "3"}
+		ad.SetTag(s)
+		ad.SetAlias(s)
+		ad.SetID(s)
+		//ad.All()
+
+		//Notice
+		var notice jpushclient.Notice
+		notice.SetAlert("alert_test")
+		notice.SetAndroidNotice(&jpushclient.AndroidNotice{Alert: "AndroidNotice"})
+		notice.SetIOSNotice(&jpushclient.IOSNotice{Alert: "IOSNotice"})
+		notice.SetWinPhoneNotice(&jpushclient.WinPhoneNotice{Alert: "WinPhoneNotice"})
+
+		var msg jpushclient.Message
+		msg.Title = "Hello"
+		msg.Content = "你是ylywn"
+
+		payload := jpushclient.NewPushPayLoad()
+		payload.SetPlatform(&pf)
+		payload.SetAudience(&ad)
+		payload.SetMessage(&msg)
+		payload.SetNotice(&notice)
+
+		bytes, _ := payload.ToBytes()
+		fmt.Printf("%s\r\n", string(bytes))
+
+		//push
+		c := jpushclient.NewPushClient(secret, appKey)
+		str, err := c.Send(bytes)
+		if err != nil {
+			fmt.Printf("err:%s", err.Error())
+		} else {
+			fmt.Printf("ok:%s", str)
+		}
+	}
+

+ 46 - 0
audience.go

@@ -0,0 +1,46 @@
+package jpushclient
+
+const (
+	TAG     = "tag"
+	TAG_AND = "tag_and"
+	ALIAS   = "alias"
+	ID      = "registration_id"
+)
+
+type Audience struct {
+	Object   interface{}
+	audience map[string][]string
+}
+
+func (this *Audience) All() {
+	this.Object = "all"
+}
+
+func (this *Audience) SetID(ids []string) {
+	this.set(ID, ids)
+}
+
+func (this *Audience) SetTag(tags []string) {
+	this.set(TAG, tags)
+}
+
+func (this *Audience) SetTagAnd(tags []string) {
+	this.set(TAG_AND, tags)
+}
+
+func (this *Audience) SetAlias(alias []string) {
+	this.set(ALIAS, alias)
+}
+
+func (this *Audience) set(key string, v []string) {
+	if this.audience == nil {
+		this.audience = make(map[string][]string)
+		this.Object = this.audience
+	}
+
+	//v, ok = this.audience[key]
+	//if ok {
+	//	return
+	//}
+	this.audience[key] = v
+}

+ 71 - 0
httpclient.go

@@ -0,0 +1,71 @@
+package jpushclient
+
+import (
+	"bytes"
+	"io/ioutil"
+	"net/http"
+	"time"
+)
+
+const (
+	CHARSET                    = "UTF-8"
+	CONTENT_TYPE_JSON          = "application/json"
+	DEFAULT_CONNECTION_TIMEOUT = 20 //seconds
+	DEFAULT_SOCKET_TIMEOUT     = 30 // seconds
+)
+
+func SendPostString(url, content, authCode string) (string, error) {
+
+	//req := Post(url).Debug(true)
+	req := Post(url)
+	req.SetTimeout(DEFAULT_CONNECTION_TIMEOUT*time.Second, DEFAULT_SOCKET_TIMEOUT*time.Second)
+	req.Header("Connection", "Keep-Alive")
+	req.Header("Charset", CHARSET)
+	req.Header("Authorization", authCode)
+	req.Header("Content-Type", CONTENT_TYPE_JSON)
+	req.SetProtocolVersion("HTTP/1.1")
+	req.Body(content)
+
+	return req.String()
+}
+
+func SendPostBytes(url string, content []byte, authCode string) (string, error) {
+
+	req := Post(url)
+	req.SetTimeout(DEFAULT_CONNECTION_TIMEOUT*time.Second, DEFAULT_SOCKET_TIMEOUT*time.Second)
+	req.Header("Connection", "Keep-Alive")
+	req.Header("Charset", CHARSET)
+	req.Header("Authorization", authCode)
+	req.Header("Content-Type", CONTENT_TYPE_JSON)
+	req.SetProtocolVersion("HTTP/1.1")
+	req.Body(content)
+
+	return req.String()
+}
+
+func SendPostBytes2(url string, data []byte, authCode string) (string, error) {
+
+	client := &http.Client{}
+	req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
+	req.Header.Add("Charset", CHARSET)
+	req.Header.Add("Authorization", authCode)
+	req.Header.Add("Content-Type", CONTENT_TYPE_JSON)
+	resp, err := client.Do(req)
+
+	if err != nil {
+		if resp != nil {
+			resp.Body.Close()
+		}
+		return "", err
+	}
+	if resp == nil {
+		return "", nil
+	}
+
+	defer resp.Body.Close()
+	r, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return "", err
+	}
+	return string(r), nil
+}

+ 307 - 0
httplib.go

@@ -0,0 +1,307 @@
+package jpushclient
+
+import (
+	"bytes"
+	"crypto/tls"
+	"encoding/json"
+	"encoding/xml"
+	"io"
+	"io/ioutil"
+	"net"
+	"net/http"
+	"net/url"
+	"os"
+	"strings"
+	"time"
+)
+
+//  Get returns *HttpRequest with GET method.
+func Get(url string) *HttpRequest {
+	var req http.Request
+	req.Method = "GET"
+	req.Header = http.Header{}
+	return &HttpRequest{url, &req, map[string]string{}, 60 * time.Second, 60 * time.Second, nil, nil, nil}
+}
+
+// Post returns *HttpRequest with POST method.
+func Post(url string) *HttpRequest {
+	var req http.Request
+	req.Method = "POST"
+	req.Header = http.Header{}
+	return &HttpRequest{url, &req, map[string]string{}, 60 * time.Second, 60 * time.Second, nil, nil, nil}
+}
+
+func Delete(url string) *HttpRequest {
+	var req http.Request
+	req.Method = "DELETE"
+	req.Header = http.Header{}
+	return &HttpRequest{url, &req, map[string]string{}, 60 * time.Second, 60 * time.Second, nil, nil, nil}
+}
+
+// HttpRequest provides more useful methods for requesting one url than http.Request.
+type HttpRequest struct {
+	url              string
+	req              *http.Request
+	params           map[string]string
+	connectTimeout   time.Duration
+	readWriteTimeout time.Duration
+	tlsClientConfig  *tls.Config
+	proxy            func(*http.Request) (*url.URL, error)
+	transport        http.RoundTripper
+}
+
+// SetTimeout sets connect time out and read-write time out for Request.
+func (b *HttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *HttpRequest {
+	b.connectTimeout = connectTimeout
+	b.readWriteTimeout = readWriteTimeout
+	return b
+}
+
+func (b *HttpRequest) SetBasicAuth(userName, password string) *HttpRequest {
+	b.req.SetBasicAuth(userName, password)
+	return b
+}
+
+// SetTLSClientConfig sets tls connection configurations if visiting https url.
+func (b *HttpRequest) SetTLSClientConfig(config *tls.Config) *HttpRequest {
+	b.tlsClientConfig = config
+	return b
+}
+
+// Header add header item string in request.
+func (b *HttpRequest) Header(key, value string) *HttpRequest {
+	b.req.Header.Set(key, value)
+	return b
+}
+
+// Set the protocol version for incoming requests.
+// Client requests always use HTTP/1.1.
+func (b *HttpRequest) SetProtocolVersion(vers string) *HttpRequest {
+	if len(vers) == 0 {
+		vers = "HTTP/1.1"
+	}
+
+	major, minor, ok := http.ParseHTTPVersion(vers)
+	if ok {
+		b.req.Proto = vers
+		b.req.ProtoMajor = major
+		b.req.ProtoMinor = minor
+	}
+
+	return b
+}
+
+// SetCookie add cookie into request.
+func (b *HttpRequest) SetCookie(cookie *http.Cookie) *HttpRequest {
+	b.req.Header.Add("Cookie", cookie.String())
+	return b
+}
+
+// Set transport to
+func (b *HttpRequest) SetTransport(transport http.RoundTripper) *HttpRequest {
+	b.transport = transport
+	return b
+}
+
+// Set http proxy
+// example:
+//
+//	func(req *http.Request) (*url.URL, error) {
+// 		u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
+// 		return u, nil
+// 	}
+func (b *HttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *HttpRequest {
+	b.proxy = proxy
+	return b
+}
+
+// Param adds query param in to request.
+// params build query string as ?key1=value1&key2=value2...
+func (b *HttpRequest) Param(key, value string) *HttpRequest {
+	b.params[key] = value
+	return b
+}
+
+// Body adds request raw body.
+// it supports string and []byte.
+func (b *HttpRequest) Body(data interface{}) *HttpRequest {
+	switch t := data.(type) {
+	case string:
+		bf := bytes.NewBufferString(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	case []byte:
+		bf := bytes.NewBuffer(t)
+		b.req.Body = ioutil.NopCloser(bf)
+		b.req.ContentLength = int64(len(t))
+	}
+	return b
+}
+
+func (b *HttpRequest) getResponse() (*http.Response, error) {
+	var paramBody string
+	if len(b.params) > 0 {
+		var buf bytes.Buffer
+		for k, v := range b.params {
+			buf.WriteString(url.QueryEscape(k))
+			buf.WriteByte('=')
+			buf.WriteString(url.QueryEscape(v))
+			buf.WriteByte('&')
+		}
+		paramBody = buf.String()
+		paramBody = paramBody[0 : len(paramBody)-1]
+	}
+
+	if b.req.Method == "GET" && len(paramBody) > 0 {
+		if strings.Index(b.url, "?") != -1 {
+			b.url += "&" + paramBody
+		} else {
+			b.url = b.url + "?" + paramBody
+		}
+	} else if b.req.Method == "POST" && b.req.Body == nil && len(paramBody) > 0 {
+		b.Header("Content-Type", "application/x-www-form-urlencoded")
+		b.Body(paramBody)
+	}
+
+	url, err := url.Parse(b.url)
+	if url.Scheme == "" {
+		b.url = "http://" + b.url
+		url, err = url.Parse(b.url)
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	b.req.URL = url
+	trans := b.transport
+
+	if trans == nil {
+		// create default transport
+		trans = &http.Transport{
+			TLSClientConfig: b.tlsClientConfig,
+			Proxy:           b.proxy,
+			Dial:            TimeoutDialer(b.connectTimeout, b.readWriteTimeout),
+		}
+	} else {
+		// if b.transport is *http.Transport then set the settings.
+		if t, ok := trans.(*http.Transport); ok {
+			if t.TLSClientConfig == nil {
+				t.TLSClientConfig = b.tlsClientConfig
+			}
+			if t.Proxy == nil {
+				t.Proxy = b.proxy
+			}
+			if t.Dial == nil {
+				t.Dial = TimeoutDialer(b.connectTimeout, b.readWriteTimeout)
+			}
+		}
+	}
+
+	client := &http.Client{
+		Transport: trans,
+	}
+
+	resp, err := client.Do(b.req)
+	if err != nil {
+		return nil, err
+	}
+	return resp, nil
+}
+
+// String returns the body string in response.
+// it calls Response inner.
+func (b *HttpRequest) String() (string, error) {
+	data, err := b.Bytes()
+	if err != nil {
+		return "", err
+	}
+
+	return string(data), nil
+}
+
+// Bytes returns the body []byte in response.
+// it calls Response inner.
+func (b *HttpRequest) Bytes() ([]byte, error) {
+	resp, err := b.getResponse()
+	if err != nil {
+		return nil, err
+	}
+	if resp.Body == nil {
+		return nil, nil
+	}
+	defer resp.Body.Close()
+	data, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	return data, nil
+}
+
+// ToFile saves the body data in response to one file.
+// it calls Response inner.
+func (b *HttpRequest) ToFile(filename string) error {
+	f, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	resp, err := b.getResponse()
+	if err != nil {
+		return err
+	}
+	if resp.Body == nil {
+		return nil
+	}
+	defer resp.Body.Close()
+	_, err = io.Copy(f, resp.Body)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// ToJson returns the map that marshals from the body bytes as json in response .
+// it calls Response inner.
+func (b *HttpRequest) ToJson(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(data, v)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// ToXml returns the map that marshals from the body bytes as xml in response .
+// it calls Response inner.
+func (b *HttpRequest) ToXML(v interface{}) error {
+	data, err := b.Bytes()
+	if err != nil {
+		return err
+	}
+	err = xml.Unmarshal(data, v)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+// Response executes request client gets response mannually.
+func (b *HttpRequest) Response() (*http.Response, error) {
+	return b.getResponse()
+}
+
+// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
+func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, addr string) (c net.Conn, err error) {
+	return func(netw, addr string) (net.Conn, error) {
+		conn, err := net.DialTimeout(netw, addr, cTimeout)
+		if err != nil {
+			return nil, err
+		}
+		conn.SetDeadline(time.Now().Add(rwTimeout))
+		return conn, nil
+	}
+}

+ 28 - 0
message.go

@@ -0,0 +1,28 @@
+package jpushclient
+
+type Message struct {
+	Content     string                 `json:"msg_content"`
+	Title       string                 `json:"title,omitempty"`
+	ContentType string                 `json:"content_type,omitempty"`
+	Extras      map[string]interface{} `json:"extras,omitempty"`
+}
+
+func (this *Message) SetContent(c string) {
+	this.Content = c
+
+}
+
+func (this *Message) SetTitle(title string) {
+	this.Title = title
+}
+
+func (this *Message) SetContentType(t string) {
+	this.ContentType = t
+}
+
+func (this *Message) AddExtras(key string, value interface{}) {
+	if this.Extras == nil {
+		this.Extras = make(map[string]interface{})
+	}
+	this.Extras[key] = value
+}

+ 48 - 0
notice.go

@@ -0,0 +1,48 @@
+package jpushclient
+
+type Notice struct {
+	Alert    string          `json:"alert,omitempty"`
+	Android  *AndroidNotice  `json:"android,omitempty"`
+	IOS      *IOSNotice      `json:"ios,omitempty"`
+	WINPhone *WinPhoneNotice `json:"winphone,omitempty"`
+}
+
+type AndroidNotice struct {
+	Alert     string                 `json:"alert"`
+	Title     string                 `json:"title,omitempty"`
+	BuilderId int                    `json:"builder_id,omitempty"`
+	Extras    map[string]interface{} `json:"extras,omitempty"`
+}
+
+type IOSNotice struct {
+	Alert            interface{}            `json:"alert"`
+	Sound            string                 `json:"sound,omitempty"`
+	Badge            string                 `json:"badge,omitempty"`
+	ContentAvailable bool                   `json:"content-available,omitempty"`
+	MutableContent   bool                   `json:"mutable-content,omitempty"`
+	Category         string                 `json:"category,omitempty"`
+	Extras           map[string]interface{} `json:"extras,omitempty"`
+}
+
+type WinPhoneNotice struct {
+	Alert    string                 `json:"alert"`
+	Title    string                 `json:"title,omitempty"`
+	OpenPage string                 `json:"_open_page,omitempty"`
+	Extras   map[string]interface{} `json:"extras,omitempty"`
+}
+
+func (this *Notice) SetAlert(alert string) {
+	this.Alert = alert
+}
+
+func (this *Notice) SetAndroidNotice(n *AndroidNotice) {
+	this.Android = n
+}
+
+func (this *Notice) SetIOSNotice(n *IOSNotice) {
+	this.IOS = n
+}
+
+func (this *Notice) SetWinPhoneNotice(n *WinPhoneNotice) {
+	this.WINPhone = n
+}

+ 29 - 0
option.go

@@ -0,0 +1,29 @@
+package jpushclient
+
+type Option struct {
+	SendNo          int   `json:"sendno,omitempty"`
+	TimeLive        int   `json:"time_to_live,omitempty"`
+	ApnsProduction  bool  `json:"apns_production"`
+	OverrideMsgId   int64 `json:"override_msg_id,omitempty"`
+	BigPushDuration int   `json:"big_push_duration,omitempty"`
+}
+
+func (this *Option) SetSendno(no int) {
+	this.SendNo = no
+}
+
+func (this *Option) SetTimelive(timelive int) {
+	this.TimeLive = timelive
+}
+
+func (this *Option) SetOverrideMsgId(id int64) {
+	this.OverrideMsgId = id
+}
+
+func (this *Option) SetApns(apns bool) {
+	this.ApnsProduction = apns
+}
+
+func (this *Option) SetBigPushDuration(bigPushDuration int) {
+	this.BigPushDuration = bigPushDuration
+}

+ 49 - 0
payload.go

@@ -0,0 +1,49 @@
+package jpushclient
+
+import (
+	"encoding/json"
+)
+
+type PayLoad struct {
+	Platform     interface{} `json:"platform"`
+	Audience     interface{} `json:"audience"`
+	Notification interface{} `json:"notification,omitempty"`
+	Message      interface{} `json:"message,omitempty"`
+	Options      *Option     `json:"options,omitempty"`
+}
+
+func NewPushPayLoad() *PayLoad {
+	pl := &PayLoad{}
+	o := &Option{}
+	o.ApnsProduction = false
+	pl.Options = o
+	return pl
+}
+
+func (this *PayLoad) SetPlatform(pf *Platform) {
+	this.Platform = pf.Os
+}
+
+func (this *PayLoad) SetAudience(ad *Audience) {
+	this.Audience = ad.Object
+}
+
+func (this *PayLoad) SetOptions(o *Option) {
+	this.Options = o
+}
+
+func (this *PayLoad) SetMessage(m *Message) {
+	this.Message = m
+}
+
+func (this *PayLoad) SetNotice(notice *Notice) {
+	this.Notification = notice
+}
+
+func (this *PayLoad) ToBytes() ([]byte, error) {
+	content, err := json.Marshal(this)
+	if err != nil {
+		return nil, err
+	}
+	return content, nil
+}

+ 65 - 0
platform.go

@@ -0,0 +1,65 @@
+package jpushclient
+
+import (
+	"errors"
+)
+
+const (
+	IOS      = "ios"
+	ANDROID  = "android"
+	WINPHONE = "winphone"
+)
+
+type Platform struct {
+	Os     interface{}
+	osArry []string
+}
+
+func (this *Platform) All() {
+	this.Os = "all"
+}
+
+func (this *Platform) Add(os string) error {
+	if this.Os == nil {
+		this.osArry = make([]string, 0, 3)
+	} else {
+		switch this.Os.(type) {
+		case string:
+			return errors.New("platform is all")
+		default:
+		}
+	}
+
+	//判断是否重复
+	for _, value := range this.osArry {
+		if os == value {
+			return nil
+		}
+	}
+
+	switch os {
+	case IOS:
+		fallthrough
+	case ANDROID:
+		fallthrough
+	case WINPHONE:
+		this.osArry = append(this.osArry, os)
+		this.Os = this.osArry
+	default:
+		return errors.New("unknow platform")
+	}
+
+	return nil
+}
+
+func (this *Platform) AddIOS() {
+	this.Add(IOS)
+}
+
+func (this *Platform) AddAndrid() {
+	this.Add(ANDROID)
+}
+
+func (this *Platform) AddWinphone() {
+	this.Add(WINPHONE)
+}

+ 134 - 0
pushclient.go

@@ -0,0 +1,134 @@
+package jpushclient
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+)
+
+const (
+	SUCCESS_FLAG  = "msg_id"
+	HOST_NAME_SSL = "https://api.jpush.cn/v3/push"
+	HOST_SCHEDULE = "https://api.jpush.cn/v3/schedules"
+	HOST_REPORT   = "https://report.jpush.cn/v3/received"
+	BASE64_TABLE  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+)
+
+var base64Coder = base64.NewEncoding(BASE64_TABLE)
+
+type PushClient struct {
+	MasterSecret string
+	AppKey       string
+	AuthCode     string
+	BaseUrl      string
+}
+
+func NewPushClient(secret, appKey string) *PushClient {
+	//base64
+	auth := "Basic " + base64Coder.EncodeToString([]byte(appKey+":"+secret))
+	pusher := &PushClient{secret, appKey, auth, HOST_NAME_SSL}
+	return pusher
+}
+
+func (this *PushClient) Send(data []byte) (string, error) {
+	return this.SendPushBytes(data)
+}
+func (this *PushClient) CreateSchedule(data []byte) (string, error) {
+	// this.BaseUrl = HOST_SCHEDULE
+	return this.SendScheduleBytes(data, HOST_SCHEDULE)
+}
+func (this *PushClient) DeleteSchedule(id string) (string, error) {
+	// this.BaseUrl = HOST_SCHEDULE
+	return this.SendDeleteScheduleRequest(id, HOST_SCHEDULE)
+}
+func (this *PushClient) GetSchedule(id string) (string, error) {
+	// GET https://api.jpush.cn/v3/schedules/{schedule_id}
+	// this.BaseUrl = HOST_SCHEDULE
+	return this.SendGetScheduleRequest(id, HOST_SCHEDULE)
+
+}
+func (this *PushClient) GetReport(msg_ids string) (string, error) {
+	// this.BaseUrl = HOST_REPORT
+	return this.SendGetReportRequest(msg_ids, HOST_REPORT)
+}
+func (this *PushClient) SendPushString(content string) (string, error) {
+	ret, err := SendPostString(this.BaseUrl, content, this.AuthCode)
+	if err != nil {
+		return ret, err
+	}
+	if strings.Contains(ret, "msg_id") {
+		return ret, nil
+	} else {
+		return "", errors.New(ret)
+	}
+}
+
+func (this *PushClient) SendPushBytes(content []byte) (string, error) {
+	//ret, err := SendPostBytes(this.BaseUrl, content, this.AuthCode)
+	ret, err := SendPostBytes2(this.BaseUrl, content, this.AuthCode)
+	if err != nil {
+		return ret, err
+	}
+	if strings.Contains(ret, "msg_id") {
+		return ret, nil
+	} else {
+		return "", errors.New(ret)
+	}
+}
+
+func (this *PushClient) SendScheduleBytes(content []byte, url string) (string, error) {
+	ret, err := SendPostBytes2(url, content, this.AuthCode)
+	if err != nil {
+		return ret, err
+	}
+	if strings.Contains(ret, "schedule_id") {
+		return ret, nil
+	} else {
+		return "", errors.New(ret)
+	}
+
+}
+
+func (this *PushClient) SendGetReportRequest(msg_ids string, url string) (string, error) {
+	return Get(url).SetBasicAuth(this.AppKey, this.MasterSecret).Param("msg_ids", msg_ids).String()
+}
+
+func UnmarshalResponse(rsp string) (map[string]interface{}, error) {
+	mapRs := map[string]interface{}{}
+	if len(strings.TrimSpace(rsp)) == 0 {
+		return mapRs, nil
+	}
+	err := json.Unmarshal([]byte(rsp), &mapRs)
+	if err != nil {
+		return nil, err
+	}
+	if _, ok := mapRs["error"]; ok {
+		return nil, fmt.Errorf(rsp)
+	}
+	return mapRs, nil
+}
+
+func (this *PushClient) SendDeleteScheduleRequest(schedule_id string, url string) (string, error) {
+	rsp, err := Delete(strings.Join([]string{url, schedule_id}, "/")).Header("Authorization", this.AuthCode).String()
+	if err != nil {
+		return "", err
+	}
+	_, err = UnmarshalResponse(rsp)
+	if err != nil {
+		return "", err
+	}
+	return rsp, nil
+}
+func (this *PushClient) SendGetScheduleRequest(schedule_id string, url string) (string, error) {
+	rsp, err := Get(strings.Join([]string{url, schedule_id}, "/")).Header("Authorization", this.AuthCode).String()
+	if err != nil {
+		return "", err
+	}
+	_, err = UnmarshalResponse(rsp)
+	if err != nil {
+		return "", err
+	}
+	return rsp, nil
+}

+ 1 - 0
report.go

@@ -0,0 +1 @@
+package jpushclient

+ 51 - 0
schedule.go

@@ -0,0 +1,51 @@
+package jpushclient
+
+import (
+	"encoding/json"
+	"time"
+)
+
+type Schedule struct {
+	Cid		string				   `json:"cid"`
+	Name    string                 `json:"name"`
+	Enabled bool                   `json:"enabled"`
+	Trigger map[string]interface{} `json:"trigger"`
+	Push    *PayLoad               `json:"push"`
+}
+
+func NewSchedule(name,cid string, enabled bool, push *PayLoad) *Schedule {
+	return &Schedule{
+		Cid:	cid,
+		Name:    name,
+		Enabled: enabled,
+		Push:    push,
+	}
+}
+func (s *Schedule) SingleTrigger(t time.Time) {
+	s.Trigger = map[string]interface{}{
+		"single": map[string]interface{}{
+			"time": t.Format("2006-01-02 15:04:05"),
+		},
+	}
+
+}
+
+func (s *Schedule) PeriodicalTrigger(start time.Time, end time.Time, time time.Time, timeUnit string, frequency int, point []string) {
+	s.Trigger = map[string]interface{}{
+		"periodical": map[string]interface{}{
+			"start":     start.Format("2006-01-02 15:04:05"),
+			"end":       end.Format("2006-01-02 15:04:05"),
+			"time":      start.Format("15:04:05"),
+			"time_unit": timeUnit,
+			"frequency": frequency,
+			"point":     point,
+		},
+	}
+}
+func (this *Schedule) ToBytes() ([]byte, error) {
+	content, err := json.Marshal(this)
+	if err != nil {
+		return nil, err
+	}
+	return content, nil
+}