diff --git a/README.md b/README.md index ba26285..978a600 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ ## TODOs + [ ] Token Store 测试(内存泄漏检查,不必要的拷贝的检查) + [ ] 添加 & 更新推送配置信息时对 Token Store 进行妥善更新 -+ [ ] 微信消息推送 API -+ [ ] 支持飞书 ++ [x] 微信消息推送 API ++ [x] 支持飞书 + [ ] 支持钉钉 + [ ] 支持 Telegram + [ ] 重新编写 README diff --git a/channel/lark.go b/channel/lark.go index 9c86d4a..7cbf720 100644 --- a/channel/lark.go +++ b/channel/lark.go @@ -1 +1,111 @@ package channel + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "message-pusher/model" + "net/http" + "strconv" + "time" +) + +type larkMessageRequestCardElementText struct { + Content string `json:"content"` + Tag string `json:"tag"` +} + +type larkMessageRequestCardElement struct { + Tag string `json:"tag"` + Text larkMessageRequestCardElementText `json:"text"` +} + +type larkMessageRequest struct { + MessageType string `json:"msg_type"` + Timestamp string `json:"timestamp"` + Sign string `json:"sign"` + Content struct { + Text string `json:"text"` + } `json:"content"` + Card struct { + Config struct { + WideScreenMode bool `json:"wide_screen_mode"` + EnableForward bool `json:"enable_forward"` + } + Elements []larkMessageRequestCardElement `json:"elements"` + } `json:"card"` +} + +type larkMessageResponse struct { + Code int `json:"code"` + Message string `json:"msg"` +} + +func SendLarkMessage(message *Message, user *model.User) error { + if user.LarkWebhookURL == "" { + return errors.New("未配置飞书群机器人消息推送方式") + } + messageRequest := larkMessageRequest{ + MessageType: "text", + } + if message.Content == "" { + messageRequest.MessageType = "text" + messageRequest.Content.Text = message.Description + } else { + messageRequest.MessageType = "interactive" + messageRequest.Card.Config.WideScreenMode = true + messageRequest.Card.Config.EnableForward = true + messageRequest.Card.Elements = append(messageRequest.Card.Elements, larkMessageRequestCardElement{ + Tag: "div", + Text: larkMessageRequestCardElementText{ + Content: message.Content, + Tag: "lark_md", + }, + }) + } + + now := time.Now() + timestamp := now.Unix() + sign, err := larkSign(user.LarkWebhookSecret, timestamp) + if err != nil { + return err + } + messageRequest.Sign = sign + messageRequest.Timestamp = strconv.FormatInt(timestamp, 10) + jsonData, err := json.Marshal(messageRequest) + if err != nil { + return err + } + resp, err := http.Post(user.LarkWebhookURL, "application/json", + bytes.NewBuffer(jsonData)) + if err != nil { + return err + } + var res larkMessageResponse + err = json.NewDecoder(resp.Body).Decode(&res) + if err != nil { + return err + } + if res.Code != 0 { + return errors.New(res.Message) + } + return nil +} + +func larkSign(secret string, timestamp int64) (string, error) { + // https://open.feishu.cn/document/ukTMukTMukTM/ucTM5YjL3ETO24yNxkjN?lang=zh-CN + // timestamp + key -> sha256 -> base64 encode + stringToSign := fmt.Sprintf("%v", timestamp) + "\n" + secret + var data []byte + h := hmac.New(sha256.New, []byte(stringToSign)) + _, err := h.Write(data) + if err != nil { + return "", err + } + signature := base64.StdEncoding.EncodeToString(h.Sum(nil)) + return signature, nil +} diff --git a/channel/main.go b/channel/main.go index 9dfe29c..21661b8 100644 --- a/channel/main.go +++ b/channel/main.go @@ -21,6 +21,7 @@ type Message struct { URL string `json:"url"` Channel string `json:"channel"` Token string `json:"token"` + HTMLContent string `json:"html_content"` } func (message *Message) Send(user *model.User) error { @@ -32,6 +33,7 @@ func (message *Message) Send(user *model.User) error { case TypeWeChatCorpAccount: return SendWeChatCorpMessage(message, user) case TypeLark: + return SendLarkMessage(message, user) case TypeDingTalk: case TypeTelegram: default: diff --git a/controller/message.go b/controller/message.go index c66cf50..46cb180 100644 --- a/controller/message.go +++ b/controller/message.go @@ -85,13 +85,7 @@ func pushMessageHelper(c *gin.Context, message *channel.Message) { if err != nil { common.SysLog(err.Error()) } else { - message.Content = buf.String() - } - } else { - if message.Description != "" { - message.Content = message.Description - } else { - message.Content = "无内容" + message.HTMLContent = buf.String() } } if message.Channel == "" { diff --git a/model/user.go b/model/user.go index cb20ce1..7757ac6 100644 --- a/model/user.go +++ b/model/user.go @@ -27,6 +27,8 @@ type User struct { WeChatCorpAccountSecret string `json:"wechat_corp_account_secret" gorm:"column:wechat_corp_account_secret"` WeChatCorpAccountAgentId string `json:"wechat_corp_account_agent_id" gorm:"column:wechat_corp_account_agent_id"` WeChatCorpAccountUserId string `json:"wechat_corp_account_user_id" gorm:"column:wechat_corp_account_user_id"` + LarkWebhookURL string `json:"lark_webhook_url"` + LarkWebhookSecret string `json:"lark_webhook_secret"` } func GetMaxUserId() int {