generated from bing/readnotes
feat 添加一些东西
This commit is contained in:
parent
9d64a57c6c
commit
3c1bff3aaa
|
@ -16,4 +16,6 @@
|
|||
# vendor/
|
||||
|
||||
.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/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
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/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=
|
||||
|
|
|
@ -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,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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue