Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/2367 Reviewed-by: lewis <747342561@qq.com>tags/v1.22.6.2^2
| @@ -2749,15 +2749,10 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi | |||||
| log.Error("ReadLatestFileInRepo: Close: %v", err) | 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 := "" | commitId := "" | ||||
| if blob != nil { | if blob != nil { | ||||
| commitId = fmt.Sprint(blob.ID) | commitId = fmt.Sprint(blob.ID) | ||||
| } | } | ||||
| return &RepoFile{CommitId: commitId, Content: buf}, nil | |||||
| return &RepoFile{CommitId: commitId, Content: d}, nil | |||||
| } | } | ||||
| @@ -0,0 +1,139 @@ | |||||
| 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" | |||||
| "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 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 | |||||
| IsFullMatch int | |||||
| } | |||||
| type ReplyContent struct { | |||||
| Content string | |||||
| MediaId string | |||||
| Title string | |||||
| Description string | |||||
| MusicUrl string | |||||
| HQMusicUrl string | |||||
| ThumbMediaId string | |||||
| Articles []ArticlesContent | |||||
| } | |||||
| func GetAutomaticReply(msg string) *WechatReplyContent { | |||||
| r, err := LoadReplyFromCacheAndDisk(AutoMsgReply) | |||||
| 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 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.UserNameOfWechatReply, repo.Name, setting.RefNameOfWechatReply, replyConfig.TreePath()) | |||||
| if err != nil { | |||||
| log.Error("get AutomaticReply failed, error=%v", err) | |||||
| return nil, err | |||||
| } | |||||
| res := make([]*WechatReplyContent, 0) | |||||
| json.Unmarshal(repoFile.Content, &res) | |||||
| if res == nil || len(res) == 0 { | |||||
| return nil, err | |||||
| } | |||||
| return res, nil | |||||
| } | |||||
| func LoadReplyFromCacheAndDisk(replyConfig ReplyConfigType) ([]*WechatReplyContent, error) { | |||||
| v, success := WechatReplyCache.Get(replyConfig.Name()) | |||||
| if success { | |||||
| log.Info("LoadReply from cache,value = %v", v) | |||||
| if v == nil { | |||||
| return nil, nil | |||||
| } | |||||
| n := v.([]*WechatReplyContent) | |||||
| return n, nil | |||||
| } | |||||
| content, err := loadReplyFromDisk(replyConfig) | |||||
| if err != nil { | |||||
| log.Error("LoadReply failed, error=%v", err) | |||||
| WechatReplyCache.Set(replyConfig.Name(), nil, 30*time.Second) | |||||
| return nil, err | |||||
| } | |||||
| WechatReplyCache.Set(replyConfig.Name(), content, 60*time.Second) | |||||
| return content, nil | |||||
| } | |||||
| @@ -17,7 +17,8 @@ var ( | |||||
| const ( | const ( | ||||
| GRANT_TYPE = "client_credential" | GRANT_TYPE = "client_credential" | ||||
| ACCESS_TOKEN_PATH = "/cgi-bin/token" | 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" | ACTION_QR_STR_SCENE = "QR_STR_SCENE" | ||||
| ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | ||||
| @@ -40,6 +41,11 @@ type QRCodeRequest struct { | |||||
| Action_info ActionInfo `json:"action_info"` | Action_info ActionInfo `json:"action_info"` | ||||
| Expire_seconds int `json:"expire_seconds"` | Expire_seconds int `json:"expire_seconds"` | ||||
| } | } | ||||
| type MaterialRequest struct { | |||||
| Type string `json:"type"` | |||||
| Offset int `json:"offset"` | |||||
| Count int `json:"count"` | |||||
| } | |||||
| type ActionInfo struct { | type ActionInfo struct { | ||||
| Scene Scene `json:"scene"` | Scene Scene `json:"scene"` | ||||
| @@ -97,7 +103,7 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { | |||||
| SetQueryParam("access_token", GetWechatAccessToken()). | SetQueryParam("access_token", GetWechatAccessToken()). | ||||
| SetBody(bodyJson). | SetBody(bodyJson). | ||||
| SetResult(&result). | SetResult(&result). | ||||
| Post(setting.WechatApiHost + QR_CODE_Path) | |||||
| Post(setting.WechatApiHost + QR_CODE_PATH) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("create QR code failed,e=%v", err) | log.Error("create QR code failed,e=%v", err) | ||||
| return nil, false | return nil, false | ||||
| @@ -113,6 +119,37 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { | |||||
| return &result, false | 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 { | func getErrorCodeFromResponse(r *resty.Response) int { | ||||
| a := r.Body() | a := r.Body() | ||||
| resultMap := make(map[string]interface{}, 0) | resultMap := make(map[string]interface{}, 0) | ||||
| @@ -18,7 +18,7 @@ import ( | |||||
| // <EventKey><![CDATA[SCENE_VALUE]]></EventKey> | // <EventKey><![CDATA[SCENE_VALUE]]></EventKey> | ||||
| // <Ticket><![CDATA[TICKET]]></Ticket> | // <Ticket><![CDATA[TICKET]]></Ticket> | ||||
| //</xml> | //</xml> | ||||
| type WechatEvent struct { | |||||
| type WechatMsg struct { | |||||
| ToUserName string | ToUserName string | ||||
| FromUserName string | FromUserName string | ||||
| CreateTime int64 | CreateTime int64 | ||||
| @@ -26,9 +26,13 @@ type WechatEvent struct { | |||||
| Event string | Event string | ||||
| EventKey string | EventKey string | ||||
| Ticket string | Ticket string | ||||
| Content string | |||||
| MsgId string | |||||
| MsgDataId string | |||||
| Idx string | |||||
| } | } | ||||
| type EventReply struct { | |||||
| type MsgReply struct { | |||||
| XMLName xml.Name `xml:"xml"` | XMLName xml.Name `xml:"xml"` | ||||
| ToUserName string | ToUserName string | ||||
| FromUserName string | FromUserName string | ||||
| @@ -37,16 +41,97 @@ type EventReply struct { | |||||
| Content string | Content string | ||||
| } | } | ||||
| type TextMsgReply struct { | |||||
| XMLName xml.Name `xml:"xml"` | |||||
| ToUserName string | |||||
| FromUserName string | |||||
| CreateTime int64 | |||||
| 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 NewsMsgReply struct { | |||||
| XMLName xml.Name `xml:"xml"` | |||||
| ToUserName string | |||||
| FromUserName string | |||||
| CreateTime int64 | |||||
| MsgType string | |||||
| ArticleCount int | |||||
| Articles ArticleItem | |||||
| } | |||||
| type ArticleItem struct { | |||||
| Item []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 { | |||||
| XMLName xml.Name `xml:"item"` | |||||
| Title string | |||||
| Description string | |||||
| PicUrl string | |||||
| Url string | |||||
| } | |||||
| const ( | const ( | ||||
| WECHAT_EVENT_SUBSCRIBE = "subscribe" | WECHAT_EVENT_SUBSCRIBE = "subscribe" | ||||
| WECHAT_EVENT_SCAN = "SCAN" | WECHAT_EVENT_SCAN = "SCAN" | ||||
| ) | ) | ||||
| const ( | const ( | ||||
| WECHAT_MSG_TYPE_TEXT = "text" | |||||
| WECHAT_MSG_TYPE_TEXT = "text" | |||||
| WECHAT_MSG_TYPE_EVENT = "event" | |||||
| ) | ) | ||||
| func HandleSubscribeEvent(we WechatEvent) string { | |||||
| func HandleScanEvent(we WechatMsg) string { | |||||
| eventKey := we.EventKey | eventKey := we.EventKey | ||||
| if eventKey == "" { | if eventKey == "" { | ||||
| return "" | return "" | ||||
| @@ -74,3 +159,11 @@ func HandleSubscribeEvent(we WechatEvent) string { | |||||
| return BIND_REPLY_SUCCESS | return BIND_REPLY_SUCCESS | ||||
| } | } | ||||
| func HandleSubscribeEvent(we WechatMsg) *WechatReplyContent { | |||||
| r, err := LoadReplyFromCacheAndDisk(SubscribeReply) | |||||
| if err != nil || len(r) == 0 { | |||||
| return nil | |||||
| } | |||||
| return r[0] | |||||
| } | |||||
| @@ -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 | |||||
| } | |||||
| @@ -545,6 +545,13 @@ var ( | |||||
| WechatQRCodeExpireSeconds int | WechatQRCodeExpireSeconds int | ||||
| WechatAuthSwitch bool | WechatAuthSwitch bool | ||||
| //wechat auto reply config | |||||
| UserNameOfWechatReply string | |||||
| RepoNameOfWechatReply string | |||||
| RefNameOfWechatReply string | |||||
| TreePathOfAutoMsgReply string | |||||
| TreePathOfSubscribe string | |||||
| //nginx proxy | //nginx proxy | ||||
| PROXYURL string | PROXYURL string | ||||
| RadarMap = struct { | RadarMap = struct { | ||||
| @@ -1372,6 +1379,11 @@ func NewContext() { | |||||
| WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") | WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") | ||||
| WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) | WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) | ||||
| WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(true) | WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(true) | ||||
| 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() | SetRadarMapConfig() | ||||
| @@ -1046,6 +1046,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/prd/event", authentication.ValidEventSource) | m.Get("/prd/event", authentication.ValidEventSource) | ||||
| m.Post("/prd/event", authentication.AcceptWechatEvent) | m.Post("/prd/event", authentication.AcceptWechatEvent) | ||||
| }) | }) | ||||
| m.Get("/wechat/material", authentication.GetMaterial) | |||||
| }, securityHeaders(), context.APIContexter(), sudo()) | }, securityHeaders(), context.APIContexter(), sudo()) | ||||
| } | } | ||||
| @@ -8,9 +8,11 @@ import ( | |||||
| "code.gitea.io/gitea/modules/redis/redis_client" | "code.gitea.io/gitea/modules/redis/redis_client" | ||||
| "code.gitea.io/gitea/modules/redis/redis_key" | "code.gitea.io/gitea/modules/redis/redis_key" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/routers/response" | |||||
| "encoding/json" | "encoding/json" | ||||
| "errors" | "errors" | ||||
| gouuid "github.com/satori/go.uuid" | gouuid "github.com/satori/go.uuid" | ||||
| "strconv" | |||||
| "time" | "time" | ||||
| ) | ) | ||||
| @@ -124,3 +126,23 @@ func createQRCode4Bind(userId int64) (*QRCodeResponse, error) { | |||||
| } | } | ||||
| return result, nil | 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)) | |||||
| } | |||||
| @@ -14,24 +14,48 @@ import ( | |||||
| // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html | // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html | ||||
| func AcceptWechatEvent(ctx *context.Context) { | func AcceptWechatEvent(ctx *context.Context) { | ||||
| b, _ := ioutil.ReadAll(ctx.Req.Request.Body) | b, _ := ioutil.ReadAll(ctx.Req.Request.Body) | ||||
| we := wechat.WechatEvent{} | |||||
| we := wechat.WechatMsg{} | |||||
| xml.Unmarshal(b, &we) | 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) | log.Info("accept wechat event= %+v", we) | ||||
| var replyStr string | |||||
| switch we.Event { | |||||
| case wechat.WECHAT_EVENT_SUBSCRIBE, wechat.WECHAT_EVENT_SCAN: | |||||
| replyStr = wechat.HandleSubscribeEvent(we) | |||||
| break | |||||
| } | |||||
| // ValidEventSource | |||||
| func ValidEventSource(ctx *context.Context) { | |||||
| echostr := ctx.Query("echostr") | |||||
| ctx.Write([]byte(echostr)) | |||||
| return | |||||
| } | |||||
| func HandleEventMsg(ctx *context.Context, msg wechat.WechatMsg) { | |||||
| switch msg.Event { | |||||
| 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 == "" { | if replyStr == "" { | ||||
| log.Info("reply str is empty") | log.Info("reply str is empty") | ||||
| return | return | ||||
| } | } | ||||
| reply := &wechat.EventReply{ | |||||
| ToUserName: we.FromUserName, | |||||
| FromUserName: we.ToUserName, | |||||
| reply := &wechat.MsgReply{ | |||||
| ToUserName: msg.FromUserName, | |||||
| FromUserName: msg.ToUserName, | |||||
| CreateTime: time.Now().Unix(), | CreateTime: time.Now().Unix(), | ||||
| MsgType: wechat.WECHAT_MSG_TYPE_TEXT, | MsgType: wechat.WECHAT_MSG_TYPE_TEXT, | ||||
| Content: replyStr, | Content: replyStr, | ||||
| @@ -39,9 +63,99 @@ func AcceptWechatEvent(ctx *context.Context) { | |||||
| ctx.XML(200, reply) | ctx.XML(200, reply) | ||||
| } | } | ||||
| // ValidEventSource | |||||
| func ValidEventSource(ctx *context.Context) { | |||||
| echostr := ctx.Query("echostr") | |||||
| ctx.Write([]byte(echostr)) | |||||
| return | |||||
| 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 { | |||||
| log.Info("TextMsg reply is empty") | |||||
| return | |||||
| } | |||||
| reply := buildReplyContent(msg, r) | |||||
| ctx.XML(200, reply) | |||||
| } | |||||
| func buildReplyContent(msg wechat.WechatMsg, r *wechat.WechatReplyContent) 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, | |||||
| }, | |||||
| } | |||||
| 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 | |||||
| } | } | ||||