Skip to content

Commit

Permalink
feat:机器人发消息支持文本(text)、markdown(markdown)、图片(image)、图文(news)、文件(file)、…
Browse files Browse the repository at this point in the history
…语音(voice)、模板卡片(template_card)七种消息类型
  • Loading branch information
wudongodong authored and dongowu committed Jan 9, 2025
1 parent b8c13c9 commit 26aafdf
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 1 deletion.
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
module github.com/xen0n/go-workwx/v2

go 1.21
toolchain go1.23.4

require (
github.com/PuerkitoBio/goquery v1.10.1
github.com/cenkalti/backoff/v4 v4.3.0
github.com/go-playground/validator/v10 v10.23.0
github.com/russross/blackfriday/v2 v2.1.0
github.com/smartystreets/goconvey v1.8.1
github.com/urfave/cli/v2 v2.27.5
Expand All @@ -15,9 +15,15 @@ require (
require (
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/smarty/assertions v1.15.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
)
22 changes: 22 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,35 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
Expand All @@ -27,6 +45,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
Expand Down Expand Up @@ -60,6 +79,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand Down Expand Up @@ -87,3 +107,5 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
32 changes: 32 additions & 0 deletions webhook_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,35 @@ func (c *WebhookClient) sendMessage(

return nil
}

// SendMessage 机器人支持文本(text)、markdown(markdown)、图片(image)、图文(news)、文件(file)、语音(voice)、模板卡片(template_card)七种消息类型
func (c *WebhookClient) SendMessage(msg WebHookMessage) error {

if err := msg.Validate(); err != nil {
return err
}
req, err := msg.Struct2Map()
if err != nil {
return err
}
switch msg.(type) {
case *TextMessage:
req["msgtype"] = "text"
case *MarkdownMessage:
req["msgtype"] = "markdown"

case *ImageMessage:
req["msgtype"] = "image"
case *ImageArticles:
req["msgtype"] = "news"

case *FileMessage:
req["msgtype"] = "file"
case *VoiceMessage:
req["msgtype"] = "voice"
case *TemplateCardMessage:
req["msgtype"] = "template_card"
}
return c.executeQyapiJSONPost("/cgi-bin/webhook/send", req, nil)

}
219 changes: 219 additions & 0 deletions webhook_message.md.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
package workwx

import (
"encoding/json"
"github.com/go-playground/validator/v10"
)

var (
validate = validator.New()
)

type WebHookMessage interface {
Struct2Map() (map[string]any, error)
Validate() error
}

// See https://developer.work.weixin.qq.com/document/path/99110#%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E7%BE%A4%E6%9C%BA%E5%99%A8%E4%BA%BA

// TextMessage 文本消息
type TextMessage struct {
Text struct {
Content string `json:"content" validate:"required"`
MentionedList []string `json:"mentioned_list"`
MentionedMobileList []string `json:"mentioned_mobile_list"`
} `json:"text"`
}

func (t *TextMessage) Struct2Map() (map[string]any, error) {
var dataMap = make(map[string]any)
buf, err := json.Marshal(t)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, &dataMap)

return dataMap, err
}

func (t *TextMessage) Validate() error {
return validate.Struct(t)
}

// MarkdownMessage markdown
type MarkdownMessage struct {
Markdown struct {
Content string `json:"content" validate:"required"`
} `json:"markdown" validate:"required"`
}

func (t *MarkdownMessage) Struct2Map() (map[string]any, error) {
var dataMap = make(map[string]any)
buf, err := json.Marshal(t)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, &dataMap)

return dataMap, err
}

func (t *MarkdownMessage) Validate() error {
return validate.Struct(t)
}

// ImageMessage 图片类型
type ImageMessage struct {
Image struct {
Base64 string `json:"base64" validate:"required"` //图片内容的base64编码
Md5 string `json:"md5" validate:"required"`
} `json:"image" validate:"required"`
}

func (t *ImageMessage) Struct2Map() (map[string]any, error) {
var dataMap = make(map[string]any)
buf, err := json.Marshal(t)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, &dataMap)

return dataMap, err
}

func (t *ImageMessage) Validate() error {
return validate.Struct(t)
}

// VoiceMessage 语音类型
type VoiceMessage struct {
Voice struct {
MediaID string `json:"media_id" validate:"required" ` // 语音文件id,通过下文的文件上传接口获取
} `json:"voice" validate:"required"`
}

func (t *VoiceMessage) Struct2Map() (map[string]any, error) {
var dataMap = make(map[string]any)
buf, err := json.Marshal(t)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, &dataMap)

return dataMap, err
}

func (t *VoiceMessage) Validate() error {
return validate.Struct(t)
}

// ImageArticles 图文类型
type ImageArticles struct {
News struct {
Articles []struct {
Title string `json:"title" validate:"required"`
Description string `json:"description" validate:"required"`
URL string `json:"url" validate:"required"`
PicURL string `json:"picurl"`
} `json:"articles"`
} `json:"news"`
}

func (t *ImageArticles) Struct2Map() (map[string]any, error) {
var dataMap = make(map[string]any)
buf, err := json.Marshal(t)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, &dataMap)

return dataMap, err
}

func (t *ImageArticles) Validate() error {
return validate.Struct(t)
}

type FileMessage struct {
File struct {
MediaID string `json:"media_id" validate:"required"` //文件id,通过下文的文件上传接口获取
} `json:"file" validate:"required"`
}

func (t *FileMessage) Struct2Map() (map[string]any, error) {
var dataMap = make(map[string]any)
buf, err := json.Marshal(t)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, &dataMap)

return dataMap, err
}

func (t *FileMessage) Validate() error {
return validate.Struct(t)
}

type TemplateCardMessage struct {
TemplateCard struct {
CardType string `json:"card_type" validate:"required"`
Source struct {
IconURL string `json:"icon_url"`
Desc string `json:"desc"`
DescColor int `json:"desc_color"`
} `json:"source"`
MainTitle struct {
Title string `json:"title"`
Desc string `json:"desc"`
} `json:"main_title" validate:"required"`
EmphasisContent struct {
Title string `json:"title"`
Desc string `json:"desc"`
} `json:"emphasis_content"`
QuoteArea struct {
Type int `json:"type"`
URL string `json:"url"`
Appid string `json:"appid"`
Pagepath string `json:"pagepath"`
Title string `json:"title"`
QuoteText string `json:"quote_text"`
} `json:"quote_area"`
SubTitleText string `json:"sub_title_text"`
HorizontalContentList []struct {
Keyname string `json:"keyname" validate:"required"`
Value string `json:"value"`
Type int `json:"type,omitempty"`
URL string `json:"url,omitempty"`
MediaID string `json:"media_id,omitempty"`
} `json:"horizontal_content_list"`
JumpList []struct {
Type int `json:"type"`
URL string `json:"url,omitempty"`
Title string `json:"title" validate:"required"`
Appid string `json:"appid,omitempty"`
Pagepath string `json:"pagepath,omitempty"`
} `json:"jump_list"`
CardAction struct {
Type int `json:"type" validate:"required"`
URL string `json:"url"`
Appid string `json:"appid"`
Pagepath string `json:"pagepath"`
} `json:"card_action" validate:"required"`
} `json:"template_card"`
}

func (t *TemplateCardMessage) Struct2Map() (map[string]any, error) {
var dataMap = make(map[string]any)
buf, err := json.Marshal(t)
if err != nil {
return nil, err
}
err = json.Unmarshal(buf, &dataMap)

return dataMap, err
}

func (t *TemplateCardMessage) Validate() error {
return validate.Struct(t)
}

0 comments on commit 26aafdf

Please sign in to comment.