Bladeren bron

feat: channel Lark is done

JustSong 2 jaren geleden
bovenliggende
commit
d634445c25
5 gewijzigde bestanden met toevoegingen van 117 en 9 verwijderingen
  1. 2 2
      README.md
  2. 110 0
      channel/lark.go
  3. 2 0
      channel/main.go
  4. 1 7
      controller/message.go
  5. 2 0
      model/user.go

+ 2 - 2
README.md

@@ -4,8 +4,8 @@
 ## TODOs
 + [ ] Token Store 测试(内存泄漏检查,不必要的拷贝的检查)
 + [ ] 添加 & 更新推送配置信息时对 Token Store 进行妥善更新
-+ [ ] 微信消息推送 API
-+ [ ] 支持飞书
++ [x] 微信消息推送 API
++ [x] 支持飞书
 + [ ] 支持钉钉
 + [ ] 支持 Telegram
 + [ ] 重新编写 README

+ 110 - 0
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
+}

+ 2 - 0
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:

+ 1 - 7
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 == "" {

+ 2 - 0
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 {