From 6f42bd1b336d006416989bb03c0c72eea369385d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Thu, 28 Sep 2023 07:10:09 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E2=9C=A8=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=BA=86news=E7=B1=BB=E5=9E=8B=E6=97=A0=E6=B3=95=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=BA=86message=E7=9A=84=E6=B5=8B=E8=AF=95=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=A8=A1=E6=9D=BF=E5=8D=A1=E7=89=87=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- message.go | 44 ++++----- message_test.go | 249 ++++++++++++++++++++++++++++++++++++++++++++++++ models.go | 190 ++++++++++++++++++++++++++++++++++++ 3 files changed, 458 insertions(+), 25 deletions(-) create mode 100644 message_test.go diff --git a/message.go b/message.go index 928bea5..7710128 100644 --- a/message.go +++ b/message.go @@ -122,20 +122,14 @@ func (c *WorkwxApp) SendTextCardMessage( // 否则为单纯的【发送应用消息】接口调用。 func (c *WorkwxApp) SendNewsMessage( recipient *Recipient, - title string, - description string, - url string, - picURL string, + articles []Article, isSafe bool, ) error { return c.sendMessage( recipient, "news", map[string]interface{}{ - "title": title, - "description": description, // TODO: 零值 - "url": url, - "picurl": picURL, // TODO: 零值 + "articles": articles, }, isSafe, ) } @@ -146,29 +140,14 @@ func (c *WorkwxApp) SendNewsMessage( // 否则为单纯的【发送应用消息】接口调用。 func (c *WorkwxApp) SendMPNewsMessage( recipient *Recipient, - title string, - thumbMediaID string, - author string, - sourceContentURL string, - content string, - digest string, + mparticles []MPArticle, isSafe bool, ) error { return c.sendMessage( recipient, "mpnews", map[string]interface{}{ - // TODO: 支持发送多条图文 - "articles": []interface{}{ - map[string]interface{}{ - "title": title, - "thumb_media_id": thumbMediaID, - "author": author, // TODO: 零值 - "content_source_url": sourceContentURL, // TODO: 零值 - "content": content, - "digest": digest, - }, - }, + "articles": mparticles, }, isSafe, ) } @@ -210,6 +189,21 @@ func (c *WorkwxApp) SendTaskCardMessage( ) } +// SendTemplateCardMessage 发送卡片模板消息 +func (c *WorkwxApp) SendTemplateCardMessage( + recipient *Recipient, + template_card TemplateCard, + isSafe bool, +) error { + return c.sendMessage( + recipient, + "template_card", + map[string]interface{}{ + "template_card": template_card, + }, isSafe, + ) +} + // sendMessage 发送消息底层接口 // // 收件人参数如果仅设置了 `ChatID` 字段,则为【发送消息到群聊会话】接口调用; diff --git a/message_test.go b/message_test.go new file mode 100644 index 0000000..a862bf3 --- /dev/null +++ b/message_test.go @@ -0,0 +1,249 @@ +package workwx + +import ( + "bufio" + "fmt" + "net/http" + "os" + "testing" +) + +var ( + wxclient *WorkwxApp + // 企业ID + corpID string = "xxxxxxx" + // 应用ID + agentID int64 = 007 + // 应用秘钥 + agentSecret string = "xxxxxxxxxx" + // 测试接收消息的用户ID + userID string = "userid" +) + +func init() { + var wx = New(corpID) + wxclient = wx.WithApp(agentSecret, agentID) + wxclient.SpawnAccessTokenRefresher() // 自动刷新token +} + +func TestSendTextMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + _ = wxclient.SendTextMessage(&recipient, "这是一条普通文本消息", false) +} + +func TestSendMarkdownMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + _ = wxclient.SendMarkdownMessage(&recipient, "您的会议室已经预定,稍后会同步到`邮箱` \n>**事项详情** \n>事 项:开会 \n>组织者:@miglioguan \n>参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n> \n>会议室:广州TIT 1楼 301 \n>日 期:2018年5月18日 \n>时 间:上午9:00-11:00 \n> \n>请准时参加会议。 \n> \n>如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)", false) +} + +func TestSendImageMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + url := "https://wwcdn.weixin.qq.com/node/wework/images/202201062104.366e5ee28e.png" + resp, err := http.Get(url) + if err != nil { + fmt.Println("HTTP GET请求失败:", err) + return + } + defer resp.Body.Close() + reader := resp.Body + + rst, err := wxclient.UploadTempImageMedia(&Media{ + filename: "test.jpg", + filesize: 0, + stream: reader, + }) + if err != nil { + fmt.Printf("upload temp image failed: %v\n", err) + } + _ = wxclient.SendImageMessage(&recipient, rst.MediaID, false) +} + +func TestSendFileMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + file, err := os.Open("go.mod") + if err != nil { + fmt.Println("Error opening file:", err) + return + } + defer file.Close() + + reader := bufio.NewReader(file) + + rst, err := wxclient.UploadTempFileMedia(&Media{ + filename: "go.mod", + filesize: 0, + stream: reader, + }) + if err != nil { + fmt.Printf("upload temp file failed: %v\n", err) + } + _ = wxclient.SendFileMessage(&recipient, rst.MediaID, false) +} + +func TestSendTextCardMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + _ = wxclient.SendTextCardMessage( + &recipient, "领奖通知", "
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
", "https://wiki.eryajf.net", "更多", false) +} + +func TestSendNewsMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + + msgObj := []Article{ + {Title: "中秋节礼品领取", + Description: "今年中秋节公司有豪礼相送", + Url: "https://wiki.eryajf.net", + PicUrl: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", + AppId: "", + PagePath: ""}, + {Title: "中秋节礼品领取2", + Description: "今年中秋节公司有豪礼相送2", + Url: "https://wiki.eryajf.net", + PicUrl: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", + AppId: "", + PagePath: ""}, + } + + _ = wxclient.SendNewsMessage(&recipient, msgObj, false) +} +func TestSendMPNewsMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + + url := "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png" + resp, err := http.Get(url) + if err != nil { + fmt.Println("HTTP GET请求失败:", err) + return + } + defer resp.Body.Close() + reader := resp.Body + + rst, err := wxclient.UploadTempImageMedia(&Media{ + filename: "test.jpg", + filesize: 0, + stream: reader, + }) + if err != nil { + fmt.Printf("upload temp image failed: %v\n", err) + } + + msgObj := []MPArticle{ + {Title: "中秋节礼品领取", + ThumbMediaID: rst.MediaID, + Author: "eryajf", + ContentSourceUrl: "https://wiki.eryajf.net", + Content: "这是正文里边的内容。", + Digest: "这里是图文消息的描述"}, + } + + _ = wxclient.SendMPNewsMessage(&recipient, msgObj, false) +} + +func TestSendTaskCardMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + btn := []TaskCardBtn{ + { + Key: "yes", + Name: "通过", + ReplaceName: "已通过", + Color: "blue", + IsBold: false, + }, { + Key: "no", + Name: "拒绝", + ReplaceName: "已拒绝", + Color: "red", + IsBold: false, + }} + _ = wxclient.SendTaskCardMessage(&recipient, "请审核该条信息", "这是说明信息", "https://wiki.eryajf.net", "aaab", btn, false) +} + +func TestSendTemplateCardMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + msgObj := TemplateCard{ + CardType: CardTypeTextNotice, + Source: Source{ + IconURL: "https://t.eryajf.net/imgs/2023/02/712e2287455b9a0c.png", + Desc: "二丫讲梵的公众号", + DescColor: 0, + }, + ActionMenu: &ActionMenu{ + Desc: "卡片副交互辅助文本说明", + ActionList: []ActionList{ + {Text: "接受推送", Key: "A"}, + {Text: "不再推送", Key: "B"}, + }, + }, + TaskID: "aaadaa", + MainTitle: MainTitle{ + Title: "欢迎使用企业微信", + Desc: "你的朋友也都在用。", + }, + QuoteArea: QuoteArea{ + Type: 0, + URL: "baidu.com", + Title: "百度", + QuoteText: "去往百度", + }, + EmphasisContent: &EmphasisContent{ + Title: "100", + Desc: "核心数据", + }, + SubTitleText: "下载企业微信还能抢红包!", + CardAction: CardAction{ + Type: 1, + URL: "qq.com", + Appid: "aaaaaaa", + Pagepath: "/index.html", + }, + } + err := wxclient.SendTemplateCardMessage(&recipient, msgObj, false) + if err != nil { + fmt.Printf("get err: %v\n", err) + } +} diff --git a/models.go b/models.go index fc0d345..7f20e6d 100644 --- a/models.go +++ b/models.go @@ -1040,6 +1040,196 @@ type TaskCardBtn struct { IsBold bool `json:"is_bold"` } +// news 类型的文章 +type Article struct { + Title string `json:"title"` + Description string `json:"description"` + Url string `json:"url"` + PicUrl string `json:"picurl"` + AppId string `json:"appid"` + PagePath string `json:"pagepath"` +} + +// mpnews 类型的文章 +type MPArticle struct { + Title string `json:"title"` + ThumbMediaID string `json:"thumb_media_id"` + Author string `json:"author"` + ContentSourceUrl string `json:"content_source_url"` + Content string `json:"content"` + Digest string `json:"digest"` +} + +// ============================== + +// TemplateCardMessage 测试发送模板卡片消息必需配置应用回调地址 +type TemplateCardMessage struct { + // Message + TemplateCard TemplateCard `json:"template_card"` +} + +type Source struct { + IconURL string `json:"icon_url"` + Desc string `json:"desc"` + DescColor int `json:"desc_color"` +} +type ActionList struct { + Text string `json:"text"` + Key string `json:"key"` +} +type ActionMenu struct { + Desc string `json:"desc"` + ActionList []ActionList `json:"action_list"` +} +type MainTitle struct { + Title string `json:"title"` + Desc string `json:"desc"` +} +type QuoteArea struct { + Type int `json:"type"` + URL string `json:"url"` + Title string `json:"title"` + QuoteText string `json:"quote_text"` +} + +// EmphasisContent 文本通知型 +type EmphasisContent struct { + Title string `json:"title"` + Desc string `json:"desc"` +} +type HorizontalContentList struct { + KeyName string `json:"keyname"` + Value string `json:"value"` + Type int `json:"type,omitempty"` + URL string `json:"url,omitempty"` + MediaID string `json:"media_id,omitempty"` + Userid string `json:"userid,omitempty"` +} +type JumpList struct { + Type int `json:"type"` + Title string `json:"title"` + URL string `json:"url,omitempty"` + Appid string `json:"appid,omitempty"` + PagePath string `json:"pagepath,omitempty"` +} +type CardAction struct { + Type int `json:"type"` + URL string `json:"url"` + Appid string `json:"appid"` + Pagepath string `json:"pagepath"` +} + +// ImageTextArea 图文展示型 +type ImageTextArea struct { + Type int `json:"type" validate:"omitempty,oneof=0 1 2"` + URL string `json:"url"` + AppId string `json:"appid,omitempty"` + PagePath string `json:"pagepath,omitempty"` + Title string `json:"title"` + Desc string `json:"desc"` + ImageURL string `json:"image_url" validate:"required"` +} +type CardImage struct { + Url string `json:"url" validate:"required"` + AspectRatio float32 `json:"aspect_ratio" validate:"max=2.25,min=1.3"` +} + +// ButtonSelection 按钮交互型 +type ButtonSelection struct { + QuestionKey string `json:"question_key" validate:"required"` + Title string `json:"title"` + OptionList []struct { + ID string `json:"id" validate:"required"` + Text string `json:"text" validate:"required"` + } `json:"option_list" validate:"required"` + SelectedID string `json:"selected_id"` +} +type Button struct { + Type int `json:"type,omitempty"` //按钮点击事件类型,0 或不填代表回调点击事件,1 代表跳转url + Text string `json:"text" validate:"required"` + Style int `json:"style,omitempty"` //按钮样式,目前可填1~4,不填或错填默认1 + Key string `json:"key,omitempty"` // 按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复,button_list.type是0时必填 + Url string `json:"url,omitempty"` //跳转事件的url,button_list.type是1时必填 +} + +// CheckBox 投票选择型 +type CheckBox struct { + QuestionKey string `json:"question_key" validate:"required"` + OptionList []struct { + ID string `json:"id" validate:"required"` + Text string `json:"text" validate:"required"` + IsChecked bool `json:"is_checked" validate:"required"` + } `json:"option_list" validate:"required,min=1,max=20"` + Mode int `json:"mode" validate:"omitempty,oneof=0 1"` +} +type SubmitButton struct { + Text string `json:"text" validate:"required"` + Key string `json:"key" validate:"required"` +} + +// SelectList 多项选择型 +type SelectList struct { + QuestionKey string `json:"question_key" validate:"required"` + Title string `json:"title,omitempty"` + SelectedID string `json:"selected_id,omitempty"` + OptionList []OptionList `json:"option_list" validate:"required"` +} + +type OptionList struct { + ID string `json:"id" validate:"required"` + Text string `json:"text" validate:"required"` +} + +type TemplateCardType string + +const ( + CardTypeTextNotice TemplateCardType = "text_notice" + CardTypeNewsNotice TemplateCardType = "news_notice" + CardTypeButtonInteraction TemplateCardType = "button_interaction" + CardTypeVoteInteraction TemplateCardType = "vote_interaction" + CardTypeMultipleInteraction TemplateCardType = "multiple_interaction" +) + +type TemplateCard struct { + CardType TemplateCardType `json:"card_type"` + Source Source `json:"source"` + ActionMenu *ActionMenu `json:"action_menu,omitempty" validate:"required_with=TaskID"` + TaskID string `json:"task_id,omitempty" validate:"required_with=ActionMenu"` + MainTitle MainTitle `json:"main_title"` + QuoteArea QuoteArea `json:"quote_area"` + // 文本通知型 + EmphasisContent *EmphasisContent `json:"emphasis_content,omitempty"` + SubTitleText string `json:"sub_title_text,omitempty"` + // 图文展示型 + ImageTextArea *ImageTextArea `json:"image_text_area,omitempty"` + CardImage *CardImage `json:"card_image,omitempty"` + HorizontalContentList []HorizontalContentList `json:"horizontal_content_list"` + JumpList []JumpList `json:"jump_list"` + CardAction CardAction `json:"card_action,omitempty"` + // 按钮交互型 + ButtonSelection *ButtonSelection `json:"button_selection,omitempty"` + ButtonList []Button `json:"button_list,omitempty" validate:"omitempty,max=6"` + // 投票选择型 + CheckBox *CheckBox `json:"checkbox,omitempty"` + SelectList []SelectList `json:"select_list,omitempty" validate:"max=3"` + SubmitButton *SubmitButton `json:"submit_button,omitempty"` +} + +type TemplateCardUpdateMessage struct { + UserIds []string `json:"userids" validate:"omitempty,max=100"` + PartyIds []int64 `json:"partyids" validate:"omitempty,max=100"` + TagIds []int32 `json:"tagids" validate:"omitempty,max=100"` + AtAll int `json:"atall,omitempty"` + ResponseCode string `json:"response_code" validate:"required"` + Button struct { + ReplaceName string `json:"replace_name" validate:"required"` + } `json:"button" validate:"required_without=TemplateCard"` + TemplateCard TemplateCard `json:"template_card" validate:"required_without=Button"` + ReplaceText string `json:"replace_text,omitempty"` +} + +// ============================== + type reqTransferCustomer struct { // HandoverUserID 原跟进成员的userid HandoverUserID string `json:"handover_userid"` From 5be0a83cfcddabc72996709550b49c61dfc70b30 Mon Sep 17 00:00:00 2001 From: eryajf Date: Thu, 28 Sep 2023 23:04:59 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E2=9C=A8=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=BA=86news=E7=B1=BB=E5=9E=8B=E6=97=A0=E6=B3=95=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=8C=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=8D=A1=E7=89=87=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/workwxctl/commands/cmd_send_message.go | 30 ++- message.go | 4 +- message_test.go | 84 ++++-- models.go | 292 ++++++++++++++------- 4 files changed, 283 insertions(+), 127 deletions(-) diff --git a/cmd/workwxctl/commands/cmd_send_message.go b/cmd/workwxctl/commands/cmd_send_message.go index 3d55cae..7188852 100644 --- a/cmd/workwxctl/commands/cmd_send_message.go +++ b/cmd/workwxctl/commands/cmd_send_message.go @@ -26,7 +26,7 @@ func cmdSendMessage(c *cli.Context) error { url := c.String(flagURL) picURL := c.String(flagPicURL) buttonText := c.String(flagButtonText) - sourceContentURL := c.String(flagSourceContentURL) + // sourceContentURL := c.String(flagSourceContentURL) digest := c.String(flagDigest) app := cfg.MakeWorkwxApp() @@ -73,21 +73,29 @@ func cmdSendMessage(c *cli.Context) error { case "news": err = app.SendNewsMessage( &recipient, - title, - description, - url, - picURL, + []workwx.Article{ + workwx.Article{ + Title: title, + Description: description, + URL: url, + PicURL: picURL, + AppID: "", + PagePath: "", + }, + }, isSafe, ) case "mpnews": err = app.SendMPNewsMessage( &recipient, - title, - thumbMediaID, - author, - sourceContentURL, - content, - digest, + []workwx.MPArticle{workwx.MPArticle{ + Title: title, + ThumbMediaID: thumbMediaID, + Author: author, + ContentSourceURL: content, + Content: content, + Digest: digest, + }}, isSafe, ) default: diff --git a/message.go b/message.go index 7710128..aac213d 100644 --- a/message.go +++ b/message.go @@ -192,14 +192,14 @@ func (c *WorkwxApp) SendTaskCardMessage( // SendTemplateCardMessage 发送卡片模板消息 func (c *WorkwxApp) SendTemplateCardMessage( recipient *Recipient, - template_card TemplateCard, + templateCard TemplateCard, isSafe bool, ) error { return c.sendMessage( recipient, "template_card", map[string]interface{}{ - "template_card": template_card, + "template_card": templateCard, }, isSafe, ) } diff --git a/message_test.go b/message_test.go index a862bf3..a96a5ca 100644 --- a/message_test.go +++ b/message_test.go @@ -33,7 +33,10 @@ func TestSendTextMessage(t *testing.T) { TagIDs: []string{}, ChatID: "", } - _ = wxclient.SendTextMessage(&recipient, "这是一条普通文本消息", false) + err := wxclient.SendTextMessage(&recipient, "这是一条普通文本消息", false) + if err != nil { + fmt.Printf("get err: %v\n", err) + } } func TestSendMarkdownMessage(t *testing.T) { @@ -122,15 +125,15 @@ func TestSendNewsMessage(t *testing.T) { msgObj := []Article{ {Title: "中秋节礼品领取", Description: "今年中秋节公司有豪礼相送", - Url: "https://wiki.eryajf.net", - PicUrl: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", - AppId: "", + URL: "https://wiki.eryajf.net", + PicURL: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", + AppID: "", PagePath: ""}, {Title: "中秋节礼品领取2", Description: "今年中秋节公司有豪礼相送2", - Url: "https://wiki.eryajf.net", - PicUrl: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", - AppId: "", + URL: "https://wiki.eryajf.net", + PicURL: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", + AppID: "", PagePath: ""}, } @@ -166,7 +169,7 @@ func TestSendMPNewsMessage(t *testing.T) { {Title: "中秋节礼品领取", ThumbMediaID: rst.MediaID, Author: "eryajf", - ContentSourceUrl: "https://wiki.eryajf.net", + ContentSourceURL: "https://wiki.eryajf.net", Content: "这是正文里边的内容。", Digest: "这里是图文消息的描述"}, } @@ -195,10 +198,10 @@ func TestSendTaskCardMessage(t *testing.T) { Color: "red", IsBold: false, }} - _ = wxclient.SendTaskCardMessage(&recipient, "请审核该条信息", "这是说明信息", "https://wiki.eryajf.net", "aaab", btn, false) + _ = wxclient.SendTaskCardMessage(&recipient, "请审核该条信息", "这是说明信息", "https://wiki.eryajf.net", "aaadb", btn, false) } -func TestSendTemplateCardMessage(t *testing.T) { +func TestSendTemplateCardTextNoticeMessage(t *testing.T) { recipient := Recipient{ UserIDs: []string{userID}, PartyIDs: []string{}, @@ -208,18 +211,19 @@ func TestSendTemplateCardMessage(t *testing.T) { msgObj := TemplateCard{ CardType: CardTypeTextNotice, Source: Source{ - IconURL: "https://t.eryajf.net/imgs/2023/02/712e2287455b9a0c.png", - Desc: "二丫讲梵的公众号", + IconURL: "https://wwcdn.weixin.qq.com/node/wework/images/202201062104.366e5ee28e.png", + Desc: "企业微信logo", DescColor: 0, }, - ActionMenu: &ActionMenu{ + ActionMenu: ActionMenu{ Desc: "卡片副交互辅助文本说明", ActionList: []ActionList{ {Text: "接受推送", Key: "A"}, {Text: "不再推送", Key: "B"}, }, }, - TaskID: "aaadaa", + // 确保唯一 + TaskID: "aaacddada", MainTitle: MainTitle{ Title: "欢迎使用企业微信", Desc: "你的朋友也都在用。", @@ -230,7 +234,7 @@ func TestSendTemplateCardMessage(t *testing.T) { Title: "百度", QuoteText: "去往百度", }, - EmphasisContent: &EmphasisContent{ + EmphasisContent: EmphasisContent{ Title: "100", Desc: "核心数据", }, @@ -247,3 +251,53 @@ func TestSendTemplateCardMessage(t *testing.T) { fmt.Printf("get err: %v\n", err) } } + +func TestSendTemplateCardVoteInTeractionMessage(t *testing.T) { + recipient := Recipient{ + UserIDs: []string{userID}, + PartyIDs: []string{}, + TagIDs: []string{}, + ChatID: "", + } + msgObj := TemplateCard{ + CardType: CardTypeVoteInteraction, + Source: Source{ + IconURL: "https://wwcdn.weixin.qq.com/node/wework/images/202201062104.366e5ee28e.png", + Desc: "企业微信logo", + }, + // 确保唯一 + TaskID: "1", + MainTitle: MainTitle{ + Title: "欢迎使用企业微信", + Desc: "你的朋友也都在用。", + }, + CheckBox: CheckBox{ + QuestionKey: "qa1", + OptionList: []struct { + ID string `json:"id"` + Text string `json:"text"` + IsChecked bool `json:"is_checked"` + }{ + { + ID: "op1", + Text: "选项1", + IsChecked: false, + }, + { + ID: "op2", + Text: "选项2", + IsChecked: false, + }, + }, + Mode: 0, + }, + SubmitButton: SubmitButton{ + Text: "提交", + Key: "key", + }, + } + err := wxclient.SendTemplateCardMessage(&recipient, msgObj, false) + if err != nil { + fmt.Printf("get err: %v\n", err) + } +} diff --git a/models.go b/models.go index 7f20e6d..df8066a 100644 --- a/models.go +++ b/models.go @@ -119,7 +119,11 @@ func (x reqMessage) intoBody() ([]byte, error) { } // msgtype polymorphism - obj[x.MsgType] = x.Content + if x.MsgType != "template_card" { + obj[x.MsgType] = x.Content + } else { + obj[x.MsgType] = x.Content["template_card"] + } // 复用这个结构体,因为是 package-private 的所以这么做没风险 if x.ChatID != "" { @@ -1040,179 +1044,271 @@ type TaskCardBtn struct { IsBold bool `json:"is_bold"` } -// news 类型的文章 +// Article news 类型的文章 type Article struct { - Title string `json:"title"` + // 标题,不超过128个字节,超过会自动截断(支持id转译) + Title string `json:"title"` + // 描述,不超过512个字节,超过会自动截断(支持id转译) Description string `json:"description"` - Url string `json:"url"` - PicUrl string `json:"picurl"` - AppId string `json:"appid"` - PagePath string `json:"pagepath"` + // 点击后跳转的链接。 最长2048字节,请确保包含了协议头(http/https),小程序或者url必须填写一个 + URL string `json:"url"` + // 图文消息的图片链接,最长2048字节,支持JPG、PNG格式,较好的效果为大图 1068*455,小图150*150 + PicURL string `json:"picurl"` + // 小程序appid,必须是与当前应用关联的小程序,appid和pagepath必须同时填写,填写后会忽略url字段 + AppID string `json:"appid"` + // 点击消息卡片后的小程序页面,最长128字节,仅限本小程序内的页面。appid和pagepath必须同时填写,填写后会忽略url字段 + PagePath string `json:"pagepath"` } -// mpnews 类型的文章 +// MPArticle mpnews 类型的文章 type MPArticle struct { - Title string `json:"title"` - ThumbMediaID string `json:"thumb_media_id"` - Author string `json:"author"` - ContentSourceUrl string `json:"content_source_url"` - Content string `json:"content"` - Digest string `json:"digest"` -} - -// ============================== - -// TemplateCardMessage 测试发送模板卡片消息必需配置应用回调地址 -type TemplateCardMessage struct { - // Message - TemplateCard TemplateCard `json:"template_card"` -} - + // 标题,不超过128个字节,超过会自动截断(支持id转译) + Title string `json:"title"` + // 图文消息缩略图的media_id, 可以通过素材管理接口获得。此处thumb_media_id即上传接口返回的media_id + ThumbMediaID string `json:"thumb_media_id"` + // 图文消息的作者,不超过64个字节 + Author string `json:"author"` + // 图文消息点击“阅读原文”之后的页面链接 + ContentSourceURL string `json:"content_source_url"` + // 图文消息的内容,支持html标签,不超过666 K个字节(支持id转译) + Content string `json:"content"` + // 图文消息的描述,不超过512个字节,超过会自动截断(支持id转译) + Digest string `json:"digest"` +} + +// Source 卡片来源样式信息,不需要来源样式可不填写 type Source struct { - IconURL string `json:"icon_url"` - Desc string `json:"desc"` - DescColor int `json:"desc_color"` + // 来源图片的url,来源图片的尺寸建议为72*72 + IconURL string `json:"icon_url"` + // 来源图片的描述,建议不超过20个字,(支持id转译) + Desc string `json:"desc"` + // 来源文字的颜色,目前支持:0(默认) 灰色,1 黑色,2 红色,3 绿色 + DescColor int `json:"desc_color"` } + +// ActionList 操作列表,列表长度取值范围为 [1, 3] type ActionList struct { + // 操作的描述文案 Text string `json:"text"` - Key string `json:"key"` + // 操作key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复 + Key string `json:"key"` } + +// ActionMenu 卡片右上角更多操作按钮 type ActionMenu struct { + // 更多操作界面的描述 Desc string `json:"desc"` ActionList []ActionList `json:"action_list"` } + +// MainTitle 一级标题 type MainTitle struct { + // 一级标题,建议不超过36个字,文本通知型卡片本字段非必填,但不可本字段和sub_title_text都不填,(支持id转译) Title string `json:"title"` - Desc string `json:"desc"` + // 标题辅助信息,建议不超过160个字,(支持id转译) + Desc string `json:"desc"` } + +// QuoteArea 引用文献样式 type QuoteArea struct { - Type int `json:"type"` - URL string `json:"url"` - Title string `json:"title"` + // 引用文献样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 + Type int `json:"type"` + // 点击跳转的url,quote_area.type是1时必填 + URL string `json:"url"` + // 引用文献样式的标题 + Title string `json:"title"` + // 引用文献样式的引用文案 QuoteText string `json:"quote_text"` + // 小程序appid,必须是与当前应用关联的小程序,appid和pagepath必须同时填写,填写后会忽略url字段 + AppID string `json:"appid"` + // 点击消息卡片后的小程序页面,最长128字节,仅限本小程序内的页面。appid和pagepath必须同时填写,填写后会忽略url字段 + PagePath string `json:"pagepath"` } -// EmphasisContent 文本通知型 +// EmphasisContent 关键数据样式 type EmphasisContent struct { + // 关键数据样式的数据内容,建议不超过14个字 Title string `json:"title"` - Desc string `json:"desc"` + // 关键数据样式的数据描述内容,建议不超过22个字 + Desc string `json:"desc"` } + +// HorizontalContentList 二级标题+文本列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过6 type HorizontalContentList struct { + // 二级标题,建议不超过5个字 KeyName string `json:"keyname"` - Value string `json:"value"` - Type int `json:"type,omitempty"` - URL string `json:"url,omitempty"` + // 二级文本,如果horizontal_content_list.type是2,该字段代表文件名称(要包含文件类型),建议不超过30个字,(支持id转译) + Value string `json:"value"` + // 链接类型,0或不填代表不是链接,1 代表跳转url,2 代表下载附件,3 代表点击跳转成员详情 + Type int `json:"type,omitempty"` + // 链接跳转的url,horizontal_content_list.type是1时必填 + URL string `json:"url,omitempty"` + // 附件的media_id,horizontal_content_list.type是2时必填 MediaID string `json:"media_id,omitempty"` - Userid string `json:"userid,omitempty"` + // 成员详情的userid,horizontal_content_list.type是3时必填 + Userid string `json:"userid,omitempty"` } + +// JumpList 跳转指引样式的列表,该字段可为空数组,但有数据的话需确认对应字段是否必填,列表长度不超过3 type JumpList struct { - Type int `json:"type"` - Title string `json:"title"` - URL string `json:"url,omitempty"` - Appid string `json:"appid,omitempty"` + // 跳转链接类型,0或不填代表不是链接,1 代表跳转url,2 代表跳转小程序 + Type int `json:"type"` + // 跳转链接样式的文案内容,建议不超过18个字 + Title string `json:"title"` + // 跳转链接的url,jump_list.type是1时必填 + URL string `json:"url,omitempty"` + // 跳转链接的小程序的appid,必须是与当前应用关联的小程序,jump_list.type是2时必填 + Appid string `json:"appid,omitempty"` + // 跳转链接的小程序的pagepath,jump_list.type是2时选填 PagePath string `json:"pagepath,omitempty"` } + +// CardAction 整体卡片的点击跳转事件,text_notice必填本字段 type CardAction struct { - Type int `json:"type"` - URL string `json:"url"` - Appid string `json:"appid"` + // 跳转事件类型,1 代表跳转url,2 代表打开小程序。text_notice卡片模版中该字段取值范围为[1,2] + Type int `json:"type"` + // 跳转事件的url,card_action.type是1时必填 + URL string `json:"url"` + // 跳转事件的小程序的appid,必须是与当前应用关联的小程序,card_action.type是2时必填 + Appid string `json:"appid"` + // 跳转事件的小程序的pagepath,card_action.type是2时选填 Pagepath string `json:"pagepath"` } -// ImageTextArea 图文展示型 +// ImageTextArea 左图右文样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 type ImageTextArea struct { - Type int `json:"type" validate:"omitempty,oneof=0 1 2"` - URL string `json:"url"` - AppId string `json:"appid,omitempty"` + // 左图右文样式区域点击事件,0或不填代表没有点击事件,1 代表跳转url,2 代表跳转小程序 + Type int `json:"type"` + // 点击跳转的url,image_text_area.type是1时必填 + URL string `json:"url"` + // 点击跳转的小程序的appid,必须是与当前应用关联的小程序,image_text_area.type是2时必填 + AppID string `json:"appid,omitempty"` + // 点击跳转的小程序的pagepath,image_text_area.type是2时选填 PagePath string `json:"pagepath,omitempty"` - Title string `json:"title"` - Desc string `json:"desc"` - ImageURL string `json:"image_url" validate:"required"` + // 左图右文样式的标题 + Title string `json:"title"` + // 左图右文样式的描述 + Desc string `json:"desc"` + // 左图右文样式的图片url + ImageURL string `json:"image_url"` } + +// CardImage 图片样式,news_notice类型的卡片,card_image和image_text_area两者必填一个字段,不可都不填 type CardImage struct { - Url string `json:"url" validate:"required"` - AspectRatio float32 `json:"aspect_ratio" validate:"max=2.25,min=1.3"` + // 图片的url + URL string `json:"url"` + // 图片的宽高比,宽高比要小于2.25,大于1.3,不填该参数默认1.3 + AspectRatio float32 `json:"aspect_ratio"` } // ButtonSelection 按钮交互型 type ButtonSelection struct { - QuestionKey string `json:"question_key" validate:"required"` - Title string `json:"title"` - OptionList []struct { - ID string `json:"id" validate:"required"` - Text string `json:"text" validate:"required"` - } `json:"option_list" validate:"required"` + // 下拉式的选择器的key,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + QuestionKey string `json:"question_key"` + // 下拉式的选择器的key,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + Title string `json:"title"` + // 选项列表,下拉选项不超过 10 个,最少1个 + OptionList []struct { + // 下拉式的选择器选项的id,用户提交后,会产生回调事件,回调事件会带上该id值表示该选项,最长支持128字节,不可重复 + ID string `json:"id"` + // 下拉式的选择器选项的文案,建议不超过16个字 + Text string `json:"text"` + } `json:"option_list"` + // 默认选定的id,不填或错填默认第一个 SelectedID string `json:"selected_id"` } + type Button struct { - Type int `json:"type,omitempty"` //按钮点击事件类型,0 或不填代表回调点击事件,1 代表跳转url - Text string `json:"text" validate:"required"` - Style int `json:"style,omitempty"` //按钮样式,目前可填1~4,不填或错填默认1 - Key string `json:"key,omitempty"` // 按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复,button_list.type是0时必填 - Url string `json:"url,omitempty"` //跳转事件的url,button_list.type是1时必填 + // 按钮点击事件类型,0 或不填代表回调点击事件,1 代表跳转url + Type int `json:"type,omitempty"` + // 按钮文案,建议不超过10个字 + Text string `json:"text"` + //按钮样式,目前可填1~4,不填或错填默认1 + Style int `json:"style,omitempty"` + // 按钮key值,用户点击后,会产生回调事件将本参数作为EventKey返回,回调事件会带上该key值,最长支持1024字节,不可重复,button_list.type是0时必填 + Key string `json:"key,omitempty"` + //跳转事件的url,button_list.type是1时必填 + URL string `json:"url,omitempty"` } -// CheckBox 投票选择型 +// CheckBox 选择题样式 type CheckBox struct { - QuestionKey string `json:"question_key" validate:"required"` - OptionList []struct { - ID string `json:"id" validate:"required"` - Text string `json:"text" validate:"required"` - IsChecked bool `json:"is_checked" validate:"required"` + // 选择题key值,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节 + QuestionKey string `json:"question_key"` + // 选项list,选项个数不超过 20 个,最少1个 + OptionList []struct { + // 选项id,用户提交选项后,会产生回调事件,回调事件会带上该id值表示该选项,最长支持128字节,不可重复 + ID string `json:"id"` + // 选项文案描述,建议不超过17个字 + Text string `json:"text"` + // 该选项是否要默认选中 + IsChecked bool `json:"is_checked"` } `json:"option_list" validate:"required,min=1,max=20"` + // 选择题模式,单选:0,多选:1,不填默认0 Mode int `json:"mode" validate:"omitempty,oneof=0 1"` } + +// SubmitButton 提交按钮样式 type SubmitButton struct { - Text string `json:"text" validate:"required"` - Key string `json:"key" validate:"required"` + // 按钮文案,建议不超过10个字,不填默认为提交 + Text string `json:"text"` + // 提交按钮的key,会产生回调事件将本参数作为EventKey返回,最长支持1024字节 + Key string `json:"key"` } -// SelectList 多项选择型 +// SelectList 下拉式的选择器列表,multiple_interaction类型的卡片该字段不可为空,一个消息最多支持 3 个选择器 type SelectList struct { - QuestionKey string `json:"question_key" validate:"required"` - Title string `json:"title,omitempty"` - SelectedID string `json:"selected_id,omitempty"` - OptionList []OptionList `json:"option_list" validate:"required"` + // 下拉式的选择器题目的key,用户提交选项后,会产生回调事件,回调事件会带上该key值表示该题,最长支持1024字节,不可重复 + QuestionKey string `json:"question_key"` + // 下拉式的选择器上面的title + Title string `json:"title,omitempty"` + // 默认选定的id,不填或错填默认第一个 + SelectedID string `json:"selected_id,omitempty"` + OptionList []OptionList `json:"option_list"` } +// 项列表,下拉选项不超过 10 个,最少1个 type OptionList struct { - ID string `json:"id" validate:"required"` - Text string `json:"text" validate:"required"` + // 下拉式的选择器选项的id,用户提交选项后,会产生回调事件,回调事件会带上该id值表示该选项,最长支持128字节,不可重复 + ID string `json:"id"` + // 下拉式的选择器选项的文案,建议不超过16个字 + Text string `json:"text"` } +// TemplateCardType 模板卡片的类型 type TemplateCardType string const ( - CardTypeTextNotice TemplateCardType = "text_notice" - CardTypeNewsNotice TemplateCardType = "news_notice" - CardTypeButtonInteraction TemplateCardType = "button_interaction" - CardTypeVoteInteraction TemplateCardType = "vote_interaction" - CardTypeMultipleInteraction TemplateCardType = "multiple_interaction" + CardTypeTextNotice TemplateCardType = "text_notice" // 文本通知型 + CardTypeNewsNotice TemplateCardType = "news_notice" // 图文展示型 + CardTypeButtonInteraction TemplateCardType = "button_interaction" // 按钮交互型 + CardTypeVoteInteraction TemplateCardType = "vote_interaction" // 投票选择型 + CardTypeMultipleInteraction TemplateCardType = "multiple_interaction" // 多项选择型 ) type TemplateCard struct { CardType TemplateCardType `json:"card_type"` Source Source `json:"source"` - ActionMenu *ActionMenu `json:"action_menu,omitempty" validate:"required_with=TaskID"` + ActionMenu ActionMenu `json:"action_menu,omitempty" validate:"required_with=TaskID"` TaskID string `json:"task_id,omitempty" validate:"required_with=ActionMenu"` MainTitle MainTitle `json:"main_title"` QuoteArea QuoteArea `json:"quote_area"` // 文本通知型 - EmphasisContent *EmphasisContent `json:"emphasis_content,omitempty"` - SubTitleText string `json:"sub_title_text,omitempty"` + EmphasisContent EmphasisContent `json:"emphasis_content,omitempty"` + SubTitleText string `json:"sub_title_text,omitempty"` // 图文展示型 - ImageTextArea *ImageTextArea `json:"image_text_area,omitempty"` - CardImage *CardImage `json:"card_image,omitempty"` + ImageTextArea ImageTextArea `json:"image_text_area,omitempty"` + CardImage CardImage `json:"card_image,omitempty"` HorizontalContentList []HorizontalContentList `json:"horizontal_content_list"` JumpList []JumpList `json:"jump_list"` CardAction CardAction `json:"card_action,omitempty"` // 按钮交互型 - ButtonSelection *ButtonSelection `json:"button_selection,omitempty"` - ButtonList []Button `json:"button_list,omitempty" validate:"omitempty,max=6"` + ButtonSelection ButtonSelection `json:"button_selection,omitempty"` + ButtonList []Button `json:"button_list,omitempty" validate:"omitempty,max=6"` // 投票选择型 - CheckBox *CheckBox `json:"checkbox,omitempty"` - SelectList []SelectList `json:"select_list,omitempty" validate:"max=3"` - SubmitButton *SubmitButton `json:"submit_button,omitempty"` + CheckBox CheckBox `json:"checkbox,omitempty"` + SelectList []SelectList `json:"select_list,omitempty" validate:"max=3"` + SubmitButton SubmitButton `json:"submit_button,omitempty"` } type TemplateCardUpdateMessage struct { @@ -1220,16 +1316,14 @@ type TemplateCardUpdateMessage struct { PartyIds []int64 `json:"partyids" validate:"omitempty,max=100"` TagIds []int32 `json:"tagids" validate:"omitempty,max=100"` AtAll int `json:"atall,omitempty"` - ResponseCode string `json:"response_code" validate:"required"` + ResponseCode string `json:"response_code"` Button struct { - ReplaceName string `json:"replace_name" validate:"required"` + ReplaceName string `json:"replace_name"` } `json:"button" validate:"required_without=TemplateCard"` TemplateCard TemplateCard `json:"template_card" validate:"required_without=Button"` ReplaceText string `json:"replace_text,omitempty"` } -// ============================== - type reqTransferCustomer struct { // HandoverUserID 原跟进成员的userid HandoverUserID string `json:"handover_userid"` From 631415f2e0476eed54d737b7c71acbbf4d73268f Mon Sep 17 00:00:00 2001 From: eryajf Date: Thu, 5 Oct 2023 16:20:06 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=9A=82=E6=97=B6=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8D=95=E6=B5=8B=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- message_test.go | 303 ------------------------------------------------ 1 file changed, 303 deletions(-) delete mode 100644 message_test.go diff --git a/message_test.go b/message_test.go deleted file mode 100644 index a96a5ca..0000000 --- a/message_test.go +++ /dev/null @@ -1,303 +0,0 @@ -package workwx - -import ( - "bufio" - "fmt" - "net/http" - "os" - "testing" -) - -var ( - wxclient *WorkwxApp - // 企业ID - corpID string = "xxxxxxx" - // 应用ID - agentID int64 = 007 - // 应用秘钥 - agentSecret string = "xxxxxxxxxx" - // 测试接收消息的用户ID - userID string = "userid" -) - -func init() { - var wx = New(corpID) - wxclient = wx.WithApp(agentSecret, agentID) - wxclient.SpawnAccessTokenRefresher() // 自动刷新token -} - -func TestSendTextMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - err := wxclient.SendTextMessage(&recipient, "这是一条普通文本消息", false) - if err != nil { - fmt.Printf("get err: %v\n", err) - } -} - -func TestSendMarkdownMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - _ = wxclient.SendMarkdownMessage(&recipient, "您的会议室已经预定,稍后会同步到`邮箱` \n>**事项详情** \n>事 项:开会 \n>组织者:@miglioguan \n>参与者:@miglioguan、@kunliu、@jamdeezhou、@kanexiong、@kisonwang \n> \n>会议室:广州TIT 1楼 301 \n>日 期:2018年5月18日 \n>时 间:上午9:00-11:00 \n> \n>请准时参加会议。 \n> \n>如需修改会议信息,请点击:[修改会议信息](https://work.weixin.qq.com)", false) -} - -func TestSendImageMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - url := "https://wwcdn.weixin.qq.com/node/wework/images/202201062104.366e5ee28e.png" - resp, err := http.Get(url) - if err != nil { - fmt.Println("HTTP GET请求失败:", err) - return - } - defer resp.Body.Close() - reader := resp.Body - - rst, err := wxclient.UploadTempImageMedia(&Media{ - filename: "test.jpg", - filesize: 0, - stream: reader, - }) - if err != nil { - fmt.Printf("upload temp image failed: %v\n", err) - } - _ = wxclient.SendImageMessage(&recipient, rst.MediaID, false) -} - -func TestSendFileMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - file, err := os.Open("go.mod") - if err != nil { - fmt.Println("Error opening file:", err) - return - } - defer file.Close() - - reader := bufio.NewReader(file) - - rst, err := wxclient.UploadTempFileMedia(&Media{ - filename: "go.mod", - filesize: 0, - stream: reader, - }) - if err != nil { - fmt.Printf("upload temp file failed: %v\n", err) - } - _ = wxclient.SendFileMessage(&recipient, rst.MediaID, false) -} - -func TestSendTextCardMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - _ = wxclient.SendTextCardMessage( - &recipient, "领奖通知", "
2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
", "https://wiki.eryajf.net", "更多", false) -} - -func TestSendNewsMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - - msgObj := []Article{ - {Title: "中秋节礼品领取", - Description: "今年中秋节公司有豪礼相送", - URL: "https://wiki.eryajf.net", - PicURL: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", - AppID: "", - PagePath: ""}, - {Title: "中秋节礼品领取2", - Description: "今年中秋节公司有豪礼相送2", - URL: "https://wiki.eryajf.net", - PicURL: "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png", - AppID: "", - PagePath: ""}, - } - - _ = wxclient.SendNewsMessage(&recipient, msgObj, false) -} -func TestSendMPNewsMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - - url := "http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png" - resp, err := http.Get(url) - if err != nil { - fmt.Println("HTTP GET请求失败:", err) - return - } - defer resp.Body.Close() - reader := resp.Body - - rst, err := wxclient.UploadTempImageMedia(&Media{ - filename: "test.jpg", - filesize: 0, - stream: reader, - }) - if err != nil { - fmt.Printf("upload temp image failed: %v\n", err) - } - - msgObj := []MPArticle{ - {Title: "中秋节礼品领取", - ThumbMediaID: rst.MediaID, - Author: "eryajf", - ContentSourceURL: "https://wiki.eryajf.net", - Content: "这是正文里边的内容。", - Digest: "这里是图文消息的描述"}, - } - - _ = wxclient.SendMPNewsMessage(&recipient, msgObj, false) -} - -func TestSendTaskCardMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - btn := []TaskCardBtn{ - { - Key: "yes", - Name: "通过", - ReplaceName: "已通过", - Color: "blue", - IsBold: false, - }, { - Key: "no", - Name: "拒绝", - ReplaceName: "已拒绝", - Color: "red", - IsBold: false, - }} - _ = wxclient.SendTaskCardMessage(&recipient, "请审核该条信息", "这是说明信息", "https://wiki.eryajf.net", "aaadb", btn, false) -} - -func TestSendTemplateCardTextNoticeMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - msgObj := TemplateCard{ - CardType: CardTypeTextNotice, - Source: Source{ - IconURL: "https://wwcdn.weixin.qq.com/node/wework/images/202201062104.366e5ee28e.png", - Desc: "企业微信logo", - DescColor: 0, - }, - ActionMenu: ActionMenu{ - Desc: "卡片副交互辅助文本说明", - ActionList: []ActionList{ - {Text: "接受推送", Key: "A"}, - {Text: "不再推送", Key: "B"}, - }, - }, - // 确保唯一 - TaskID: "aaacddada", - MainTitle: MainTitle{ - Title: "欢迎使用企业微信", - Desc: "你的朋友也都在用。", - }, - QuoteArea: QuoteArea{ - Type: 0, - URL: "baidu.com", - Title: "百度", - QuoteText: "去往百度", - }, - EmphasisContent: EmphasisContent{ - Title: "100", - Desc: "核心数据", - }, - SubTitleText: "下载企业微信还能抢红包!", - CardAction: CardAction{ - Type: 1, - URL: "qq.com", - Appid: "aaaaaaa", - Pagepath: "/index.html", - }, - } - err := wxclient.SendTemplateCardMessage(&recipient, msgObj, false) - if err != nil { - fmt.Printf("get err: %v\n", err) - } -} - -func TestSendTemplateCardVoteInTeractionMessage(t *testing.T) { - recipient := Recipient{ - UserIDs: []string{userID}, - PartyIDs: []string{}, - TagIDs: []string{}, - ChatID: "", - } - msgObj := TemplateCard{ - CardType: CardTypeVoteInteraction, - Source: Source{ - IconURL: "https://wwcdn.weixin.qq.com/node/wework/images/202201062104.366e5ee28e.png", - Desc: "企业微信logo", - }, - // 确保唯一 - TaskID: "1", - MainTitle: MainTitle{ - Title: "欢迎使用企业微信", - Desc: "你的朋友也都在用。", - }, - CheckBox: CheckBox{ - QuestionKey: "qa1", - OptionList: []struct { - ID string `json:"id"` - Text string `json:"text"` - IsChecked bool `json:"is_checked"` - }{ - { - ID: "op1", - Text: "选项1", - IsChecked: false, - }, - { - ID: "op2", - Text: "选项2", - IsChecked: false, - }, - }, - Mode: 0, - }, - SubmitButton: SubmitButton{ - Text: "提交", - Key: "key", - }, - } - err := wxclient.SendTemplateCardMessage(&recipient, msgObj, false) - if err != nil { - fmt.Printf("get err: %v\n", err) - } -}