package main
import (
"crypto/sha1"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"sort"
"strings"
)
//apiV1Main version 1 main entry for all wechat callbacks
//
func apiV1Main(w http.ResponseWriter, r *http.Request) {
logRequestDebug(httputil.DumpRequest(r, true))
if checkSignature(r) == false {
log.Println("signature of URL incorrect")
w.Header().Set("Content-Type", "text/xml; charset=utf-8")
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintf(w, "") //empty string
return
}
switch r.Method {
case "POST":
//answerWechatPostEcho(w, r)
answerWechatPost(w, r)
case "GET":
answerInitialAuth(w, r)
default:
log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method))
fmt.Fprintf(w, "Protocol Error: Expect GET or POST only")
}
}
//
//answerInitialAuth, when wechat first verify our URL for API hooks
//
func answerInitialAuth(w http.ResponseWriter, r *http.Request) {
rq := r.URL.RawQuery
m, _ := url.ParseQuery(rq)
echostr, eok := m["echostr"]
if checkSignature(r) && eok {
fmt.Fprintf(w, echostr[0])
} else {
fmt.Fprintf(w, "wtf is wrong with the Internet")
}
}
//answerWechatPost distribute PostRequest according to xml body info
//
func answerWechatPost(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
d := decryptToXML(string(body))
fmt.Printf("decrypt as: %s\n", d)
h := ReadCommonHeader(d)
reply, _ := BuildTextMsg(h.MsgType, h.FromUserName)
if h.MsgType == "text" {
// url := "http://www.google.com.au/"
// first := "很高兴有你参加志愿者"
// remark := "明天给你发1万块钱"
// name := "利于修"
// staffID := "1235465"
// joinDate := time.Now().Format("2006-01-02 15:04:06 Mon MST -07")
// totalCount := "50次"
// totalTime := "2小时"
// log.Println("send kf msg")
// templateSendJoinVolunteer(h.FromUserName, url, first, remark, name, staffID, joinDate, totalCount, totalTime)
reply, _ = BuildKFTransferAnyOneMsg(h.FromUserName)
//reply, _ = BuildKFTransferMsg(h.FromUserName, "kf2001@gh_f09231355c68")
//reply, _ = BuildTextMsg("test some link ", h.FromUserName)
}
if h.MsgType == "voice" {
a := ReadVoiceMsg(d)
reply, _ = BuildTextMsg(a.Recognition, h.FromUserName)
}
if h.MsgType == "event" {
a := ReadEventMsg(d)
if a.Event == "LOCATION" {
reply, _ = BuildTextMsg("test some link ", h.FromUserName)
fmt.Printf("output %s", reply)
} else {
reply, _ = BuildTextMsg(a.Event+"/"+a.EventKey, h.FromUserName)
}
//mediaID := "cU8BYvAEp3H25V-yGO3WBtMVk2bZcEBgf_kje7V-EPkRA_U4x-OAWb_ONg6Y-Qxt" //video
//mediaID := "e2iNEiSxCX5TV1WbFd0TQPTdMx8WbvpkOs_iNhSVQHY" // 236 second mp3
//mediaID := "e2iNEiSxCX5TV1WbFd0TQDVyk970GxTcBWMnqc2RzF0" //5 sec mp3
//mediaID := "e2iNEiSxCX5TV1WbFd0TQMqvVrqFDbDOacdjgQ-OAuE" //news
//reply = buildVideoMsg(h.FromUserName, mediaID, "标题", a.Event+"/"+a.EventKey)
//reply = buildUploadPicMsg(h.FromUserName, "media_for_test/640x480.jpg")
//reply = buildUploadVoiceMsg(h.FromUserName, "media_for_test/music.mp3")
//reply = buildVoiceMsg(h.FromUserName, mediaID)
reply = buildSampleMusicMsg(h.FromUserName)
//reply = buildSampleArticleMsg(h.FromUserName)
}
w.Header().Set("Content-Type", "text/xml; charset=utf-8")
fmt.Fprint(w, reply)
return
}
func answerWechatPostEcho(w http.ResponseWriter, r *http.Request) {
body, _ := ioutil.ReadAll(r.Body)
//fmt.Printf("get body: %s", string(body))
s := ReadEncryptedMsg(string(body))
//fmt.Printf("to decrypt %s", s.Encrypt)
d := Decode(s.Encrypt)
fmt.Printf("echo as: \n%s", d)
e := strings.Replace(d, "ToUserName", "FDDD20170506xyzunique", 2)
f := strings.Replace(e, "FromUserName", "ToUserName", 2)
g := strings.Replace(f, "FDDD20170506xyzunique", "FromUserName", 2)
fmt.Fprint(w, g)
}
//
func checkSignature(r *http.Request) bool {
rq := r.URL.RawQuery
m, _ := url.ParseQuery(rq)
signature, sok := m["signature"]
timestamp, tok := m["timestamp"]
nonce, nok := m["nonce"]
token := APIConfig.Token
if sok && tok && nok {
//sort token, timestamp, nonce and join them
strs := []string{token, timestamp[0], nonce[0]}
sort.Strings(strs)
s := strings.Join(strs, "")
//calculate sha1
h := sha1.New()
h.Write([]byte(s))
calculated := fmt.Sprintf("%x", h.Sum(nil))
return signature[0] == calculated
}
return false
}
func checkSignature1() bool {
s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c"
t1 := "1492970761"
n1 := "1850971833"
token := APIConfig.Token
strs := []string{token, t1, n1}
sort.Strings(strs)
s := strings.Join(strs, "")
h := sha1.New()
h.Write([]byte(s))
us := fmt.Sprintf("%x", h.Sum(nil))
return s1 == us
}
//webrootHandler sending contents to client when request "/"
// essentially to prove the webserver is still alive
// echo query string to the client
func webrootHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
rq := r.URL.RawQuery
m, _ := url.ParseQuery(rq)
for index, element := range m {
fmt.Fprintf(w, "\n%s => %s", index, element)
}
logRequestDebug(httputil.DumpRequest(r, true))
}
func logRequestDebug(data []byte, err error) {
if err == nil {
fmt.Printf("%s\n\n", string(data))
} else {
log.Fatalf("%s\n\n", err)
}
}
//save uploaded 'file' into temporary location
func uploadHandler(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(32 << 20) //32MB memory
file, handler, err := r.FormFile("file") //form-field 'file'
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
f, err := os.OpenFile("/tmp/wechat_hitxy_"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
}
//when user requst an attachment from our server, we get the file from CRM server and reply it back
//in this way, user has no 'direct-access' to the CRM server.
func crmAttachmentHandler(w http.ResponseWriter, r *http.Request) {
fileID := getCrmFileID(r.URL.Path)
saveAs := GlobalPath.CRMAttachment + "crm_attach_" + fileID //add prefix for easy deleting
if isFileExist(saveAs) {
http.ServeFile(w, r, saveAs)
return
}
if fileID == "" {
log.Println("invalid fileID")
response404Handler(w)
return
}
log.Printf("download %s => %s", fileID, saveAs)
crmDownloadAttachmentAs(fileID, saveAs)
if !isFileExist(saveAs) {
response404Handler(w)
return
}
http.ServeFile(w, r, saveAs)
}
func getCrmFileID(urlPath string) string {
return strings.TrimPrefix(urlPath, "/crmfiles/")
}
func response404Handler(w http.ResponseWriter) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "not found")
}