feat 添加一些东西

This commit is contained in:
bing 2022-10-10 10:34:56 +08:00
parent 9d64a57c6c
commit 3c1bff3aaa
13 changed files with 653 additions and 89 deletions

2
.gitignore vendored
View File

@ -17,3 +17,5 @@
.vscode
*.log
go/bin

1
go.mod
View File

@ -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

2
go.sum
View File

@ -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=

19
go/gin/main.go Normal file
View File

@ -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")
}

25
go/notify/index.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>SSE test</title>
<script type="text/javascript">
window.addEventListener("DOMContentLoaded", function () {
var evsrc = new EventSource("http://192.168.1.13:18000/api/notify?client=aaaa&user=2");
evsrc.onmessage = function (ev) {
document.getElementById("log")
.insertAdjacentHTML("beforeend", "<li>" + ev.data + "</li>");
}
evsrc.onerror = function (ev) {
console.log("readyState = " + ev.currentTarget.readyState);
}
})
</script>
</head>
<body>
<h1>SSE test</h1>
<div>
<ul id="log">
</ul>
</div>
</body>
</html>

126
go/notify/notify.go Normal file
View File

@ -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))
// }

View File

@ -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
}

View File

@ -0,0 +1,6 @@
package notify
type Message struct {
User string
Message string
}

104
go/notify/notify/notify.go Normal file
View File

@ -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}
}()
}

5
go/test/main.go Normal file
View File

@ -0,0 +1,5 @@
package main
func main(){
}

View File

@ -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(`<![CDATA[%s]]>`, 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 `<html>
<head>
<meta charset="utf-8">
<title>Login</title>
</head><body></body><script>window.location.href='{{.next}}?#wechat_redirect';</script></html>`
}
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)
}
}

View File

@ -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"`
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"`
}

View File

@ -3,6 +3,7 @@ package wechat
import (
"encoding/json"
"fmt"
"log"
"time"
"blog.bing89.com/go/wechat/curl"
@ -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
}