-
Notifications
You must be signed in to change notification settings - Fork 281
/
customer_service_message.go
288 lines (233 loc) · 7.36 KB
/
customer_service_message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package weapp
import (
"encoding/json"
"errors"
"net/http"
"strings"
"github.com/medivhzhan/weapp/v3/request"
)
const (
apiSendMessage = "/cgi-bin/message/custom/send"
apiSetTyping = "/cgi-bin/message/custom/typing"
apiUploadTemplateMedia = "/cgi-bin/media/upload"
apiGetTemplateMedia = "/cgi-bin/media/get"
)
// csMsgType 消息类型
type csMsgType string
// 所有消息类型
const (
csMsgTypeText csMsgType = "text" // 文本消息类型
csMsgTypeLink csMsgType = "link" // 图文链接消息类型
csMsgTypeImage csMsgType = "image" // 图片消息类型
csMsgTypeMPCard csMsgType = "miniprogrampage" // 小程序卡片消息类型
)
// csMessage 消息体
type csMessage struct {
Receiver string `json:"touser"` // user openID
Type csMsgType `json:"msgtype"` // text | image | link | miniprogrampage
Text *CSMsgText `json:"text,omitempty"`
Image *CSMsgImage `json:"image,omitempty"`
Link *CSMsgLink `json:"link,omitempty"`
MPCard *CSMsgMPCard `json:"miniprogrampage,omitempty"`
}
// CSMsgText 接收的文本消息
type CSMsgText struct {
Content string `json:"content"`
}
// SendTo 发送文本消息
//
// openID 用户openID
func (cli *Client) SendTextMsg(openID string, msg *CSMsgText) (*request.CommonError, error) {
params := csMessage{
Receiver: openID,
Type: csMsgTypeText,
Text: msg,
}
return cli.sendMessage(params)
}
// CSMsgImage 客服图片消息
type CSMsgImage struct {
MediaID string `json:"media_id"` // 发送的图片的媒体ID,通过 新增素材接口 上传图片文件获得。
}
// SendTo 发送图片消息
//
// openID 用户openID
func (cli *Client) SendImageMsg(openID string, msg *CSMsgImage) (*request.CommonError, error) {
params := csMessage{
Receiver: openID,
Type: csMsgTypeImage,
Image: msg,
}
return cli.sendMessage(params)
}
// CSMsgLink 图文链接消息
type CSMsgLink struct {
Title string `json:"title"`
Description string `json:"description"`
URL string `json:"url"`
ThumbURL string `json:"thumb_url"`
}
// SendTo 发送图文链接消息
//
// openID 用户openID
func (cli *Client) SendLinkMsg(openID string, msg *CSMsgLink) (*request.CommonError, error) {
params := csMessage{
Receiver: openID,
Type: csMsgTypeLink,
Link: msg,
}
return cli.sendMessage(params)
}
// CSMsgMPCard 接收的卡片消息
type CSMsgMPCard struct {
Title string `json:"title"` // 标题
PagePath string `json:"pagepath"` // 小程序页面路径
ThumbMediaID string `json:"thumb_media_id"` // 小程序消息卡片的封面, image 类型的 media_id,通过 新增素材接口 上传图片文件获得,建议大小为 520*416
}
// SendTo 发送卡片消息
//
// openID 用户openID
func (cli *Client) SendCardMsg(openID string, msg *CSMsgMPCard) (*request.CommonError, error) {
params := csMessage{
Receiver: openID,
Type: csMsgTypeMPCard,
MPCard: msg,
}
return cli.sendMessage(params)
}
// send 发送消息
func (cli *Client) sendMessage(params interface{}) (*request.CommonError, error) {
api := baseURL + apiSendMessage
token, err := cli.AccessToken()
if err != nil {
return nil, err
}
return cli.doSendMessage(api, token, params)
}
func (cli *Client) doSendMessage(api, token string, params interface{}) (*request.CommonError, error) {
url, err := tokenAPI(api, token)
if err != nil {
return nil, err
}
res := new(request.CommonError)
if err := cli.request.Post(url, params, res); err != nil {
return nil, err
}
return res, nil
}
// SetTypingCommand 下发客服当前输入状态命令
type SetTypingCommand = string
// 所有下发客服当前输入状态命令
const (
SetTypingCommandTyping SetTypingCommand = "Typing" // 对用户下发"正在输入"状态
SetTypingCommandCancelTyping SetTypingCommand = "CancelTyping" // 取消对用户的"正在输入"状态
)
// SetTyping 下发客服当前输入状态给用户。
//
// token 接口调用凭证
// openID 用户的 OpenID
// cmd 命令
func (cli *Client) SetTyping(openID string, cmd SetTypingCommand) (*request.CommonError, error) {
api := baseURL + apiSetTyping
token, err := cli.AccessToken()
if err != nil {
return nil, err
}
return cli.setTyping(token, openID, cmd, api)
}
func (cli *Client) setTyping(token, openID string, cmd SetTypingCommand, api string) (*request.CommonError, error) {
url, err := tokenAPI(api, token)
if err != nil {
return nil, err
}
params := requestParams{
"touser": openID,
"command": cmd,
}
res := new(request.CommonError)
if err := cli.request.Post(url, params, res); err != nil {
return nil, err
}
return res, nil
}
// TempMediaType 文件类型
type TempMediaType = string
// 所有文件类型
const (
TempMediaTypeImage TempMediaType = "image" // 图片
)
// UploadTempMediaResponse 上传媒体文件返回
type UploadTempMediaResponse struct {
request.CommonError
Type string `json:"type"` // 文件类型
MediaID string `json:"media_id"` // 媒体文件上传后,获取标识,3天内有效。
CreatedAt uint `json:"created_at"` // 媒体文件上传时间戳
}
// UploadTempMedia 把媒体文件上传到微信服务器。目前仅支持图片。用于发送客服消息或被动回复用户消息。
//
// token 接口调用凭证
// mediaType 文件类型
// medianame 媒体文件名
func (cli *Client) UploadTempMedia(mediaType TempMediaType, medianame string) (*UploadTempMediaResponse, error) {
api := baseURL + apiUploadTemplateMedia
token, err := cli.AccessToken()
if err != nil {
return nil, err
}
return cli.uploadTempMedia(token, mediaType, medianame, api)
}
func (cli *Client) uploadTempMedia(token string, mediaType TempMediaType, medianame, api string) (*UploadTempMediaResponse, error) {
queries := requestQueries{
"type": mediaType,
"access_token": token,
}
url, err := request.EncodeURL(api, queries)
if err != nil {
return nil, err
}
res := new(UploadTempMediaResponse)
if err := cli.request.FormPostWithFile(url, "media", medianame, res); err != nil {
return nil, err
}
return res, nil
}
// GetTempMedia 获取客服消息内的临时素材。即下载临时的多媒体文件。目前小程序仅支持下载图片文件。
//
// token 接口调用凭证
// mediaID 媒体文件 ID
func (cli *Client) GetTempMedia(mediaID string) (*http.Response, *request.CommonError, error) {
api := baseURL + apiGetTemplateMedia
token, err := cli.AccessToken()
if err != nil {
return nil, nil, err
}
return cli.getTempMedia(token, mediaID, api)
}
func (cli *Client) getTempMedia(token, mediaID, api string) (*http.Response, *request.CommonError, error) {
queries := requestQueries{
"access_token": token,
"media_id": mediaID,
}
url, err := request.EncodeURL(api, queries)
if err != nil {
return nil, nil, err
}
res, err := http.Get(url)
if err != nil {
return nil, nil, err
}
response := new(request.CommonError)
switch header := res.Header.Get("Content-Type"); {
case strings.HasPrefix(header, "application/json"): // 返回错误信息
if err := json.NewDecoder(res.Body).Decode(response); err != nil {
res.Body.Close()
return nil, nil, err
}
return res, response, nil
case strings.HasPrefix(header, "image"):
return res, response, nil
default:
res.Body.Close()
return nil, nil, errors.New("invalid response header: " + header)
}
}