Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 实现机器人发送多种格式的消息 #212

Open
wants to merge 4 commits into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
Loading