manager.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package customerservice
  2. import (
  3. "fmt"
  4. "github.com/silenceper/wechat/v2/officialaccount/context"
  5. "github.com/silenceper/wechat/v2/util"
  6. )
  7. // TypingStatus 输入状态类型
  8. type TypingStatus string
  9. const (
  10. customerServiceListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist"
  11. customerServiceOnlineListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist"
  12. customerServiceAddURL = "https://api.weixin.qq.com/customservice/kfaccount/add"
  13. customerServiceUpdateURL = "https://api.weixin.qq.com/customservice/kfaccount/update"
  14. customerServiceDeleteURL = "https://api.weixin.qq.com/customservice/kfaccount/del"
  15. customerServiceInviteURL = "https://api.weixin.qq.com/customservice/kfaccount/inviteworker"
  16. customerServiceUploadHeadImg = "https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg"
  17. customerServiceTypingURL = "https://api.weixin.qq.com/cgi-bin/message/custom/typing"
  18. )
  19. const (
  20. // Typing 表示正在输入状态
  21. Typing TypingStatus = "Typing"
  22. // CancelTyping 表示取消正在输入状态
  23. CancelTyping TypingStatus = "CancelTyping"
  24. )
  25. // Manager 客服管理者,可以管理客服
  26. type Manager struct {
  27. *context.Context
  28. }
  29. // NewCustomerServiceManager 实例化客服管理
  30. func NewCustomerServiceManager(ctx *context.Context) *Manager {
  31. csm := new(Manager)
  32. csm.Context = ctx
  33. return csm
  34. }
  35. // KeFuInfo 客服基本信息
  36. type KeFuInfo struct {
  37. KfAccount string `json:"kf_account"` // 完整客服帐号,格式为:帐号前缀@公众号微信号
  38. KfNick string `json:"kf_nick"` // 客服昵称
  39. KfID int `json:"kf_id"` // 客服编号
  40. KfHeadImgURL string `json:"kf_headimgurl"` // 客服头像
  41. KfWX string `json:"kf_wx"` // 如果客服帐号已绑定了客服人员微信号, 则此处显示微信号
  42. InviteWX string `json:"invite_wx"` // 如果客服帐号尚未绑定微信号,但是已经发起了一个绑定邀请, 则此处显示绑定邀请的微信号
  43. InviteExpTime int `json:"invite_expire_time"` // 如果客服帐号尚未绑定微信号,但是已经发起过一个绑定邀请, 邀请的过期时间,为unix 时间戳
  44. InviteStatus string `json:"invite_status"` // 邀请的状态,有等待确认“waiting”,被拒绝“rejected”, 过期“expired”
  45. }
  46. type resKeFuList struct {
  47. util.CommonError
  48. KfList []*KeFuInfo `json:"kf_list"`
  49. }
  50. // List 获取所有客服基本信息
  51. func (csm *Manager) List() (customerServiceList []*KeFuInfo, err error) {
  52. var accessToken string
  53. accessToken, err = csm.GetAccessToken()
  54. if err != nil {
  55. return
  56. }
  57. uri := fmt.Sprintf("%s?access_token=%s", customerServiceListURL, accessToken)
  58. var response []byte
  59. response, err = util.HTTPGet(uri)
  60. if err != nil {
  61. return
  62. }
  63. var res resKeFuList
  64. err = util.DecodeWithError(response, &res, "ListCustomerService")
  65. return res.KfList, err
  66. }
  67. // KeFuOnlineInfo 客服在线信息
  68. type KeFuOnlineInfo struct {
  69. KfAccount string `json:"kf_account"`
  70. Status int `json:"status"`
  71. KfID int `json:"kf_id"`
  72. AcceptedCase int `json:"accepted_case"`
  73. }
  74. type resKeFuOnlineList struct {
  75. util.CommonError
  76. KfOnlineList []*KeFuOnlineInfo `json:"kf_online_list"`
  77. }
  78. // OnlineList 获取在线客服列表
  79. func (csm *Manager) OnlineList() (customerServiceOnlineList []*KeFuOnlineInfo, err error) {
  80. var accessToken string
  81. accessToken, err = csm.GetAccessToken()
  82. if err != nil {
  83. return
  84. }
  85. uri := fmt.Sprintf("%s?access_token=%s", customerServiceOnlineListURL, accessToken)
  86. var response []byte
  87. response, err = util.HTTPGet(uri)
  88. if err != nil {
  89. return
  90. }
  91. var res resKeFuOnlineList
  92. err = util.DecodeWithError(response, &res, "ListOnlineCustomerService")
  93. return res.KfOnlineList, err
  94. }
  95. // Add 添加客服账号
  96. func (csm *Manager) Add(kfAccount, nickName string) (err error) {
  97. // kfAccount:完整客服帐号,格式为:帐号前缀@公众号微信号,帐号前缀最多10个字符,必须是英文、数字字符或者下划线,后缀为公众号微信号,长度不超过30个字符
  98. // nickName:客服昵称,最长16个字
  99. // 参数此处均不做校验
  100. var accessToken string
  101. accessToken, err = csm.GetAccessToken()
  102. if err != nil {
  103. return
  104. }
  105. uri := fmt.Sprintf("%s?access_token=%s", customerServiceAddURL, accessToken)
  106. data := struct {
  107. KfAccount string `json:"kf_account"`
  108. NickName string `json:"nickname"`
  109. }{
  110. KfAccount: kfAccount,
  111. NickName: nickName,
  112. }
  113. var response []byte
  114. response, err = util.PostJSON(uri, data)
  115. if err != nil {
  116. return
  117. }
  118. err = util.DecodeWithCommonError(response, "AddCustomerService")
  119. return
  120. }
  121. // Update 修改客服账号
  122. func (csm *Manager) Update(kfAccount, nickName string) (err error) {
  123. var accessToken string
  124. accessToken, err = csm.GetAccessToken()
  125. if err != nil {
  126. return
  127. }
  128. uri := fmt.Sprintf("%s?access_token=%s", customerServiceUpdateURL, accessToken)
  129. data := struct {
  130. KfAccount string `json:"kf_account"`
  131. NickName string `json:"nickname"`
  132. }{
  133. KfAccount: kfAccount,
  134. NickName: nickName,
  135. }
  136. var response []byte
  137. response, err = util.PostJSON(uri, data)
  138. if err != nil {
  139. return
  140. }
  141. err = util.DecodeWithCommonError(response, "UpdateCustomerService")
  142. return
  143. }
  144. // Delete 删除客服帐号
  145. func (csm *Manager) Delete(kfAccount string) (err error) {
  146. var accessToken string
  147. accessToken, err = csm.GetAccessToken()
  148. if err != nil {
  149. return
  150. }
  151. uri := fmt.Sprintf("%s?access_token=%s", customerServiceDeleteURL, accessToken)
  152. data := struct {
  153. KfAccount string `json:"kf_account"`
  154. }{
  155. KfAccount: kfAccount,
  156. }
  157. var response []byte
  158. response, err = util.PostJSON(uri, data)
  159. if err != nil {
  160. return
  161. }
  162. err = util.DecodeWithCommonError(response, "DeleteCustomerService")
  163. return
  164. }
  165. // InviteBind 邀请绑定客服帐号和微信号
  166. func (csm *Manager) InviteBind(kfAccount, inviteWX string) (err error) {
  167. var accessToken string
  168. accessToken, err = csm.GetAccessToken()
  169. if err != nil {
  170. return
  171. }
  172. uri := fmt.Sprintf("%s?access_token=%s", customerServiceInviteURL, accessToken)
  173. data := struct {
  174. KfAccount string `json:"kf_account"`
  175. InviteWX string `json:"invite_wx"`
  176. }{
  177. KfAccount: kfAccount,
  178. InviteWX: inviteWX,
  179. }
  180. var response []byte
  181. response, err = util.PostJSON(uri, data)
  182. if err != nil {
  183. return
  184. }
  185. err = util.DecodeWithCommonError(response, "InviteBindCustomerService")
  186. return
  187. }
  188. // UploadHeadImg 上传客服头像
  189. func (csm *Manager) UploadHeadImg(kfAccount, fileName string) (err error) {
  190. var accessToken string
  191. accessToken, err = csm.GetAccessToken()
  192. if err != nil {
  193. return
  194. }
  195. uri := fmt.Sprintf("%s?access_token=%s&kf_account=%s", customerServiceUploadHeadImg, accessToken, kfAccount)
  196. var response []byte
  197. response, err = util.PostFile("media", fileName, uri)
  198. if err != nil {
  199. return
  200. }
  201. err = util.DecodeWithCommonError(response, "UploadCustomerServiceHeadImg")
  202. return
  203. }
  204. // SendTypingStatus 下发客服输入状态给用户
  205. func (csm *Manager) SendTypingStatus(openid string, cmd TypingStatus) (err error) {
  206. var accessToken string
  207. accessToken, err = csm.GetAccessToken()
  208. if err != nil {
  209. return
  210. }
  211. uri := fmt.Sprintf("%s?access_token=%s", customerServiceTypingURL, accessToken)
  212. data := struct {
  213. ToUser string `json:"touser"`
  214. Command string `json:"command"`
  215. }{
  216. ToUser: openid,
  217. Command: string(cmd),
  218. }
  219. var response []byte
  220. response, err = util.PostJSON(uri, data)
  221. if err != nil {
  222. return
  223. }
  224. err = util.DecodeWithCommonError(response, "SendTypingStatus")
  225. return
  226. }