package customerservice import ( "fmt" "github.com/silenceper/wechat/v2/officialaccount/context" "github.com/silenceper/wechat/v2/util" ) // TypingStatus 输入状态类型 type TypingStatus string const ( customerServiceListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist" customerServiceOnlineListURL = "https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist" customerServiceAddURL = "https://api.weixin.qq.com/customservice/kfaccount/add" customerServiceUpdateURL = "https://api.weixin.qq.com/customservice/kfaccount/update" customerServiceDeleteURL = "https://api.weixin.qq.com/customservice/kfaccount/del" customerServiceInviteURL = "https://api.weixin.qq.com/customservice/kfaccount/inviteworker" customerServiceUploadHeadImg = "https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg" customerServiceTypingURL = "https://api.weixin.qq.com/cgi-bin/message/custom/typing" ) const ( // Typing 表示正在输入状态 Typing TypingStatus = "Typing" // CancelTyping 表示取消正在输入状态 CancelTyping TypingStatus = "CancelTyping" ) // Manager 客服管理者,可以管理客服 type Manager struct { *context.Context } // NewCustomerServiceManager 实例化客服管理 func NewCustomerServiceManager(ctx *context.Context) *Manager { csm := new(Manager) csm.Context = ctx return csm } // KeFuInfo 客服基本信息 type KeFuInfo struct { KfAccount string `json:"kf_account"` // 完整客服帐号,格式为:帐号前缀@公众号微信号 KfNick string `json:"kf_nick"` // 客服昵称 KfID int `json:"kf_id"` // 客服编号 KfHeadImgURL string `json:"kf_headimgurl"` // 客服头像 KfWX string `json:"kf_wx"` // 如果客服帐号已绑定了客服人员微信号, 则此处显示微信号 InviteWX string `json:"invite_wx"` // 如果客服帐号尚未绑定微信号,但是已经发起了一个绑定邀请, 则此处显示绑定邀请的微信号 InviteExpTime int `json:"invite_expire_time"` // 如果客服帐号尚未绑定微信号,但是已经发起过一个绑定邀请, 邀请的过期时间,为unix 时间戳 InviteStatus string `json:"invite_status"` // 邀请的状态,有等待确认“waiting”,被拒绝“rejected”, 过期“expired” } type resKeFuList struct { util.CommonError KfList []*KeFuInfo `json:"kf_list"` } // List 获取所有客服基本信息 func (csm *Manager) List() (customerServiceList []*KeFuInfo, err error) { var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", customerServiceListURL, accessToken) var response []byte response, err = util.HTTPGet(uri) if err != nil { return } var res resKeFuList err = util.DecodeWithError(response, &res, "ListCustomerService") return res.KfList, err } // KeFuOnlineInfo 客服在线信息 type KeFuOnlineInfo struct { KfAccount string `json:"kf_account"` Status int `json:"status"` KfID int `json:"kf_id"` AcceptedCase int `json:"accepted_case"` } type resKeFuOnlineList struct { util.CommonError KfOnlineList []*KeFuOnlineInfo `json:"kf_online_list"` } // OnlineList 获取在线客服列表 func (csm *Manager) OnlineList() (customerServiceOnlineList []*KeFuOnlineInfo, err error) { var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", customerServiceOnlineListURL, accessToken) var response []byte response, err = util.HTTPGet(uri) if err != nil { return } var res resKeFuOnlineList err = util.DecodeWithError(response, &res, "ListOnlineCustomerService") return res.KfOnlineList, err } // Add 添加客服账号 func (csm *Manager) Add(kfAccount, nickName string) (err error) { // kfAccount:完整客服帐号,格式为:帐号前缀@公众号微信号,帐号前缀最多10个字符,必须是英文、数字字符或者下划线,后缀为公众号微信号,长度不超过30个字符 // nickName:客服昵称,最长16个字 // 参数此处均不做校验 var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", customerServiceAddURL, accessToken) data := struct { KfAccount string `json:"kf_account"` NickName string `json:"nickname"` }{ KfAccount: kfAccount, NickName: nickName, } var response []byte response, err = util.PostJSON(uri, data) if err != nil { return } err = util.DecodeWithCommonError(response, "AddCustomerService") return } // Update 修改客服账号 func (csm *Manager) Update(kfAccount, nickName string) (err error) { var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", customerServiceUpdateURL, accessToken) data := struct { KfAccount string `json:"kf_account"` NickName string `json:"nickname"` }{ KfAccount: kfAccount, NickName: nickName, } var response []byte response, err = util.PostJSON(uri, data) if err != nil { return } err = util.DecodeWithCommonError(response, "UpdateCustomerService") return } // Delete 删除客服帐号 func (csm *Manager) Delete(kfAccount string) (err error) { var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", customerServiceDeleteURL, accessToken) data := struct { KfAccount string `json:"kf_account"` }{ KfAccount: kfAccount, } var response []byte response, err = util.PostJSON(uri, data) if err != nil { return } err = util.DecodeWithCommonError(response, "DeleteCustomerService") return } // InviteBind 邀请绑定客服帐号和微信号 func (csm *Manager) InviteBind(kfAccount, inviteWX string) (err error) { var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", customerServiceInviteURL, accessToken) data := struct { KfAccount string `json:"kf_account"` InviteWX string `json:"invite_wx"` }{ KfAccount: kfAccount, InviteWX: inviteWX, } var response []byte response, err = util.PostJSON(uri, data) if err != nil { return } err = util.DecodeWithCommonError(response, "InviteBindCustomerService") return } // UploadHeadImg 上传客服头像 func (csm *Manager) UploadHeadImg(kfAccount, fileName string) (err error) { var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s&kf_account=%s", customerServiceUploadHeadImg, accessToken, kfAccount) var response []byte response, err = util.PostFile("media", fileName, uri) if err != nil { return } err = util.DecodeWithCommonError(response, "UploadCustomerServiceHeadImg") return } // SendTypingStatus 下发客服输入状态给用户 func (csm *Manager) SendTypingStatus(openid string, cmd TypingStatus) (err error) { var accessToken string accessToken, err = csm.GetAccessToken() if err != nil { return } uri := fmt.Sprintf("%s?access_token=%s", customerServiceTypingURL, accessToken) data := struct { ToUser string `json:"touser"` Command string `json:"command"` }{ ToUser: openid, Command: string(cmd), } var response []byte response, err = util.PostJSON(uri, data) if err != nil { return } err = util.DecodeWithCommonError(response, "SendTypingStatus") return }