461 lines
12 KiB
Go
461 lines
12 KiB
Go
package qyweixin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"git.u8t.cn/open/gosdk/util"
|
|
"git.u8t.cn/open/gosdk/wechat/message"
|
|
"github.com/smbrave/goutil"
|
|
"github.com/spf13/cast"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
ChangeTypeFromPool = 1
|
|
ChangeTypeTransform = 2
|
|
ChangeTypeFinish = 3
|
|
ChangeTypeRepeat = 4
|
|
|
|
OriginCustomer = 3 //用户发送的
|
|
OriginSystem = 4 //系统发送的
|
|
OriginServicer = 5 //客服发送的
|
|
OriginAi = 6 //AI发送的
|
|
|
|
EventTypeEnterSession = "enter_session"
|
|
EventTypeSessionChange = "session_status_change"
|
|
|
|
CustomerStateEnterSession = 0 //未处理,新会话,用户已发消息
|
|
CustomerStateRobot = 1 //由智能助手接待
|
|
CustomerStateWaitPool = 2 //待接入池排队中
|
|
CustomerStateServicing = 3 //由人工接待
|
|
CustomerStateComplete = 4 //已结束/未开始
|
|
)
|
|
|
|
type CustomerAccount struct {
|
|
Name string
|
|
KfId string
|
|
}
|
|
|
|
type CustomerMessage struct {
|
|
Msgid string
|
|
Msgtype string
|
|
ExternalUserid string
|
|
ServicerUserid string
|
|
OpenKfid string
|
|
Origin int
|
|
SendTime int64
|
|
SyncTime int64
|
|
Raw string
|
|
Text *CustomerText
|
|
Event *CustomerEvent
|
|
Image *CustomerImage
|
|
Video *CustomerVideo
|
|
Voice *CustomerVoice
|
|
Channels *CustomerChannels
|
|
}
|
|
|
|
type CustomerServicer struct {
|
|
Userid string
|
|
Status int
|
|
}
|
|
|
|
type CustomerState struct {
|
|
State int
|
|
Servicer string
|
|
}
|
|
|
|
type CustomerText struct {
|
|
Content string
|
|
MenuId string
|
|
}
|
|
|
|
type CustomerChannels struct {
|
|
Nickname string
|
|
Title string
|
|
SubType int
|
|
}
|
|
|
|
type CustomerImage struct {
|
|
MediaId string
|
|
}
|
|
type CustomerVideo struct {
|
|
MediaId string
|
|
}
|
|
|
|
type CustomerVoice struct {
|
|
MediaId string
|
|
}
|
|
|
|
type CustomerEvent struct {
|
|
EventType string `json:"event_type"`
|
|
Scene string `json:"scene"`
|
|
ChangeType int `json:"change_type"`
|
|
OldServicerUserid string `json:"old_servicer_userid"`
|
|
NewServicerUserid string `json:"new_servicer_userid"`
|
|
MsgCode string `json:"msg_code"`
|
|
WelcomeCode string `json:"welcome_code"`
|
|
}
|
|
|
|
type CustomerInfo struct {
|
|
Nickname string `json:"nickname"`
|
|
Avatar string `json:"avatar"`
|
|
Gender int `json:"gender"`
|
|
Unionid string `json:"unionid"`
|
|
Context interface{} `json:"enter_session_context"`
|
|
}
|
|
|
|
type AppCustomer struct {
|
|
App
|
|
}
|
|
|
|
func NewAppCustomer(cfg *AppConfig) *AppCustomer {
|
|
return &AppCustomer{
|
|
App: *NewApp(cfg),
|
|
}
|
|
}
|
|
|
|
func (a *AppCustomer) AccountList() ([]*CustomerAccount, error) {
|
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/account/list?access_token=%s", a.GetToken())
|
|
rspBody, err := util.HttpGet(reqUrl, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := a.GetResult(rspBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data := cast.ToSlice(result["account_list"])
|
|
accounts := make([]*CustomerAccount, 0)
|
|
for _, ac := range data {
|
|
acc := cast.ToStringMap(ac)
|
|
account := new(CustomerAccount)
|
|
account.Name = cast.ToString(acc["name"])
|
|
account.KfId = cast.ToString(acc["open_kfid"])
|
|
accounts = append(accounts, account)
|
|
}
|
|
return accounts, nil
|
|
}
|
|
|
|
func (a *AppCustomer) ServicerList(openKfid string) ([]*CustomerServicer, error) {
|
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/servicer/list?access_token=%s&open_kfid=%s", a.GetToken(), openKfid)
|
|
|
|
rspBody, err := util.HttpGet(reqUrl, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := a.GetResult(rspBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resultList := make([]*CustomerServicer, 0)
|
|
servicerList := cast.ToSlice(result["servicer_list"])
|
|
for _, s := range servicerList {
|
|
serv := cast.ToStringMap(s)
|
|
r := new(CustomerServicer)
|
|
r.Userid = cast.ToString(serv["userid"])
|
|
r.Status = cast.ToInt(serv["status"])
|
|
resultList = append(resultList, r)
|
|
}
|
|
return resultList, nil
|
|
}
|
|
|
|
func (a *AppCustomer) SetCustomerState(openKfid, externalUserid string, state *CustomerState) error {
|
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/trans?access_token=%s", a.GetToken())
|
|
params := make(map[string]interface{})
|
|
params["external_userid"] = externalUserid
|
|
params["open_kfid"] = openKfid
|
|
params["service_state"] = state.State
|
|
params["servicer_userid"] = state.Servicer
|
|
reqBody, _ := json.Marshal(params)
|
|
rspBody, err := util.HttpPostJson(reqUrl, nil, reqBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = a.GetResult(rspBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *AppCustomer) GetCustomerState(openKfid, externalUserid string) (*CustomerState, error) {
|
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/service_state/get?access_token=%s", a.GetToken())
|
|
params := make(map[string]interface{})
|
|
params["external_userid"] = externalUserid
|
|
params["open_kfid"] = openKfid
|
|
reqBody, _ := json.Marshal(params)
|
|
rspBody, err := util.HttpPostJson(reqUrl, nil, reqBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := a.GetResult(rspBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
state := new(CustomerState)
|
|
state.State = cast.ToInt(result["service_state"])
|
|
state.Servicer = cast.ToString(result["servicer_userid"])
|
|
return state, nil
|
|
}
|
|
|
|
func (q *AppCustomer) GetCustomerInfo(externalUserid string) (*CustomerInfo, error) {
|
|
|
|
postdata := map[string]interface{}{
|
|
"external_userid_list": []string{
|
|
externalUserid,
|
|
},
|
|
"need_enter_session_context": 1,
|
|
}
|
|
dataByte, err := json.Marshal(postdata)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rspBody, err := util.HttpPostJson("https://qyapi.weixin.qq.com/cgi-bin/kf/customer/batchget?access_token="+q.GetToken(), nil, dataByte)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result, err := q.GetResult(rspBody)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
users := cast.ToSlice(result["customer_list"])
|
|
if len(users) == 0 {
|
|
return nil, errors.New("no user info")
|
|
}
|
|
user := cast.ToStringMap(users[0])
|
|
u := new(CustomerInfo)
|
|
u.Nickname = cast.ToString(user["nickname"])
|
|
u.Avatar = cast.ToString(user["avatar"])
|
|
u.Unionid = cast.ToString(user["unionid"])
|
|
u.Gender = cast.ToInt(user["gender"])
|
|
u.Context = user["enter_session_context"]
|
|
return u, nil
|
|
}
|
|
|
|
func (a *AppCustomer) SendCustomerText(openKfId, toUser, content string) error {
|
|
return a.sendCustomerMessage(openKfId, toUser, "text", map[string]interface{}{
|
|
"content": content,
|
|
})
|
|
}
|
|
|
|
func (a *AppCustomer) SendCustomerVideo(openKfId, toUser, mediaId string) error {
|
|
return a.sendCustomerMessage(openKfId, toUser, "video", map[string]interface{}{
|
|
"media_id": mediaId,
|
|
})
|
|
}
|
|
|
|
func (a *AppCustomer) SendCustomerImage(openKfId, toUser, mediaId string) error {
|
|
return a.sendCustomerMessage(openKfId, toUser, "image", map[string]interface{}{
|
|
"media_id": mediaId,
|
|
})
|
|
}
|
|
|
|
func (a *AppCustomer) SendCustomerVoice(openKfId, toUser, mediaId string) error {
|
|
return a.sendCustomerMessage(openKfId, toUser, "voice", map[string]interface{}{
|
|
"media_id": mediaId,
|
|
})
|
|
}
|
|
|
|
func (a *AppCustomer) SendCustomerFile(openKfId, toUser, mediaId string) error {
|
|
return a.sendCustomerMessage(openKfId, toUser, "file", map[string]interface{}{
|
|
"media_id": mediaId,
|
|
})
|
|
}
|
|
|
|
func (a *AppCustomer) SendCustomerWelcome(code, message string) error {
|
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg_on_event?access_token=?access_token=%s", a.GetToken())
|
|
params := make(map[string]interface{})
|
|
params["code"] = code
|
|
|
|
params["text"] = map[string]interface{}{
|
|
"content": message,
|
|
}
|
|
reqBody, _ := json.Marshal(params)
|
|
rspBody, err := util.HttpPostJson(reqUrl, nil, reqBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = a.GetResult(rspBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *AppCustomer) sendCustomerMessage(openKfId, toUser, msgtype string, data interface{}) error {
|
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/send_msg?access_token=%s", a.GetToken())
|
|
params := make(map[string]interface{})
|
|
params["touser"] = toUser
|
|
params["open_kfid"] = openKfId
|
|
params["msgtype"] = msgtype
|
|
params[msgtype] = data
|
|
reqBody, _ := json.Marshal(params)
|
|
rspBody, err := util.HttpPostJson(reqUrl, nil, reqBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = a.GetResult(rspBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a *AppCustomer) SyncMessage(openKfId, cursor, token string) ([]*CustomerMessage, string, error) {
|
|
reqUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/kf/sync_msg?access_token=%s", a.GetToken())
|
|
params := make(map[string]interface{})
|
|
|
|
params["token"] = token
|
|
params["limit"] = 1000
|
|
params["voice_format"] = 0
|
|
params["open_kfid"] = openKfId
|
|
|
|
message := make([]*CustomerMessage, 0)
|
|
for {
|
|
params["cursor"] = cursor
|
|
reqBody, _ := json.Marshal(params)
|
|
rspBody, err := util.HttpPostJson(reqUrl, nil, reqBody)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
result, err := a.GetResult(rspBody)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
msgList := cast.ToSlice(result["msg_list"])
|
|
for _, msg := range msgList {
|
|
m := new(CustomerMessage)
|
|
m.From(cast.ToStringMap(msg))
|
|
message = append(message, m)
|
|
}
|
|
cursor = cast.ToString(result["next_cursor"])
|
|
hasMore := cast.ToInt(result["has_more"])
|
|
if hasMore == 0 {
|
|
break
|
|
}
|
|
}
|
|
return message, cursor, nil
|
|
}
|
|
|
|
func (q *AppCustomer) GetServiceLink(openKfId, sence string) (string, error) {
|
|
|
|
params := make(map[string]string)
|
|
params["open_kfid"] = openKfId
|
|
params["scene"] = sence
|
|
reqBody, _ := json.Marshal(params)
|
|
reqUrl := "https://qyapi.weixin.qq.com/cgi-bin/kf/add_contact_way?access_token=" + q.GetToken()
|
|
rspBody, err := util.HttpPostJson(reqUrl, nil, reqBody)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
result, err := q.GetResult(rspBody)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
customerServcieUrl := cast.ToString(result["url"])
|
|
if customerServcieUrl == "" {
|
|
return "", fmt.Errorf("%d:%s", cast.ToInt64(result["errcode"]), cast.ToString(result["errmsg"]))
|
|
}
|
|
return customerServcieUrl, nil
|
|
}
|
|
|
|
func (m *CustomerMessage) From(data map[string]interface{}) {
|
|
m.SyncTime = time.Now().Unix()
|
|
m.SendTime = cast.ToInt64(data["send_time"])
|
|
m.Msgid = cast.ToString(data["msgid"])
|
|
m.Msgtype = cast.ToString(data["msgtype"])
|
|
m.Origin = cast.ToInt(data["origin"])
|
|
m.ExternalUserid = cast.ToString(data["external_userid"])
|
|
m.ServicerUserid = cast.ToString(data["servicer_userid"])
|
|
m.OpenKfid = cast.ToString(data["open_kfid"])
|
|
m.Raw = goutil.EncodeJSON(data)
|
|
|
|
if m.Msgtype == message.MsgTypeEvent {
|
|
m.parseEvent(data)
|
|
} else if m.Msgtype == string(message.MsgTypeText) {
|
|
m.parseText(data)
|
|
} else if m.Msgtype == message.MsgTypeImage || m.Msgtype == message.MsgTypeVideo || m.Msgtype == message.MsgTypeVoice {
|
|
m.parseMedia(m.Msgtype, data)
|
|
} else if m.Msgtype == message.MsgTypeChannels {
|
|
m.parseChannels(data)
|
|
}
|
|
}
|
|
func (m *CustomerMessage) parseChannels(data map[string]interface{}) error {
|
|
channels, err := cast.ToStringMapE(data["channels"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
m.Channels = &CustomerChannels{
|
|
Title: cast.ToString(channels["title"]),
|
|
Nickname: cast.ToString(channels["nickname"]),
|
|
SubType: cast.ToInt(channels["sub_type"]),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *CustomerMessage) parseEvent(data map[string]interface{}) error {
|
|
event, err := cast.ToStringMapE(data["event"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
externalUserid := cast.ToString(event["external_userid"])
|
|
if externalUserid != "" {
|
|
m.ExternalUserid = externalUserid
|
|
}
|
|
|
|
openKfid := cast.ToString(event["open_kfid"])
|
|
if openKfid != "" {
|
|
m.OpenKfid = openKfid
|
|
}
|
|
|
|
m.Event = &CustomerEvent{
|
|
EventType: cast.ToString(event["event_type"]),
|
|
Scene: cast.ToString(event["scene"]),
|
|
ChangeType: cast.ToInt(event["change_type"]),
|
|
NewServicerUserid: cast.ToString(event["new_servicer_userid"]),
|
|
OldServicerUserid: cast.ToString(event["old_servicer_userid"]),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *CustomerMessage) parseText(data map[string]interface{}) error {
|
|
image, err := cast.ToStringMapE(data["text"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
m.Text = &CustomerText{
|
|
Content: cast.ToString(image["content"]),
|
|
MenuId: cast.ToString(image["menu_id"]),
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *CustomerMessage) parseMedia(msgType string, data map[string]interface{}) error {
|
|
media, err := cast.ToStringMapE(data[msgType])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if msgType == message.MsgTypeImage {
|
|
m.Image = &CustomerImage{
|
|
MediaId: cast.ToString(media["media_id"]),
|
|
}
|
|
} else if msgType == message.MsgTypeVideo {
|
|
m.Video = &CustomerVideo{
|
|
MediaId: cast.ToString(media["media_id"]),
|
|
}
|
|
} else if msgType == message.MsgTypeVoice {
|
|
m.Voice = &CustomerVoice{
|
|
MediaId: cast.ToString(media["media_id"]),
|
|
}
|
|
}
|
|
return nil
|
|
}
|