From c3315438c1f39e5b0dff21d02ac8b7b149458b82 Mon Sep 17 00:00:00 2001 From: dchaofei Date: Sat, 4 Apr 2020 20:36:48 +0800 Subject: [PATCH 1/8] memory_card --- wechaty-puppet/memory-card/memory_card.go | 95 +++++++++ .../memory-card/memory_card_test.go | 192 ++++++++++++++++++ wechaty-puppet/memory-card/storage/backend.go | 7 + wechaty-puppet/memory-card/storage/file.go | 76 +++++++ .../memory-card/storage/file_test.go | 47 +++++ wechaty-puppet/memory-card/storage/nop.go | 16 ++ .../memory-card/storage/testdata/.gitignore | 1 + .../memory-card/testdata/.gitignore | 1 + 8 files changed, 435 insertions(+) create mode 100644 wechaty-puppet/memory-card/memory_card.go create mode 100644 wechaty-puppet/memory-card/memory_card_test.go create mode 100644 wechaty-puppet/memory-card/storage/backend.go create mode 100644 wechaty-puppet/memory-card/storage/file.go create mode 100644 wechaty-puppet/memory-card/storage/file_test.go create mode 100644 wechaty-puppet/memory-card/storage/nop.go create mode 100644 wechaty-puppet/memory-card/storage/testdata/.gitignore create mode 100644 wechaty-puppet/memory-card/testdata/.gitignore diff --git a/wechaty-puppet/memory-card/memory_card.go b/wechaty-puppet/memory-card/memory_card.go new file mode 100644 index 0000000..a180a2f --- /dev/null +++ b/wechaty-puppet/memory-card/memory_card.go @@ -0,0 +1,95 @@ +package memory_card + +import ( + "encoding/json" + storage2 "github.com/wechaty/go-wechaty/wechaty-puppet/memory-card/storage" + "sync" +) + +type MemoryCard struct { + payload *sync.Map + storage storage2.IStorage +} + +func NewMemoryCard(storage storage2.IStorage) *MemoryCard { + return &MemoryCard{ + payload: &sync.Map{}, + storage: storage, + } +} + +func (mc *MemoryCard) GetInt64(key string) int64 { + raw := mc.get(key) + switch raw.(type) { + case json.Number: + value, _ := raw.(json.Number).Int64() + return value + case int64: + return raw.(int64) + default: + return 0 + } +} + +func (mc *MemoryCard) GetString(key string) string { + value, ok := mc.get(key).(string) + if ok { + return value + } + return "" +} + +func (mc *MemoryCard) get(key string) interface{} { + v, _ := mc.payload.Load(key) + return v +} + +func (mc *MemoryCard) SetInt64(key string, value int64) { + mc.set(key, value) +} + +func (mc *MemoryCard) SetString(key string, value string) { + mc.set(key, value) +} + +func (mc *MemoryCard) set(key string, value interface{}) { + mc.payload.Store(key, value) +} + +func (mc *MemoryCard) Clear() { + mc.payload = &sync.Map{} +} + +func (mc *MemoryCard) Delete(key string) { + mc.payload.Delete(key) +} + +func (mc *MemoryCard) Has(key string) bool { + _, ok := mc.payload.Load(key) + return ok +} + +func (mc *MemoryCard) Load() error { + raw, err := mc.storage.Load() + if err != nil { + return err + } + for k, v := range raw { + mc.set(k, v) + } + return nil +} + +func (mc *MemoryCard) Save() error { + raw := map[string]interface{}{} + mc.payload.Range(func(key, value interface{}) bool { + raw[key.(string)] = value + return true + }) + return mc.storage.Save(raw) +} + +func (mc *MemoryCard) Destroy() error { + mc.Clear() + return mc.storage.Destroy() +} diff --git a/wechaty-puppet/memory-card/memory_card_test.go b/wechaty-puppet/memory-card/memory_card_test.go new file mode 100644 index 0000000..4eca7f8 --- /dev/null +++ b/wechaty-puppet/memory-card/memory_card_test.go @@ -0,0 +1,192 @@ +package memory_card + +import ( + "crypto/md5" + "fmt" + storage2 "github.com/wechaty/go-wechaty/wechaty-puppet/memory-card/storage" + "io" + "math/rand" + "testing" + "time" +) + +func TestMemoryCard_GetInt(t *testing.T) { + mc := newMemoryCard(t) + + t.Run("not value return 0", func(t *testing.T) { + got := mc.GetInt64("not") + if got != 0 { + t.Fatalf("got %d expect 0", got) + } + }) + + t.Run("set string return 0", func(t *testing.T) { + key := "set_string_return_0" + mc.SetString(key, "string") + got := mc.GetInt64(key) + if got != 0 { + t.Fatalf("got %d expect 0", got) + } + }) + + t.Run("success", func(t *testing.T) { + key := "success" + expect := rand.Int63() + mc.SetInt64(key, expect) + got := mc.GetInt64(key) + if got != expect { + t.Fatalf("got %d expect %d", got, expect) + } + }) +} + +func TestMemoryCard_GetString(t *testing.T) { + mc := newMemoryCard(t) + t.Run("not value return empty string", func(t *testing.T) { + got := mc.GetString("not") + if got != "" { + t.Fatalf("got %s expect empty stirng", got) + } + }) + + t.Run("set int return empty string", func(t *testing.T) { + key := "set_int_return_empty_string" + mc.SetInt64(key, 1) + got := mc.GetString(key) + if got != "" { + t.Fatalf("got %s expect empty stirng", got) + } + }) + + t.Run("success", func(t *testing.T) { + key := "success" + expect := randString() + mc.SetString(key, expect) + got := mc.GetString(key) + if got != expect { + t.Fatalf("got %s expect %s", got, expect) + } + }) +} + +func TestMemoryCard_SetInt(t *testing.T) { + mc := newMemoryCard(t) + key := "success" + expect := rand.Int63() + mc.SetInt64(key, expect) + got := mc.GetInt64(key) + if got != expect { + t.Fatalf("got %d expect %d", got, expect) + } +} + +func TestMemoryCard_SetString(t *testing.T) { + mc := newMemoryCard(t) + key := "success" + expect := randString() + mc.SetString(key, expect) + got := mc.GetString(key) + if got != expect { + t.Fatalf("got %s expect %s", got, expect) + } +} + +func TestMemoryCard_Clear(t *testing.T) { + mc := newMemoryCard(t) + table := []struct { + key string + value int64 + }{ + {"key1", 1}, + {"key2", 2}, + } + for _, v := range table { + mc.SetInt64(v.key, v.value) + } + mc.Clear() + for _, v := range table { + got := mc.GetInt64(v.key) + if got != 0 { + t.Fatalf("got %d expect 0", got) + } + } +} + +func TestMemoryCard_Delete(t *testing.T) { + mc := newMemoryCard(t) + table := []struct { + key string + value int64 + }{ + {"key1", 1}, + {"key2", 2}, + } + for _, v := range table { + mc.SetInt64(v.key, v.value) + } + mc.Delete(table[0].key) + got := mc.GetInt64(table[0].key) + if got != 0 { + t.Fatalf("got %d expect 0", got) + } + got = mc.GetInt64(table[1].key) + if got != table[1].value { + t.Fatalf("got %d expect d", table[1].value) + } +} + +func TestMemoryCard_Has(t *testing.T) { + mc := newMemoryCard(t) + if false != mc.Has("null") { + t.Fatalf("got true expect false") + } + mc.SetString("key", "value") + if true != mc.Has("key") { + t.Fatalf("got false expect false") + } +} + +func TestMemoryCard_SaveAndLoad(t *testing.T) { + table := []struct { + key string + value int64 + }{ + {"key0", 0}, + {"key1", 1}, + {"key2", 2}, + } + mc1 := newMemoryCard(t) + for _, v := range table { + mc1.set(v.key, v.value) + } + err := mc1.Save() + if err != nil { + t.Fatal(err.Error()) + } + mc2 := newMemoryCard(t) + err = mc2.Load() + if err != nil { + t.Fatalf(err.Error()) + } + for _, v := range table { + got := mc2.GetInt64(v.key) + if got != v.value { + t.Fatalf("got %d expected %d", got, v.value) + } + } +} + +func randString() string { + t := time.Now() + h := md5.New() + _, _ = io.WriteString(h, t.String()) + return fmt.Sprintf("%x", h.Sum(nil)) +} + +func newMemoryCard(t *testing.T) *MemoryCard { + storage, err := storage2.NewFileStorage("testdata/file") + if err != nil { + t.Fatalf(err.Error()) + } + return NewMemoryCard(storage) +} diff --git a/wechaty-puppet/memory-card/storage/backend.go b/wechaty-puppet/memory-card/storage/backend.go new file mode 100644 index 0000000..2f6fbc5 --- /dev/null +++ b/wechaty-puppet/memory-card/storage/backend.go @@ -0,0 +1,7 @@ +package storage + +type IStorage interface { + Save(payload map[string]interface{}) error + Load() (map[string]interface{}, error) + Destroy() error +} diff --git a/wechaty-puppet/memory-card/storage/file.go b/wechaty-puppet/memory-card/storage/file.go new file mode 100644 index 0000000..373186c --- /dev/null +++ b/wechaty-puppet/memory-card/storage/file.go @@ -0,0 +1,76 @@ +package storage + +import ( + "encoding/json" + "io/ioutil" + "os" + "path/filepath" + "strings" +) + +type FileStorage struct { + absFileName string +} + +func NewFileStorage(absFileName string) (*FileStorage, error) { + absFileName, err := handleAbsFileName(absFileName) + if err != nil { + return nil, err + } + return &FileStorage{absFileName: absFileName}, nil +} + +func (f *FileStorage) Save(payload map[string]interface{}) error { + jsonBytes, err := json.Marshal(payload) + if err != nil { + return err + } + return ioutil.WriteFile(f.absFileName, jsonBytes, os.ModePerm) +} + +func (f *FileStorage) Load() (map[string]interface{}, error) { + if !exists(f.absFileName) { + return map[string]interface{}{}, nil + } + file, err := os.Open(f.absFileName) + if err != nil { + return nil, err + } + result := map[string]interface{}{} + decoder := json.NewDecoder(file) + decoder.UseNumber() + if err := decoder.Decode(&result); err != nil { + return nil, err + } + return result, nil +} + +func (f *FileStorage) Destroy() error { + return os.Remove(f.absFileName) +} + +func handleAbsFileName(absFileName string) (string, error) { + const suffix = ".memory-card.json" + if !strings.HasSuffix(absFileName, suffix) { + absFileName = absFileName + suffix + } + if !filepath.IsAbs(absFileName) { + dir, err := os.Getwd() + if err != nil { + return "", err + } + absFileName = filepath.Join(dir, absFileName) + } + return absFileName, nil +} + +func exists(path string) bool { + _, err := os.Stat(path) //os.Stat获取文件信息 + if err != nil { + if os.IsExist(err) { + return true + } + return false + } + return true +} diff --git a/wechaty-puppet/memory-card/storage/file_test.go b/wechaty-puppet/memory-card/storage/file_test.go new file mode 100644 index 0000000..bd9502a --- /dev/null +++ b/wechaty-puppet/memory-card/storage/file_test.go @@ -0,0 +1,47 @@ +package storage + +import ( + "log" + "reflect" + "testing" +) + +var data = map[string]interface{}{ + "key1": "key1", + "key2": "key2", +} + +func TestFileStorage_Save(t *testing.T) { + storage := newFileStorage(t) + err := storage.Save(data) + if err != nil { + t.Fatalf(err.Error()) + } +} + +func TestFileStorage_Load(t *testing.T) { + storage := newFileStorage(t) + got, err := storage.Load() + if err != nil { + log.Fatalf(err.Error()) + } + if !reflect.DeepEqual(got, data) { + log.Fatalf("got %v expect %v", got, data) + } +} + +func TestNopStorage_Destroy(t *testing.T) { + storage := newFileStorage(t) + err := storage.Destroy() + if err != nil { + t.Fatalf(err.Error()) + } +} + +func newFileStorage(t *testing.T) *FileStorage { + storage, err := NewFileStorage("testdata/file") + if err != nil { + t.Fatalf(err.Error()) + } + return storage +} diff --git a/wechaty-puppet/memory-card/storage/nop.go b/wechaty-puppet/memory-card/storage/nop.go new file mode 100644 index 0000000..c08c973 --- /dev/null +++ b/wechaty-puppet/memory-card/storage/nop.go @@ -0,0 +1,16 @@ +package storage + +type NopStorage struct { +} + +func (n NopStorage) Save(payload map[string]interface{}) error { + return nil +} + +func (n NopStorage) Load() (map[string]interface{}, error) { + return map[string]interface{}{}, nil +} + +func (n NopStorage) Destroy() { + return +} diff --git a/wechaty-puppet/memory-card/storage/testdata/.gitignore b/wechaty-puppet/memory-card/storage/testdata/.gitignore new file mode 100644 index 0000000..202218d --- /dev/null +++ b/wechaty-puppet/memory-card/storage/testdata/.gitignore @@ -0,0 +1 @@ +file.memory-card.json diff --git a/wechaty-puppet/memory-card/testdata/.gitignore b/wechaty-puppet/memory-card/testdata/.gitignore new file mode 100644 index 0000000..202218d --- /dev/null +++ b/wechaty-puppet/memory-card/testdata/.gitignore @@ -0,0 +1 @@ +file.memory-card.json From b43f97b89e3ef0c9bb703c06c72b835315d3ecb8 Mon Sep 17 00:00:00 2001 From: dchaofei Date: Tue, 7 Apr 2020 13:05:19 +0800 Subject: [PATCH 2/8] Add Event --- examples/ding-dong-bot.go | 25 +++-- go.mod | 3 +- wechaty-puppet/schemas/puppet.go | 24 +++++ wechaty/accessory.go | 10 +- wechaty/impl/event.go | 25 +++++ wechaty/impl/wechaty.go | 152 +++++++++++++++++++++++++++++++ wechaty/impl/wechaty_test.go | 50 ++++++++++ wechaty/user/contact.go | 8 +- wechaty/wechaty.go | 54 +---------- wechaty/wechaty_test.go | 22 ----- 10 files changed, 278 insertions(+), 95 deletions(-) create mode 100644 wechaty/impl/event.go create mode 100644 wechaty/impl/wechaty.go create mode 100644 wechaty/impl/wechaty_test.go delete mode 100644 wechaty/wechaty_test.go diff --git a/examples/ding-dong-bot.go b/examples/ding-dong-bot.go index cd0c778..ab49eea 100644 --- a/examples/ding-dong-bot.go +++ b/examples/ding-dong-bot.go @@ -1,17 +1,22 @@ package main import ( - "fmt" - - "github.com/wechaty/go-wechaty/wechaty" + "fmt" + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty/impl" + "github.com/wechaty/go-wechaty/wechaty/user" ) func main() { - _ = wechaty.NewWechaty(). - OnScan(func(qrCode, status string) { - fmt.Printf("Scan QR Code to login: %s\nhttps://api.qrserver.com/v1/create-qr-code/?data=%s\n", status, qrCode) - }). - OnLogin(func(user string) { fmt.Printf("User %s logined\n", user) }). - OnMessage(func(message string) { fmt.Printf("Message: %s\n", message) }). - Start() + _ = impl.NewWechaty(). + OnScan(func(qrCode string, status schemas.ScanStatus, data string) { + fmt.Printf("Scan QR Code to login: %v\nhttps://api.qrserver.com/v1/create-qr-code/?data=%s\n", status, qrCode) + }). + OnLogin(func(user string) { + fmt.Printf("User %s logined\n", user) + }). + OnMessage(func(message user.Message) { + fmt.Println(fmt.Printf("Message: %v\n", message)) + }). + Start() } diff --git a/go.mod b/go.mod index e092880..8062765 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,5 @@ go 1.14 require ( github.com/hashicorp/golang-lru v0.5.4 github.com/otiai10/opengraph v1.1.1 - golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect - golang.org/x/tools v0.0.0-20200402223321-bcf690261a44 // indirect + golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect ) diff --git a/wechaty-puppet/schemas/puppet.go b/wechaty-puppet/schemas/puppet.go index 7a11956..f6bf606 100644 --- a/wechaty-puppet/schemas/puppet.go +++ b/wechaty-puppet/schemas/puppet.go @@ -5,3 +5,27 @@ type PuppetOptions struct { timeout int64 token string } + +type PuppetEventName int + +const ( + PuppetEventNameUnknown PuppetEventName = iota + PuppetEventNameFriendShip + PuppetEventNameLogin + PuppetEventNameLogout + PuppetEventNameMessage + PuppetEventNameRoomInvite + PuppetEventNameRoomJoin + PuppetEventNameRoomLeave + PuppetEventNameRoomTopic + PuppetEventNameScan + + PuppetEventNameDong + PuppetEventNameError + PuppetEventNameHeartbeat + PuppetEventNameReady + PuppetEventNameReset + + PuppetEventNameStop + PuppetEventNameStart +) diff --git a/wechaty/accessory.go b/wechaty/accessory.go index 791a04f..5f6a2f5 100644 --- a/wechaty/accessory.go +++ b/wechaty/accessory.go @@ -1,14 +1,14 @@ package wechaty import ( - wechatyPuppet "github.com/wechaty/go-wechaty/wechaty-puppet" + wechatyPuppet "github.com/wechaty/go-wechaty/wechaty-puppet" ) // Accessory accessory interface type Accessory interface { - SetPuppet(puppet wechatyPuppet.Puppet) - GetPuppet() *wechatyPuppet.Puppet + SetPuppet(puppet wechatyPuppet.Puppet) + GetPuppet() *wechatyPuppet.Puppet - SetWechaty(wechaty Wechaty) - GetWechaty() *Wechaty + SetWechaty(wechaty Wechaty) + GetWechaty() Wechaty } diff --git a/wechaty/impl/event.go b/wechaty/impl/event.go new file mode 100644 index 0000000..b95eb21 --- /dev/null +++ b/wechaty/impl/event.go @@ -0,0 +1,25 @@ +package impl + +import ( + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty/user" + "time" +) + +type ( + EventDong func(data string) + EventError func(err error) + EventFriendship func(friendship string) // TODO(dchaofei): Friendship struct + EventHeartbeat func(data string) + EventLogin func(user string) // TODO(dchaofei): ContactSelf struct + EventLogout func(user string, reason string) // TODO(dchaofei): ContactSelf struct + EventMessage func(message user.Message) + EventReady func() + EventRoomInvite func(roomInvitation string) // TODO(dchaofei): RoomInvitation struct + EventRoomJoin func(room user.Room, inviteeList []user.Contact, inviter user.Contact, date time.Time) + EventRoomLeave func(room user.Room, leaverList []user.Contact, remover user.Contact, date time.Time) + EventRoomTopic func(room user.Room, newTopic string, oldTopic string, changer user.Contact, date time.Time) + EventScan func(qrCode string, status schemas.ScanStatus, data string) + EventStart func() + EventStop func() +) diff --git a/wechaty/impl/wechaty.go b/wechaty/impl/wechaty.go new file mode 100644 index 0000000..369f826 --- /dev/null +++ b/wechaty/impl/wechaty.go @@ -0,0 +1,152 @@ +/** + * Go Wechaty - https://github.com/wechaty/go-wechaty + * + * Authors: Huan LI (李卓桓) + * Xiaoyu DING (丁小雨) + * + * 2020-now @ Copyright Wechaty + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package wechaty ... +package impl + +import ( + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "reflect" +) + +// Wechaty ... +type Wechaty struct { + eventMap map[schemas.PuppetEventName]interface{} +} + +// NewWechaty ... +// instance by golang. +func NewWechaty() *Wechaty { + return &Wechaty{ + eventMap: map[schemas.PuppetEventName]interface{}{}, + } +} + +func (w *Wechaty) registerEvent(name schemas.PuppetEventName, event interface{}) { + w.eventMap[name] = event +} + +// OnScan ... +func (w *Wechaty) OnScan(f EventScan) *Wechaty { + w.registerEvent(schemas.PuppetEventNameScan, f) + return w +} + +// OnLogin ... +func (w *Wechaty) OnLogin(f EventLogin) *Wechaty { + w.registerEvent(schemas.PuppetEventNameLogin, f) + return w +} + +// OnMessage ... +func (w *Wechaty) OnMessage(f EventMessage) *Wechaty { + w.registerEvent(schemas.PuppetEventNameMessage, f) + return w +} + +// OnDong ... +func (w *Wechaty) OnDong(f EventDong) *Wechaty { + w.registerEvent(schemas.PuppetEventNameDong, f) + return w +} + +// OnError ... +func (w *Wechaty) OnError(f EventError) *Wechaty { + w.registerEvent(schemas.PuppetEventNameError, f) + return w +} + +// OnFriendship ... +func (w *Wechaty) OnFriendship(f EventFriendship) *Wechaty { + w.registerEvent(schemas.PuppetEventNameFriendShip, f) + return w +} + +// OnHeartbeat ... +func (w *Wechaty) OnHeartbeat(f EventHeartbeat) *Wechaty { + w.registerEvent(schemas.PuppetEventNameHeartbeat, f) + return w +} + +// OnLogout ... +func (w *Wechaty) OnLogout(f EventLogout) *Wechaty { + w.registerEvent(schemas.PuppetEventNameLogout, f) + return w +} + +// OnReady ... +func (w *Wechaty) OnReady(f EventReady) *Wechaty { + w.registerEvent(schemas.PuppetEventNameReady, f) + return w +} + +// OnRoomInvite ... +func (w *Wechaty) OnRoomInvite(f EventRoomInvite) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomInvite, f) + return w +} + +// OnRoomJoin ... +func (w *Wechaty) OnRoomJoin(f EventRoomJoin) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomJoin, f) + return w +} + +// OnRoomLeave ... +func (w *Wechaty) OnRoomLeave(f EventRoomLeave) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomLeave, f) + return w +} + +// OnRoomTopic ... +func (w *Wechaty) OnRoomTopic(f EventRoomTopic) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomTopic, f) + return w +} + +// OnStart ... +func (w *Wechaty) OnStart(f EventStart) *Wechaty { + w.registerEvent(schemas.PuppetEventNameStart, f) + return w +} + +// OnStop ... +func (w *Wechaty) OnStop(f EventStop) *Wechaty { + w.registerEvent(schemas.PuppetEventNameStop, f) + return w +} + +// Emit ... +func (w *Wechaty) Emit(name schemas.PuppetEventName, data ...interface{}) { + f, ok := w.eventMap[name] + if ok { + values := make([]reflect.Value, 0, len(data)) + for _, v := range data { + values = append(values, reflect.ValueOf(v)) + } + _ = reflect.ValueOf(f).Call(values) + } +} + +// Start ... +func (w *Wechaty) Start() *Wechaty { + return w +} diff --git a/wechaty/impl/wechaty_test.go b/wechaty/impl/wechaty_test.go new file mode 100644 index 0000000..75fa040 --- /dev/null +++ b/wechaty/impl/wechaty_test.go @@ -0,0 +1,50 @@ +package impl + +import ( + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "log" + "reflect" + "testing" +) + +func TestNewWechaty(t *testing.T) { + tests := []struct { + name string + want *Wechaty + }{ + {name: "new", want: NewWechaty()}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NewWechaty(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewWechaty() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestWechaty_Emit(t *testing.T) { + wechaty := NewWechaty() + got := "" + expect := "test" + wechaty.OnHeartbeat(func(data string) { + got = data + }) + wechaty.Emit(schemas.PuppetEventNameHeartbeat, expect) + if got != expect { + log.Fatalf("got %s expect %s", got, expect) + } +} + +func TestWechaty_On(t *testing.T) { + wechaty := NewWechaty() + got := "" + expect := "ding" + wechaty.OnDong(func(data string) { + got = data + }) + wechaty.Emit(schemas.PuppetEventNameDong, expect) + if got != expect { + log.Fatalf("got %s expect %s", got, expect) + } +} diff --git a/wechaty/user/contact.go b/wechaty/user/contact.go index a651dc1..0c7f04c 100644 --- a/wechaty/user/contact.go +++ b/wechaty/user/contact.go @@ -24,15 +24,15 @@ package user import "github.com/wechaty/go-wechaty/wechaty" type Contact struct { - wechaty.Accessory + wechaty.Accessory - Id string + Id string } func (r *Contact) Load(id string) Contact { - return Contact{} + return Contact{} } func (r *Contact) Ready(forceSync bool) bool { - return true + return true } diff --git a/wechaty/wechaty.go b/wechaty/wechaty.go index cf98f4a..2ada625 100644 --- a/wechaty/wechaty.go +++ b/wechaty/wechaty.go @@ -1,55 +1,5 @@ -/** - * Go Wechaty - https://github.com/wechaty/go-wechaty - * - * Authors: Huan LI (李卓桓) - * Xiaoyu DING (丁小雨) - * - * 2020-now @ Copyright Wechaty - * - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Package wechaty ... package wechaty -// Wechaty ... -type Wechaty struct { -} - -// NewWechaty ... -// instance by golang. -func NewWechaty() *Wechaty { - return &Wechaty{} -} - -// OnScan ... -func (w *Wechaty) OnScan(f func(qrCode, status string)) *Wechaty { - return w -} - -// OnLogin ... -// todo:: fake code. user should be struct -func (w *Wechaty) OnLogin(func(user string)) *Wechaty { - return w -} - -// OnMessage ... -// todo:: fake code. message should be struct -func (w *Wechaty) OnMessage(func(message string)) *Wechaty { - return w -} - -// Start ... -func (w *Wechaty) Start() *Wechaty { - return w +// Wechaty interface +type Wechaty interface { } diff --git a/wechaty/wechaty_test.go b/wechaty/wechaty_test.go deleted file mode 100644 index 70f3d90..0000000 --- a/wechaty/wechaty_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package wechaty - -import ( - "reflect" - "testing" -) - -func TestNewWechaty(t *testing.T) { - tests := []struct { - name string - want *Wechaty - }{ - {name: "new", want: NewWechaty()}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := NewWechaty(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewWechaty() = %v, want %v", got, tt.want) - } - }) - } -} From 1967ee23d67fec6e8a86084effe004ff5f94f1ef Mon Sep 17 00:00:00 2001 From: dchaofei Date: Tue, 7 Apr 2020 17:11:24 +0800 Subject: [PATCH 3/8] Add friendship --- examples/ding-dong-bot.go | 2 +- go.mod | 2 + wechaty-puppet/helper-functions/try_wait.go | 18 +++ wechaty-puppet/puppet.go | 110 ++++++++++++------ wechaty-puppet/schemas/events.go | 2 +- wechaty-puppet/schemas/friendship.go | 1 + .../schemas/friendshiptype_string.go | 26 +++++ wechaty-puppet/schemas/puppet.go | 6 +- wechaty/impl/event.go | 32 ++--- wechaty/impl/wechaty.go | 18 ++- wechaty/user/contact.go | 15 ++- wechaty/user/friendship.go | 30 +++++ wechaty/user/friendship_receive.go | 35 ++++++ 13 files changed, 237 insertions(+), 60 deletions(-) create mode 100644 wechaty-puppet/helper-functions/try_wait.go create mode 100644 wechaty-puppet/schemas/friendshiptype_string.go create mode 100644 wechaty/user/friendship.go create mode 100644 wechaty/user/friendship_receive.go diff --git a/examples/ding-dong-bot.go b/examples/ding-dong-bot.go index ab49eea..df3eba8 100644 --- a/examples/ding-dong-bot.go +++ b/examples/ding-dong-bot.go @@ -15,7 +15,7 @@ func main() { OnLogin(func(user string) { fmt.Printf("User %s logined\n", user) }). - OnMessage(func(message user.Message) { + OnMessage(func(message *user.Message) { fmt.Println(fmt.Printf("Message: %v\n", message)) }). Start() diff --git a/go.mod b/go.mod index 8062765..ce82749 100644 --- a/go.mod +++ b/go.mod @@ -6,4 +6,6 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/otiai10/opengraph v1.1.1 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect + k8s.io/apimachinery v0.18.0 + k8s.io/client-go v11.0.0+incompatible ) diff --git a/wechaty-puppet/helper-functions/try_wait.go b/wechaty-puppet/helper-functions/try_wait.go new file mode 100644 index 0000000..e860dc9 --- /dev/null +++ b/wechaty-puppet/helper-functions/try_wait.go @@ -0,0 +1,18 @@ +package helper_functions + +import ( + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" + "time" +) + +var defaultRetry = wait.Backoff{ + Steps: 9, + Duration: 20 * time.Millisecond, + Factor: 3.0, + Jitter: 0.1, +} + +func TryWait(f func() error) error { + return retry.RetryOnConflict(defaultRetry, f) +} diff --git a/wechaty-puppet/puppet.go b/wechaty-puppet/puppet.go index e30833d..69ec86e 100644 --- a/wechaty-puppet/puppet.go +++ b/wechaty-puppet/puppet.go @@ -1,63 +1,101 @@ package wechatypuppet import ( - lru "github.com/hashicorp/golang-lru" + lru "github.com/hashicorp/golang-lru" - "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" ) -// PuppetInterface puppet interface -type PuppetInterface interface { - MessageImage(messageID string, imageType schemas.ImageType) FileBox +// puppetInterface puppet interface +type puppetInterface interface { + MessageImage(messageID string, imageType schemas.ImageType) FileBox + FriendshipPayloadReceive(friendshipID string) schemas.FriendshipPayloadReceive + FriendshipPayloadConfirm(friendshipID string) schemas.FriendshipPayloadConfirm + FriendshipPayloadVerify(friendshipID string) schemas.FriendshipPayloadVerify + FriendshipAccept(friendshipID string) } -// Puppet puppet struce +// Puppet puppet struct type Puppet struct { - PuppetInterface + puppetInterface - CacheMessagePayload *lru.Cache + CacheMessagePayload *lru.Cache + CacheFriendshipPayload *lru.Cache } // MessageList message list func (p *Puppet) MessageList() (ks []string) { - keys := p.CacheMessagePayload.Keys() - for _, v := range keys { - if k, ok := v.(string); ok { - ks = append(ks, k) - } - } - return + keys := p.CacheMessagePayload.Keys() + for _, v := range keys { + if k, ok := v.(string); ok { + ks = append(ks, k) + } + } + return } // MessageSearch search message func (p *Puppet) MessageSearch(query schemas.MessageUserQueryFilter) []string { - allMessageIDList := p.MessageList() - if len(allMessageIDList) <= 0 { - return allMessageIDList - } - - // load - var messagePayloadList []schemas.MessagePayload - for _, v := range allMessageIDList { - messagePayloadList = append(messagePayloadList, p.MessagePayload(v)) - } - // Filter todo:: messageQueryFilterFactory - var messageIDList []string - for _, message := range messagePayloadList { - if message.FromId == query.FromId || message.RoomId == query.RoomId || message.ToId == query.ToId { - messageIDList = append(messageIDList, message.Id) - } - } - - return messageIDList + allMessageIDList := p.MessageList() + if len(allMessageIDList) <= 0 { + return allMessageIDList + } + + // load + var messagePayloadList []schemas.MessagePayload + for _, v := range allMessageIDList { + messagePayloadList = append(messagePayloadList, p.MessagePayload(v)) + } + // Filter todo:: messageQueryFilterFactory + var messageIDList []string + for _, message := range messagePayloadList { + if message.FromId == query.FromId || message.RoomId == query.RoomId || message.ToId == query.ToId { + messageIDList = append(messageIDList, message.Id) + } + } + + return messageIDList } // messageQueryFilterFactory 实现正则和直接匹配 func (p *Puppet) messageQueryFilterFactory(query string) schemas.MessagePayloadFilterFunction { - return nil + return nil } // MessagePayload message payload todo:: no finish func (p *Puppet) MessagePayload(messageID string) (payload schemas.MessagePayload) { - return payload + return payload +} + +// FriendshipPayloadReceive ... +func (p *Puppet) FriendshipPayloadReceive(friendshipID string) schemas.FriendshipPayloadReceive { + cachePayload, ok := p.CacheFriendshipPayload.Get(friendshipID) + if ok { + return cachePayload.(schemas.FriendshipPayloadReceive) + } + payload := p.puppetInterface.FriendshipPayloadReceive(friendshipID) + p.CacheFriendshipPayload.Add(friendshipID, payload) + return payload +} + +// FriendshipPayloadConfirm ... +func (p *Puppet) FriendshipPayloadConfirm(friendshipID string) schemas.FriendshipPayloadConfirm { + cachePayload, ok := p.CacheFriendshipPayload.Get(friendshipID) + if ok { + return cachePayload.(schemas.FriendshipPayloadConfirm) + } + payload := p.puppetInterface.FriendshipPayloadConfirm(friendshipID) + p.CacheFriendshipPayload.Add(friendshipID, payload) + return payload +} + +// FriendshipPayloadVerify ... +func (p *Puppet) FriendshipPayloadVerify(friendshipID string) schemas.FriendshipPayloadVerify { + cachePayload, ok := p.CacheFriendshipPayload.Get(friendshipID) + if ok { + return cachePayload.(schemas.FriendshipPayloadVerify) + } + payload := p.puppetInterface.FriendshipPayloadVerify(friendshipID) + p.CacheFriendshipPayload.Add(friendshipID, payload) + return payload } diff --git a/wechaty-puppet/schemas/events.go b/wechaty-puppet/schemas/events.go index 5b965d2..a43a513 100644 --- a/wechaty-puppet/schemas/events.go +++ b/wechaty-puppet/schemas/events.go @@ -12,7 +12,7 @@ const ( ) type EventFriendshipPayload struct { - FriendshipId string + friendshipID string } type EventLoginPayload struct { diff --git a/wechaty-puppet/schemas/friendship.go b/wechaty-puppet/schemas/friendship.go index af9195b..c0299f5 100644 --- a/wechaty-puppet/schemas/friendship.go +++ b/wechaty-puppet/schemas/friendship.go @@ -1,5 +1,6 @@ package schemas +//go:generate stringer -type=FriendshipType type FriendshipType uint8 const ( diff --git a/wechaty-puppet/schemas/friendshiptype_string.go b/wechaty-puppet/schemas/friendshiptype_string.go new file mode 100644 index 0000000..f95ce23 --- /dev/null +++ b/wechaty-puppet/schemas/friendshiptype_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=FriendshipType"; DO NOT EDIT. + +package schemas + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[FriendshipTypeUnknown-0] + _ = x[FriendshipTypeConfirm-1] + _ = x[FriendshipTypeReceive-2] + _ = x[FriendshipTypeVerify-3] +} + +const _FriendshipType_name = "FriendshipTypeUnknownFriendshipTypeConfirmFriendshipTypeReceiveFriendshipTypeVerify" + +var _FriendshipType_index = [...]uint8{0, 21, 42, 63, 83} + +func (i FriendshipType) String() string { + if i >= FriendshipType(len(_FriendshipType_index)-1) { + return "FriendshipType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _FriendshipType_name[_FriendshipType_index[i]:_FriendshipType_index[i+1]] +} diff --git a/wechaty-puppet/schemas/puppet.go b/wechaty-puppet/schemas/puppet.go index f6bf606..5d28bb4 100644 --- a/wechaty-puppet/schemas/puppet.go +++ b/wechaty-puppet/schemas/puppet.go @@ -6,11 +6,13 @@ type PuppetOptions struct { token string } -type PuppetEventName int +type PuppetEventName uint8 const ( PuppetEventNameUnknown PuppetEventName = iota - PuppetEventNameFriendShip + PuppetEventNameFriendShipConfirm + PuppetEventNameFriendShipReceive + PuppetEventNameFriendShipVerify PuppetEventNameLogin PuppetEventNameLogout PuppetEventNameMessage diff --git a/wechaty/impl/event.go b/wechaty/impl/event.go index b95eb21..4a1723b 100644 --- a/wechaty/impl/event.go +++ b/wechaty/impl/event.go @@ -7,19 +7,21 @@ import ( ) type ( - EventDong func(data string) - EventError func(err error) - EventFriendship func(friendship string) // TODO(dchaofei): Friendship struct - EventHeartbeat func(data string) - EventLogin func(user string) // TODO(dchaofei): ContactSelf struct - EventLogout func(user string, reason string) // TODO(dchaofei): ContactSelf struct - EventMessage func(message user.Message) - EventReady func() - EventRoomInvite func(roomInvitation string) // TODO(dchaofei): RoomInvitation struct - EventRoomJoin func(room user.Room, inviteeList []user.Contact, inviter user.Contact, date time.Time) - EventRoomLeave func(room user.Room, leaverList []user.Contact, remover user.Contact, date time.Time) - EventRoomTopic func(room user.Room, newTopic string, oldTopic string, changer user.Contact, date time.Time) - EventScan func(qrCode string, status schemas.ScanStatus, data string) - EventStart func() - EventStop func() + EventDong func(data string) + EventError func(err error) + EventFriendshipConfirm func(friendship *user.Friendship) + EventFriendshipVerify func(friendship *user.Friendship) + EventFriendshipReceive func(friendshipReceive *user.FriendshipReceive) + EventHeartbeat func(data string) + EventLogin func(user string) // TODO(dchaofei): ContactSelf struct + EventLogout func(user string, reason string) // TODO(dchaofei): ContactSelf struct + EventMessage func(message *user.Message) + EventReady func() + EventRoomInvite func(roomInvitation string) // TODO(dchaofei): RoomInvitation struct + EventRoomJoin func(room *user.Room, inviteeList []*user.Contact, inviter *user.Contact, date time.Time) + EventRoomLeave func(room *user.Room, leaverList []*user.Contact, remover *user.Contact, date time.Time) + EventRoomTopic func(room *user.Room, newTopic string, oldTopic string, changer *user.Contact, date time.Time) + EventScan func(qrCode string, status schemas.ScanStatus, data string) + EventStart func() + EventStop func() ) diff --git a/wechaty/impl/wechaty.go b/wechaty/impl/wechaty.go index 369f826..5ac2f61 100644 --- a/wechaty/impl/wechaty.go +++ b/wechaty/impl/wechaty.go @@ -74,9 +74,21 @@ func (w *Wechaty) OnError(f EventError) *Wechaty { return w } -// OnFriendship ... -func (w *Wechaty) OnFriendship(f EventFriendship) *Wechaty { - w.registerEvent(schemas.PuppetEventNameFriendShip, f) +// OnFriendshipConfirm ... +func (w *Wechaty) OnFriendshipConfirm(f EventFriendshipConfirm) *Wechaty { + w.registerEvent(schemas.PuppetEventNameFriendShipConfirm, f) + return w +} + +// OnFriendshipVerify ... +func (w *Wechaty) OnFriendshipVerify(f EventFriendshipVerify) *Wechaty { + w.registerEvent(schemas.PuppetEventNameFriendShipVerify, f) + return w +} + +// OnFriendshipReceive ... +func (w *Wechaty) OnFriendshipReceive(f EventFriendshipReceive) *Wechaty { + w.registerEvent(schemas.PuppetEventNameFriendShipReceive, f) return w } diff --git a/wechaty/user/contact.go b/wechaty/user/contact.go index 0c7f04c..00da082 100644 --- a/wechaty/user/contact.go +++ b/wechaty/user/contact.go @@ -29,10 +29,21 @@ type Contact struct { Id string } -func (r *Contact) Load(id string) Contact { - return Contact{} +func NewContact(accessory wechaty.Accessory, id string) *Contact { + return &Contact{ + Accessory: accessory, + Id: id, + } } func (r *Contact) Ready(forceSync bool) bool { return true } + +func (r *Contact) isReady() bool { + return true +} + +func (r *Contact) Sync() { + r.Ready(true) +} diff --git a/wechaty/user/friendship.go b/wechaty/user/friendship.go new file mode 100644 index 0000000..e890fe1 --- /dev/null +++ b/wechaty/user/friendship.go @@ -0,0 +1,30 @@ +package user + +import ( + wechaty "github.com/wechaty/go-wechaty/wechaty" + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" +) + +type friendship interface { + Contact() *Contact +} + +type Friendship struct { + wechaty.Accessory + friendshipPayloadBase schemas.FriendshipPayloadBase +} + +func NewFriendship(accessory wechaty.Accessory, friendshipPayloadBase schemas.FriendshipPayloadBase) *Friendship { + return &Friendship{ + Accessory: accessory, + friendshipPayloadBase: friendshipPayloadBase, + } +} + +func (f *Friendship) Contact() *Contact { + return NewContact(f.Accessory, f.friendshipPayloadBase.ContactId) +} + +func (f *Friendship) Hello() string { + return f.friendshipPayloadBase.Hello +} diff --git a/wechaty/user/friendship_receive.go b/wechaty/user/friendship_receive.go new file mode 100644 index 0000000..686cff1 --- /dev/null +++ b/wechaty/user/friendship_receive.go @@ -0,0 +1,35 @@ +package user + +import ( + "errors" + "github.com/wechaty/go-wechaty/wechaty" + helper_functions "github.com/wechaty/go-wechaty/wechaty-puppet/helper-functions" + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" +) + +type FriendshipReceive struct { + wechaty.Accessory + payload *schemas.FriendshipPayloadReceive + *Friendship +} + +func NewFriendshipReceive(accessory wechaty.Accessory, payload *schemas.FriendshipPayloadReceive) *FriendshipReceive { + return &FriendshipReceive{ + Accessory: accessory, + payload: payload, + Friendship: NewFriendship(accessory, payload.FriendshipPayloadBase), + } +} + +func (f *FriendshipReceive) Accept() { + f.GetPuppet().FriendshipAccept(f.payload.Id) + contact := f.Contact() + _ = helper_functions.TryWait(func() error { + contact.Ready(false) + if contact.isReady() { + return nil + } + return errors.New("friendshipReceive.accept() contact.ready() not ready") + }) + contact.Sync() +} From 780eb2f1b4748aa99257dfb74ee5e3a022ab7e40 Mon Sep 17 00:00:00 2001 From: dchaofei Date: Thu, 9 Apr 2020 11:54:27 +0800 Subject: [PATCH 4/8] file-box impl --- go.mod | 5 + wechaty-puppet/file-box/file_box.go | 159 ++++++++++++++++++ wechaty-puppet/file-box/file_box_base64.go | 27 +++ wechaty-puppet/file-box/file_box_qrcode.go | 27 +++ wechaty-puppet/file-box/file_box_test.go | 92 ++++++++++ wechaty-puppet/file-box/file_box_url.go | 36 ++++ wechaty-puppet/file-box/testdata/.gitignore | 1 + wechaty-puppet/file-box/type.go | 38 +++++ wechaty-puppet/file_box.go | 20 --- wechaty-puppet/helper-functions/file.go | 14 ++ .../helper-functions/http_client.go | 13 ++ wechaty-puppet/memory-card/storage/file.go | 14 +- wechaty-puppet/puppet.go | 3 +- wechaty/user/image.go | 12 +- 14 files changed, 422 insertions(+), 39 deletions(-) create mode 100644 wechaty-puppet/file-box/file_box.go create mode 100644 wechaty-puppet/file-box/file_box_base64.go create mode 100644 wechaty-puppet/file-box/file_box_qrcode.go create mode 100644 wechaty-puppet/file-box/file_box_test.go create mode 100644 wechaty-puppet/file-box/file_box_url.go create mode 100644 wechaty-puppet/file-box/testdata/.gitignore create mode 100644 wechaty-puppet/file-box/type.go delete mode 100644 wechaty-puppet/file_box.go create mode 100644 wechaty-puppet/helper-functions/file.go create mode 100644 wechaty-puppet/helper-functions/http_client.go diff --git a/go.mod b/go.mod index ce82749..8a858c8 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,13 @@ module github.com/wechaty/go-wechaty go 1.14 require ( + github.com/bitly/go-simplejson v0.5.0 + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/hashicorp/golang-lru v0.5.4 github.com/otiai10/opengraph v1.1.1 + github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 + github.com/tuotoo/qrcode v0.0.0-20190222102259-ac9c44189bf2 + github.com/willf/bitset v1.1.10 // indirect golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect k8s.io/apimachinery v0.18.0 k8s.io/client-go v11.0.0+incompatible diff --git a/wechaty-puppet/file-box/file_box.go b/wechaty-puppet/file-box/file_box.go new file mode 100644 index 0000000..70f5839 --- /dev/null +++ b/wechaty-puppet/file-box/file_box.go @@ -0,0 +1,159 @@ +package file_box + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "github.com/bitly/go-simplejson" + "github.com/tuotoo/qrcode" + helper_functions "github.com/wechaty/go-wechaty/wechaty-puppet/helper-functions" + "io/ioutil" + "mime" + "os" + "path/filepath" +) + +type fileImplInterface interface { + toJSONMap() map[string]interface{} + toBytes() ([]byte, error) +} + +// FileBox struct +type FileBox struct { + fileImpl fileImplInterface + name string + metadata map[string]interface{} + boxType FileBoxType + fileBytes []byte + mimeType string +} + +func newFileBox(common *FileBoxJsonObjectCommon, fileImpl fileImplInterface) *FileBox { + return &FileBox{ + fileImpl: fileImpl, + name: common.Name, + metadata: common.Metadata, + boxType: common.BoxType, + mimeType: mime.TypeByExtension(filepath.Ext(common.Name)), + } +} + +func NewFileBoxFromJSONString(s string) (*FileBox, error) { + newJson, err := simplejson.NewJson([]byte(s)) + if err != nil { + return nil, err + } + boxType, err := newJson.Get("boxType").Int64() + if err != nil { + return nil, err + } + switch boxType { + case FileBoxTypeBase64: + fileBoxStruct := new(FileBoxJsonObjectBase64) + if err := json.Unmarshal([]byte(s), fileBoxStruct); err != nil { + return nil, err + } + return NewFileBoxFromJSONObjectBase64(fileBoxStruct), nil + case FileBoxTypeQRCode: + fileBoxStruct := new(FileBoxJsonObjectQRCode) + if err := json.Unmarshal([]byte(s), fileBoxStruct); err != nil { + return nil, err + } + return NewFileBoxFromJSONObjectQRCode(fileBoxStruct), nil + case FileBoxTypeUrl: + fileBoxStruct := new(FileBoxJsonObjectUrl) + if err := json.Unmarshal([]byte(s), fileBoxStruct); err != nil { + return nil, err + } + return NewFileBoxFromJSONObjectUrl(fileBoxStruct), nil + default: + return nil, errors.New("invalid value boxType") + } +} + +func NewFileBoxFromJSONObjectBase64(data *FileBoxJsonObjectBase64) *FileBox { + return newFileBox(data.FileBoxJsonObjectCommon, newFileBoxBase64(data.Base64)) +} + +func NewFileBoxFromJSONObjectUrl(data *FileBoxJsonObjectUrl) *FileBox { + return newFileBox(data.FileBoxJsonObjectCommon, NewFileBoxUrl(data.RemoteUrl, data.Headers)) +} + +func NewFileBoxFromJSONObjectQRCode(data *FileBoxJsonObjectQRCode) *FileBox { + return newFileBox(data.FileBoxJsonObjectCommon, NewFileBoxQRCode(data.QrCode)) +} + +func (fb *FileBox) ToJSONString() (string, error) { + jsonMap := map[string]interface{}{ + "name": fb.name, + "metadata": fb.metadata, + "boxType": fb.boxType, + } + implJsonMap := fb.fileImpl.toJSONMap() + for k, v := range implJsonMap { + jsonMap[k] = v + } + marshal, err := json.Marshal(jsonMap) + return string(marshal), err +} + +func (fb *FileBox) ToFile(filePath string, overwrite bool) error { + if filePath == "" { + filePath = fb.name + } + path, err := os.Getwd() + if err != nil { + return err + } + fullPath := filepath.Join(path, filePath) + if !overwrite && helper_functions.FileExists(fullPath) { + return os.ErrExist + } + fileBytes, err := fb.ToBytes() + if err != nil { + return err + } + return ioutil.WriteFile(filePath, fileBytes, os.ModePerm) +} + +func (fb *FileBox) ToBytes() ([]byte, error) { + if fb.fileBytes != nil { + return fb.fileBytes, nil + } + toBytes, err := fb.fileImpl.toBytes() + if err != nil { + return nil, err + } + fb.fileBytes = toBytes + return fb.fileBytes, nil +} + +func (fb *FileBox) ToBase64() (string, error) { + fileBytes, err := fb.ToBytes() + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(fileBytes), nil +} + +func (fb *FileBox) ToDataURL() (string, error) { + toBase64, err := fb.ToBase64() + if err != nil { + return "", nil + } + return fmt.Sprintf("data:%s;base64,%s", fb.mimeType, toBase64), nil +} + +func (fb *FileBox) ToQrCode() (string, error) { + fileBytes, err := fb.ToBytes() + if err != nil { + return "", err + } + decode, err := qrcode.Decode(bytes.NewReader(fileBytes)) + if err != nil { + return "", nil + } + return decode.Content, nil +} diff --git a/wechaty-puppet/file-box/file_box_base64.go b/wechaty-puppet/file-box/file_box_base64.go new file mode 100644 index 0000000..e5959b0 --- /dev/null +++ b/wechaty-puppet/file-box/file_box_base64.go @@ -0,0 +1,27 @@ +package file_box + +import ( + "encoding/base64" +) + +type fileBoxBase64 struct { + base64Data string +} + +func newFileBoxBase64(base64Data string) *fileBoxBase64 { + return &fileBoxBase64{base64Data: base64Data} +} + +func (fb *fileBoxBase64) toJSONMap() map[string]interface{} { + return map[string]interface{}{ + "base64": fb.base64Data, + } +} + +func (fb *fileBoxBase64) toBytes() ([]byte, error) { + dec, err := base64.StdEncoding.DecodeString(fb.base64Data) + if err != nil { + return nil, err + } + return dec, nil +} diff --git a/wechaty-puppet/file-box/file_box_qrcode.go b/wechaty-puppet/file-box/file_box_qrcode.go new file mode 100644 index 0000000..b8c6874 --- /dev/null +++ b/wechaty-puppet/file-box/file_box_qrcode.go @@ -0,0 +1,27 @@ +package file_box + +import ( + "github.com/skip2/go-qrcode" +) + +type fileBoxQRCode struct { + qrCode string +} + +func NewFileBoxQRCode(qrCode string) *fileBoxQRCode { + return &fileBoxQRCode{qrCode: qrCode} +} + +func (fb *fileBoxQRCode) toJSONMap() map[string]interface{} { + return map[string]interface{}{ + "qrCode": fb.qrCode, + } +} + +func (fb *fileBoxQRCode) toBytes() ([]byte, error) { + qr, err := qrcode.New(fb.qrCode, qrcode.Medium) + if err != nil { + return nil, err + } + return qr.PNG(256) +} diff --git a/wechaty-puppet/file-box/file_box_test.go b/wechaty-puppet/file-box/file_box_test.go new file mode 100644 index 0000000..f744aef --- /dev/null +++ b/wechaty-puppet/file-box/file_box_test.go @@ -0,0 +1,92 @@ +package file_box + +import ( + "encoding/base64" + "io/ioutil" + "log" + "os" + "reflect" + "testing" +) + +func TestFileBox_ToFile(t *testing.T) { + expect := "test content" + fileBox := NewFileBoxFromJSONObjectBase64(&FileBoxJsonObjectBase64{ + FileBoxJsonObjectCommon: &FileBoxJsonObjectCommon{ + Name: "test.text", + Metadata: nil, + BoxType: FileBoxTypeBase64, + }, + Base64: base64.StdEncoding.EncodeToString([]byte(expect)), + }) + const filename = "testdata/test.text" + t.Run("toFile success", func(t *testing.T) { + err := fileBox.ToFile(filename, true) + if err != nil { + log.Fatal(err) + } + file, err := os.Open(filename) + if err != nil { + log.Fatal(err) + } + got, err := ioutil.ReadAll(file) + if err != nil { + log.Fatal(err) + } + if expect != string(got) { + log.Fatalf("got %s expect %s", got, expect) + } + }) + t.Run("file exists", func(t *testing.T) { + err := fileBox.ToFile(filename, false) + if err != os.ErrExist { + log.Fatalf("got %s expect %s", err, os.ErrExist) + } + }) +} + +func TestNewFileBoxFromJSONString(t *testing.T) { + tests := []struct { + jsonString string + expectFileImpl reflect.Type + }{ + { + jsonString: `{ +"name":"test.png", +"metadata": null, +"boxType":1, +"base64":"dGVzdCBjb250ZW50" +}`, + expectFileImpl: reflect.TypeOf(new(fileBoxBase64)), + }, + { + jsonString: `{ +"name":"test.png", +"metadata": null, +"boxType":2, +"remoteUrl":"http://www.example.com", +"header":null +}`, + expectFileImpl: reflect.TypeOf(new(fileBoxUrl)), + }, + { + jsonString: `{ +"name":"test.png", +"metadata": null, +"boxType":3, +"qrCode":"test content" +}`, + expectFileImpl: reflect.TypeOf(new(fileBoxQRCode)), + }, + } + for _, t := range tests { + fileBox, err := NewFileBoxFromJSONString(t.jsonString) + if err != nil { + log.Fatal(err) + } + gotReflectType := reflect.TypeOf(fileBox.fileImpl) + if gotReflectType != t.expectFileImpl { + log.Fatalf("got %v expect %v", gotReflectType, t.expectFileImpl) + } + } +} diff --git a/wechaty-puppet/file-box/file_box_url.go b/wechaty-puppet/file-box/file_box_url.go new file mode 100644 index 0000000..212af82 --- /dev/null +++ b/wechaty-puppet/file-box/file_box_url.go @@ -0,0 +1,36 @@ +package file_box + +import ( + helper_functions "github.com/wechaty/go-wechaty/wechaty-puppet/helper-functions" + "io/ioutil" + "net/http" +) + +type fileBoxUrl struct { + remoteUrl string + headers http.Header +} + +func NewFileBoxUrl(remoteUrl string, headers http.Header) *fileBoxUrl { + return &fileBoxUrl{remoteUrl: remoteUrl, headers: headers} +} + +func (fb *fileBoxUrl) toJSONMap() map[string]interface{} { + return map[string]interface{}{ + "headers": fb.headers, + "remoteUrl": fb.remoteUrl, + } +} + +func (fb *fileBoxUrl) toBytes() ([]byte, error) { + request, err := http.NewRequest(http.MethodGet, fb.remoteUrl, nil) + if err != nil { + return nil, err + } + request.Header = fb.headers + response, err := helper_functions.HttpClient.Do(request) + if err != nil { + return nil, err + } + return ioutil.ReadAll(response.Body) +} diff --git a/wechaty-puppet/file-box/testdata/.gitignore b/wechaty-puppet/file-box/testdata/.gitignore new file mode 100644 index 0000000..0790484 --- /dev/null +++ b/wechaty-puppet/file-box/testdata/.gitignore @@ -0,0 +1 @@ +test.text diff --git a/wechaty-puppet/file-box/type.go b/wechaty-puppet/file-box/type.go new file mode 100644 index 0000000..f46ff5c --- /dev/null +++ b/wechaty-puppet/file-box/type.go @@ -0,0 +1,38 @@ +package file_box + +import "net/http" + +type FileBoxJsonObjectCommon struct { + Name string `json:"name"` + Metadata map[string]interface{} `json:"metadata"` + BoxType FileBoxType `json:"boxType"` +} + +type FileBoxJsonObjectBase64 struct { + *FileBoxJsonObjectCommon + Base64 string `json:"base64"` +} + +type FileBoxJsonObjectUrl struct { + *FileBoxJsonObjectCommon + RemoteUrl string `json:"remoteUrl"` + Headers http.Header `json:"headers"` +} + +type FileBoxJsonObjectQRCode struct { + *FileBoxJsonObjectCommon + QrCode string `json:"qrCode"` +} + +type FileBoxType uint8 + +const ( + FileBoxTypeUnknown FileBoxType = 0 + + FileBoxTypeBase64 = 1 + FileBoxTypeUrl = 2 + FileBoxTypeQRCode = 3 + FileBoxTypeBuffer = 4 + FileBoxTypeFile = 5 + FileBoxTypeStream = 6 +) diff --git a/wechaty-puppet/file_box.go b/wechaty-puppet/file_box.go deleted file mode 100644 index 0043a7f..0000000 --- a/wechaty-puppet/file_box.go +++ /dev/null @@ -1,20 +0,0 @@ -package wechatypuppet - -// FileBox file struct -type FileBox struct { -} - -// ToJSON struct to map -func (f *FileBox) ToJSON() map[string]interface{} { - return nil -} - -// ToFile save to file -func (f *FileBox) ToFile(path string) { - return -} - -// FromQrCode from qr code -func (f *FileBox) FromQrCode(path string) { - return -} diff --git a/wechaty-puppet/helper-functions/file.go b/wechaty-puppet/helper-functions/file.go new file mode 100644 index 0000000..0d1c00b --- /dev/null +++ b/wechaty-puppet/helper-functions/file.go @@ -0,0 +1,14 @@ +package helper_functions + +import "os" + +func FileExists(path string) bool { + _, err := os.Stat(path) + if err != nil { + if os.IsExist(err) { + return true + } + return false + } + return true +} diff --git a/wechaty-puppet/helper-functions/http_client.go b/wechaty-puppet/helper-functions/http_client.go new file mode 100644 index 0000000..a3346a0 --- /dev/null +++ b/wechaty-puppet/helper-functions/http_client.go @@ -0,0 +1,13 @@ +package helper_functions + +import ( + "net/http" + "time" +) + +var HttpClient = http.Client{ + Transport: nil, + CheckRedirect: nil, + Jar: nil, + Timeout: 30 * time.Second, +} diff --git a/wechaty-puppet/memory-card/storage/file.go b/wechaty-puppet/memory-card/storage/file.go index 373186c..b872776 100644 --- a/wechaty-puppet/memory-card/storage/file.go +++ b/wechaty-puppet/memory-card/storage/file.go @@ -2,6 +2,7 @@ package storage import ( "encoding/json" + helper_functions "github.com/wechaty/go-wechaty/wechaty-puppet/helper-functions" "io/ioutil" "os" "path/filepath" @@ -29,7 +30,7 @@ func (f *FileStorage) Save(payload map[string]interface{}) error { } func (f *FileStorage) Load() (map[string]interface{}, error) { - if !exists(f.absFileName) { + if !helper_functions.FileExists(f.absFileName) { return map[string]interface{}{}, nil } file, err := os.Open(f.absFileName) @@ -63,14 +64,3 @@ func handleAbsFileName(absFileName string) (string, error) { } return absFileName, nil } - -func exists(path string) bool { - _, err := os.Stat(path) //os.Stat获取文件信息 - if err != nil { - if os.IsExist(err) { - return true - } - return false - } - return true -} diff --git a/wechaty-puppet/puppet.go b/wechaty-puppet/puppet.go index 69ec86e..39bfd29 100644 --- a/wechaty-puppet/puppet.go +++ b/wechaty-puppet/puppet.go @@ -2,13 +2,14 @@ package wechatypuppet import ( lru "github.com/hashicorp/golang-lru" + "github.com/wechaty/go-wechaty/wechaty-puppet/file-box" "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" ) // puppetInterface puppet interface type puppetInterface interface { - MessageImage(messageID string, imageType schemas.ImageType) FileBox + MessageImage(messageID string, imageType schemas.ImageType) file_box.FileBox FriendshipPayloadReceive(friendshipID string) schemas.FriendshipPayloadReceive FriendshipPayloadConfirm(friendshipID string) schemas.FriendshipPayloadConfirm FriendshipPayloadVerify(friendshipID string) schemas.FriendshipPayloadVerify diff --git a/wechaty/user/image.go b/wechaty/user/image.go index 86fc2e9..0cfa507 100644 --- a/wechaty/user/image.go +++ b/wechaty/user/image.go @@ -1,9 +1,9 @@ package user import ( - "github.com/wechaty/go-wechaty/wechaty" - wechatyPuppet "github.com/wechaty/go-wechaty/wechaty-puppet" - "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty" + "github.com/wechaty/go-wechaty/wechaty-puppet/file-box" + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" ) type Images struct { @@ -20,16 +20,16 @@ func NewImages(id string, accessory wechaty.Accessory) *Images { } // Thumbnail message thumbnail images -func (img *Images) Thumbnail() wechatyPuppet.FileBox { +func (img *Images) Thumbnail() file_box.FileBox { return img.Accessory.GetPuppet().MessageImage(img.ImageId, schemas.ImageTypeThumbnail) } // HD message hd images -func (img *Images) HD() wechatyPuppet.FileBox { +func (img *Images) HD() file_box.FileBox { return img.Accessory.GetPuppet().MessageImage(img.ImageId, schemas.ImageTypeHD) } // Artwork message artwork images -func (img *Images) Artwork() wechatyPuppet.FileBox { +func (img *Images) Artwork() file_box.FileBox { return img.Accessory.GetPuppet().MessageImage(img.ImageId, schemas.ImageTypeArtwork) } From 650ce29e715e0f2e269a3054df281ec834e84ab3 Mon Sep 17 00:00:00 2001 From: dchaofei Date: Thu, 9 Apr 2020 14:21:28 +0800 Subject: [PATCH 5/8] move impl/wechaty to wechaty package --- examples/ding-dong-bot.go | 4 +- wechaty/{impl => }/event.go | 2 +- wechaty/impl/wechaty.go | 164 --------------------------- wechaty/{ => interface}/accessory.go | 2 +- wechaty/interface/wechaty.go | 5 + wechaty/user/contact.go | 8 +- wechaty/user/friendship.go | 6 +- wechaty/user/friendship_receive.go | 6 +- wechaty/user/image.go | 6 +- wechaty/user/message.go | 8 +- wechaty/user/tag.go | 6 +- wechaty/wechaty.go | 163 +++++++++++++++++++++++++- wechaty/{impl => }/wechaty_test.go | 2 +- 13 files changed, 192 insertions(+), 190 deletions(-) rename wechaty/{impl => }/event.go (98%) delete mode 100644 wechaty/impl/wechaty.go rename wechaty/{ => interface}/accessory.go (93%) create mode 100644 wechaty/interface/wechaty.go rename wechaty/{impl => }/wechaty_test.go (98%) diff --git a/examples/ding-dong-bot.go b/examples/ding-dong-bot.go index df3eba8..c662e4f 100644 --- a/examples/ding-dong-bot.go +++ b/examples/ding-dong-bot.go @@ -2,13 +2,13 @@ package main import ( "fmt" + "github.com/wechaty/go-wechaty/wechaty" "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" - "github.com/wechaty/go-wechaty/wechaty/impl" "github.com/wechaty/go-wechaty/wechaty/user" ) func main() { - _ = impl.NewWechaty(). + _ = wechaty.NewWechaty(). OnScan(func(qrCode string, status schemas.ScanStatus, data string) { fmt.Printf("Scan QR Code to login: %v\nhttps://api.qrserver.com/v1/create-qr-code/?data=%s\n", status, qrCode) }). diff --git a/wechaty/impl/event.go b/wechaty/event.go similarity index 98% rename from wechaty/impl/event.go rename to wechaty/event.go index 4a1723b..5c353d5 100644 --- a/wechaty/impl/event.go +++ b/wechaty/event.go @@ -1,4 +1,4 @@ -package impl +package wechaty import ( "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" diff --git a/wechaty/impl/wechaty.go b/wechaty/impl/wechaty.go deleted file mode 100644 index 5ac2f61..0000000 --- a/wechaty/impl/wechaty.go +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Go Wechaty - https://github.com/wechaty/go-wechaty - * - * Authors: Huan LI (李卓桓) - * Xiaoyu DING (丁小雨) - * - * 2020-now @ Copyright Wechaty - * - * Licensed under the Apache License, Version 2.0 (the 'License'); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an 'AS IS' BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Package wechaty ... -package impl - -import ( - "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" - "reflect" -) - -// Wechaty ... -type Wechaty struct { - eventMap map[schemas.PuppetEventName]interface{} -} - -// NewWechaty ... -// instance by golang. -func NewWechaty() *Wechaty { - return &Wechaty{ - eventMap: map[schemas.PuppetEventName]interface{}{}, - } -} - -func (w *Wechaty) registerEvent(name schemas.PuppetEventName, event interface{}) { - w.eventMap[name] = event -} - -// OnScan ... -func (w *Wechaty) OnScan(f EventScan) *Wechaty { - w.registerEvent(schemas.PuppetEventNameScan, f) - return w -} - -// OnLogin ... -func (w *Wechaty) OnLogin(f EventLogin) *Wechaty { - w.registerEvent(schemas.PuppetEventNameLogin, f) - return w -} - -// OnMessage ... -func (w *Wechaty) OnMessage(f EventMessage) *Wechaty { - w.registerEvent(schemas.PuppetEventNameMessage, f) - return w -} - -// OnDong ... -func (w *Wechaty) OnDong(f EventDong) *Wechaty { - w.registerEvent(schemas.PuppetEventNameDong, f) - return w -} - -// OnError ... -func (w *Wechaty) OnError(f EventError) *Wechaty { - w.registerEvent(schemas.PuppetEventNameError, f) - return w -} - -// OnFriendshipConfirm ... -func (w *Wechaty) OnFriendshipConfirm(f EventFriendshipConfirm) *Wechaty { - w.registerEvent(schemas.PuppetEventNameFriendShipConfirm, f) - return w -} - -// OnFriendshipVerify ... -func (w *Wechaty) OnFriendshipVerify(f EventFriendshipVerify) *Wechaty { - w.registerEvent(schemas.PuppetEventNameFriendShipVerify, f) - return w -} - -// OnFriendshipReceive ... -func (w *Wechaty) OnFriendshipReceive(f EventFriendshipReceive) *Wechaty { - w.registerEvent(schemas.PuppetEventNameFriendShipReceive, f) - return w -} - -// OnHeartbeat ... -func (w *Wechaty) OnHeartbeat(f EventHeartbeat) *Wechaty { - w.registerEvent(schemas.PuppetEventNameHeartbeat, f) - return w -} - -// OnLogout ... -func (w *Wechaty) OnLogout(f EventLogout) *Wechaty { - w.registerEvent(schemas.PuppetEventNameLogout, f) - return w -} - -// OnReady ... -func (w *Wechaty) OnReady(f EventReady) *Wechaty { - w.registerEvent(schemas.PuppetEventNameReady, f) - return w -} - -// OnRoomInvite ... -func (w *Wechaty) OnRoomInvite(f EventRoomInvite) *Wechaty { - w.registerEvent(schemas.PuppetEventNameRoomInvite, f) - return w -} - -// OnRoomJoin ... -func (w *Wechaty) OnRoomJoin(f EventRoomJoin) *Wechaty { - w.registerEvent(schemas.PuppetEventNameRoomJoin, f) - return w -} - -// OnRoomLeave ... -func (w *Wechaty) OnRoomLeave(f EventRoomLeave) *Wechaty { - w.registerEvent(schemas.PuppetEventNameRoomLeave, f) - return w -} - -// OnRoomTopic ... -func (w *Wechaty) OnRoomTopic(f EventRoomTopic) *Wechaty { - w.registerEvent(schemas.PuppetEventNameRoomTopic, f) - return w -} - -// OnStart ... -func (w *Wechaty) OnStart(f EventStart) *Wechaty { - w.registerEvent(schemas.PuppetEventNameStart, f) - return w -} - -// OnStop ... -func (w *Wechaty) OnStop(f EventStop) *Wechaty { - w.registerEvent(schemas.PuppetEventNameStop, f) - return w -} - -// Emit ... -func (w *Wechaty) Emit(name schemas.PuppetEventName, data ...interface{}) { - f, ok := w.eventMap[name] - if ok { - values := make([]reflect.Value, 0, len(data)) - for _, v := range data { - values = append(values, reflect.ValueOf(v)) - } - _ = reflect.ValueOf(f).Call(values) - } -} - -// Start ... -func (w *Wechaty) Start() *Wechaty { - return w -} diff --git a/wechaty/accessory.go b/wechaty/interface/accessory.go similarity index 93% rename from wechaty/accessory.go rename to wechaty/interface/accessory.go index 5f6a2f5..d782b1f 100644 --- a/wechaty/accessory.go +++ b/wechaty/interface/accessory.go @@ -1,4 +1,4 @@ -package wechaty +package _interface import ( wechatyPuppet "github.com/wechaty/go-wechaty/wechaty-puppet" diff --git a/wechaty/interface/wechaty.go b/wechaty/interface/wechaty.go new file mode 100644 index 0000000..beb6e76 --- /dev/null +++ b/wechaty/interface/wechaty.go @@ -0,0 +1,5 @@ +package _interface + +// Wechaty interface +type Wechaty interface { +} diff --git a/wechaty/user/contact.go b/wechaty/user/contact.go index 00da082..f5194eb 100644 --- a/wechaty/user/contact.go +++ b/wechaty/user/contact.go @@ -21,15 +21,17 @@ package user -import "github.com/wechaty/go-wechaty/wechaty" +import ( + "github.com/wechaty/go-wechaty/wechaty/interface" +) type Contact struct { - wechaty.Accessory + _interface.Accessory Id string } -func NewContact(accessory wechaty.Accessory, id string) *Contact { +func NewContact(accessory _interface.Accessory, id string) *Contact { return &Contact{ Accessory: accessory, Id: id, diff --git a/wechaty/user/friendship.go b/wechaty/user/friendship.go index e890fe1..ae9e95a 100644 --- a/wechaty/user/friendship.go +++ b/wechaty/user/friendship.go @@ -1,8 +1,8 @@ package user import ( - wechaty "github.com/wechaty/go-wechaty/wechaty" "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty/interface" ) type friendship interface { @@ -10,11 +10,11 @@ type friendship interface { } type Friendship struct { - wechaty.Accessory + _interface.Accessory friendshipPayloadBase schemas.FriendshipPayloadBase } -func NewFriendship(accessory wechaty.Accessory, friendshipPayloadBase schemas.FriendshipPayloadBase) *Friendship { +func NewFriendship(accessory _interface.Accessory, friendshipPayloadBase schemas.FriendshipPayloadBase) *Friendship { return &Friendship{ Accessory: accessory, friendshipPayloadBase: friendshipPayloadBase, diff --git a/wechaty/user/friendship_receive.go b/wechaty/user/friendship_receive.go index 686cff1..af55186 100644 --- a/wechaty/user/friendship_receive.go +++ b/wechaty/user/friendship_receive.go @@ -2,18 +2,18 @@ package user import ( "errors" - "github.com/wechaty/go-wechaty/wechaty" helper_functions "github.com/wechaty/go-wechaty/wechaty-puppet/helper-functions" "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty/interface" ) type FriendshipReceive struct { - wechaty.Accessory + _interface.Accessory payload *schemas.FriendshipPayloadReceive *Friendship } -func NewFriendshipReceive(accessory wechaty.Accessory, payload *schemas.FriendshipPayloadReceive) *FriendshipReceive { +func NewFriendshipReceive(accessory _interface.Accessory, payload *schemas.FriendshipPayloadReceive) *FriendshipReceive { return &FriendshipReceive{ Accessory: accessory, payload: payload, diff --git a/wechaty/user/image.go b/wechaty/user/image.go index 0cfa507..dd0e204 100644 --- a/wechaty/user/image.go +++ b/wechaty/user/image.go @@ -1,18 +1,18 @@ package user import ( - "github.com/wechaty/go-wechaty/wechaty" "github.com/wechaty/go-wechaty/wechaty-puppet/file-box" "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty/interface" ) type Images struct { - wechaty.Accessory + _interface.Accessory ImageId string } // NewImages create image struct -func NewImages(id string, accessory wechaty.Accessory) *Images { +func NewImages(id string, accessory _interface.Accessory) *Images { if accessory.GetPuppet() == nil { panic("Image class can not be instantiated without a puppet!") } diff --git a/wechaty/user/message.go b/wechaty/user/message.go index 41de28c..e7adc76 100644 --- a/wechaty/user/message.go +++ b/wechaty/user/message.go @@ -1,10 +1,10 @@ package user import ( - "fmt" + "fmt" + "github.com/wechaty/go-wechaty/wechaty/interface" - "github.com/wechaty/go-wechaty/wechaty" - "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" ) type MessageUserQueryFilter struct { @@ -16,7 +16,7 @@ type MessageUserQueryFilter struct { } type Message struct { - wechaty.Accessory + _interface.Accessory Type schemas.MessageType diff --git a/wechaty/user/tag.go b/wechaty/user/tag.go index f8982b3..ddeeb46 100644 --- a/wechaty/user/tag.go +++ b/wechaty/user/tag.go @@ -22,15 +22,15 @@ package user import ( - "github.com/wechaty/go-wechaty/wechaty" + "github.com/wechaty/go-wechaty/wechaty/interface" ) type Tag struct { - wechaty.Accessory + _interface.Accessory TagId string } -func NewTag(id string, accessory wechaty.Accessory) *Tag { +func NewTag(id string, accessory _interface.Accessory) *Tag { if accessory.GetPuppet() == nil { panic("Tag class can not be instantiated without a puppet!") } diff --git a/wechaty/wechaty.go b/wechaty/wechaty.go index 2ada625..fd1b210 100644 --- a/wechaty/wechaty.go +++ b/wechaty/wechaty.go @@ -1,5 +1,164 @@ +/** + * Go Wechaty - https://github.com/wechaty/go-wechaty + * + * Authors: Huan LI (李卓桓) + * Xiaoyu DING (丁小雨) + * + * 2020-now @ Copyright Wechaty + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package wechaty ... package wechaty -// Wechaty interface -type Wechaty interface { +import ( + "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" + "reflect" +) + +// Wechaty ... +type Wechaty struct { + eventMap map[schemas.PuppetEventName]interface{} +} + +// NewWechaty ... +// instance by golang. +func NewWechaty() *Wechaty { + return &Wechaty{ + eventMap: map[schemas.PuppetEventName]interface{}{}, + } +} + +func (w *Wechaty) registerEvent(name schemas.PuppetEventName, event interface{}) { + w.eventMap[name] = event +} + +// OnScan ... +func (w *Wechaty) OnScan(f EventScan) *Wechaty { + w.registerEvent(schemas.PuppetEventNameScan, f) + return w +} + +// OnLogin ... +func (w *Wechaty) OnLogin(f EventLogin) *Wechaty { + w.registerEvent(schemas.PuppetEventNameLogin, f) + return w +} + +// OnMessage ... +func (w *Wechaty) OnMessage(f EventMessage) *Wechaty { + w.registerEvent(schemas.PuppetEventNameMessage, f) + return w +} + +// OnDong ... +func (w *Wechaty) OnDong(f EventDong) *Wechaty { + w.registerEvent(schemas.PuppetEventNameDong, f) + return w +} + +// OnError ... +func (w *Wechaty) OnError(f EventError) *Wechaty { + w.registerEvent(schemas.PuppetEventNameError, f) + return w +} + +// OnFriendshipConfirm ... +func (w *Wechaty) OnFriendshipConfirm(f EventFriendshipConfirm) *Wechaty { + w.registerEvent(schemas.PuppetEventNameFriendShipConfirm, f) + return w +} + +// OnFriendshipVerify ... +func (w *Wechaty) OnFriendshipVerify(f EventFriendshipVerify) *Wechaty { + w.registerEvent(schemas.PuppetEventNameFriendShipVerify, f) + return w +} + +// OnFriendshipReceive ... +func (w *Wechaty) OnFriendshipReceive(f EventFriendshipReceive) *Wechaty { + w.registerEvent(schemas.PuppetEventNameFriendShipReceive, f) + return w +} + +// OnHeartbeat ... +func (w *Wechaty) OnHeartbeat(f EventHeartbeat) *Wechaty { + w.registerEvent(schemas.PuppetEventNameHeartbeat, f) + return w +} + +// OnLogout ... +func (w *Wechaty) OnLogout(f EventLogout) *Wechaty { + w.registerEvent(schemas.PuppetEventNameLogout, f) + return w +} + +// OnReady ... +func (w *Wechaty) OnReady(f EventReady) *Wechaty { + w.registerEvent(schemas.PuppetEventNameReady, f) + return w +} + +// OnRoomInvite ... +func (w *Wechaty) OnRoomInvite(f EventRoomInvite) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomInvite, f) + return w +} + +// OnRoomJoin ... +func (w *Wechaty) OnRoomJoin(f EventRoomJoin) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomJoin, f) + return w +} + +// OnRoomLeave ... +func (w *Wechaty) OnRoomLeave(f EventRoomLeave) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomLeave, f) + return w +} + +// OnRoomTopic ... +func (w *Wechaty) OnRoomTopic(f EventRoomTopic) *Wechaty { + w.registerEvent(schemas.PuppetEventNameRoomTopic, f) + return w +} + +// OnStart ... +func (w *Wechaty) OnStart(f EventStart) *Wechaty { + w.registerEvent(schemas.PuppetEventNameStart, f) + return w +} + +// OnStop ... +func (w *Wechaty) OnStop(f EventStop) *Wechaty { + w.registerEvent(schemas.PuppetEventNameStop, f) + return w +} + +// Emit ... +func (w *Wechaty) Emit(name schemas.PuppetEventName, data ...interface{}) { + f, ok := w.eventMap[name] + if ok { + values := make([]reflect.Value, 0, len(data)) + for _, v := range data { + values = append(values, reflect.ValueOf(v)) + } + _ = reflect.ValueOf(f).Call(values) + } +} + +// Start ... +func (w *Wechaty) Start() *Wechaty { + return w } diff --git a/wechaty/impl/wechaty_test.go b/wechaty/wechaty_test.go similarity index 98% rename from wechaty/impl/wechaty_test.go rename to wechaty/wechaty_test.go index 75fa040..c69303b 100644 --- a/wechaty/impl/wechaty_test.go +++ b/wechaty/wechaty_test.go @@ -1,4 +1,4 @@ -package impl +package wechaty import ( "github.com/wechaty/go-wechaty/wechaty-puppet/schemas" From 3564a3bf2d436aea8342b90d66290241b51f28b8 Mon Sep 17 00:00:00 2001 From: dchaofei Date: Thu, 9 Apr 2020 14:21:59 +0800 Subject: [PATCH 6/8] generate scanstatus_string.go --- wechaty-puppet/schemas/events.go | 1 + wechaty-puppet/schemas/scanstatus_string.go | 28 +++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 wechaty-puppet/schemas/scanstatus_string.go diff --git a/wechaty-puppet/schemas/events.go b/wechaty-puppet/schemas/events.go index a43a513..a7912ca 100644 --- a/wechaty-puppet/schemas/events.go +++ b/wechaty-puppet/schemas/events.go @@ -1,5 +1,6 @@ package schemas +//go:generate stringer -type=ScanStatus type ScanStatus uint8 const ( diff --git a/wechaty-puppet/schemas/scanstatus_string.go b/wechaty-puppet/schemas/scanstatus_string.go new file mode 100644 index 0000000..906d790 --- /dev/null +++ b/wechaty-puppet/schemas/scanstatus_string.go @@ -0,0 +1,28 @@ +// Code generated by "stringer -type=ScanStatus"; DO NOT EDIT. + +package schemas + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ScanStatusUnknown-0] + _ = x[ScanStatusCancel-1] + _ = x[ScanStatusWaiting-2] + _ = x[ScanStatusScanned-3] + _ = x[ScanStatusConfirmed-4] + _ = x[ScanStatusTimeout-5] +} + +const _ScanStatus_name = "ScanStatusUnknownScanStatusCancelScanStatusWaitingScanStatusScannedScanStatusConfirmedScanStatusTimeout" + +var _ScanStatus_index = [...]uint8{0, 17, 33, 50, 67, 86, 103} + +func (i ScanStatus) String() string { + if i >= ScanStatus(len(_ScanStatus_index)-1) { + return "ScanStatus(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _ScanStatus_name[_ScanStatus_index[i]:_ScanStatus_index[i+1]] +} From 881caa7d506bfb9596a5dd6b05a2b0c50146287c Mon Sep 17 00:00:00 2001 From: dchaofei Date: Thu, 9 Apr 2020 14:27:32 +0800 Subject: [PATCH 7/8] fix golint --- wechaty/event.go | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/wechaty/event.go b/wechaty/event.go index 5c353d5..600a505 100644 --- a/wechaty/event.go +++ b/wechaty/event.go @@ -7,21 +7,38 @@ import ( ) type ( - EventDong func(data string) - EventError func(err error) + // EventDong ... + EventDong func(data string) + // EventError ... + EventError func(err error) + // EventFriendshipConfirm ... EventFriendshipConfirm func(friendship *user.Friendship) - EventFriendshipVerify func(friendship *user.Friendship) + // EventFriendshipVerify ... + EventFriendshipVerify func(friendship *user.Friendship) + // EventFriendshipReceive ... EventFriendshipReceive func(friendshipReceive *user.FriendshipReceive) - EventHeartbeat func(data string) - EventLogin func(user string) // TODO(dchaofei): ContactSelf struct - EventLogout func(user string, reason string) // TODO(dchaofei): ContactSelf struct - EventMessage func(message *user.Message) - EventReady func() - EventRoomInvite func(roomInvitation string) // TODO(dchaofei): RoomInvitation struct - EventRoomJoin func(room *user.Room, inviteeList []*user.Contact, inviter *user.Contact, date time.Time) - EventRoomLeave func(room *user.Room, leaverList []*user.Contact, remover *user.Contact, date time.Time) - EventRoomTopic func(room *user.Room, newTopic string, oldTopic string, changer *user.Contact, date time.Time) - EventScan func(qrCode string, status schemas.ScanStatus, data string) - EventStart func() - EventStop func() + // EventHeartbeat ... + EventHeartbeat func(data string) + // EventLogin ... + EventLogin func(user string) // TODO(dchaofei): ContactSelf struct + // EventLogout ... + EventLogout func(user string, reason string) // TODO(dchaofei): ContactSelf struct + // EventMessage ... + EventMessage func(message *user.Message) + // EventReady ... + EventReady func() + // EventRoomInvite ... + EventRoomInvite func(roomInvitation string) // TODO(dchaofei): RoomInvitation struct + // EventRoomJoin ... + EventRoomJoin func(room *user.Room, inviteeList []*user.Contact, inviter *user.Contact, date time.Time) + // EventRoomLeave ... + EventRoomLeave func(room *user.Room, leaverList []*user.Contact, remover *user.Contact, date time.Time) + // EventRoomTopic ... + EventRoomTopic func(room *user.Room, newTopic string, oldTopic string, changer *user.Contact, date time.Time) + // EventScan ... + EventScan func(qrCode string, status schemas.ScanStatus, data string) + // EventStart ... + EventStart func() + // EventStop ... + EventStop func() ) From 8cab03b7194daae808b55432974779771432fa68 Mon Sep 17 00:00:00 2001 From: dchaofei Date: Thu, 9 Apr 2020 15:38:17 +0800 Subject: [PATCH 8/8] close http body --- wechaty-puppet/file-box/file_box_url.go | 1 + 1 file changed, 1 insertion(+) diff --git a/wechaty-puppet/file-box/file_box_url.go b/wechaty-puppet/file-box/file_box_url.go index 212af82..62cfb9a 100644 --- a/wechaty-puppet/file-box/file_box_url.go +++ b/wechaty-puppet/file-box/file_box_url.go @@ -32,5 +32,6 @@ func (fb *fileBoxUrl) toBytes() ([]byte, error) { if err != nil { return nil, err } + defer response.Body.Close() return ioutil.ReadAll(response.Body) }