From 44ff7687f213d12521834b056750ff4d4f16c470 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Mon, 13 Jun 2022 17:35:39 +0800 Subject: [PATCH 1/7] #2225 add auto reply --- models/repo.go | 9 +- modules/auth/wechat/auto_reply.go | 110 +++++++++++++++++++++++++ modules/auth/wechat/event_handle.go | 87 ++++++++++++++++++- routers/authentication/wechat_event.go | 110 ++++++++++++++++++++++--- 4 files changed, 293 insertions(+), 23 deletions(-) create mode 100644 modules/auth/wechat/auto_reply.go diff --git a/models/repo.go b/models/repo.go index db2694617..4770e5415 100755 --- a/models/repo.go +++ b/models/repo.go @@ -2749,15 +2749,10 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi log.Error("ReadLatestFileInRepo: Close: %v", err) } }() - - buf := make([]byte, 1024) - n, _ := reader.Read(buf) - if n >= 0 { - buf = buf[:n] - } + d, _ := ioutil.ReadAll(reader) commitId := "" if blob != nil { commitId = fmt.Sprint(blob.ID) } - return &RepoFile{CommitId: commitId, Content: buf}, nil + return &RepoFile{CommitId: commitId, Content: d}, nil } diff --git a/modules/auth/wechat/auto_reply.go b/modules/auth/wechat/auto_reply.go new file mode 100644 index 000000000..7d3a30d07 --- /dev/null +++ b/modules/auth/wechat/auto_reply.go @@ -0,0 +1,110 @@ +package wechat + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "encoding/json" + "github.com/patrickmn/go-cache" + "strings" + "time" +) + +var WechatReplyCache = cache.New(2*time.Minute, 1*time.Minute) + +const ( + WECHAT_REPLY_CACHE_KEY = "wechat_response" +) + +const ( + ReplyTypeText = "text" + ReplyTypeImage = "image" + ReplyTypeVoice = "voice" + ReplyTypeVideo = "video" + ReplyTypeMusic = "music" + ReplyTypeNews = "news" +) + +type AutomaticResponseContent struct { + Reply *ReplyContent + ReplyType string + KeyWords []string + IsFullMatch int +} + +type ReplyContent struct { + Content string + MediaId string + Title string + Description string + MusicUrl string + HQMusicUrl string + ThumbMediaId string +} + +func GetAutomaticReply(msg string) *AutomaticResponseContent { + r, err := LoadAutomaticReplyFromCacheAndDisk() + if err != nil { + return nil + } + if r == nil || len(r) == 0 { + return nil + } + for i := 0; i < len(r); i++ { + if r[i].IsFullMatch > 0 { + for _, v := range r[i].KeyWords { + if strings.Contains(msg, v) { + return r[i] + } + } + } else if r[i].IsFullMatch == 0 { + for _, v := range r[i].KeyWords { + if msg == v { + return r[i] + } + } + } + } + return nil + +} + +func loadAutomaticReplyFromDisk() ([]*AutomaticResponseContent, error) { + log.Debug("LoadAutomaticResponseMap from disk") + repo, err := models.GetRepositoryByOwnerAndAlias("OpenIOSSG", "promote") + if err != nil { + log.Error("get notice repo failed, error=%v", err) + return nil, err + } + repoFile, err := models.ReadLatestFileInRepo("OpenIOSSG", repo.Name, "master", "wechat/auto_reply.json") + if err != nil { + log.Error("GetNewestNotice failed, error=%v", err) + return nil, err + } + res := make([]*AutomaticResponseContent, 0) + json.Unmarshal(repoFile.Content, &res) + if res == nil || len(res) == 0 { + return nil, err + } + return res, nil +} + +func LoadAutomaticReplyFromCacheAndDisk() ([]*AutomaticResponseContent, error) { + v, success := WechatReplyCache.Get(WECHAT_REPLY_CACHE_KEY) + if success { + log.Debug("LoadAutomaticResponse from cache,value = %v", v) + if v == nil { + return nil, nil + } + n := v.([]*AutomaticResponseContent) + return n, nil + } + + content, err := loadAutomaticReplyFromDisk() + if err != nil { + log.Error("GetNewestNotice failed, error=%v", err) + WechatReplyCache.Set(WECHAT_REPLY_CACHE_KEY, nil, 30*time.Second) + return nil, err + } + WechatReplyCache.Set(WECHAT_REPLY_CACHE_KEY, content, 60*time.Second) + return content, nil +} diff --git a/modules/auth/wechat/event_handle.go b/modules/auth/wechat/event_handle.go index b40ab3101..3dd8508cb 100644 --- a/modules/auth/wechat/event_handle.go +++ b/modules/auth/wechat/event_handle.go @@ -18,7 +18,7 @@ import ( // // // -type WechatEvent struct { +type WechatMsg struct { ToUserName string FromUserName string CreateTime int64 @@ -26,9 +26,22 @@ type WechatEvent struct { Event string EventKey string Ticket string + Content string + MsgId string + MsgDataId string + Idx string +} + +type MsgReply struct { + XMLName xml.Name `xml:"xml"` + ToUserName string + FromUserName string + CreateTime int64 + MsgType string + Content string } -type EventReply struct { +type TextMsgReply struct { XMLName xml.Name `xml:"xml"` ToUserName string FromUserName string @@ -36,6 +49,71 @@ type EventReply struct { MsgType string Content string } +type ImageMsgReply struct { + XMLName xml.Name `xml:"xml"` + ToUserName string + FromUserName string + CreateTime int64 + MsgType string + Image ImageContent +} +type VoiceMsgReply struct { + XMLName xml.Name `xml:"xml"` + ToUserName string + FromUserName string + CreateTime int64 + MsgType string + Voice VoiceContent +} +type VideoMsgReply struct { + XMLName xml.Name `xml:"xml"` + ToUserName string + FromUserName string + CreateTime int64 + MsgType string + Video VideoContent +} +type MusicMsgReply struct { + XMLName xml.Name `xml:"xml"` + ToUserName string + FromUserName string + CreateTime int64 + MsgType string + Music MusicContent +} +type ArticlesMsgReply struct { + XMLName xml.Name `xml:"xml"` + ToUserName string + FromUserName string + CreateTime int64 + MsgType string + Articles []ArticlesContent +} + +type ImageContent struct { + MediaId string +} +type VoiceContent struct { + MediaId string +} +type VideoContent struct { + MediaId string + Title string + Description string +} +type MusicContent struct { + Title string + Description string + MusicUrl string + HQMusicUrl string + ThumbMediaId string +} +type ArticlesContent struct { + Title string + Description string + PicUrl string + Url string +} const ( WECHAT_EVENT_SUBSCRIBE = "subscribe" @@ -43,10 +121,11 @@ const ( ) const ( - WECHAT_MSG_TYPE_TEXT = "text" + WECHAT_MSG_TYPE_TEXT = "text" + WECHAT_MSG_TYPE_EVENT = "event" ) -func HandleSubscribeEvent(we WechatEvent) string { +func HandleSubscribeEvent(we WechatMsg) string { eventKey := we.EventKey if eventKey == "" { return "" diff --git a/routers/authentication/wechat_event.go b/routers/authentication/wechat_event.go index 9b1cebec6..b8270faee 100644 --- a/routers/authentication/wechat_event.go +++ b/routers/authentication/wechat_event.go @@ -14,14 +14,30 @@ import ( // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html func AcceptWechatEvent(ctx *context.Context) { b, _ := ioutil.ReadAll(ctx.Req.Request.Body) - we := wechat.WechatEvent{} + we := wechat.WechatMsg{} xml.Unmarshal(b, &we) - + switch we.MsgType { + case wechat.WECHAT_MSG_TYPE_EVENT: + HandleEventMsg(ctx, we) + case wechat.WECHAT_MSG_TYPE_TEXT: + HandleTextMsg(ctx, we) + } log.Info("accept wechat event= %+v", we) + +} + +// ValidEventSource +func ValidEventSource(ctx *context.Context) { + echostr := ctx.Query("echostr") + ctx.Write([]byte(echostr)) + return +} + +func HandleEventMsg(ctx *context.Context, msg wechat.WechatMsg) { var replyStr string - switch we.Event { + switch msg.Event { case wechat.WECHAT_EVENT_SUBSCRIBE, wechat.WECHAT_EVENT_SCAN: - replyStr = wechat.HandleSubscribeEvent(we) + replyStr = wechat.HandleSubscribeEvent(msg) break } @@ -29,9 +45,9 @@ func AcceptWechatEvent(ctx *context.Context) { log.Info("reply str is empty") return } - reply := &wechat.EventReply{ - ToUserName: we.FromUserName, - FromUserName: we.ToUserName, + reply := &wechat.MsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, CreateTime: time.Now().Unix(), MsgType: wechat.WECHAT_MSG_TYPE_TEXT, Content: replyStr, @@ -39,9 +55,79 @@ func AcceptWechatEvent(ctx *context.Context) { ctx.XML(200, reply) } -// ValidEventSource -func ValidEventSource(ctx *context.Context) { - echostr := ctx.Query("echostr") - ctx.Write([]byte(echostr)) - return +func HandleTextMsg(ctx *context.Context, msg wechat.WechatMsg) { + r := wechat.GetAutomaticReply(msg.Content) + if r == nil { + log.Info("TextMsg reply is empty") + return + } + reply := buildReplyContent(msg, r) + ctx.XML(200, reply) +} + +func buildReplyContent(msg wechat.WechatMsg, r *wechat.AutomaticResponseContent) interface{} { + reply := &wechat.MsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, + CreateTime: time.Now().Unix(), + MsgType: r.ReplyType, + } + switch r.ReplyType { + case wechat.ReplyTypeText: + return &wechat.TextMsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, + CreateTime: time.Now().Unix(), + MsgType: r.ReplyType, + Content: r.Reply.Content, + } + + case wechat.ReplyTypeImage: + return &wechat.ImageMsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, + CreateTime: time.Now().Unix(), + MsgType: r.ReplyType, + Image: wechat.ImageContent{ + MediaId: r.Reply.MediaId, + }, + } + case wechat.ReplyTypeVoice: + return &wechat.VoiceMsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, + CreateTime: time.Now().Unix(), + MsgType: r.ReplyType, + Voice: wechat.VoiceContent{ + MediaId: r.Reply.MediaId, + }, + } + case wechat.ReplyTypeVideo: + return &wechat.VideoMsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, + CreateTime: time.Now().Unix(), + MsgType: r.ReplyType, + Video: wechat.VideoContent{ + MediaId: r.Reply.MediaId, + Title: r.Reply.Title, + Description: r.Reply.Description, + }, + } + case wechat.ReplyTypeMusic: + return &wechat.MusicMsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, + CreateTime: time.Now().Unix(), + MsgType: r.ReplyType, + Music: wechat.MusicContent{ + Title: r.Reply.Title, + Description: r.Reply.Description, + MusicUrl: r.Reply.MusicUrl, + HQMusicUrl: r.Reply.HQMusicUrl, + ThumbMediaId: r.Reply.ThumbMediaId, + }, + } + } + return reply } From 73baa29c90e62b5a035072c67e26884eb077bbd5 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 14 Jun 2022 16:43:53 +0800 Subject: [PATCH 2/7] #2225 add config --- modules/auth/wechat/auto_reply.go | 5 +++-- modules/setting/setting.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/auth/wechat/auto_reply.go b/modules/auth/wechat/auto_reply.go index 7d3a30d07..b1af33113 100644 --- a/modules/auth/wechat/auto_reply.go +++ b/modules/auth/wechat/auto_reply.go @@ -3,6 +3,7 @@ package wechat import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" "encoding/json" "github.com/patrickmn/go-cache" "strings" @@ -70,12 +71,12 @@ func GetAutomaticReply(msg string) *AutomaticResponseContent { func loadAutomaticReplyFromDisk() ([]*AutomaticResponseContent, error) { log.Debug("LoadAutomaticResponseMap from disk") - repo, err := models.GetRepositoryByOwnerAndAlias("OpenIOSSG", "promote") + repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfAutoReply, setting.RepoNameOfAutoReply) if err != nil { log.Error("get notice repo failed, error=%v", err) return nil, err } - repoFile, err := models.ReadLatestFileInRepo("OpenIOSSG", repo.Name, "master", "wechat/auto_reply.json") + repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfAutoReply, repo.Name, setting.RefNameOfAutoReply, setting.TreePathOfAutoReply) if err != nil { log.Error("GetNewestNotice failed, error=%v", err) return nil, err diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 5c87b68c5..1750d1876 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -545,6 +545,12 @@ var ( WechatQRCodeExpireSeconds int WechatAuthSwitch bool + //wechat auto reply config + UserNameOfAutoReply string + RepoNameOfAutoReply string + RefNameOfAutoReply string + TreePathOfAutoReply string + //nginx proxy PROXYURL string RadarMap = struct { @@ -1372,6 +1378,10 @@ func NewContext() { WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(true) + UserNameOfAutoReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("OpenIOSSG") + RepoNameOfAutoReply = sec.Key("AUTO_REPLY_REPO_NAME").MustString("promote") + RefNameOfAutoReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") + TreePathOfAutoReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("wechat/auto_reply.json") SetRadarMapConfig() From 577e7b2edeb10b5eb4180644057059bf4af8e8a0 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 14 Jun 2022 17:07:56 +0800 Subject: [PATCH 3/7] #2225 add config --- modules/auth/wechat/auto_reply.go | 4 ++-- modules/setting/setting.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auth/wechat/auto_reply.go b/modules/auth/wechat/auto_reply.go index b1af33113..94ee5555b 100644 --- a/modules/auth/wechat/auto_reply.go +++ b/modules/auth/wechat/auto_reply.go @@ -73,12 +73,12 @@ func loadAutomaticReplyFromDisk() ([]*AutomaticResponseContent, error) { log.Debug("LoadAutomaticResponseMap from disk") repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfAutoReply, setting.RepoNameOfAutoReply) if err != nil { - log.Error("get notice repo failed, error=%v", err) + log.Error("get AutomaticReply repo failed, error=%v", err) return nil, err } repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfAutoReply, repo.Name, setting.RefNameOfAutoReply, setting.TreePathOfAutoReply) if err != nil { - log.Error("GetNewestNotice failed, error=%v", err) + log.Error("get AutomaticReply failed, error=%v", err) return nil, err } res := make([]*AutomaticResponseContent, 0) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 1750d1876..d85653196 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -1381,7 +1381,7 @@ func NewContext() { UserNameOfAutoReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("OpenIOSSG") RepoNameOfAutoReply = sec.Key("AUTO_REPLY_REPO_NAME").MustString("promote") RefNameOfAutoReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") - TreePathOfAutoReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("wechat/auto_reply.json") + TreePathOfAutoReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") SetRadarMapConfig() From cb61264b97560c5fc9284b27294a9c0852b2fcc8 Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Tue, 14 Jun 2022 17:23:36 +0800 Subject: [PATCH 4/7] #2225 update --- modules/auth/wechat/auto_reply.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/auth/wechat/auto_reply.go b/modules/auth/wechat/auto_reply.go index 94ee5555b..c81efa558 100644 --- a/modules/auth/wechat/auto_reply.go +++ b/modules/auth/wechat/auto_reply.go @@ -70,7 +70,7 @@ func GetAutomaticReply(msg string) *AutomaticResponseContent { } func loadAutomaticReplyFromDisk() ([]*AutomaticResponseContent, error) { - log.Debug("LoadAutomaticResponseMap from disk") + log.Info("LoadAutomaticResponseMap from disk") repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfAutoReply, setting.RepoNameOfAutoReply) if err != nil { log.Error("get AutomaticReply repo failed, error=%v", err) @@ -92,7 +92,7 @@ func loadAutomaticReplyFromDisk() ([]*AutomaticResponseContent, error) { func LoadAutomaticReplyFromCacheAndDisk() ([]*AutomaticResponseContent, error) { v, success := WechatReplyCache.Get(WECHAT_REPLY_CACHE_KEY) if success { - log.Debug("LoadAutomaticResponse from cache,value = %v", v) + log.Info("LoadAutomaticResponse from cache,value = %v", v) if v == nil { return nil, nil } From 7961cb65b022a3f3a7ae7d76ef795e8903f1d30f Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Wed, 15 Jun 2022 10:59:51 +0800 Subject: [PATCH 5/7] #2225 add reply type : news --- modules/auth/wechat/auto_reply.go | 5 +++-- modules/auth/wechat/event_handle.go | 10 ++++++++-- routers/authentication/wechat_event.go | 11 +++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/modules/auth/wechat/auto_reply.go b/modules/auth/wechat/auto_reply.go index c81efa558..d077aee95 100644 --- a/modules/auth/wechat/auto_reply.go +++ b/modules/auth/wechat/auto_reply.go @@ -40,6 +40,7 @@ type ReplyContent struct { MusicUrl string HQMusicUrl string ThumbMediaId string + Articles []ArticlesContent } func GetAutomaticReply(msg string) *AutomaticResponseContent { @@ -51,13 +52,13 @@ func GetAutomaticReply(msg string) *AutomaticResponseContent { return nil } for i := 0; i < len(r); i++ { - if r[i].IsFullMatch > 0 { + if r[i].IsFullMatch == 0 { for _, v := range r[i].KeyWords { if strings.Contains(msg, v) { return r[i] } } - } else if r[i].IsFullMatch == 0 { + } else if r[i].IsFullMatch > 0 { for _, v := range r[i].KeyWords { if msg == v { return r[i] diff --git a/modules/auth/wechat/event_handle.go b/modules/auth/wechat/event_handle.go index 3dd8508cb..7e46fac04 100644 --- a/modules/auth/wechat/event_handle.go +++ b/modules/auth/wechat/event_handle.go @@ -81,13 +81,18 @@ type MusicMsgReply struct { MsgType string Music MusicContent } -type ArticlesMsgReply struct { +type NewsMsgReply struct { XMLName xml.Name `xml:"xml"` ToUserName string FromUserName string CreateTime int64 MsgType string - Articles []ArticlesContent + ArticleCount int + Articles ArticleItem +} + +type ArticleItem struct { + Item []ArticlesContent } type ImageContent struct { @@ -109,6 +114,7 @@ type MusicContent struct { ThumbMediaId string } type ArticlesContent struct { + XMLName xml.Name `xml:"item"` Title string Description string PicUrl string diff --git a/routers/authentication/wechat_event.go b/routers/authentication/wechat_event.go index b8270faee..8ae1d8991 100644 --- a/routers/authentication/wechat_event.go +++ b/routers/authentication/wechat_event.go @@ -128,6 +128,17 @@ func buildReplyContent(msg wechat.WechatMsg, r *wechat.AutomaticResponseContent) ThumbMediaId: r.Reply.ThumbMediaId, }, } + case wechat.ReplyTypeNews: + return &wechat.NewsMsgReply{ + ToUserName: msg.FromUserName, + FromUserName: msg.ToUserName, + CreateTime: time.Now().Unix(), + MsgType: r.ReplyType, + ArticleCount: len(r.Reply.Articles), + Articles: wechat.ArticleItem{ + Item: r.Reply.Articles}, + } + } return reply } From 61c15329222a2603594d49acc2665112a55cce2b Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Wed, 15 Jun 2022 11:43:32 +0800 Subject: [PATCH 6/7] #2225 add material query api --- modules/auth/wechat/client.go | 41 ++++++++++++++++++++++++++++++-- modules/auth/wechat/material.go | 13 ++++++++++ routers/api/v1/api.go | 1 + routers/authentication/wechat.go | 22 +++++++++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 modules/auth/wechat/material.go diff --git a/modules/auth/wechat/client.go b/modules/auth/wechat/client.go index 6734977a1..9ed4b543f 100644 --- a/modules/auth/wechat/client.go +++ b/modules/auth/wechat/client.go @@ -17,7 +17,8 @@ var ( const ( GRANT_TYPE = "client_credential" ACCESS_TOKEN_PATH = "/cgi-bin/token" - QR_CODE_Path = "/cgi-bin/qrcode/create" + QR_CODE_PATH = "/cgi-bin/qrcode/create" + GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material" ACTION_QR_STR_SCENE = "QR_STR_SCENE" ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 @@ -40,6 +41,11 @@ type QRCodeRequest struct { Action_info ActionInfo `json:"action_info"` Expire_seconds int `json:"expire_seconds"` } +type MaterialRequest struct { + Type string `json:"type"` + Offset int `json:"offset"` + Count int `json:"count"` +} type ActionInfo struct { Scene Scene `json:"scene"` @@ -97,7 +103,7 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { SetQueryParam("access_token", GetWechatAccessToken()). SetBody(bodyJson). SetResult(&result). - Post(setting.WechatApiHost + QR_CODE_Path) + Post(setting.WechatApiHost + QR_CODE_PATH) if err != nil { log.Error("create QR code failed,e=%v", err) return nil, false @@ -113,6 +119,37 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { return &result, false } +//getMaterial +// api doc: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html +func getMaterial(mType string, offset, count int) (interface{}, bool) { + client := getWechatRestyClient() + + body := &MaterialRequest{ + Type: mType, + Offset: offset, + Count: count, + } + bodyJson, _ := json.Marshal(body) + r, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetQueryParam("access_token", GetWechatAccessToken()). + SetBody(bodyJson). + Post(setting.WechatApiHost + GET_MATERIAL_PATH) + if err != nil { + log.Error("create QR code failed,e=%v", err) + return nil, false + } + a := r.Body() + resultMap := make(map[string]interface{}, 0) + json.Unmarshal(a, &resultMap) + errcode := resultMap["errcode"] + if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) { + return nil, true + } + log.Info("%v", r) + return &resultMap, false +} + func getErrorCodeFromResponse(r *resty.Response) int { a := r.Body() resultMap := make(map[string]interface{}, 0) diff --git a/modules/auth/wechat/material.go b/modules/auth/wechat/material.go new file mode 100644 index 000000000..526156af5 --- /dev/null +++ b/modules/auth/wechat/material.go @@ -0,0 +1,13 @@ +package wechat + +import "code.gitea.io/gitea/modules/log" + +func GetWechatMaterial(mType string, offset, count int) interface{} { + result, retryFlag := getMaterial(mType, offset, count) + if retryFlag { + log.Info("retryGetWechatMaterial calling") + refreshAccessToken() + result, _ = getMaterial(mType, offset, count) + } + return result +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index d6d3b001a..e6ccf9428 100755 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1034,6 +1034,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/prd/event", authentication.ValidEventSource) m.Post("/prd/event", authentication.AcceptWechatEvent) }) + m.Get("/wechat/material", authentication.GetMaterial) }, securityHeaders(), context.APIContexter(), sudo()) } diff --git a/routers/authentication/wechat.go b/routers/authentication/wechat.go index 72871afb3..f4a31ea0c 100644 --- a/routers/authentication/wechat.go +++ b/routers/authentication/wechat.go @@ -8,9 +8,11 @@ import ( "code.gitea.io/gitea/modules/redis/redis_client" "code.gitea.io/gitea/modules/redis/redis_key" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/routers/response" "encoding/json" "errors" gouuid "github.com/satori/go.uuid" + "strconv" "time" ) @@ -124,3 +126,23 @@ func createQRCode4Bind(userId int64) (*QRCodeResponse, error) { } return result, nil } + +// GetMaterial +func GetMaterial(ctx *context.Context) { + mType := ctx.Query("type") + offsetStr := ctx.Query("offset") + countStr := ctx.Query("count") + var offset, count int + if offsetStr == "" { + offset = 0 + } else { + offset, _ = strconv.Atoi(offsetStr) + } + if countStr == "" { + count = 20 + } else { + count, _ = strconv.Atoi(countStr) + } + r := wechat.GetWechatMaterial(mType, offset, count) + ctx.JSON(200, response.SuccessWithData(r)) +} From c5d693fb4b19e327863173ea17774a4ef112fa0c Mon Sep 17 00:00:00 2001 From: chenyifan01 Date: Wed, 15 Jun 2022 17:08:26 +0800 Subject: [PATCH 7/7] #2225 add wechat subcribe event handler --- modules/auth/wechat/auto_reply.go | 59 +++++++++++++++++++------- modules/auth/wechat/event_handle.go | 10 ++++- modules/setting/setting.go | 18 ++++---- routers/authentication/wechat_event.go | 27 +++++++++--- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/modules/auth/wechat/auto_reply.go b/modules/auth/wechat/auto_reply.go index d077aee95..440f6de6a 100644 --- a/modules/auth/wechat/auto_reply.go +++ b/modules/auth/wechat/auto_reply.go @@ -25,7 +25,34 @@ const ( ReplyTypeNews = "news" ) -type AutomaticResponseContent struct { +type ReplyConfigType string + +const ( + SubscribeReply ReplyConfigType = "subscribe" + AutoMsgReply ReplyConfigType = "autoMsg" +) + +func (r ReplyConfigType) Name() string { + switch r { + case SubscribeReply: + return "subscribe" + case AutoMsgReply: + return "autoMsg" + } + return "" +} + +func (r ReplyConfigType) TreePath() string { + switch r { + case SubscribeReply: + return setting.TreePathOfSubscribe + case AutoMsgReply: + return setting.TreePathOfAutoMsgReply + } + return "" +} + +type WechatReplyContent struct { Reply *ReplyContent ReplyType string KeyWords []string @@ -43,8 +70,8 @@ type ReplyContent struct { Articles []ArticlesContent } -func GetAutomaticReply(msg string) *AutomaticResponseContent { - r, err := LoadAutomaticReplyFromCacheAndDisk() +func GetAutomaticReply(msg string) *WechatReplyContent { + r, err := LoadReplyFromCacheAndDisk(AutoMsgReply) if err != nil { return nil } @@ -70,19 +97,19 @@ func GetAutomaticReply(msg string) *AutomaticResponseContent { } -func loadAutomaticReplyFromDisk() ([]*AutomaticResponseContent, error) { - log.Info("LoadAutomaticResponseMap from disk") - repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfAutoReply, setting.RepoNameOfAutoReply) +func loadReplyFromDisk(replyConfig ReplyConfigType) ([]*WechatReplyContent, error) { + log.Info("LoadReply from disk") + repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfWechatReply, setting.RepoNameOfWechatReply) if err != nil { log.Error("get AutomaticReply repo failed, error=%v", err) return nil, err } - repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfAutoReply, repo.Name, setting.RefNameOfAutoReply, setting.TreePathOfAutoReply) + repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfWechatReply, repo.Name, setting.RefNameOfWechatReply, replyConfig.TreePath()) if err != nil { log.Error("get AutomaticReply failed, error=%v", err) return nil, err } - res := make([]*AutomaticResponseContent, 0) + res := make([]*WechatReplyContent, 0) json.Unmarshal(repoFile.Content, &res) if res == nil || len(res) == 0 { return nil, err @@ -90,23 +117,23 @@ func loadAutomaticReplyFromDisk() ([]*AutomaticResponseContent, error) { return res, nil } -func LoadAutomaticReplyFromCacheAndDisk() ([]*AutomaticResponseContent, error) { - v, success := WechatReplyCache.Get(WECHAT_REPLY_CACHE_KEY) +func LoadReplyFromCacheAndDisk(replyConfig ReplyConfigType) ([]*WechatReplyContent, error) { + v, success := WechatReplyCache.Get(replyConfig.Name()) if success { - log.Info("LoadAutomaticResponse from cache,value = %v", v) + log.Info("LoadReply from cache,value = %v", v) if v == nil { return nil, nil } - n := v.([]*AutomaticResponseContent) + n := v.([]*WechatReplyContent) return n, nil } - content, err := loadAutomaticReplyFromDisk() + content, err := loadReplyFromDisk(replyConfig) if err != nil { - log.Error("GetNewestNotice failed, error=%v", err) - WechatReplyCache.Set(WECHAT_REPLY_CACHE_KEY, nil, 30*time.Second) + log.Error("LoadReply failed, error=%v", err) + WechatReplyCache.Set(replyConfig.Name(), nil, 30*time.Second) return nil, err } - WechatReplyCache.Set(WECHAT_REPLY_CACHE_KEY, content, 60*time.Second) + WechatReplyCache.Set(replyConfig.Name(), content, 60*time.Second) return content, nil } diff --git a/modules/auth/wechat/event_handle.go b/modules/auth/wechat/event_handle.go index 7e46fac04..27edf7343 100644 --- a/modules/auth/wechat/event_handle.go +++ b/modules/auth/wechat/event_handle.go @@ -131,7 +131,7 @@ const ( WECHAT_MSG_TYPE_EVENT = "event" ) -func HandleSubscribeEvent(we WechatMsg) string { +func HandleScanEvent(we WechatMsg) string { eventKey := we.EventKey if eventKey == "" { return "" @@ -159,3 +159,11 @@ func HandleSubscribeEvent(we WechatMsg) string { return BIND_REPLY_SUCCESS } + +func HandleSubscribeEvent(we WechatMsg) *WechatReplyContent { + r, err := LoadReplyFromCacheAndDisk(SubscribeReply) + if err != nil || len(r) == 0 { + return nil + } + return r[0] +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index d85653196..412bc09b1 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -546,10 +546,11 @@ var ( WechatAuthSwitch bool //wechat auto reply config - UserNameOfAutoReply string - RepoNameOfAutoReply string - RefNameOfAutoReply string - TreePathOfAutoReply string + UserNameOfWechatReply string + RepoNameOfWechatReply string + RefNameOfWechatReply string + TreePathOfAutoMsgReply string + TreePathOfSubscribe string //nginx proxy PROXYURL string @@ -1378,10 +1379,11 @@ func NewContext() { WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(true) - UserNameOfAutoReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("OpenIOSSG") - RepoNameOfAutoReply = sec.Key("AUTO_REPLY_REPO_NAME").MustString("promote") - RefNameOfAutoReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") - TreePathOfAutoReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") + UserNameOfWechatReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("OpenIOSSG") + RepoNameOfWechatReply = sec.Key("AUTO_REPLY_REPO_NAME").MustString("promote") + RefNameOfWechatReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") + TreePathOfAutoMsgReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") + TreePathOfSubscribe = sec.Key("SUBSCRIBE_TREE_PATH").MustString("wechat/subscribe_reply.json") SetRadarMapConfig() diff --git a/routers/authentication/wechat_event.go b/routers/authentication/wechat_event.go index 8ae1d8991..887bfba0d 100644 --- a/routers/authentication/wechat_event.go +++ b/routers/authentication/wechat_event.go @@ -34,13 +34,21 @@ func ValidEventSource(ctx *context.Context) { } func HandleEventMsg(ctx *context.Context, msg wechat.WechatMsg) { - var replyStr string switch msg.Event { - case wechat.WECHAT_EVENT_SUBSCRIBE, wechat.WECHAT_EVENT_SCAN: - replyStr = wechat.HandleSubscribeEvent(msg) - break + case wechat.WECHAT_EVENT_SCAN: + HandleEventScan(ctx, msg) + case wechat.WECHAT_EVENT_SUBSCRIBE: + if msg.EventKey != "" { + HandleEventScan(ctx, msg) + } else { + HandleEventSubscribe(ctx, msg) + } + } +} +func HandleEventScan(ctx *context.Context, msg wechat.WechatMsg) { + replyStr := wechat.HandleScanEvent(msg) if replyStr == "" { log.Info("reply str is empty") return @@ -55,6 +63,15 @@ func HandleEventMsg(ctx *context.Context, msg wechat.WechatMsg) { ctx.XML(200, reply) } +func HandleEventSubscribe(ctx *context.Context, msg wechat.WechatMsg) { + r := wechat.HandleSubscribeEvent(msg) + if r == nil { + return + } + reply := buildReplyContent(msg, r) + ctx.XML(200, reply) +} + func HandleTextMsg(ctx *context.Context, msg wechat.WechatMsg) { r := wechat.GetAutomaticReply(msg.Content) if r == nil { @@ -65,7 +82,7 @@ func HandleTextMsg(ctx *context.Context, msg wechat.WechatMsg) { ctx.XML(200, reply) } -func buildReplyContent(msg wechat.WechatMsg, r *wechat.AutomaticResponseContent) interface{} { +func buildReplyContent(msg wechat.WechatMsg, r *wechat.WechatReplyContent) interface{} { reply := &wechat.MsgReply{ ToUserName: msg.FromUserName, FromUserName: msg.ToUserName,