one-api/common/notify/channel/lark.go
Buer a3719cd78a
feat: add notifier (#144)
* ♻️ refactor: email refactor

*  feat: add notifier
2024-04-09 15:00:06 +08:00

150 lines
3.1 KiB
Go

package channel
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"one-api/common/requester"
"one-api/types"
"strconv"
"time"
)
const larkURL = "https://open.feishu.cn/open-apis/bot/v2/hook/"
type Lark struct {
token string
secret string
keyWord string
}
type larkMessage struct {
MessageType string `json:"msg_type"`
Timestamp string `json:"timestamp,omitempty"`
Sign string `json:"sign,omitempty"`
Card larkCardContent `json:"card"`
}
type larkCardContent struct {
Config struct {
WideScreenMode bool `json:"wide_screen_mode"`
EnableForward bool `json:"enable_forward"`
}
Elements []larkMessageRequestCardElement `json:"elements"`
}
type larkMessageRequestCardElementText struct {
Content string `json:"content"`
Tag string `json:"tag"`
}
type larkMessageRequestCardElement struct {
Tag string `json:"tag"`
Text larkMessageRequestCardElementText `json:"text"`
}
type larkResponse struct {
Code int `json:"code"`
Message string `json:"msg"`
}
func NewLark(token, secret string) *Lark {
return &Lark{
token: token,
secret: secret,
}
}
func NewLarkWithKeyWord(token, keyWord string) *Lark {
return &Lark{
token: token,
keyWord: keyWord,
}
}
func (l *Lark) Name() string {
return "Lark"
}
func (l *Lark) Send(ctx context.Context, title, message string) error {
msg := larkMessage{
MessageType: "interactive",
}
if l.keyWord != "" {
title = fmt.Sprintf("%s(%s)", title, l.keyWord)
}
msg.Card.Config.WideScreenMode = true
msg.Card.Config.EnableForward = true
msg.Card.Elements = append(msg.Card.Elements, larkMessageRequestCardElement{
Tag: "div",
Text: larkMessageRequestCardElementText{
Content: fmt.Sprintf("**%s**\n%s", title, message),
Tag: "lark_md",
},
})
if l.secret != "" {
t := time.Now().Unix()
msg.Timestamp = strconv.FormatInt(t, 10)
msg.Sign = l.sign(t)
}
uri := larkURL + l.token
client := requester.NewHTTPRequester("", larkErrFunc)
client.Context = ctx
client.IsOpenAI = false
req, err := client.NewRequest(http.MethodPost, uri, client.WithHeader(requester.GetJsonHeaders()), client.WithBody(msg))
if err != nil {
return err
}
resp, errWithOP := client.SendRequestRaw(req)
if errWithOP != nil {
return fmt.Errorf("%s", errWithOP.Message)
}
defer resp.Body.Close()
larkErr := larkErrFunc(resp)
if larkErr != nil {
return fmt.Errorf("%s", larkErr.Message)
}
return nil
}
func (l *Lark) sign(timestamp int64) string {
//timestamp + key 做sha256, 再进行base64 encode
stringToSign := fmt.Sprintf("%v", timestamp) + "\n" + l.secret
var data []byte
h := hmac.New(sha256.New, []byte(stringToSign))
h.Write(data)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func larkErrFunc(resp *http.Response) *types.OpenAIError {
respMsg := &larkResponse{}
err := json.NewDecoder(resp.Body).Decode(respMsg)
if err != nil {
return nil
}
if respMsg.Code == 0 {
return nil
}
return &types.OpenAIError{
Message: fmt.Sprintf("send msg err. err msg: %s", respMsg.Message),
Type: "lark_error",
Code: respMsg.Code,
}
}