diff --git a/work/message/client.go b/work/message/client.go index c348997..2047ebd 100644 --- a/work/message/client.go +++ b/work/message/client.go @@ -1,17 +1,7 @@ package message import ( - "encoding/json" - "fmt" - "github.com/silenceper/wechat/v2/util" "github.com/silenceper/wechat/v2/work/context" - "strconv" -) - -const ( - messageSendURL = "https://qyapi.weixin.qq.com/cgi-bin/message/send" - messageUpdateTemplateCardURL = "https://api.weixin.qq.com/cgi-bin/message/update_template_card" - messageDelURL = "https://api.weixin.qq.com/cgi-bin/message/recall" ) // Client 应用消息 @@ -25,146 +15,3 @@ func NewClient(ctx *context.Context) *Client { ctx, } } - -// AppMessage 发送的模板消息内容 -type AppMessage struct { - ToUser string `json:"touser"` // 必须, 成员ID列表(多个接收者用‘|’分隔,最多支持1000个 ,指定为”@all”,则向该企业应用的全部成员发送 - Toparty string `json:"toparty"` //部门ID列表,当touser为”@all”时忽略本参数 - Totag string `json:"totag"` //标签ID列表,当touser为”@all”时忽略本参数 - Msgtype MsgType `json:"msgtype"` - Agentid int `json:"agentid"` - Safe int `json:"safe"` - EnableIdTrans int `json:"enable_id_trans"` - EnableDuplicateCheck int `json:"enable_duplicate_check"` - DuplicateCheckInterval int `json:"duplicate_check_interval"` - Text *Text `json:"text"` - *Image `json:"image"` - *Voice `json:"voice"` - *Video `json:"video"` - File *PushFile `json:"file"` - TextCard *PushTextCard `json:"textcard"` - News *News `json:"news"` - MpNews *MpNews `json:"mpnews"` - Markdown *Text `json:"markdown"` - //todo(wind) 可能会发生变化的字段直接用interface{}了 - MiniprogramNotice interface{} `json:"miniprogram_notice"` - TemplateCard interface{} `json:"template_card"` -} - -type PushFile struct { - MediaID string `json:"media_id"` -} -type PushTextCard struct { - Title string `json:"title"` - Description string `json:"description"` - Url string `json:"url"` - Btntxt string `json:"btntxt"` -} - -type resTemplateSend struct { - util.CommonError - Invaliduser string `json:"invaliduser"` //不合法的userid,不区分大小写,统一转为小写 - Invalidparty string `json:"invalidparty"` //不合法的partyid - Invalidtag string `json:"invalidtag"` //不合法的标签id - MsgID string `json:"msgid"` //消息id,用于撤回应用消息 - ResponseCode string `json:"response_code"` //仅消息类型为“按钮交互型”,“投票选择型”和“多项选择型”的模板卡片消息返回,应用可使用response_code调用更新模版卡片消息接口,24小时内有效,且只能使用一次 -} - -// Send 发送应用消息 -func (r *Client) Send(msg *AppMessage) (msgID string, responseCode string, err error) { - var accessToken string - accessToken, err = r.GetAccessToken() - if err != nil { - return - } - uri := fmt.Sprintf("%s?access_token=%s", messageSendURL, accessToken) - var response []byte - if msg.Agentid == 0 { - msg.Agentid, _ = strconv.Atoi(r.Context.AgentID) - } - response, err = util.PostJSON(uri, msg) - if err != nil { - return - } - var result resTemplateSend - err = json.Unmarshal(response, &result) - if err != nil { - return - } - if result.ErrCode != 0 { - err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg) - return - } - msgID = result.MsgID - responseCode = result.ResponseCode - return -} - -// TemplateUpdate 更新模版卡片消息内容 -type TemplateUpdate struct { - Userids []string `json:"userids"` - Partyids []int `json:"partyids"` - Tagids []int `json:"tagids"` - Atall int `json:"atall"` - Agentid int `json:"agentid"` - ResponseCode string `json:"response_code"` - *UpdateButton - *TemplateCard -} - -// UpdateTemplate 更新模版卡片消息 -func (r *Client) UpdateTemplate(msg *TemplateUpdate) (msgID string, err error) { - var accessToken string - accessToken, err = r.GetAccessToken() - if err != nil { - return - } - uri := fmt.Sprintf("%s?access_token=%s", messageUpdateTemplateCardURL, accessToken) - var response []byte - response, err = util.PostJSON(uri, msg) - if err != nil { - return - } - var result resTemplateSend - err = json.Unmarshal(response, &result) - if err != nil { - return - } - if result.ErrCode != 0 { - err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg) - return - } - msgID = result.MsgID - return -} - -type ReqRecall struct { - MsgID int64 `json:"msgid"` -} - -// Recall 撤回应用消息 -func (r *Client) Recall(msgID int64) (err error) { - var accessToken string - accessToken, err = r.GetAccessToken() - if err != nil { - return - } - uri := fmt.Sprintf("%s?access_token=%s", messageDelURL, accessToken) - var response []byte - response, err = util.PostJSON(uri, &ReqRecall{ - MsgID: msgID, - }) - if err != nil { - return - } - var result util.CommonError - err = json.Unmarshal(response, &result) - if err != nil { - return - } - if result.ErrCode != 0 { - err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg) - return - } - return -} diff --git a/work/message/template_card.go b/work/message/template_card.go index 29d3bef..c91bdef 100644 --- a/work/message/template_card.go +++ b/work/message/template_card.go @@ -1,6 +1,19 @@ package message -//UpdateButton 模板卡片按钮 +import ( + "encoding/json" + "fmt" + "github.com/silenceper/wechat/v2/util" + "strconv" +) + +const ( + messageSendURL = "https://qyapi.weixin.qq.com/cgi-bin/message/send" + messageUpdateTemplateCardURL = "https://api.weixin.qq.com/cgi-bin/message/update_template_card" + messageDelURL = "https://api.weixin.qq.com/cgi-bin/message/recall" +) + +// UpdateButton 模板卡片按钮 type UpdateButton struct { CommonToken `json:"-"` Button struct { @@ -8,15 +21,15 @@ type UpdateButton struct { } `xml:"Button" json:"button"` } -//NewUpdateButton 更新点击用户的按钮文案 +// NewUpdateButton 更新点击用户的按钮文案 func NewUpdateButton(replaceName string) *UpdateButton { btn := new(UpdateButton) btn.Button.ReplaceName = replaceName return btn } -//TemplateCard 被动回复模板卡片 -//https://open.work.weixin.qq.com/api/doc/90000/90135/90241 +// TemplateCard 被动回复模板卡片 +// https://open.work.weixin.qq.com/api/doc/90000/90135/90241 type TemplateCard struct { CommonToken `json:"-"` TemplateCard interface{} `xml:"TemplateCard" json:"template_card"` @@ -28,3 +41,252 @@ func NewTemplateCard(cardXml interface{}) *TemplateCard { card.TemplateCard = cardXml return card } + +// AppMessage 发送的模板消息内容 +type AppMessage struct { + ToUser string `json:"touser"` // 必须, 成员ID列表(多个接收者用‘|’分隔,最多支持1000个 ,指定为”@all”,则向该企业应用的全部成员发送 + Toparty string `json:"toparty,omitempty"` //部门ID列表,当touser为”@all”时忽略本参数 + Totag string `json:"totag,omitempty"` //标签ID列表,当touser为”@all”时忽略本参数 + Msgtype MsgType `json:"msgtype"` + Agentid int `json:"agentid"` + Safe int `json:"safe,omitempty"` + EnableIdTrans int `json:"enable_id_trans,omitempty"` + EnableDuplicateCheck int `json:"enable_duplicate_check,omitempty"` + DuplicateCheckInterval int `json:"duplicate_check_interval,omitempty"` + Text *Text `json:"text,omitempty"` + *Image `json:"image,omitempty"` + *Voice `json:"voice,omitempty"` + *Video `json:"video,omitempty"` + File *PushFile `json:"file,omitempty"` + TextCard *PushTextCard `json:"textcard,omitempty"` + News *News `json:"news,omitempty"` + MpNews *MpNews `json:"mpnews,omitempty"` + Markdown *Text `json:"markdown,omitempty"` + //todo(wind) 可能会发生变化的字段直接用interface{}了 + MiniprogramNotice interface{} `json:"miniprogram_notice,omitempty"` + TemplateCard *TemplateCardButton `json:"template_card,omitempty"` +} +type Action struct { + Text string `json:"text"` + Key string `json:"key"` +} +type HorizontalContent struct { + Keyname string `json:"keyname"` + Value string `json:"value,omitempty"` + Type int `json:"type,omitempty"` + Url string `json:"url,omitempty"` + MediaId string `json:"media_id,omitempty"` + Userid string `json:"userid,omitempty"` +} +type VerticalContent struct { + Title string `json:"title"` + Desc string `json:"desc"` +} +type Jump struct { + Type int `json:"type,omitempty"` + Title string `json:"title"` + Url string `json:"url,omitempty"` + Appid string `json:"appid,omitempty"` + Pagepath string `json:"pagepath,omitempty"` +} +type OptionCheckBox struct { + Id string `json:"id"` + Text string `json:"text"` + IsChecked bool `json:"is_checked"` +} +type Option struct { + Id string `json:"id"` + Text string `json:"text"` +} +type Select struct { + QuestionKey string `json:"question_key"` + Title string `json:"title"` + SelectedId string `json:"selected_id"` + OptionList []Option `json:"option_list"` +} +type Button struct { + Text string `json:"text"` + Style int `json:"style,omitempty"` + Key string `json:"key,omitempty"` +} + +type TemplateCardButton struct { + CardType string `json:"card_type"` + Source struct { + IconUrl string `json:"icon_url,omitempty"` + Desc string `json:"desc,omitempty"` + DescColor int `json:"desc_color,omitempty"` + } `json:"source,omitempty"` + + ActionMenu struct { + Desc string `json:"desc,omitempty"` + ActionList []Action `json:"action_list"` + } `json:"action_menu,omitempty"` + MainTitle struct { + Title string `json:"title"` + Desc string `json:"desc,omitempty"` + } `json:"main_title,omitempty"` + QuoteArea struct { + Type int `json:"type,omitempty"` + Url string `json:"url,omitempty"` + Title string `json:"title,omitempty"` + QuoteText string `json:"quote_text,omitempty"` + } `json:"quote_area"` + SubTitleText string `json:"sub_title_text,omitempty"` + HorizontalContentList []HorizontalContent `json:"horizontal_content_list"` + VerticalContentList []VerticalContent `json:"vertical_content_list,omitempty"` + CardAction struct { + Type int `json:"type,omitempty"` + Url string `json:"url,omitempty"` + Appid string `json:"appid,omitempty"` + Pagepath string `json:"pagepath,omitempty"` + } `json:"card_action"` + JumpList []Jump `json:"jump_list,omitempty"` + EmphasisContent struct { + Title string `json:"title,omitempty"` + Desc string `json:"desc,omitempty"` + } `json:"emphasis_content,omitempty"` + ImageTextArea struct { + Type int `json:"type"` + Url string `json:"url"` + Title string `json:"title"` + Desc string `json:"desc"` + ImageUrl string `json:"image_url"` + } `json:"image_text_area,omitempty"` + CardImage struct { + Url string `json:"url"` + AspectRatio float64 `json:"aspect_ratio"` + } `json:"card_image,omitempty"` + Checkbox struct { + QuestionKey string `json:"question_key"` + OptionList []OptionCheckBox `json:"option_list"` + Mode int `json:"mode"` + } `json:"checkbox,omitempty"` + SelectList []Select `json:"select_list"` + TaskId string `json:"task_id"` + ButtonSelection struct { + QuestionKey string `json:"question_key"` + Title string `json:"title,omitempty"` + OptionList []Option `json:"option_list,omitempty"` + SelectedId string `json:"selected_id,omitempty"` + } `json:"button_selection,omitempty"` + ButtonList []Button `json:"button_list"` +} + +type PushFile struct { + MediaID string `json:"media_id"` +} +type PushTextCard struct { + Title string `json:"title"` + Description string `json:"description"` + Url string `json:"url"` + Btntxt string `json:"btntxt"` +} + +type resTemplateSend struct { + util.CommonError + Invaliduser string `json:"invaliduser"` //不合法的userid,不区分大小写,统一转为小写 + Invalidparty string `json:"invalidparty"` //不合法的partyid + Invalidtag string `json:"invalidtag"` //不合法的标签id + MsgID string `json:"msgid"` //消息id,用于撤回应用消息 + ResponseCode string `json:"response_code"` //仅消息类型为“按钮交互型”,“投票选择型”和“多项选择型”的模板卡片消息返回,应用可使用response_code调用更新模版卡片消息接口,24小时内有效,且只能使用一次 +} + +// Send 发送应用消息 +func (r *Client) Send(msg *AppMessage) (msgID string, responseCode string, err error) { + var accessToken string + accessToken, err = r.GetAccessToken() + if err != nil { + return + } + uri := fmt.Sprintf("%s?access_token=%s", messageSendURL, accessToken) + var response []byte + if msg.Agentid == 0 { + msg.Agentid, _ = strconv.Atoi(r.Context.AgentID) + } + response, err = util.PostJSON(uri, msg) + if err != nil { + return + } + var result resTemplateSend + err = json.Unmarshal(response, &result) + if err != nil { + return + } + if result.ErrCode != 0 { + err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg) + return + } + msgID = result.MsgID + responseCode = result.ResponseCode + return +} + +// TemplateUpdate 更新模版卡片消息内容 +type TemplateUpdate struct { + Userids []string `json:"userids"` + Partyids []int `json:"partyids"` + Tagids []int `json:"tagids"` + Atall int `json:"atall"` + Agentid int `json:"agentid"` + ResponseCode string `json:"response_code"` + *UpdateButton + *TemplateCard +} + +// UpdateTemplate 更新模版卡片消息 +func (r *Client) UpdateTemplate(msg *TemplateUpdate) (msgID string, err error) { + var accessToken string + accessToken, err = r.GetAccessToken() + if err != nil { + return + } + uri := fmt.Sprintf("%s?access_token=%s", messageUpdateTemplateCardURL, accessToken) + var response []byte + response, err = util.PostJSON(uri, msg) + if err != nil { + return + } + var result resTemplateSend + err = json.Unmarshal(response, &result) + if err != nil { + return + } + if result.ErrCode != 0 { + err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg) + return + } + msgID = result.MsgID + return +} + +type ReqRecall struct { + MsgID int64 `json:"msgid"` +} + +// Recall 撤回应用消息 +func (r *Client) Recall(msgID int64) (err error) { + var accessToken string + accessToken, err = r.GetAccessToken() + if err != nil { + return + } + uri := fmt.Sprintf("%s?access_token=%s", messageDelURL, accessToken) + var response []byte + response, err = util.PostJSON(uri, &ReqRecall{ + MsgID: msgID, + }) + if err != nil { + return + } + var result util.CommonError + err = json.Unmarshal(response, &result) + if err != nil { + return + } + if result.ErrCode != 0 { + err = fmt.Errorf("template msg send error : errcode=%v , errmsg=%v", result.ErrCode, result.ErrMsg) + return + } + return +}