package wechat import ( "context" XmlUtils "encoding/xml" "net/http" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "github.com/ArtisanCloud/PowerLibs/v3/http/helper" "fmt" "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/contract" "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/messages" models2 "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models" "github.com/ArtisanCloud/PowerWeChat/v3/src/officialAccount/server/handlers/models" ) // CallbackVerifyMessage /** * 微信服务器验证消息 * GET /api/wechat/callback/message * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html */ func CallbackVerifyMessage(responseWriter http.ResponseWriter, request *http.Request) { resp, err := setting.Wechat.SDK.Server.VerifyURL(request) if err != nil { log.Error("[X] Failed to verify message in Wechat Official Account callback") responseWriter.WriteHeader(resp.StatusCode) responseWriter.Write([]byte("Failed to verify message in Wechat Official Account callback (CallbackVerifyMessage): " + err.Error())) return } // 给微信服务器回信 err = helper.HttpResponseSend(resp, responseWriter) if err != nil { log.Error("[X] Failed to reply back to Wechat Server (CallbackVerifyMessage): ", err.Error()) responseWriter.WriteHeader(500) } } // CallbackNotifyEvents /** * 微信服务器通知事件 * POST /api/wechat/callback/message * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html */ func CallbackNotifyEvents(responseWriter http.ResponseWriter, request *http.Request) { resp, err := setting.Wechat.SDK.Server.Notify(request, func(event contract.EventInterface) interface{} { switch event.GetMsgType() { case models2.CALLBACK_MSG_TYPE_EVENT: // "event" 类型消息处理 return callbackMsgEventHandler(request, event) case models2.CALLBACK_MSG_TYPE_TEXT: // "text" 类型消息处理 return callbackMsgTextHandler(event) } return messages.NewText("undefined event type: " + event.GetMsgType()) }) if err != nil { log.Error("[X] Failed to get notified in Wechat Official Account callback (CallbackNotifyEvents): ", err.Error()) responseWriter.WriteHeader(resp.StatusCode) responseWriter.Write([]byte("Failed to verify message in Wechat Official Account callback" + err.Error())) } // 给微信服务器回信 err = helper.HttpResponseSend(resp, responseWriter) if err != nil { log.Error("[X] Failed to reply back to Wechat Server (CallbackNotifyEvents): ", err.Error()) responseWriter.WriteHeader(500) } } func callbackMsgEventHandler(request *http.Request, event contract.EventInterface) *messages.Text { ctx := request.Context() // 根据不同 event 分别处理 switch event.GetEvent() { case models.CALLBACK_EVENT_SUBSCRIBE: // event:subscribe 新用户关注公众号事件 return eventSubscribeHandler(event) case models.CALLBACK_EVENT_SCAN: // event:SCAN 老用户扫描二维码事件 return eventScanHandler(ctx, event) case models.CALLBACK_EVENT_UNSUBSCRIBE: // event:unsubscribe 掉粉事件处理 return eventUnsubscribeHandler(event) } return messages.NewText("undefined event type: " + event.GetChangeType()) } // "text" 类型消息处理 func callbackMsgTextHandler(event contract.EventInterface) *messages.Text { msg := models.MessageText{} err := event.ReadMessage(&msg) if err != nil { log.Error("[X] Failed to handle callback Message TEXT: " + err.Error()) return messages.NewText("error: " + err.Error()) } // fmt.Dump(msg) log.Info("[+] Got message from " + msg.FromUserName + ": " + msg.Content) return messages.NewText("您发送的消息已收到,感谢您使用梦宁软件DevStar Studio \n\n" + " -- DevStar Studio" + "\n\n") } // event:subscribe 新用户关注公众号事件 func eventSubscribeHandler(event contract.EventInterface) *messages.Text { msg := models.EventSubscribe{} err := event.ReadMessage(&msg) if err != nil { log.Error("[X] Failed to handle callback Message event:subscribe: " + err.Error()) return messages.NewText("error: " + err.Error()) } // log.Info("[+] event:subscribe User Subscribed: " + msg.FromUserName + ", at Timestamp " + msg.CreateTime) return messages.NewText("欢迎新用户! 请关注公众号后重新扫码!\n\n" + " -- DevStar Studio" + "\n\n") } // event:SCAN 类型消息,已关注公众号的老用户扫码 func eventScanHandler(ctx context.Context, event contract.EventInterface) *messages.Text { msg := models.EventScanCodePush{} err := event.ReadMessage(&msg) if err != nil { log.Error("[X] Failed to handle callback event SCAN: " + err.Error()) return messages.NewText("error: " + err.Error()) } var qrScanResponseDigest struct { FromUserName string `xml:"FromUserName"` Ticket string `xml:"Ticket"` SceneStr string `xml:"EventKey"` } err = XmlUtils.Unmarshal([]byte(event.GetContent()), &qrScanResponseDigest) if err != nil { log.Error("[X] Failed to handle callback event SCAN: " + err.Error()) return messages.NewText("error: " + err.Error()) } // 准备扫码状态VO对象 qrStatus := WechatTempQRStatus{ SceneStr: qrScanResponseDigest.SceneStr, IsScanned: true, OpenId: qrScanResponseDigest.FromUserName, } log.Info("扫码成功:\n" + fmt.Sprintf(" Ticket: %s\n", qrScanResponseDigest.Ticket) + fmt.Sprintf(" SceneStr: %s\n", qrStatus.SceneStr)) qrStatus.IsBinded = false qrStatus.OpenId = qrScanResponseDigest.FromUserName qrVOJsonString, err := qrStatus.Marshal2JSONString() if err == nil { SetWechatQrTicketWithTTL( qrScanResponseDigest.Ticket, qrVOJsonString, setting.Wechat.TempQrExpireSeconds) } loginSuccessInfo := "您正在微信扫码登录" + fmt.Sprintf(" %s ", msg.EventKey) + "\n\n" + " -- DevStar Studio" + "\n\n" + "如非本人操作,请尽快重新绑定微信!" return messages.NewText(loginSuccessInfo) } // event:unsubscribe 掉粉事件处理 func eventUnsubscribeHandler(event contract.EventInterface) *messages.Text { msg := models.EventUnSubscribe{} err := event.ReadMessage(&msg) if err != nil { log.Error("[X] Failed to handle callback Message event:unsubscribe: " + err.Error()) return messages.NewText("error: " + err.Error()) } // log.Info("[+] event:unsubscribe User Unsubscribed: " + msg.ToUserName + ", at Timestamp " + msg.CreateTime) return messages.NewText("user unsubscribed: " + msg.FromUserName) }