wechat_push.go 4.9 KB

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