feishu.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. package notify
  2. import (
  3. "bytes"
  4. "context"
  5. "errors"
  6. "net/http"
  7. "time"
  8. "github.com/bytedance/sonic"
  9. "github.com/labring/aiproxy/core/common/config"
  10. "github.com/labring/aiproxy/core/common/trylock"
  11. )
  12. type FeishuNotifier struct {
  13. wh string
  14. }
  15. func level2Color(level Level) string {
  16. switch level {
  17. case LevelInfo:
  18. return FeishuColorGreen
  19. case LevelError:
  20. return FeishuColorRed
  21. case LevelWarn:
  22. return FeishuColorOrange
  23. default:
  24. return FeishuColorGreen
  25. }
  26. }
  27. func (f *FeishuNotifier) Notify(level Level, title, message string) {
  28. stdNotifier.Notify(level, title, message)
  29. go func() {
  30. _ = PostToFeiShuv2(context.Background(), level2Color(level), title, message, f.wh)
  31. }()
  32. }
  33. func (f *FeishuNotifier) NotifyThrottle(
  34. level Level,
  35. key string,
  36. expiration time.Duration,
  37. title, message string,
  38. ) {
  39. if trylock.Lock(key, expiration) {
  40. stdNotifier.Notify(level, title, message)
  41. go func() {
  42. _ = PostToFeiShuv2(context.Background(), level2Color(level), title, message, f.wh)
  43. }()
  44. }
  45. }
  46. func NewFeishuNotify(wh string) Notifier {
  47. return &FeishuNotifier{
  48. wh: wh,
  49. }
  50. }
  51. type FSMessagev2 struct {
  52. MsgType string `json:"msg_type"`
  53. Email string `json:"email"`
  54. Card Cards `json:"card"`
  55. }
  56. type Cards struct {
  57. Config Conf `json:"config"`
  58. Elements []Element `json:"elements"`
  59. Header Headers `json:"header"`
  60. }
  61. type Conf struct {
  62. WideScreenMode bool `json:"wide_screen_mode"`
  63. EnableForward bool `json:"enable_forward"`
  64. }
  65. type Te struct {
  66. Content string `json:"content"`
  67. Tag string `json:"tag"`
  68. }
  69. type Element struct {
  70. Tag string `json:"tag"`
  71. Text Te `json:"text"`
  72. Content string `json:"content"`
  73. Elements []Element `json:"elements"`
  74. }
  75. type Titles struct {
  76. Content string `json:"content"`
  77. Tag string `json:"tag"`
  78. }
  79. type Headers struct {
  80. Title Titles `json:"title"`
  81. Template string `json:"template"`
  82. }
  83. type TenantAccessMeg struct {
  84. AppID string `json:"app_id"`
  85. AppSecret string `json:"app_secret"`
  86. }
  87. type TenantAccessResp struct {
  88. Code int `json:"code"`
  89. Msg string `json:"msg"`
  90. TenantAccessToken string `json:"tenant_access_token"`
  91. }
  92. type FeiShuv2Resp struct {
  93. StatusCode int `json:"StatusCode"`
  94. StatusMessage string `json:"StatusMessage"`
  95. Code int `json:"code"`
  96. Data any `json:"data"`
  97. Msg string `json:"msg"`
  98. }
  99. const (
  100. FeishuColorOrange = "orange"
  101. FeishuColorGreen = "green"
  102. FeishuColorRed = "red"
  103. )
  104. func PostToFeiShuv2(ctx context.Context, color, title, text, wh string) error {
  105. if wh == "" {
  106. return errors.New("feishu webhook url is empty")
  107. }
  108. note := config.GetNotifyNote()
  109. if note == "" {
  110. note = "AI Proxy"
  111. }
  112. u := FSMessagev2{
  113. MsgType: "interactive",
  114. Card: Cards{
  115. Config: Conf{
  116. WideScreenMode: true,
  117. EnableForward: true,
  118. },
  119. Header: Headers{
  120. Title: Titles{
  121. Content: title,
  122. Tag: "plain_text",
  123. },
  124. Template: color,
  125. },
  126. Elements: []Element{
  127. {
  128. Tag: "div",
  129. Text: Te{
  130. Content: text,
  131. Tag: "lark_md",
  132. },
  133. },
  134. {
  135. Tag: "hr",
  136. },
  137. {
  138. Tag: "note",
  139. Elements: []Element{
  140. {
  141. Content: note,
  142. Tag: "lark_md",
  143. },
  144. },
  145. },
  146. },
  147. },
  148. }
  149. data, err := sonic.ConfigDefault.Marshal(u)
  150. if err != nil {
  151. return err
  152. }
  153. req, err := http.NewRequestWithContext(ctx, http.MethodPost, wh, bytes.NewReader(data))
  154. if err != nil {
  155. return err
  156. }
  157. resp, err := http.DefaultClient.Do(req)
  158. if err != nil {
  159. return err
  160. }
  161. defer resp.Body.Close()
  162. feishuResp := FeiShuv2Resp{}
  163. if err := sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&feishuResp); err != nil {
  164. return err
  165. }
  166. if feishuResp.Code != 0 {
  167. return errors.New(feishuResp.Msg)
  168. }
  169. return nil
  170. }