wechat_push.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. log "github.com/sirupsen/logrus"
  6. "github.com/spf13/cast"
  7. "io"
  8. "net/http"
  9. "os"
  10. "pmail/config"
  11. "pmail/dto/parsemail"
  12. "pmail/hooks/framework"
  13. "pmail/utils/context"
  14. "strings"
  15. "time"
  16. )
  17. type accessTokenRes struct {
  18. AccessToken string `db:"access_token" json:"access_token"`
  19. ExpiresIn int `db:"expires_in" json:"expires_in"`
  20. }
  21. type WeChatPushHook struct {
  22. appId string
  23. secret string
  24. token string
  25. tokenExpires int64
  26. templateId string
  27. pushUser string
  28. mainConfig *config.Config
  29. }
  30. func (w *WeChatPushHook) ReceiveSaveAfter(ctx *context.Context, email *parsemail.Email) {
  31. if w.appId == "" || w.secret == "" || w.pushUser == "" {
  32. return
  33. }
  34. // 被标记为已读,或者是已删除,或是垃圾邮件 就不处理了
  35. if email.IsRead == 1 || email.Status == 3 || email.MessageId <= 0 {
  36. return
  37. }
  38. content := string(email.Text)
  39. if content == "" {
  40. content = email.Subject
  41. }
  42. w.sendUserMsg(nil, w.pushUser, content)
  43. }
  44. func (w *WeChatPushHook) SendBefore(ctx *context.Context, email *parsemail.Email) {
  45. }
  46. func (w *WeChatPushHook) SendAfter(ctx *context.Context, email *parsemail.Email, err map[string]error) {
  47. }
  48. func (w *WeChatPushHook) ReceiveParseBefore(ctx *context.Context, email *[]byte) {
  49. }
  50. func (w *WeChatPushHook) ReceiveParseAfter(ctx *context.Context, email *parsemail.Email) {}
  51. func (w *WeChatPushHook) getWxAccessToken() string {
  52. if w.tokenExpires > time.Now().Unix() {
  53. return w.token
  54. }
  55. resp, err := http.Get(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", w.appId, w.secret))
  56. if err != nil {
  57. return ""
  58. }
  59. defer resp.Body.Close()
  60. body, _ := io.ReadAll(resp.Body)
  61. var ret accessTokenRes
  62. _ = json.Unmarshal(body, &ret)
  63. if ret.AccessToken != "" {
  64. w.token = ret.AccessToken
  65. w.tokenExpires = time.Now().Unix() + cast.ToInt64(ret.ExpiresIn)
  66. }
  67. return ret.AccessToken
  68. }
  69. type sendMsgRequest struct {
  70. Touser string `db:"touser" json:"touser"`
  71. Template_id string `db:"template_id" json:"template_id"`
  72. Url string `db:"url" json:"url"`
  73. Data SendData `db:"data" json:"data"`
  74. }
  75. type SendData struct {
  76. Content DataItem `json:"Content"`
  77. }
  78. type DataItem struct {
  79. Value string `json:"value"`
  80. Color string `json:"color"`
  81. }
  82. func (w *WeChatPushHook) sendUserMsg(ctx *context.Context, userId string, content string) {
  83. url := w.mainConfig.WebDomain
  84. if w.mainConfig.HttpsEnabled > 1 {
  85. url = "http://" + url
  86. } else {
  87. url = "https://" + url
  88. }
  89. sendMsgReq, _ := json.Marshal(sendMsgRequest{
  90. Touser: userId,
  91. Template_id: w.templateId,
  92. Url: url,
  93. Data: SendData{Content: DataItem{Value: content, Color: "#000000"}},
  94. })
  95. _, err := http.Post("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+w.getWxAccessToken(), "application/json", strings.NewReader(string(sendMsgReq)))
  96. if err != nil {
  97. log.WithContext(ctx).Errorf("wechat push error %+v", err)
  98. }
  99. }
  100. type Config struct {
  101. WeChatPushAppId string `json:"weChatPushAppId"`
  102. WeChatPushSecret string `json:"weChatPushSecret"`
  103. WeChatPushTemplateId string `json:"weChatPushTemplateId"`
  104. WeChatPushUserId string `json:"weChatPushUserId"`
  105. }
  106. func NewWechatPushHook() *WeChatPushHook {
  107. var cfgData []byte
  108. var err error
  109. cfgData, err = os.ReadFile("../../config/config.json")
  110. if err != nil {
  111. panic(err)
  112. }
  113. var mainConfig *config.Config
  114. err = json.Unmarshal(cfgData, &mainConfig)
  115. if err != nil {
  116. panic(err)
  117. }
  118. var pluginConfig *Config
  119. if _, err := os.Stat("./plugins/wechat_push_config.json"); err == nil {
  120. cfgData, err = os.ReadFile("./plugins/wechat_push_config.json")
  121. if err != nil {
  122. panic(err)
  123. }
  124. err = json.Unmarshal(cfgData, &pluginConfig)
  125. if err != nil {
  126. panic(err)
  127. }
  128. }
  129. appid := ""
  130. secret := ""
  131. templateId := ""
  132. userId := ""
  133. if pluginConfig != nil {
  134. appid = pluginConfig.WeChatPushAppId
  135. secret = pluginConfig.WeChatPushSecret
  136. templateId = pluginConfig.WeChatPushTemplateId
  137. userId = pluginConfig.WeChatPushUserId
  138. } else {
  139. appid = mainConfig.WeChatPushAppId
  140. secret = mainConfig.WeChatPushSecret
  141. templateId = mainConfig.WeChatPushTemplateId
  142. userId = mainConfig.WeChatPushUserId
  143. }
  144. ret := &WeChatPushHook{
  145. appId: appid,
  146. secret: secret,
  147. templateId: templateId,
  148. pushUser: userId,
  149. mainConfig: mainConfig,
  150. }
  151. return ret
  152. }
  153. // 插件将以独立进程运行,因此需要主函数。
  154. func main() {
  155. framework.CreatePlugin("wechat_push", NewWechatPushHook()).Run()
  156. }