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 }