diff --git a/.gitignore b/.gitignore
index 39aa122..92b1626 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,6 @@
# vendor/
.vscode
-*.log
\ No newline at end of file
+*.log
+
+go/bin
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 28e2699..b333d71 100644
--- a/go.mod
+++ b/go.mod
@@ -7,6 +7,7 @@ require (
github.com/gin-gonic/gin v1.8.1
github.com/spf13/cobra v1.3.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
+ gopkg.in/antage/eventsource.v1 v1.0.0-20150318155416-803f4c5af225
k8s.io/api v0.23.1
k8s.io/apimachinery v0.23.1
k8s.io/client-go v0.23.1
diff --git a/go.sum b/go.sum
index 5d817c7..acab370 100644
--- a/go.sum
+++ b/go.sum
@@ -868,6 +868,8 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/antage/eventsource.v1 v1.0.0-20150318155416-803f4c5af225 h1:xy+AV3uSExoRQc2qWXeZdbhFGwBFK/AmGlrBZEjbvuQ=
+gopkg.in/antage/eventsource.v1 v1.0.0-20150318155416-803f4c5af225/go.mod h1:SiXNRpUllqhl+GIw2V/BtKI7BUlz+uxov9vBFtXHqh8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/go/gin/main.go b/go/gin/main.go
new file mode 100644
index 0000000..0f5c492
--- /dev/null
+++ b/go/gin/main.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+ "log"
+
+ "github.com/gin-gonic/gin"
+)
+
+
+func get(c *gin.Context){
+ //c.ClientIP()
+ log.Println()
+}
+
+func main(){
+ engine := gin.Default()
+ engine.GET("/", get)
+ engine.Run("0.0.0.0:10000")
+}
\ No newline at end of file
diff --git a/go/notify/index.html b/go/notify/index.html
new file mode 100644
index 0000000..87c298b
--- /dev/null
+++ b/go/notify/index.html
@@ -0,0 +1,25 @@
+
+
+
+ SSE test
+
+
+
+SSE test
+
+
+
\ No newline at end of file
diff --git a/go/notify/notify.go b/go/notify/notify.go
new file mode 100644
index 0000000..580bf64
--- /dev/null
+++ b/go/notify/notify.go
@@ -0,0 +1,126 @@
+package main
+
+import (
+ "fmt"
+ "net"
+ "net/http"
+ "time"
+
+ "blog.bing89.com/go/notify/notify"
+ "github.com/gin-gonic/gin"
+)
+
+type Client struct {
+ User string
+ ID string
+ msgChan chan string
+}
+
+func (c *Client)Write(msg string){
+ c.msgChan <- msg
+}
+
+type NServer struct {
+ notifier *notify.Notifier
+ conn net.Conn
+ flusher http.Flusher
+}
+
+var ch = make(chan string)
+
+func (n *NServer)Get(c *gin.Context){
+ user := c.Query("user")
+ id := c.Query("client")
+ if user == "" || id == "" {
+ c.String(http.StatusBadRequest, "failed")
+ return
+ }
+
+ flusher, ok := c.Writer.(http.Flusher)
+ if !ok {
+ c.String(http.StatusBadRequest, "failed")
+ return
+ }
+ // h, _, err := c.Writer.Hijack()
+ // if err != nil {
+ // c.String(http.StatusBadRequest, err.Error())
+ // return
+ // }
+ // if n.conn == nil {
+ // n.conn = h
+ // }
+ c.Header("Content-Type", "text/event-stream")
+ c.Header("Cache-Control", "no-cache")
+ c.Header("Connection", "keep-alive")
+ c.Header("Access-Control-Allow-Origin", "*")
+
+ // if n.flusher == nil {
+ // n.flusher = flusher
+ // }
+ for str := range ch {
+
+ c.Writer.Write([]byte(fmt.Sprintf("data: %v\n\n", str)))
+ flusher.Flush()
+ }
+ // err := n.notifier.AddConsumer(c)
+ // if err != nil {
+ // fmt.Println("add consumer:", err)
+ // }
+ // c.Next()
+ // time.Sleep(100*time.Second)
+}
+
+func (n *NServer)Run(){
+ ticker := time.NewTicker(2*time.Second)
+ for t := range ticker.C {
+ str := fmt.Sprintf("data: %v\n\n", t.Format("2006-01-02 15:04:05"))
+ fmt.Println("send data to n")
+ if n.conn != nil {
+ n, err := n.conn.Write([]byte(str))
+ fmt.Println("sent it length: ", n, " result: ", err)
+ }
+ ch <- str
+ // if n.flusher != nil {
+ // n.flusher.Flush()
+ // }
+ // n.notifier.SendMessage("2", fmt.Sprintf("hello world %d", t.Unix()))
+ }
+}
+
+func main(){
+ n := NServer{
+ notifier: notify.NewNotifier(),
+ }
+ // ctx, cancel := context.WithCancel(context.Background())
+ // defer cancel()
+ // go n.notifier.Run(ctx)
+ engine := gin.Default()
+ engine.GET("/api/notify", n.Get)
+ go n.Run()
+ engine.Run("0.0.0.0:18000")
+}
+
+// package main
+
+// import (
+// "gopkg.in/antage/eventsource.v1"
+// "log"
+// "net/http"
+// "strconv"
+// "time"
+// )
+
+// func main() {
+// es := eventsource.New(nil, nil)
+// defer es.Close()
+// http.Handle("/events", es)
+// go func() {
+// id := 1
+// for {
+// es.SendEventMessage("tick", "tick-event", strconv.Itoa(id))
+// id++
+// time.Sleep(2 * time.Second)
+// }
+// }()
+// log.Fatal(http.ListenAndServe(":18000", nil))
+// }
\ No newline at end of file
diff --git a/go/notify/notify/consumer.go b/go/notify/notify/consumer.go
new file mode 100644
index 0000000..1016bfb
--- /dev/null
+++ b/go/notify/notify/consumer.go
@@ -0,0 +1,66 @@
+package notify
+
+import (
+ // "compress/gzip"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "strings"
+
+ "github.com/gin-gonic/gin"
+)
+
+type Consumer struct {
+ UserID string
+ ClientID string
+ conn io.WriteCloser
+}
+
+func NewConsumer(c *gin.Context)(*Consumer, error){
+ user := c.Query("user")
+ clientID := c.Query("client")
+ if clientID == "" {
+ return nil, fmt.Errorf("client id is empty")
+ }
+ conn, _, err := c.Writer.Hijack()
+ if err != nil {
+ return nil, err
+ }
+ consumer := Consumer{
+ UserID: user,
+ ClientID: clientID,
+ conn: conn,
+ }
+ useGzip := false
+ if strings.Contains(c.GetHeader("Accept-Encoding"), "gzip") {
+ useGzip = true
+ }
+
+ err = consumer.init(useGzip)
+ if err != nil {
+ conn.Close()
+ }
+ return &consumer, err
+}
+
+func (c *Consumer)Write(msg string)error{
+ _, err := c.conn.Write([]byte(fmt.Sprintf("data: %s\n\n", msg)))
+ return err
+}
+
+func (c *Consumer)init(gz bool)error{
+ if err := c.Write("HTTP/1.1 200 OK\r\nContent-Type: text/event-stream\r\n"); err != nil {
+ return err
+ }
+ if err := c.Write("Vary: Accept-Encoding\r\n"); err != nil {
+ return err
+ }
+ if gz {
+ if err := c.Write("Content-Encoding: gzip\r\n"); err != nil {
+ return err
+ }
+ c.conn = gzip.NewWriter(c.conn)
+ }
+ return c.Write("\r\n")
+ return nil
+}
\ No newline at end of file
diff --git a/go/notify/notify/message.go b/go/notify/notify/message.go
new file mode 100644
index 0000000..63a849b
--- /dev/null
+++ b/go/notify/notify/message.go
@@ -0,0 +1,6 @@
+package notify
+
+type Message struct {
+ User string
+ Message string
+}
\ No newline at end of file
diff --git a/go/notify/notify/notify.go b/go/notify/notify/notify.go
new file mode 100644
index 0000000..383fab3
--- /dev/null
+++ b/go/notify/notify/notify.go
@@ -0,0 +1,104 @@
+package notify
+
+import (
+ "context"
+ "sync"
+
+ "github.com/gin-gonic/gin"
+)
+
+type Notifier struct {
+ Consumers map[string]*Consumer
+ userClients map[string][]string
+ lock sync.Mutex
+ consumerChan chan *Consumer
+ messageChan chan Message
+}
+
+func NewNotifier()*Notifier{
+ return &Notifier{
+ Consumers: make(map[string]*Consumer),
+ lock: sync.Mutex{},
+ consumerChan: make(chan *Consumer),
+ messageChan: make(chan Message),
+ userClients: map[string][]string{},
+ }
+}
+
+func (n *Notifier)AddConsumer(c *gin.Context)error{
+ consumer, err := NewConsumer(c)
+ if err != nil{
+ return err
+ }
+ go func () {
+ n.consumerChan <- consumer
+ }()
+ return nil
+}
+
+func (n *Notifier)recieveMessage(ctx context.Context){
+ for {
+ select{
+ case <- ctx.Done():
+ return
+ case msg := <- n.messageChan:
+ n.notify(msg)
+ }
+ }
+}
+
+func (n *Notifier)notify(msg Message){
+ n.lock.Lock()
+ defer n.lock.Unlock()
+ clts, ok := n.userClients[msg.User];
+ if !ok || len(clts) == 0{
+ return
+ }
+ newClts := []string{}
+ for _, clt := range clts{
+ client, ok := n.Consumers[clt]
+ if ok {
+ err := client.Write(msg.Message)
+ if err != nil {
+ delete(n.Consumers, clt)
+ }else{
+ newClts = append(newClts, clt)
+ }
+ }
+ }
+ n.userClients[msg.User] = newClts
+}
+
+func (n *Notifier)recieveConsumer(ctx context.Context){
+ for {
+ select{
+ case <- ctx.Done():
+ return
+ case c := <- n.consumerChan:
+ n.addConsumer(c)
+ }
+ }
+}
+
+func (n *Notifier)addConsumer(c *Consumer){
+ n.lock.Lock()
+ defer n.lock.Unlock()
+ n.Consumers[c.ClientID] = c
+ us := []string{}
+ if u, ok := n.userClients[c.UserID]; ok {
+ us=append(us, u...)
+ }
+ us=append(us, c.ClientID)
+ n.userClients[c.UserID] = us
+}
+
+func (n *Notifier)Run(ctx context.Context){
+ go n.recieveConsumer(ctx)
+ n.recieveMessage(ctx)
+}
+
+func (n *Notifier)SendMessage(user, msg string){
+ go func () {
+ n.messageChan <- Message{User: user, Message: msg}
+ }()
+}
\ No newline at end of file
diff --git a/go/test/main.go b/go/test/main.go
new file mode 100644
index 0000000..39c4c98
--- /dev/null
+++ b/go/test/main.go
@@ -0,0 +1,5 @@
+package main
+
+func main(){
+
+}
\ No newline at end of file
diff --git a/go/wechat/main.go b/go/wechat/main.go
index b4af910..6478134 100644
--- a/go/wechat/main.go
+++ b/go/wechat/main.go
@@ -1,42 +1,43 @@
package main
import (
- "crypto/sha1"
- "encoding/xml"
"fmt"
+ "html/template"
"log"
"net/http"
- "sort"
- "strings"
- "time"
+ "blog.bing89.com/go/wechat/wechat"
"github.com/gin-gonic/gin"
)
const (
- appID = "wxc39301065b66300c"
- secret = "7fee3a1cba8c555cf6c4caec4f05844c"
- token = "qwertyuiopasdfghjkl"
+ appID = "wxc39301065b66300c"
+ secret = "7fee3a1cba8c555cf6c4caec4f05844c"
+ token = "qwertyuiopasdfghjkl"
+ Secret = "0a05cb757a9e4fdc9b1aac4f0406a3df"
+ Appid = "wx2fa7fb238aa6896a"
+ Encodingaeskey = "Lw2YDq9Svzhi5HeTzldcG5ID7rlgsJkQ5tDv8wwa092"
)
-//////
-type messageEntity struct {
- Signature string `form:"signature"`
- Timestamp string `form:"timestamp"`
- Nonce string `form:"nonce"`
- EchoStr string `form:"echostr"`
+type Server struct {
+ w *wechat.Wechat
+ users map[string]*wechat.UserInfo
}
-func (m *messageEntity) CheckSignature(token string) bool {
- item := []string{token, m.Timestamp, m.Nonce}
- sort.Strings(item)
- itemByte := strings.Join(item, "")
- signature := fmt.Sprintf("%x", sha1.Sum([]byte(itemByte)))
- return signature == m.Signature
+func NewServer() (*Server, error) {
+ w, err := wechat.NewWechat(Appid, Secret)
+ if err != nil {
+ return nil, err
+ }
+ s := Server{
+ w: w,
+ users: make(map[string]*wechat.UserInfo),
+ }
+ return &s, nil
}
-func Check(c *gin.Context) {
- me := &messageEntity{}
+func (s *Server) Check(c *gin.Context) {
+ me := &wechat.MessageEntity{}
me.Signature = c.Query("signature")
me.Timestamp = c.Query("timestamp")
me.Nonce = c.Query("nonce")
@@ -47,43 +48,25 @@ func Check(c *gin.Context) {
c.AbortWithError(http.StatusForbidden, fmt.Errorf("token invalid"))
return
}
+ c.Set("echostr", me.EchoStr)
c.Next()
}
////
-type foucsData struct {
- ToUserName string `xml:"ToUserName"`
- FromUserName string `xml:"FromUserName"`
- CreateTime int64 `xml:"CreateTime"`
- MsgType string `xml:"MsgType"`
- Event string `xml:"Event"`
-}
-////
-type textMessage struct {
- ToUserName string `xml:"ToUserName"`
- FromUserName string `xml:"FromUserName"`
- CreateTime int64 `xml:"CreateTime"`
- MsgType string `xml:"MsgType"`
- Content string `xml:"Content"`
-}
-
-func cdata(data string)string{
+func cdata(data string) string {
return fmt.Sprintf(``, data)
}
-func handleGet(c *gin.Context) {
- me := &messageEntity{}
- me.Signature = c.Query("signature")
- me.Timestamp = c.Query("timestamp")
- me.Nonce = c.Query("nonce")
- me.EchoStr = c.Query("echostr")
- log.Println("signature:", me.Signature, "timestamp:", me.Timestamp, "noce:", me.Nonce, "echostr:", me.EchoStr)
+func (s *Server) handleGet(c *gin.Context) {
+ echoStr := c.GetString("echostr")
log.Println("token is ok")
- c.String(http.StatusOK, me.EchoStr)
+ c.String(http.StatusOK, echoStr)
}
-func handlePost(c *gin.Context) {
- data := foucsData{}
+
+//关注
+func (s *Server) handlePost(c *gin.Context) {
+ data := wechat.FoucsRequest{}
err := c.BindXML(&data)
if err != nil {
log.Println(err)
@@ -91,22 +74,21 @@ func handlePost(c *gin.Context) {
log.Println("data: ", data)
openID := c.Query("openid")
log.Println("openid is ", openID)
- t := textMessage{
- ToUserName: cdata(data.FromUserName),
- FromUserName: cdata(data.ToUserName),
- CreateTime: time.Now().Unix(),
- MsgType: cdata("text"),
- Content: cdata("你关注了我!"),
+ res, err := s.w.SendText(data.FromUserName, "welcome to xk.design.")
+ log.Printf("res: %s\nerr:%v", string(res), err)
+ ui, err := s.w.GetUserInfo(data.FromUserName)
+ if err == nil {
+ s.users[ui.OpenID] = ui
+ ui.Show()
}
- d, err := xml.Marshal(&t)
if err != nil {
- log.Println("marshar message faild", err)
+ log.Printf("get ui err:%v", err)
}
- c.String(http.StatusOK, string(d))
+ c.String(http.StatusOK, "success")
}
-func handlePut(c *gin.Context) {
- data :=foucsData{}
+func (s *Server) handlePut(c *gin.Context) {
+ data := wechat.FoucsRequest{}
err := c.BindXML(&data)
if err != nil {
log.Println(err)
@@ -115,8 +97,8 @@ func handlePut(c *gin.Context) {
c.String(http.StatusOK, "success")
}
-func handleDelete(c *gin.Context) {
- data := foucsData{}
+func (s *Server) handleDelete(c *gin.Context) {
+ data := wechat.FoucsRequest{}
err := c.BindXML(&data)
if err != nil {
log.Println(err)
@@ -125,13 +107,62 @@ func handleDelete(c *gin.Context) {
c.String(http.StatusOK, "success")
}
+//@router /auth/login
+func (s *Server) login(c *gin.Context) {
+ code := c.Query("code")
+ if code == "" {
+ c.AbortWithError(http.StatusBadRequest, fmt.Errorf("no code"))
+ return
+ }
+ ticket, err := s.w.CreateQRCode()
+ if err != nil {
-func main() {
+ }
+ log.Printf("ticket: %s\nurl: %s\n err:%v\n", ticket.Ticket, ticket.URL, ticket.ExpireSeconds)
+ log.Println("==========> code is: ", code)
+ c.HTML(http.StatusOK, "next", gin.H{"next": template.HTML(ticket.URL)})
+
+}
+
+func (s *Server) Template(name string) *template.Template {
+ tmpl := template.New(name)
+ tmpl.Parse(s.html())
+ return tmpl
+}
+
+func (s *Server) html() string {
+ return `
+
+
+ Login
+ `
+}
+
+func (s *Server) Run(addr string) error {
engine := gin.Default()
- engine.Use(Check)
- engine.GET("/api/wechat/signature", handleGet)
- engine.POST("/api/wechat/signature", handlePost)
- engine.PUT("/api/wechat/signature", handlePut)
- engine.DELETE("/api/wechat/signature", handleDelete)
- engine.Run("0.0.0.0:15043")
+ engine.SetHTMLTemplate(s.Template("next"))
+ apiGroup := engine.Group("/api/wechat")
+ apiGroup.Use(s.Check)
+ apiGroup.GET("/signature", s.handleGet)
+ apiGroup.POST("/signature", s.handlePost)
+ apiGroup.PUT("/signature", s.handlePut)
+ apiGroup.DELETE("/signature", s.handleDelete)
+ authGroup := engine.Group("/auth")
+ authGroup.GET("/login", s.login)
+
+ return engine.Run(addr)
+}
+
+//https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxc39301065b66300c&redirect_uri=http://106.55.59.210:15043/auth/login&response_type=code&scope=snsapi_userinfo#wechat_redirect
+// https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxc39301065b66300c&redirect_uri=http%3A%2F%2F106.55.59.210%3A15043%2Fauth%2Flogin&response_type=code&scope=snsapi_userinfo&connect_redirect=1#wechat_redirect
+func main() {
+ s, err := NewServer()
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ err = s.Run("0.0.0.0:15043")
+ if err != nil {
+ log.Fatal(err)
+ }
}
diff --git a/go/wechat/wechat/api.go b/go/wechat/wechat/api.go
index 739e3a3..09110e9 100644
--- a/go/wechat/wechat/api.go
+++ b/go/wechat/wechat/api.go
@@ -1,8 +1,19 @@
package wechat
+import (
+ "crypto/sha1"
+ "encoding/json"
+ "fmt"
+ "sort"
+ "strings"
+)
+
const (
APIGetAccessToken = "https://api.weixin.qq.com/cgi-bin/token"
- APISendMessage= "https://api.weixin.qq.com/cgi-bin/message/custom/send"
+ APISendMessage = "https://api.weixin.qq.com/cgi-bin/message/custom/send"
+ APIGetUserInfo = "https://api.weixin.qq.com/cgi-bin/user/info"
+ APIGetGrantToken = "https://api.weixin.qq.com/sns/oauth2/access_token"
+ APICreateQRCode = "https://api.weixin.qq.com/cgi-bin/qrcode/create"
)
type MessageType string
@@ -16,7 +27,98 @@ type TextMessage struct {
}
type Message struct {
- ToUser string `json:"touser"`
- Type MessageType `json:"msgtype"`
- Text TextMessage `json:"text"`
-}
\ No newline at end of file
+ ToUser string `json:"touser"`
+ Type MessageType `json:"msgtype"`
+ Text TextMessage `json:"text"`
+}
+
+func (m *Message) Show() {
+ data, err := json.MarshalIndent(m, "", "\t")
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(string(data))
+}
+
+//////
+type MessageEntity struct {
+ Signature string `form:"signature"`
+ Timestamp string `form:"timestamp"`
+ Nonce string `form:"nonce"`
+ EchoStr string `form:"echostr"`
+}
+
+func (m *MessageEntity) CheckSignature(token string) bool {
+ item := []string{token, m.Timestamp, m.Nonce}
+ sort.Strings(item)
+ itemByte := strings.Join(item, "")
+ signature := fmt.Sprintf("%x", sha1.Sum([]byte(itemByte)))
+ return signature == m.Signature
+}
+
+type FoucsRequest struct {
+ ToUserName string `xml:"ToUserName"`
+ FromUserName string `xml:"FromUserName"`
+ CreateTime int64 `xml:"CreateTime"`
+ MsgType string `xml:"MsgType"`
+ Event string `xml:"Event"`
+}
+
+/*
+{
+ "subscribe": 1,
+ "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
+ "language": "zh_CN",
+ "subscribe_time": 1382694957,
+ "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL",
+ "remark": "",
+ "groupid": 0,
+ "tagid_list":[128,2],
+ "subscribe_scene": "ADD_SCENE_QR_CODE",
+ "qr_scene": 98765,
+ "qr_scene_str": ""
+}
+*/
+type UserInfo struct {
+ Subscribe int `json:"subscribe"`
+ OpenID string `json:"openid"`
+ Language string `json:"language"`
+ SubscribeTime int64 `json:"subscribe_time"`
+ UnionID string `json:"unionid"`
+ Remark string `json:"remark"`
+ GroupID int `json:"groupid"`
+ LagIDList []int `json:"tagid_list"`
+ SubscribeScene string `json:"subscribe_scene"`
+ QRScene int `json:"qr_scene"`
+ QRSceneStr string `json:"qr_scene_str"`
+ Nickname string `json:"nickname"`
+ Sex int `json:"sex"`
+ Province string `json:"province"`
+ Country string `json:"country"`
+ HeadImage string `json:"headimgurl"`
+}
+
+func (u *UserInfo) Show() {
+ data, err := json.MarshalIndent(u, "", "\t")
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(string(data))
+}
+
+type GrantToken struct {
+ Token string `json:"access_token"`
+ ExpiresIn int64 `json:"expires_in"`
+ ExpiredAt int64 `json:"expiredAt"`
+ RefreshToken string `json:"refresh_token"`
+ OpenID string `json:"openid"`
+ Scope string `json:"scope"`
+}
+
+type Ticket struct {
+ Ticket string `json:"ticket"`
+ ExpireSeconds int `json:"expire_seconds"`
+ URL string `json:"url"`
+}
diff --git a/go/wechat/wechat/wechat.go b/go/wechat/wechat/wechat.go
index 906b4d2..2802e01 100644
--- a/go/wechat/wechat/wechat.go
+++ b/go/wechat/wechat/wechat.go
@@ -3,6 +3,7 @@ package wechat
import (
"encoding/json"
"fmt"
+ "log"
"time"
"blog.bing89.com/go/wechat/curl"
@@ -13,7 +14,7 @@ type Token struct {
ExpiresIn int64 `json:"expires_in"`
ExpiredAt int64 `json:"expiredAt"`
ErrCode int `json:"errcode"`
- ErrMsg string `json:"errmsg"`
+ ErrMsg string `json:"errmsg"`
}
type Wechat struct {
@@ -22,38 +23,42 @@ type Wechat struct {
token Token
}
-func NewWechat(id, secret string) *Wechat {
+func NewWechat(id, secret string) (*Wechat, error) {
w := Wechat{
AppID: id,
AppSecret: secret,
}
- w.checkToken()
- return &w
+ err := w.checkToken()
+ return &w, err
}
-func (w *Wechat) checkToken() {
+func (w *Wechat) checkToken() error {
if w.token.Token == "" || w.token.ExpiredAt < time.Now().Unix() {
- w.getToken()
+ return w.getToken()
}
+ return nil
}
func (w *Wechat) queryStr() string {
return fmt.Sprintf("appid=%s&secret=%s", w.AppID, w.AppSecret)
}
-func (w *Wechat)queryToken()string {
- w.checkToken()
- return fmt.Sprintf("access_token=%s", w.token.Token)
+func (w *Wechat) queryToken() (string, error) {
+ err := w.checkToken()
+ return fmt.Sprintf("access_token=%s", w.token.Token), err
}
func (w *Wechat) getToken() error {
- url := fmt.Sprintf("%s??grant_type=client_credential&%s", APIGetAccessToken, w.queryStr())
+ url := fmt.Sprintf("%s?grant_type=client_credential&%s", APIGetAccessToken, w.queryStr())
data, err := curl.SimpleGet(url)
if err != nil {
return err
}
- err = json.Unmarshal(data, &w.token)
+ token := Token{}
+ err = json.Unmarshal(data, &token)
+ log.Println("====>token:", string(data), " err:", err)
if err == nil {
+ w.token = token
w.token.ExpiredAt = w.token.ExpiresIn + time.Now().Unix()
}
return err
@@ -62,12 +67,82 @@ func (w *Wechat) getToken() error {
func (w *Wechat) SendText(to, text string) ([]byte, error) {
msg := Message{
ToUser: to,
- Type: MessageTypeText,
+ Type: MessageTypeText,
Text: TextMessage{
Content: text,
},
}
- url := fmt.Sprintf("%s?%s", APISendMessage, w.queryToken())
+ token, err := w.queryToken()
+ if err != nil {
+ return nil, err
+ }
+ url := fmt.Sprintf("%s?%s", APISendMessage, token)
+ log.Println("=====>", url)
+ msg.Show()
data, err := curl.Post(url, &msg, nil)
- return data ,err
+ return data, err
+}
+
+func (w *Wechat)GetUserInfo(openID string)(*UserInfo, error){
+ token, err := w.queryToken()
+ if err != nil {
+ return nil, err
+ }
+ url := fmt.Sprintf("%s?lang=zh_CN&%s&openid=%s", APIGetUserInfo, token, openID)
+ data, err := curl.SimpleGet(url)
+ if err != nil {
+ return nil, err
+ }
+ ui := UserInfo{}
+ err = json.Unmarshal(data, &ui)
+ return &ui, err
+}
+
+func (w *Wechat)GetGrantToken(code string)(*GrantToken, error){
+ url := fmt.Sprintf("%s?%s&code=%s&grant_type=authorization_code", APIGetGrantToken, w.queryStr(), code)
+
+ data, err := curl.SimpleGet(url)
+ if err != nil {
+ return nil, err
+ }
+ token := GrantToken{}
+ err = json.Unmarshal(data, &token)
+ return &token, err
}
+//{"expire_seconds":"60","action_name": "QR_STR_SCENE", "action_info": {"scene": {"scene_str": '.$id.'}}}
+type Scene struct{
+ Str string `json:"scene_str"`
+}
+
+type ActionInfo struct {
+ ActionInfo Scene `json:"scene"`
+}
+type qrCodeRequest struct{
+ ExpireSeconds int `json:"expire_seconds"`
+ Action string `json:"action_name"`
+ ActionInfo ActionInfo `json:"action_info"`
+}
+
+func (w *Wechat)CreateQRCode()(*Ticket, error){
+ req := qrCodeRequest{
+ ExpireSeconds: 3600,
+ Action: "QR_STR_SCENE",
+ ActionInfo: ActionInfo{
+ Scene{
+ Str: fmt.Sprintf("aaa%d", time.Now().UnixNano()),
+ },
+ },
+ }
+ token, err := w.queryToken()
+ if err != nil {
+ return nil, err
+ }
+ url := fmt.Sprintf("%s?%s", APICreateQRCode, token)
+ data, err := curl.Post(url, req, nil)
+ if err != nil {
+ return nil, err
+ }
+ ticket := Ticket{}
+ err = json.Unmarshal(data, &ticket)
+ return &ticket, err
+}
\ No newline at end of file