Skip to content

Commit ca24a8d

Browse files
author
wudongodong
committed
feat:机器人发消息支持文本(text)、markdown(markdown)、图片(image)、图文(news)、文件(file)、语音(voice)、模板卡片(template_card)七种消息类型
1 parent b8c13c9 commit ca24a8d

File tree

4 files changed

+281
-0
lines changed

4 files changed

+281
-0
lines changed

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
module github.com/xen0n/go-workwx/v2
22

33
go 1.21
4+
45
toolchain go1.23.4
56

67
require (
78
github.com/PuerkitoBio/goquery v1.10.1
89
github.com/cenkalti/backoff/v4 v4.3.0
10+
github.com/go-playground/validator/v10 v10.23.0
911
github.com/russross/blackfriday/v2 v2.1.0
1012
github.com/smartystreets/goconvey v1.8.1
1113
github.com/urfave/cli/v2 v2.27.5
@@ -15,9 +17,15 @@ require (
1517
require (
1618
github.com/andybalholm/cascadia v1.3.3 // indirect
1719
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
20+
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
21+
github.com/go-playground/locales v0.14.1 // indirect
22+
github.com/go-playground/universal-translator v0.18.1 // indirect
1823
github.com/gopherjs/gopherjs v1.17.2 // indirect
1924
github.com/jtolds/gls v4.20.0+incompatible // indirect
25+
github.com/leodido/go-urn v1.4.0 // indirect
2026
github.com/smarty/assertions v1.15.0 // indirect
2127
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
28+
golang.org/x/crypto v0.31.0 // indirect
29+
golang.org/x/sys v0.28.0 // indirect
2230
golang.org/x/text v0.21.0 // indirect
2331
)

go.sum

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,35 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
66
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
77
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
88
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
9+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
10+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11+
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
12+
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
13+
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
14+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
15+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
16+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
17+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
18+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
19+
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
20+
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
921
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
1022
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
1123
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
1224
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
1325
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
26+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
27+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
28+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
29+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1430
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
1531
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
1632
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
1733
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
1834
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
1935
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
36+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
37+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
2038
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
2139
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
2240
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
@@ -27,6 +45,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
2745
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
2846
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
2947
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
48+
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
3049
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
3150
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
3251
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -60,6 +79,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
6079
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
6180
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
6281
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
82+
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
6383
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
6484
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
6585
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -87,3 +107,5 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
87107
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
88108
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
89109
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
110+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
111+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

webhook_message.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,35 @@ func (c *WebhookClient) sendMessage(
6868

6969
return nil
7070
}
71+
72+
// SendMessage 机器人支持文本(text)、markdown(markdown)、图片(image)、图文(news)、文件(file)、语音(voice)、模板卡片(template_card)七种消息类型
73+
func (c *WebhookClient) SendMessage(msg WebHookMessage) error {
74+
75+
if err := msg.Validate(); err != nil {
76+
return err
77+
}
78+
req, err := msg.Struct2Map()
79+
if err != nil {
80+
return err
81+
}
82+
switch msg.(type) {
83+
case *TextMessage:
84+
req["msgtype"] = "text"
85+
case *MarkdownMessage:
86+
req["msgtype"] = "markdown"
87+
88+
case *ImageMessage:
89+
req["msgtype"] = "image"
90+
case *ImageArticles:
91+
req["msgtype"] = "news"
92+
93+
case *FileMessage:
94+
req["msgtype"] = "file"
95+
case *VoiceMessage:
96+
req["msgtype"] = "voice"
97+
case *TemplateCardMessage:
98+
req["msgtype"] = "template_card"
99+
}
100+
return c.executeQyapiJSONPost("/cgi-bin/webhook/send", req, nil)
101+
102+
}

webhook_message.md.go

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
package workwx
2+
3+
import (
4+
"encoding/json"
5+
"github.com/go-playground/validator/v10"
6+
)
7+
8+
var (
9+
validate = validator.New()
10+
)
11+
12+
type WebHookMessage interface {
13+
Struct2Map() (map[string]any, error)
14+
Validate() error
15+
}
16+
17+
// 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
18+
19+
// TextMessage 文本消息
20+
type TextMessage struct {
21+
Text struct {
22+
Content string `json:"content" validate:"required"`
23+
MentionedList []string `json:"mentioned_list"`
24+
MentionedMobileList []string `json:"mentioned_mobile_list"`
25+
} `json:"text"`
26+
}
27+
28+
func (t *TextMessage) Struct2Map() (map[string]any, error) {
29+
var dataMap = make(map[string]any)
30+
buf, err := json.Marshal(t)
31+
if err != nil {
32+
return nil, err
33+
}
34+
err = json.Unmarshal(buf, &dataMap)
35+
36+
return dataMap, err
37+
}
38+
39+
func (t *TextMessage) Validate() error {
40+
return validate.Struct(t)
41+
}
42+
43+
// MarkdownMessage markdown
44+
type MarkdownMessage struct {
45+
Markdown struct {
46+
Content string `json:"content" validate:"required"`
47+
} `json:"markdown" validate:"required"`
48+
}
49+
50+
func (t *MarkdownMessage) Struct2Map() (map[string]any, error) {
51+
var dataMap = make(map[string]any)
52+
buf, err := json.Marshal(t)
53+
if err != nil {
54+
return nil, err
55+
}
56+
err = json.Unmarshal(buf, &dataMap)
57+
58+
return dataMap, err
59+
}
60+
61+
func (t *MarkdownMessage) Validate() error {
62+
return validate.Struct(t)
63+
}
64+
65+
// ImageMessage 图片类型
66+
type ImageMessage struct {
67+
Image struct {
68+
Base64 string `json:"base64" validate:"required"` //图片内容的base64编码
69+
Md5 string `json:"md5" validate:"required"`
70+
} `json:"image" validate:"required"`
71+
}
72+
73+
func (t *ImageMessage) Struct2Map() (map[string]any, error) {
74+
var dataMap = make(map[string]any)
75+
buf, err := json.Marshal(t)
76+
if err != nil {
77+
return nil, err
78+
}
79+
err = json.Unmarshal(buf, &dataMap)
80+
81+
return dataMap, err
82+
}
83+
84+
func (t *ImageMessage) Validate() error {
85+
return validate.Struct(t)
86+
}
87+
88+
// VoiceMessage 语音类型
89+
type VoiceMessage struct {
90+
Voice struct {
91+
MediaID string `json:"media_id" validate:"required" ` // 语音文件id,通过下文的文件上传接口获取
92+
} `json:"voice" validate:"required"`
93+
}
94+
95+
func (t *VoiceMessage) Struct2Map() (map[string]any, error) {
96+
var dataMap = make(map[string]any)
97+
buf, err := json.Marshal(t)
98+
if err != nil {
99+
return nil, err
100+
}
101+
err = json.Unmarshal(buf, &dataMap)
102+
103+
return dataMap, err
104+
}
105+
106+
func (t *VoiceMessage) Validate() error {
107+
return validate.Struct(t)
108+
}
109+
110+
// ImageArticles 图文类型
111+
type ImageArticles struct {
112+
News struct {
113+
Articles []struct {
114+
Title string `json:"title" validate:"required"`
115+
Description string `json:"description" validate:"required"`
116+
Url string `json:"url" validate:"required"`
117+
Picurl string `json:"picurl"`
118+
} `json:"articles"`
119+
} `json:"news"`
120+
}
121+
122+
func (t *ImageArticles) Struct2Map() (map[string]any, error) {
123+
var dataMap = make(map[string]any)
124+
buf, err := json.Marshal(t)
125+
if err != nil {
126+
return nil, err
127+
}
128+
err = json.Unmarshal(buf, &dataMap)
129+
130+
return dataMap, err
131+
}
132+
133+
func (t *ImageArticles) Validate() error {
134+
return validate.Struct(t)
135+
}
136+
137+
type FileMessage struct {
138+
File struct {
139+
MediaId string `json:"media_id" validate:"required"` //文件id,通过下文的文件上传接口获取
140+
} `json:"file" validate:"required"`
141+
}
142+
143+
func (t *FileMessage) Struct2Map() (map[string]any, error) {
144+
var dataMap = make(map[string]any)
145+
buf, err := json.Marshal(t)
146+
if err != nil {
147+
return nil, err
148+
}
149+
err = json.Unmarshal(buf, &dataMap)
150+
151+
return dataMap, err
152+
}
153+
154+
func (t *FileMessage) Validate() error {
155+
return validate.Struct(t)
156+
}
157+
158+
type TemplateCardMessage struct {
159+
TemplateCard struct {
160+
CardType string `json:"card_type" validate:"required"`
161+
Source struct {
162+
IconUrl string `json:"icon_url"`
163+
Desc string `json:"desc"`
164+
DescColor int `json:"desc_color"`
165+
} `json:"source"`
166+
MainTitle struct {
167+
Title string `json:"title"`
168+
Desc string `json:"desc"`
169+
} `json:"main_title" validate:"required"`
170+
EmphasisContent struct {
171+
Title string `json:"title"`
172+
Desc string `json:"desc"`
173+
} `json:"emphasis_content"`
174+
QuoteArea struct {
175+
Type int `json:"type"`
176+
Url string `json:"url"`
177+
Appid string `json:"appid"`
178+
Pagepath string `json:"pagepath"`
179+
Title string `json:"title"`
180+
QuoteText string `json:"quote_text"`
181+
} `json:"quote_area"`
182+
SubTitleText string `json:"sub_title_text"`
183+
HorizontalContentList []struct {
184+
Keyname string `json:"keyname" validate:"required"`
185+
Value string `json:"value"`
186+
Type int `json:"type,omitempty"`
187+
Url string `json:"url,omitempty"`
188+
MediaId string `json:"media_id,omitempty"`
189+
} `json:"horizontal_content_list"`
190+
JumpList []struct {
191+
Type int `json:"type"`
192+
Url string `json:"url,omitempty"`
193+
Title string `json:"title" validate:"required"`
194+
Appid string `json:"appid,omitempty"`
195+
Pagepath string `json:"pagepath,omitempty"`
196+
} `json:"jump_list"`
197+
CardAction struct {
198+
Type int `json:"type" validate:"required"`
199+
Url string `json:"url"`
200+
Appid string `json:"appid"`
201+
Pagepath string `json:"pagepath"`
202+
} `json:"card_action" validate:"required"`
203+
} `json:"template_card"`
204+
}
205+
206+
func (t *TemplateCardMessage) Struct2Map() (map[string]any, error) {
207+
var dataMap = make(map[string]any)
208+
buf, err := json.Marshal(t)
209+
if err != nil {
210+
return nil, err
211+
}
212+
err = json.Unmarshal(buf, &dataMap)
213+
214+
return dataMap, err
215+
}
216+
217+
func (t *TemplateCardMessage) Validate() error {
218+
return validate.Struct(t)
219+
}

0 commit comments

Comments
 (0)