135 lines
3.0 KiB
Go
135 lines
3.0 KiB
Go
|
package wxworkbot
|
|||
|
|
|||
|
import (
|
|||
|
"bytes"
|
|||
|
"encoding/json"
|
|||
|
"errors"
|
|||
|
"fmt"
|
|||
|
"io/ioutil"
|
|||
|
"net/http"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
func init() {
|
|||
|
}
|
|||
|
|
|||
|
const (
|
|||
|
defaultWebHookUrlTemplate = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s"
|
|||
|
)
|
|||
|
|
|||
|
var (
|
|||
|
ErrUnsupportedMessage = errors.New("尚不支持的消息类型")
|
|||
|
)
|
|||
|
|
|||
|
type WxWorkBot struct {
|
|||
|
Key string
|
|||
|
WebHookUrl string
|
|||
|
Client *http.Client
|
|||
|
}
|
|||
|
|
|||
|
// 创建一个新的机器人实例
|
|||
|
func New(botKey string) *WxWorkBot {
|
|||
|
bot := WxWorkBot{
|
|||
|
Key: botKey,
|
|||
|
// 直接拼接出接口 URL
|
|||
|
WebHookUrl: fmt.Sprintf(defaultWebHookUrlTemplate, botKey),
|
|||
|
// 默认 5 秒超时
|
|||
|
Client: &http.Client{Timeout: 5 * time.Second},
|
|||
|
}
|
|||
|
return &bot
|
|||
|
}
|
|||
|
|
|||
|
// 发送消息,允许的参数类型:Text、Markdown、Image、News
|
|||
|
func (bot *WxWorkBot) Send(msg interface{}) error {
|
|||
|
msgBytes, err := marshalMessage(msg)
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
webHookUrl := bot.WebHookUrl
|
|||
|
if len(webHookUrl) == 0 {
|
|||
|
webHookUrl = fmt.Sprintf(defaultWebHookUrlTemplate, bot.Key)
|
|||
|
}
|
|||
|
req, err := http.NewRequest(http.MethodPost, webHookUrl, bytes.NewBuffer(msgBytes))
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
req.Header.Set("Content-Type", "application/json")
|
|||
|
resp, err := bot.Client.Do(req)
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
body, _ := ioutil.ReadAll(resp.Body)
|
|||
|
defer resp.Body.Close()
|
|||
|
var wxWorkResp wxWorkResponse
|
|||
|
err = json.Unmarshal(body, &wxWorkResp)
|
|||
|
if err != nil {
|
|||
|
return err
|
|||
|
}
|
|||
|
if wxWorkResp.ErrorCode != 0 && wxWorkResp.ErrorMessage != "" {
|
|||
|
return errors.New(string(body))
|
|||
|
}
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// 防止 < > 等 HTML 字符在 json.marshal 时被 escape
|
|||
|
func marshal(msg interface{}) ([]byte, error) {
|
|||
|
buf := bytes.NewBuffer([]byte{})
|
|||
|
jsonEncoder := json.NewEncoder(buf)
|
|||
|
jsonEncoder.SetEscapeHTML(false)
|
|||
|
jsonEncoder.SetIndent("", "")
|
|||
|
err := jsonEncoder.Encode(msg)
|
|||
|
if err != nil {
|
|||
|
return nil, nil
|
|||
|
}
|
|||
|
return buf.Bytes(), nil
|
|||
|
}
|
|||
|
|
|||
|
// 将消息包装成企信接口要求的格式,返回 json bytes
|
|||
|
func marshalMessage(msg interface{}) ([]byte, error) {
|
|||
|
if text, ok := msg.(Text); ok {
|
|||
|
textMsg := textMessage{
|
|||
|
message: message{MsgType: "text"},
|
|||
|
Text: text,
|
|||
|
}
|
|||
|
return marshal(textMsg)
|
|||
|
}
|
|||
|
if textMsg, ok := msg.(textMessage); ok {
|
|||
|
textMsg.MsgType = "text"
|
|||
|
return marshal(textMsg)
|
|||
|
}
|
|||
|
if markdown, ok := msg.(Markdown); ok {
|
|||
|
markdownMsg := markdownMessage{
|
|||
|
message: message{MsgType: "markdown"},
|
|||
|
Markdown: markdown,
|
|||
|
}
|
|||
|
return marshal(markdownMsg)
|
|||
|
}
|
|||
|
if markdownMsg, ok := msg.(markdownMessage); ok {
|
|||
|
markdownMsg.MsgType = "markdown"
|
|||
|
return marshal(markdownMsg)
|
|||
|
}
|
|||
|
if image, ok := msg.(Image); ok {
|
|||
|
imageMsg := imageMessage{
|
|||
|
message: message{MsgType: "image"},
|
|||
|
Image: image,
|
|||
|
}
|
|||
|
return marshal(imageMsg)
|
|||
|
}
|
|||
|
if imageMsg, ok := msg.(imageMessage); ok {
|
|||
|
imageMsg.MsgType = "image"
|
|||
|
return marshal(imageMsg)
|
|||
|
}
|
|||
|
if news, ok := msg.(News); ok {
|
|||
|
newsMsg := newsMessage{
|
|||
|
message: message{MsgType: "news"},
|
|||
|
News: news,
|
|||
|
}
|
|||
|
return marshal(newsMsg)
|
|||
|
}
|
|||
|
if newsMsg, ok := msg.(newsMessage); ok {
|
|||
|
newsMsg.MsgType = "news"
|
|||
|
return marshal(newsMsg)
|
|||
|
}
|
|||
|
return nil, ErrUnsupportedMessage
|
|||
|
}
|