generated from bing/readnotes
feat 添加一些东西
This commit is contained in:
parent
9d64a57c6c
commit
3c1bff3aaa
|
@ -17,3 +17,5 @@
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
go/bin
|
1
go.mod
1
go.mod
|
@ -7,6 +7,7 @@ require (
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/gin-gonic/gin v1.8.1
|
||||||
github.com/spf13/cobra v1.3.0
|
github.com/spf13/cobra v1.3.0
|
||||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
|
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/api v0.23.1
|
||||||
k8s.io/apimachinery v0.23.1
|
k8s.io/apimachinery v0.23.1
|
||||||
k8s.io/client-go v0.23.1
|
k8s.io/client-go v0.23.1
|
||||||
|
|
2
go.sum
2
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 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
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/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 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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
|
@ -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>
|
|
@ -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))
|
||||||
|
// }
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package notify
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
User string
|
||||||
|
Message string
|
||||||
|
}
|
|
@ -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}
|
||||||
|
}()
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main(){
|
||||||
|
|
||||||
|
}
|
|
@ -1,15 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"blog.bing89.com/go/wechat/wechat"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,26 +14,30 @@ const (
|
||||||
appID = "wxc39301065b66300c"
|
appID = "wxc39301065b66300c"
|
||||||
secret = "7fee3a1cba8c555cf6c4caec4f05844c"
|
secret = "7fee3a1cba8c555cf6c4caec4f05844c"
|
||||||
token = "qwertyuiopasdfghjkl"
|
token = "qwertyuiopasdfghjkl"
|
||||||
|
Secret = "0a05cb757a9e4fdc9b1aac4f0406a3df"
|
||||||
|
Appid = "wx2fa7fb238aa6896a"
|
||||||
|
Encodingaeskey = "Lw2YDq9Svzhi5HeTzldcG5ID7rlgsJkQ5tDv8wwa092"
|
||||||
)
|
)
|
||||||
|
|
||||||
//////
|
type Server struct {
|
||||||
type messageEntity struct {
|
w *wechat.Wechat
|
||||||
Signature string `form:"signature"`
|
users map[string]*wechat.UserInfo
|
||||||
Timestamp string `form:"timestamp"`
|
|
||||||
Nonce string `form:"nonce"`
|
|
||||||
EchoStr string `form:"echostr"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *messageEntity) CheckSignature(token string) bool {
|
func NewServer() (*Server, error) {
|
||||||
item := []string{token, m.Timestamp, m.Nonce}
|
w, err := wechat.NewWechat(Appid, Secret)
|
||||||
sort.Strings(item)
|
if err != nil {
|
||||||
itemByte := strings.Join(item, "")
|
return nil, err
|
||||||
signature := fmt.Sprintf("%x", sha1.Sum([]byte(itemByte)))
|
}
|
||||||
return signature == m.Signature
|
s := Server{
|
||||||
|
w: w,
|
||||||
|
users: make(map[string]*wechat.UserInfo),
|
||||||
|
}
|
||||||
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Check(c *gin.Context) {
|
func (s *Server) Check(c *gin.Context) {
|
||||||
me := &messageEntity{}
|
me := &wechat.MessageEntity{}
|
||||||
me.Signature = c.Query("signature")
|
me.Signature = c.Query("signature")
|
||||||
me.Timestamp = c.Query("timestamp")
|
me.Timestamp = c.Query("timestamp")
|
||||||
me.Nonce = c.Query("nonce")
|
me.Nonce = c.Query("nonce")
|
||||||
|
@ -47,43 +48,25 @@ func Check(c *gin.Context) {
|
||||||
c.AbortWithError(http.StatusForbidden, fmt.Errorf("token invalid"))
|
c.AbortWithError(http.StatusForbidden, fmt.Errorf("token invalid"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
c.Set("echostr", me.EchoStr)
|
||||||
c.Next()
|
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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
////
|
func cdata(data string) string {
|
||||||
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{
|
|
||||||
return fmt.Sprintf(`<![CDATA[%s]]>`, data)
|
return fmt.Sprintf(`<![CDATA[%s]]>`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGet(c *gin.Context) {
|
func (s *Server) handleGet(c *gin.Context) {
|
||||||
me := &messageEntity{}
|
echoStr := c.GetString("echostr")
|
||||||
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)
|
|
||||||
log.Println("token is ok")
|
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)
|
err := c.BindXML(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
@ -91,22 +74,21 @@ func handlePost(c *gin.Context) {
|
||||||
log.Println("data: ", data)
|
log.Println("data: ", data)
|
||||||
openID := c.Query("openid")
|
openID := c.Query("openid")
|
||||||
log.Println("openid is ", openID)
|
log.Println("openid is ", openID)
|
||||||
t := textMessage{
|
res, err := s.w.SendText(data.FromUserName, "welcome to xk.design.")
|
||||||
ToUserName: cdata(data.FromUserName),
|
log.Printf("res: %s\nerr:%v", string(res), err)
|
||||||
FromUserName: cdata(data.ToUserName),
|
ui, err := s.w.GetUserInfo(data.FromUserName)
|
||||||
CreateTime: time.Now().Unix(),
|
if err == nil {
|
||||||
MsgType: cdata("text"),
|
s.users[ui.OpenID] = ui
|
||||||
Content: cdata("你关注了我!"),
|
ui.Show()
|
||||||
}
|
}
|
||||||
d, err := xml.Marshal(&t)
|
|
||||||
if err != nil {
|
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) {
|
func (s *Server) handlePut(c *gin.Context) {
|
||||||
data :=foucsData{}
|
data := wechat.FoucsRequest{}
|
||||||
err := c.BindXML(&data)
|
err := c.BindXML(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
@ -115,8 +97,8 @@ func handlePut(c *gin.Context) {
|
||||||
c.String(http.StatusOK, "success")
|
c.String(http.StatusOK, "success")
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleDelete(c *gin.Context) {
|
func (s *Server) handleDelete(c *gin.Context) {
|
||||||
data := foucsData{}
|
data := wechat.FoucsRequest{}
|
||||||
err := c.BindXML(&data)
|
err := c.BindXML(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
@ -125,13 +107,62 @@ func handleDelete(c *gin.Context) {
|
||||||
c.String(http.StatusOK, "success")
|
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 := gin.Default()
|
||||||
engine.Use(Check)
|
engine.SetHTMLTemplate(s.Template("next"))
|
||||||
engine.GET("/api/wechat/signature", handleGet)
|
apiGroup := engine.Group("/api/wechat")
|
||||||
engine.POST("/api/wechat/signature", handlePost)
|
apiGroup.Use(s.Check)
|
||||||
engine.PUT("/api/wechat/signature", handlePut)
|
apiGroup.GET("/signature", s.handleGet)
|
||||||
engine.DELETE("/api/wechat/signature", handleDelete)
|
apiGroup.POST("/signature", s.handlePost)
|
||||||
engine.Run("0.0.0.0:15043")
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
package wechat
|
package wechat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
APIGetAccessToken = "https://api.weixin.qq.com/cgi-bin/token"
|
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
|
type MessageType string
|
||||||
|
@ -20,3 +31,94 @@ type Message struct {
|
||||||
Type MessageType `json:"msgtype"`
|
Type MessageType `json:"msgtype"`
|
||||||
Text TextMessage `json:"text"`
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package wechat
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"blog.bing89.com/go/wechat/curl"
|
"blog.bing89.com/go/wechat/curl"
|
||||||
|
@ -22,38 +23,42 @@ type Wechat struct {
|
||||||
token Token
|
token Token
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWechat(id, secret string) *Wechat {
|
func NewWechat(id, secret string) (*Wechat, error) {
|
||||||
w := Wechat{
|
w := Wechat{
|
||||||
AppID: id,
|
AppID: id,
|
||||||
AppSecret: secret,
|
AppSecret: secret,
|
||||||
}
|
}
|
||||||
w.checkToken()
|
err := w.checkToken()
|
||||||
return &w
|
return &w, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wechat) checkToken() {
|
func (w *Wechat) checkToken() error {
|
||||||
if w.token.Token == "" || w.token.ExpiredAt < time.Now().Unix() {
|
if w.token.Token == "" || w.token.ExpiredAt < time.Now().Unix() {
|
||||||
w.getToken()
|
return w.getToken()
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wechat) queryStr() string {
|
func (w *Wechat) queryStr() string {
|
||||||
return fmt.Sprintf("appid=%s&secret=%s", w.AppID, w.AppSecret)
|
return fmt.Sprintf("appid=%s&secret=%s", w.AppID, w.AppSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wechat)queryToken()string {
|
func (w *Wechat) queryToken() (string, error) {
|
||||||
w.checkToken()
|
err := w.checkToken()
|
||||||
return fmt.Sprintf("access_token=%s", w.token.Token)
|
return fmt.Sprintf("access_token=%s", w.token.Token), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wechat) getToken() error {
|
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)
|
data, err := curl.SimpleGet(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err == nil {
|
||||||
|
w.token = token
|
||||||
w.token.ExpiredAt = w.token.ExpiresIn + time.Now().Unix()
|
w.token.ExpiredAt = w.token.ExpiresIn + time.Now().Unix()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -67,7 +72,77 @@ func (w *Wechat) SendText(to, text string) ([]byte, error) {
|
||||||
Content: text,
|
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)
|
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
|
||||||
}
|
}
|
Loading…
Reference in New Issue