Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

236 linhas
6.6KB

  1. package main
  2. import (
  3. "crypto/sha1"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. "net/http/httputil"
  10. "net/url"
  11. "os"
  12. "sort"
  13. "strings"
  14. )
  15. //apiV1Main version 1 main entry for all wechat callbacks
  16. //
  17. func apiV1Main(w http.ResponseWriter, r *http.Request) {
  18. logRequestDebug(httputil.DumpRequest(r, true))
  19. if checkSignature(r) == false {
  20. log.Println("signature of URL incorrect")
  21. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  22. w.WriteHeader(http.StatusUnauthorized)
  23. fmt.Fprintf(w, "") //empty string
  24. return
  25. }
  26. switch r.Method {
  27. case "POST":
  28. //answerWechatPostEcho(w, r)
  29. answerWechatPost(w, r)
  30. case "GET":
  31. answerInitialAuth(w, r)
  32. default:
  33. log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method))
  34. fmt.Fprintf(w, "Protocol Error: Expect GET or POST only")
  35. }
  36. }
  37. //
  38. //answerInitialAuth, when wechat first verify our URL for API hooks
  39. //
  40. func answerInitialAuth(w http.ResponseWriter, r *http.Request) {
  41. rq := r.URL.RawQuery
  42. m, _ := url.ParseQuery(rq)
  43. echostr, eok := m["echostr"]
  44. if checkSignature(r) && eok {
  45. fmt.Fprintf(w, echostr[0])
  46. } else {
  47. fmt.Fprintf(w, "wtf is wrong with the Internet")
  48. }
  49. }
  50. //answerWechatPost distribute PostRequest according to xml body info
  51. //
  52. func answerWechatPost(w http.ResponseWriter, r *http.Request) {
  53. body, _ := ioutil.ReadAll(r.Body)
  54. d := decryptToXML(string(body))
  55. fmt.Printf("decrypt as: %s\n", d)
  56. h := ReadCommonHeader(d)
  57. reply, _ := BuildTextMsg(h.MsgType, h.FromUserName)
  58. if h.MsgType == "text" {
  59. // url := "http://www.google.com.au/"
  60. // first := "很高兴有你参加志愿者"
  61. // remark := "明天给你发1万块钱"
  62. // name := "利于修"
  63. // staffID := "1235465"
  64. // joinDate := time.Now().Format("2006-01-02 15:04:06 Mon MST -07")
  65. // totalCount := "50次"
  66. // totalTime := "2小时"
  67. // log.Println("send kf msg")
  68. // templateSendJoinVolunteer(h.FromUserName, url, first, remark, name, staffID, joinDate, totalCount, totalTime)
  69. reply, _ = BuildKFTransferAnyOneMsg(h.FromUserName)
  70. //reply, _ = BuildKFTransferMsg(h.FromUserName, "kf2001@gh_f09231355c68")
  71. //reply, _ = BuildTextMsg("test <a href=http://www.hitxy.org.au/> some link </a>", h.FromUserName)
  72. }
  73. if h.MsgType == "voice" {
  74. a := ReadVoiceMsg(d)
  75. reply, _ = BuildTextMsg(a.Recognition, h.FromUserName)
  76. }
  77. if h.MsgType == "event" {
  78. a := ReadEventMsg(d)
  79. if a.Event == "LOCATION" {
  80. reply, _ = BuildTextMsg("test <a href=http://www.hitxy.org.au/> some link </a>", h.FromUserName)
  81. fmt.Printf("output %s", reply)
  82. } else {
  83. reply, _ = BuildTextMsg(a.Event+"/"+a.EventKey, h.FromUserName)
  84. }
  85. //mediaID := "cU8BYvAEp3H25V-yGO3WBtMVk2bZcEBgf_kje7V-EPkRA_U4x-OAWb_ONg6Y-Qxt" //video
  86. //mediaID := "e2iNEiSxCX5TV1WbFd0TQPTdMx8WbvpkOs_iNhSVQHY" // 236 second mp3
  87. //mediaID := "e2iNEiSxCX5TV1WbFd0TQDVyk970GxTcBWMnqc2RzF0" //5 sec mp3
  88. //mediaID := "e2iNEiSxCX5TV1WbFd0TQMqvVrqFDbDOacdjgQ-OAuE" //news
  89. //reply = buildVideoMsg(h.FromUserName, mediaID, "标题", a.Event+"/"+a.EventKey)
  90. //reply = buildUploadPicMsg(h.FromUserName, "media_for_test/640x480.jpg")
  91. //reply = buildUploadVoiceMsg(h.FromUserName, "media_for_test/music.mp3")
  92. //reply = buildVoiceMsg(h.FromUserName, mediaID)
  93. reply = buildSampleMusicMsg(h.FromUserName)
  94. //reply = buildSampleArticleMsg(h.FromUserName)
  95. }
  96. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  97. fmt.Fprint(w, reply)
  98. return
  99. }
  100. func answerWechatPostEcho(w http.ResponseWriter, r *http.Request) {
  101. body, _ := ioutil.ReadAll(r.Body)
  102. //fmt.Printf("get body: %s", string(body))
  103. s := ReadEncryptedMsg(string(body))
  104. //fmt.Printf("to decrypt %s", s.Encrypt)
  105. d := Decode(s.Encrypt)
  106. fmt.Printf("echo as: \n%s", d)
  107. e := strings.Replace(d, "ToUserName", "FDDD20170506xyzunique", 2)
  108. f := strings.Replace(e, "FromUserName", "ToUserName", 2)
  109. g := strings.Replace(f, "FDDD20170506xyzunique", "FromUserName", 2)
  110. fmt.Fprint(w, g)
  111. }
  112. //
  113. func checkSignature(r *http.Request) bool {
  114. rq := r.URL.RawQuery
  115. m, _ := url.ParseQuery(rq)
  116. signature, sok := m["signature"]
  117. timestamp, tok := m["timestamp"]
  118. nonce, nok := m["nonce"]
  119. token := APIConfig.Token
  120. if sok && tok && nok {
  121. //sort token, timestamp, nonce and join them
  122. strs := []string{token, timestamp[0], nonce[0]}
  123. sort.Strings(strs)
  124. s := strings.Join(strs, "")
  125. //calculate sha1
  126. h := sha1.New()
  127. h.Write([]byte(s))
  128. calculated := fmt.Sprintf("%x", h.Sum(nil))
  129. return signature[0] == calculated
  130. }
  131. return false
  132. }
  133. func checkSignature1() bool {
  134. s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c"
  135. t1 := "1492970761"
  136. n1 := "1850971833"
  137. token := APIConfig.Token
  138. strs := []string{token, t1, n1}
  139. sort.Strings(strs)
  140. s := strings.Join(strs, "")
  141. h := sha1.New()
  142. h.Write([]byte(s))
  143. us := fmt.Sprintf("%x", h.Sum(nil))
  144. return s1 == us
  145. }
  146. //webrootHandler sending contents to client when request "/"
  147. // essentially to prove the webserver is still alive
  148. // echo query string to the client
  149. func webrootHandler(w http.ResponseWriter, r *http.Request) {
  150. fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
  151. rq := r.URL.RawQuery
  152. m, _ := url.ParseQuery(rq)
  153. for index, element := range m {
  154. fmt.Fprintf(w, "\n%s => %s", index, element)
  155. }
  156. logRequestDebug(httputil.DumpRequest(r, true))
  157. }
  158. func logRequestDebug(data []byte, err error) {
  159. if err == nil {
  160. fmt.Printf("%s\n\n", string(data))
  161. } else {
  162. log.Fatalf("%s\n\n", err)
  163. }
  164. }
  165. //save uploaded 'file' into temporary location
  166. func uploadHandler(w http.ResponseWriter, r *http.Request) {
  167. r.ParseMultipartForm(32 << 20) //32MB memory
  168. file, handler, err := r.FormFile("file") //form-field 'file'
  169. if err != nil {
  170. fmt.Println(err)
  171. return
  172. }
  173. defer file.Close()
  174. fmt.Fprintf(w, "%v", handler.Header)
  175. f, err := os.OpenFile("/tmp/wechat_hitxy_"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
  176. if err != nil {
  177. fmt.Println(err)
  178. return
  179. }
  180. defer f.Close()
  181. io.Copy(f, file)
  182. }
  183. //when user requst an attachment from our server, we get the file from CRM server and reply it back
  184. //in this way, user has no 'direct-access' to the CRM server.
  185. func crmAttachmentHandler(w http.ResponseWriter, r *http.Request) {
  186. fileID := getCrmFileID(r.URL.Path)
  187. saveAs := GlobalPath.CRMAttachment + "crm_attach_" + fileID //add prefix for easy deleting
  188. if isFileExist(saveAs) {
  189. http.ServeFile(w, r, saveAs)
  190. return
  191. }
  192. if fileID == "" {
  193. log.Println("invalid fileID")
  194. response404Handler(w)
  195. return
  196. }
  197. log.Printf("download %s => %s", fileID, saveAs)
  198. crmDownloadAttachmentAs(fileID, saveAs)
  199. if !isFileExist(saveAs) {
  200. response404Handler(w)
  201. return
  202. }
  203. http.ServeFile(w, r, saveAs)
  204. }
  205. func getCrmFileID(urlPath string) string {
  206. return strings.TrimPrefix(urlPath, "/crmfiles/")
  207. }
  208. func response404Handler(w http.ResponseWriter) {
  209. w.WriteHeader(http.StatusNotFound)
  210. fmt.Fprintf(w, "not found")
  211. }