| <Event><![CDATA[unsubscribe]]></Event> | <Event><![CDATA[unsubscribe]]></Event> | ||||
| <EventKey><![CDATA[]]></EventKey> | <EventKey><![CDATA[]]></EventKey> | ||||
| <Encrypt><![CDATA[brFRc2b7iWU+sxTJfQ1Alp+GcR69Wkeh6Bb88z6XP/q8feWBbvo1odwZ97WrPV5UE2ctrzY88F0J5gt1ECX6OgcKMtZQiZgucXwsh53N9jyDRV+j6zJ1kpJiAf0UxUV7B0Q6OEX830Exoaylj9ZwoR1iQL84eB8CSVmUHRGNxqMK5hg6frE65guh9WysbIs2MWvHVXo7FsXj7ipK9s2VNqawR0VctKPxSnjAPjIxu2Q9MQXnhegePbuLnjTfW2YyY0cKx4XU+tvKlnQbwGgw4rxmcOjuF5lkknxK6JefeHtnKXTiXgcRmEtcbVneWxfW5FsUQkIDQa62IuTs/m4THZDc4opvLmOFXgUR9+BBnIVXyUNvt8zm9DDUUTd+rGz0SDx6sQ3S6WxITp7HKAR6wEHf+eSEwquzmSrGfgAR9Vs=]]></Encrypt> | <Encrypt><![CDATA[brFRc2b7iWU+sxTJfQ1Alp+GcR69Wkeh6Bb88z6XP/q8feWBbvo1odwZ97WrPV5UE2ctrzY88F0J5gt1ECX6OgcKMtZQiZgucXwsh53N9jyDRV+j6zJ1kpJiAf0UxUV7B0Q6OEX830Exoaylj9ZwoR1iQL84eB8CSVmUHRGNxqMK5hg6frE65guh9WysbIs2MWvHVXo7FsXj7ipK9s2VNqawR0VctKPxSnjAPjIxu2Q9MQXnhegePbuLnjTfW2YyY0cKx4XU+tvKlnQbwGgw4rxmcOjuF5lkknxK6JefeHtnKXTiXgcRmEtcbVneWxfW5FsUQkIDQa62IuTs/m4THZDc4opvLmOFXgUR9+BBnIVXyUNvt8zm9DDUUTd+rGz0SDx6sQ3S6WxITp7HKAR6wEHf+eSEwquzmSrGfgAR9Vs=]]></Encrypt> | ||||
| </xml> | |||||
| </xml> | |||||
| Receive : text | |||||
| POST /api?signature=3335bc3514757c602e6b1630cd84812caacb43d5×tamp=1492791675&nonce=515316134&openid=oUN420bxqFqlx0ZQHciUOesZO3PE&encrypt_type=aes&msg_signature=c73e7c78c10c0b87846a422cce275ec0d2894ef9 HTTP/1.1 | |||||
| Host: wechat.hitxy.org.au | |||||
| Accept: */* | |||||
| Cache-Control: no-cache | |||||
| Connection: Keep-Alive | |||||
| Content-Length: 765 | |||||
| Content-Type: text/xml | |||||
| Pragma: no-cache | |||||
| User-Agent: Mozilla/4.0 | |||||
| X-Forwarded-For: 103.7.30.105 | |||||
| X-Forwarded-Host: wechat.hitxy.org.au | |||||
| X-Forwarded-Server: wechat.hitxy.org.au | |||||
| <xml> | |||||
| <ToUserName><![CDATA[gh_f09231355c68]]></ToUserName> | |||||
| <FromUserName><![CDATA[oUN420bxqFqlx0ZQHciUOesZO3PE]]></FromUserName> | |||||
| <CreateTime>1492791675</CreateTime> | |||||
| <MsgType><![CDATA[text]]></MsgType> | |||||
| <Content><![CDATA[13]]></Content> | |||||
| <MsgId>6411491424278595449</MsgId> | |||||
| <Encrypt><![CDATA[02dtsmvIHONkhxYtyPivtKCkKSQ/ly8UDxxlJPecQFNZGeBMYyMtBq0ufgXJkF5AJYEa5SwSXHeJe7mSsrq5GbNbbUVzLiRERhYK7iVaQuacF/WPY1qOwbTAOHFsZfsUD1oNf64GdKh5Evx5mzXfynmonFinCDUTloWX5M8EW323XNtHcGi2eF11+KMOL9HBdQdG4kSoM/OxaSeXls+D+Co08ZNt9pMDlUvlpHzeYVhudiamxja0+FWSLDlitYENHWNN+LnXyqqfAnAb82dhzKRRSw1c+d/TGFVOYgU1NWt9fZ036qY6/JIwyFNwbFkDdYa0QAzkPJU36zxYesnGUWmu5xHjnrFpfmwG+eZSUuWbNcnu81VbrYhkaD0/4vJwk/EvFySA0Fa/r+B3Lav/d762KhZ2D+at6BD1oWRHFYA=]]></Encrypt> | |||||
| </xml> | |||||
| Create menu | |||||
| { | |||||
| "button":[ | |||||
| { | |||||
| "type":"click", | |||||
| "name":"今日歌曲", | |||||
| "key":"V1001_TODAY_MUSIC" | |||||
| }, | |||||
| { | |||||
| "name":"菜单", | |||||
| "sub_button":[ | |||||
| { | |||||
| "type":"view", | |||||
| "name":"搜索", | |||||
| "url":"http://www.soso.com/" | |||||
| }, | |||||
| { | |||||
| "type":"miniprogram", | |||||
| "name":"wxa", | |||||
| "url":"http://mp.weixin.qq.com", | |||||
| "appid":"wx286b93c14bbf93aa", | |||||
| "pagepath":"pages/lunar/index.html" | |||||
| }, | |||||
| { | |||||
| "type":"click", | |||||
| "name":"赞一下我们", | |||||
| "key":"V1001_GOOD" | |||||
| }] | |||||
| }] | |||||
| } | |||||
| { | |||||
| "button": [ | |||||
| { | |||||
| "name": "扫码", | |||||
| "sub_button": [ | |||||
| { | |||||
| "type": "scancode_waitmsg", | |||||
| "name": "扫码带提示", | |||||
| "key": "rselfmenu_0_0", | |||||
| "sub_button": [ ] | |||||
| }, | |||||
| { | |||||
| "type": "scancode_push", | |||||
| "name": "扫码推事件", | |||||
| "key": "rselfmenu_0_1", | |||||
| "sub_button": [ ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| { | |||||
| "name": "发图", | |||||
| "sub_button": [ | |||||
| { | |||||
| "type": "pic_sysphoto", | |||||
| "name": "系统拍照发图", | |||||
| "key": "rselfmenu_1_0", | |||||
| "sub_button": [ ] | |||||
| }, | |||||
| { | |||||
| "type": "pic_photo_or_album", | |||||
| "name": "拍照或者相册发图", | |||||
| "key": "rselfmenu_1_1", | |||||
| "sub_button": [ ] | |||||
| }, | |||||
| { | |||||
| "type": "pic_weixin", | |||||
| "name": "微信相册发图", | |||||
| "key": "rselfmenu_1_2", | |||||
| "sub_button": [ ] | |||||
| } | |||||
| ] | |||||
| }, | |||||
| { | |||||
| "name": "发送位置", | |||||
| "type": "location_select", | |||||
| "key": "rselfmenu_2_0" | |||||
| }, | |||||
| { | |||||
| "type": "media_id", | |||||
| "name": "图片", | |||||
| "media_id": "MEDIA_ID1" | |||||
| }, | |||||
| { | |||||
| "type": "view_limited", | |||||
| "name": "图文消息", | |||||
| "media_id": "MEDIA_ID2" | |||||
| } | |||||
| ] | |||||
| } |
| Time time.Time `json:"created_at"` | Time time.Time `json:"created_at"` | ||||
| } | } | ||||
| func main2() { | |||||
| e, _ := renewAuthtoken() | |||||
| t, _ := readTokenFromFile("/tmp/wechat_hitxy_token") | |||||
| fmt.Println(isAuthTokenExpired(t), e) | |||||
| //GetAccessToken returns a valid access_token for wechat_api | |||||
| // the token is auomatically renewed if it's about to expire | |||||
| // if error happens, return empty string and error | |||||
| // if success, return token string and nil | |||||
| func GetAccessToken() (string, error) { | |||||
| t, err := readTokenFromFile("/tmp/wechat_hitxy_token") | |||||
| if isAuthTokenExpired(t) { | |||||
| t, err = renewAuthtoken() | |||||
| if err != nil { | |||||
| return "", err // cannot get token | |||||
| } | |||||
| } | |||||
| return t.AccessToken, nil | |||||
| } | } | ||||
| func isAuthTokenExpired(t authToken) bool { | func isAuthTokenExpired(t authToken) bool { | ||||
| return time.Now().After(expire) | return time.Now().After(expire) | ||||
| } | } | ||||
| //save token to perminant storage | |||||
| func writeTokenToFile(body []byte, path string) { | func writeTokenToFile(body []byte, path string) { | ||||
| log.Printf("writing authtoke to %s\r\n", path) | log.Printf("writing authtoke to %s\r\n", path) | ||||
| ioutil.WriteFile(path, body, 0600) | ioutil.WriteFile(path, body, 0600) | ||||
| } | } | ||||
| //read token from permenant storage | |||||
| func readTokenFromFile(path string) (authToken, error) { | func readTokenFromFile(path string) (authToken, error) { | ||||
| var m authToken | var m authToken | ||||
| log.Printf("read authtoke from %s\r\n", path) | log.Printf("read authtoke from %s\r\n", path) | ||||
| return m, err | return m, err | ||||
| } | } | ||||
| //issue web request to get token from wechat | |||||
| func renewAuthtoken() (authToken, error) { | func renewAuthtoken() (authToken, error) { | ||||
| //url := "http://vimeo.com/api/v2/brad/info.json" | //url := "http://vimeo.com/api/v2/brad/info.json" | ||||
| url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx876e233fde456b7b&secret=4a91aa328569b10a9fb97adeb8b0af58" | url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx876e233fde456b7b&secret=4a91aa328569b10a9fb97adeb8b0af58" |
| "net/url" | "net/url" | ||||
| ) | ) | ||||
| func handler(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, "<br>%s => %s", index, element) | |||||
| } | |||||
| fmt.Fprintf(w, m["k"][0]) | |||||
| logRequestDebug(httputil.DumpRequest(r, true)) | |||||
| func main() { | |||||
| http.HandleFunc("/", webrootHandler) | |||||
| http.HandleFunc("/api", apiV1Main) | |||||
| //http.ListenAndServe("127.0.0.1:65500", nil) | |||||
| http.ListenAndServe(":65500", nil) | |||||
| } | } | ||||
| //apiV1Main version 1 main entry for all wechat callbacks | |||||
| // | |||||
| func apiV1Main(w http.ResponseWriter, r *http.Request) { | func apiV1Main(w http.ResponseWriter, r *http.Request) { | ||||
| logRequestDebug(httputil.DumpRequest(r, true)) | logRequestDebug(httputil.DumpRequest(r, true)) | ||||
| rq := r.URL.RawQuery | rq := r.URL.RawQuery | ||||
| fmt.Fprintf(w, m["echostr"][0]) | fmt.Fprintf(w, m["echostr"][0]) | ||||
| } | } | ||||
| func main() { | |||||
| http.HandleFunc("/", handler) | |||||
| http.HandleFunc("/api", apiV1Main) | |||||
| //http.ListenAndServe("127.0.0.1:65500", nil) | |||||
| http.ListenAndServe(":65500", nil) | |||||
| //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, "\r\n%s => %s", index, element) | |||||
| } | |||||
| logRequestDebug(httputil.DumpRequest(r, true)) | |||||
| } | } | ||||
| func logRequestDebug(data []byte, err error) { | func logRequestDebug(data []byte, err error) { |