manager.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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. if err != nil {
  66. return
  67. }
  68. customerServiceList = res.KfList
  69. return
  70. }
  71. // KeFuOnlineInfo 客服在线信息
  72. type KeFuOnlineInfo struct {
  73. KfAccount string `json:"kf_account"`
  74. Status int `json:"status"`
  75. KfID int `json:"kf_id"`
  76. AcceptedCase int `json:"accepted_case"`
  77. }
  78. type resKeFuOnlineList struct {
  79. util.CommonError
  80. KfOnlineList []*KeFuOnlineInfo `json:"kf_online_list"`
  81. }
  82. // OnlineList 获取在线客服列表
  83. func (csm *Manager) OnlineList() (customerServiceOnlineList []*KeFuOnlineInfo, err error) {
  84. var accessToken string
  85. accessToken, err = csm.GetAccessToken()
  86. if err != nil {
  87. return
  88. }
  89. uri := fmt.Sprintf("%s?access_token=%s", customerServiceOnlineListURL, accessToken)
  90. var response []byte
  91. response, err = util.HTTPGet(uri)
  92. if err != nil {
  93. return
  94. }
  95. var res resKeFuOnlineList
  96. err = util.DecodeWithError(response, &res, "ListOnlineCustomerService")
  97. if err != nil {
  98. return
  99. }
  100. customerServiceOnlineList = res.KfOnlineList
  101. return
  102. }
  103. // Add 添加客服账号
  104. func (csm *Manager) Add(kfAccount, nickName string) (err error) {
  105. // kfAccount:完整客服帐号,格式为:帐号前缀@公众号微信号,帐号前缀最多10个字符,必须是英文、数字字符或者下划线,后缀为公众号微信号,长度不超过30个字符
  106. // nickName:客服昵称,最长16个字
  107. // 参数此处均不做校验
  108. var accessToken string
  109. accessToken, err = csm.GetAccessToken()
  110. if err != nil {
  111. return
  112. }
  113. uri := fmt.Sprintf("%s?access_token=%s", customerServiceAddURL, accessToken)
  114. data := struct {
  115. KfAccount string `json:"kf_account"`
  116. NickName string `json:"nickname"`
  117. }{
  118. KfAccount: kfAccount,
  119. NickName: nickName,
  120. }
  121. var response []byte
  122. response, err = util.PostJSON(uri, data)
  123. if err != nil {
  124. return
  125. }
  126. err = util.DecodeWithCommonError(response, "AddCustomerService")
  127. return
  128. }
  129. // Update 修改客服账号
  130. func (csm *Manager) Update(kfAccount, nickName string) (err error) {
  131. var accessToken string
  132. accessToken, err = csm.GetAccessToken()
  133. if err != nil {
  134. return
  135. }
  136. uri := fmt.Sprintf("%s?access_token=%s", customerServiceUpdateURL, accessToken)
  137. data := struct {
  138. KfAccount string `json:"kf_account"`
  139. NickName string `json:"nickname"`
  140. }{
  141. KfAccount: kfAccount,
  142. NickName: nickName,
  143. }
  144. var response []byte
  145. response, err = util.PostJSON(uri, data)
  146. if err != nil {
  147. return
  148. }
  149. err = util.DecodeWithCommonError(response, "UpdateCustomerService")
  150. return
  151. }
  152. // Delete 删除客服帐号
  153. func (csm *Manager) Delete(kfAccount string) (err error) {
  154. var accessToken string
  155. accessToken, err = csm.GetAccessToken()
  156. if err != nil {
  157. return
  158. }
  159. uri := fmt.Sprintf("%s?access_token=%s", customerServiceDeleteURL, accessToken)
  160. data := struct {
  161. KfAccount string `json:"kf_account"`
  162. }{
  163. KfAccount: kfAccount,
  164. }
  165. var response []byte
  166. response, err = util.PostJSON(uri, data)
  167. if err != nil {
  168. return
  169. }
  170. err = util.DecodeWithCommonError(response, "DeleteCustomerService")
  171. return
  172. }
  173. // InviteBind 邀请绑定客服帐号和微信号
  174. func (csm *Manager) InviteBind(kfAccount, inviteWX string) (err error) {
  175. var accessToken string
  176. accessToken, err = csm.GetAccessToken()
  177. if err != nil {
  178. return
  179. }
  180. uri := fmt.Sprintf("%s?access_token=%s", customerServiceInviteURL, accessToken)
  181. data := struct {
  182. KfAccount string `json:"kf_account"`
  183. InviteWX string `json:"invite_wx"`
  184. }{
  185. KfAccount: kfAccount,
  186. InviteWX: inviteWX,
  187. }
  188. var response []byte
  189. response, err = util.PostJSON(uri, data)
  190. if err != nil {
  191. return
  192. }
  193. err = util.DecodeWithCommonError(response, "InviteBindCustomerService")
  194. return
  195. }
  196. // UploadHeadImg 上传客服头像
  197. func (csm *Manager) UploadHeadImg(kfAccount, fileName string) (err error) {
  198. var accessToken string
  199. accessToken, err = csm.GetAccessToken()
  200. if err != nil {
  201. return
  202. }
  203. uri := fmt.Sprintf("%s?access_token=%s&kf_account=%s", customerServiceUploadHeadImg, accessToken, kfAccount)
  204. var response []byte
  205. response, err = util.PostFile("media", fileName, uri)
  206. if err != nil {
  207. return
  208. }
  209. err = util.DecodeWithCommonError(response, "UploadCustomerServiceHeadImg")
  210. return
  211. }
  212. // SendTypingStatus 下发客服输入状态给用户
  213. func (csm *Manager) SendTypingStatus(openid string, cmd TypingStatus) (err error) {
  214. var accessToken string
  215. accessToken, err = csm.GetAccessToken()
  216. if err != nil {
  217. return
  218. }
  219. uri := fmt.Sprintf("%s?access_token=%s", customerServiceTypingURL, accessToken)
  220. data := struct {
  221. ToUser string `json:"touser"`
  222. Command string `json:"command"`
  223. }{
  224. ToUser: openid,
  225. Command: string(cmd),
  226. }
  227. var response []byte
  228. response, err = util.PostJSON(uri, data)
  229. if err != nil {
  230. return
  231. }
  232. err = util.DecodeWithCommonError(response, "SendTypingStatus")
  233. return
  234. }