From 89e7a7721454bc66d306c914374d001daf63c856 Mon Sep 17 00:00:00 2001 From: Zhao Xiaojie Date: Mon, 14 Jan 2019 16:05:12 +0800 Subject: [PATCH] fixes can not search --- Makefile | 10 ++ main.go | 49 +++++----- pkg/article/api.go | 62 +++++++++++++ pkg/article/api_test.go | 33 +++++++ articles.go => pkg/article/articles.go | 63 ++++++++----- .../article/articles_test.go | 11 +-- pkg/article/types.go | 7 ++ pkg/mock/article/articles-mock.go | 61 +++++++++++++ pkg/reply/core.go | 31 ++++--- pkg/reply/core_test.go | 12 +++ pkg/reply/match.go | 69 ++++++++++---- pkg/reply/match_test.go | 50 ++++++---- pkg/reply/register.go | 17 ++++ pkg/reply/search.go | 91 +++++++++++++++++++ pkg/reply/search_test.go | 20 ++++ pkg/reply/welcome.go | 31 ++++--- pkg/{reply => }/types.go | 4 +- .../go-diff/diffmatchpatch/stringutil.go | 2 +- vendor/gopkg.in/yaml.v2/apic.go | 2 +- vendor/gopkg.in/yaml.v2/yamlh.go | 2 +- vendor/gopkg.in/yaml.v2/yamlprivateh.go | 2 +- 21 files changed, 512 insertions(+), 117 deletions(-) create mode 100644 pkg/article/api.go create mode 100644 pkg/article/api_test.go rename articles.go => pkg/article/articles.go (60%) rename articles_test.go => pkg/article/articles_test.go (86%) create mode 100644 pkg/article/types.go create mode 100644 pkg/mock/article/articles-mock.go create mode 100644 pkg/reply/core_test.go create mode 100644 pkg/reply/register.go create mode 100644 pkg/reply/search.go create mode 100644 pkg/reply/search_test.go rename pkg/{reply => }/types.go (95%) diff --git a/Makefile b/Makefile index 66768c1..24d0f09 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,15 @@ build: image: build docker build -t surenpi/jenkins-wechat . +image-alauda: build + docker build -t index.alauda.cn/alaudak8s/jenkins-wechat . + push-image: image docker push surenpi/jenkins-wechat +push-image-alauda: image-alauda + docker push index.alauda.cn/alaudak8s/jenkins-wechat + image-ubuntu: build docker build -t surenpi/jenkins-wechat:ubuntu . -f Dockerfile.ubuntu docker push surenpi/jenkins-wechat:ubuntu @@ -20,6 +26,10 @@ update: kubectl set image deploy/wechat wechat=surenpi/jenkins-wechat make restart +update-alauda: + kubectl set image deploy/wechat wechat=index.alauda.cn/alaudak8s/jenkins-wechat + make restart + restart: kubectl scale deploy/wechat --replicas=0 kubectl scale deploy/wechat --replicas=1 \ No newline at end of file diff --git a/main.go b/main.go index 2005ffb..c54c92e 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,8 @@ import ( "sort" "strings" + core "github.com/linuxsuren/wechat-backend/pkg" + "github.com/linuxsuren/wechat-backend/pkg/article" "github.com/linuxsuren/wechat-backend/pkg/config" "github.com/linuxsuren/wechat-backend/pkg/github" "github.com/linuxsuren/wechat-backend/pkg/reply" @@ -63,45 +65,50 @@ func (we *WeChat) normalRequest(w http.ResponseWriter, r *http.Request) { w.Write([]byte("welcome aboard WeChat.")) } -func (we *WeChat) wechatRequest(w http.ResponseWriter, r *http.Request) { +func (we *WeChat) wechatRequest(writer http.ResponseWriter, r *http.Request) { textRequestBody := we.parseTextRequestBody(r) if textRequestBody != nil { - fmt.Printf("Wechat Service: Recv [%s] msg [%s] from user [%s]!\n", - textRequestBody.MsgType, - textRequestBody.Content, - textRequestBody.FromUserName) - + autoReplyInitChains := reply.AutoReplyChains() + fmt.Printf("found [%d] autoReply", len(autoReplyInitChains)) for _, autoReplyInit := range autoReplyInitChains { + if autoReplyInit == nil { + fmt.Printf("found a nil autoReply.") + continue + } autoReply := autoReplyInit() if !autoReply.Accept(textRequestBody) { continue } - var data []byte - var err error - if data, err = autoReply.Handle(); err != nil { - log.Println("handle auto replay error:", err) + fmt.Printf("going to handle by %s\n", autoReply.Name()) + + // var data []byte + // var err error + if data, err := autoReply.Handle(); err != nil { + fmt.Printf("handle auto replay error: %v\n", err) + } else if len(data) == 0 { + fmt.Println("response body is empty.") + } else { + fmt.Printf("response:%s\n", data) + fmt.Fprintf(writer, data) + break } - fmt.Fprintf(w, string(data)) - break } } } -func (w *WeChat) parseTextRequestBody(r *http.Request) *reply.TextRequestBody { +func (w *WeChat) parseTextRequestBody(r *http.Request) *core.TextRequestBody { body, err := ioutil.ReadAll(r.Body) if err != nil { log.Fatal(err) return nil } fmt.Println(string(body)) - requestBody := &reply.TextRequestBody{} + requestBody := &core.TextRequestBody{} xml.Unmarshal(body, requestBody) return requestBody } -var autoReplyInitChains []reply.Init - func main() { weConfig, err := config.LoadConfig("config/wechat.yaml") if err != nil { @@ -118,21 +125,21 @@ func main() { weConfig.ServerPort = 8080 } + defaultRM := article.NewDefaultResponseManager() + reply.SetResponseManager(defaultRM) + wechat := WeChat{ Config: weConfig, } go func() { - initCheck(weConfig) + defaultRM.InitCheck(weConfig) }() createWxMenu() - autoReplyInitChains = make([]reply.Init, 1) - autoReplyInitChains = append(autoReplyInitChains, reply.InitMatchAutoReply) - http.HandleFunc("/", wechat.procRequest) http.HandleFunc("/status", healthHandler) http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) { - github.WebhookHandler(w, r, weConfig, initCheck) + github.WebhookHandler(w, r, weConfig, defaultRM.InitCheck) }) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", weConfig.ServerPort), nil)) diff --git a/pkg/article/api.go b/pkg/article/api.go new file mode 100644 index 0000000..6d02783 --- /dev/null +++ b/pkg/article/api.go @@ -0,0 +1,62 @@ +package article + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "net/url" + "strings" +) + +type ArticleReader struct { + API string +} + +func (a *ArticleReader) FetchArticles() (articles []Article, err error) { + var apiURL *url.URL + + apiURL, err = url.Parse(a.API) + if err != nil { + return + } + + var resp *http.Response + resp, err = http.Get(apiURL.String()) + if err != nil { + return + } + + var data []byte + data, err = ioutil.ReadAll(resp.Body) + if err != nil { + return + } + + var allArticles []Article + err = json.Unmarshal(data, &allArticles) + + for _, article := range allArticles { + if article.Title == "" || article.Description == "" || + article.URI == "" { + continue + } + articles = append(articles, article) + } + return +} + +func (a *ArticleReader) FindByTitle(title string) (articles []Article, err error) { + var allArticles []Article + + allArticles, err = a.FetchArticles() + if err != nil { + return + } + + for _, article := range allArticles { + if strings.Contains(article.Title, title) { + articles = append(articles, article) + } + } + return +} diff --git a/pkg/article/api_test.go b/pkg/article/api_test.go new file mode 100644 index 0000000..a8de43e --- /dev/null +++ b/pkg/article/api_test.go @@ -0,0 +1,33 @@ +package article + +import ( + "testing" +) + +func TestFetchArticles(t *testing.T) { + reader := &ArticleReader{ + API: "https://jenkins-zh.github.io/index.json", + } + articles, err := reader.FetchArticles() + if err != nil { + t.Errorf("fetch error %v", err) + } else if len(articles) == 0 { + t.Errorf("fetch zero article") + } else { + for i, article := range articles { + if article.Title == "" || article.Description == "" || + article.URI == "" { + t.Errorf("article [%d] title, description or uri is empty", i) + } + } + } + + ar, err := reader.FindByTitle("行为") + if err != nil { + t.Errorf("%v", err) + } + + for _, a := range ar { + t.Errorf("%v", a) + } +} diff --git a/articles.go b/pkg/article/articles.go similarity index 60% rename from articles.go rename to pkg/article/articles.go index 7f0a9c3..e58dcb1 100644 --- a/articles.go +++ b/pkg/article/articles.go @@ -1,4 +1,4 @@ -package main +package article import ( "fmt" @@ -8,8 +8,8 @@ import ( "strings" + core "github.com/linuxsuren/wechat-backend/pkg" "github.com/linuxsuren/wechat-backend/pkg/config" - "github.com/linuxsuren/wechat-backend/pkg/reply" "gopkg.in/src-d/go-git.v4" "gopkg.in/yaml.v2" ) @@ -18,7 +18,29 @@ const ( CONFIG = "wechat" ) -func initCheck(weConfig *config.WeChatConfig) { +type ResponseManager interface { + GetResponse(string) (interface{}, bool) + InitCheck(weConfig *config.WeChatConfig) +} + +type DefaultResponseManager struct { + ResponseMap map[string]interface{} +} + +// NewDefaultResponseManager should always call this method to get a object +func NewDefaultResponseManager() (mgr *DefaultResponseManager) { + mgr = &DefaultResponseManager{ + ResponseMap: make(map[string]interface{}, 10), + } + return +} + +func (drm *DefaultResponseManager) GetResponse(keyword string) (interface{}, bool) { + res, ok := drm.ResponseMap[keyword] + return res, ok +} + +func (drm *DefaultResponseManager) InitCheck(weConfig *config.WeChatConfig) { var err error _, err = os.Stat(CONFIG) @@ -47,50 +69,47 @@ func initCheck(weConfig *config.WeChatConfig) { } else { log.Println("open work tree with git error", err) os.Remove(CONFIG) - initCheck(weConfig) + drm.InitCheck(weConfig) } } else { log.Println("open dir with git error", err) os.Remove(CONFIG) - initCheck(weConfig) + drm.InitCheck(weConfig) } } } else { log.Println("can't get config dir status", err) if os.RemoveAll(CONFIG) == nil { - initCheck(weConfig) + drm.InitCheck(weConfig) } } if err == nil { log.Println("going to update the cache.") - update() + drm.update() } } -var respMap = make(map[string]interface{}) - -func responseHandler(yamlContent []byte) { - reps := reply.ResponseBody{} +func (drm *DefaultResponseManager) responseHandler(yamlContent []byte) { + reps := core.ResponseBody{} err := yaml.Unmarshal(yamlContent, &reps) if err == nil { log.Println(reps.MsgType, reps.Keyword, reps) - // reps.MsgType = reps.Kind switch reps.MsgType { case "text": - text := reply.TextResponseBody{} + text := core.TextResponseBody{} yaml.Unmarshal(yamlContent, &text) - respMap[reps.Keyword] = text + drm.ResponseMap[reps.Keyword] = text case "image": - image := reply.ImageResponseBody{} + image := core.ImageResponseBody{} yaml.Unmarshal(yamlContent, &image) - respMap[reps.Keyword] = image + drm.ResponseMap[reps.Keyword] = image case "news": - news := reply.NewsResponseBody{} + news := core.NewsResponseBody{} yaml.Unmarshal(yamlContent, &news) - respMap[reps.Keyword] = news + drm.ResponseMap[reps.Keyword] = news default: log.Println("unknow type", reps.MsgType) } @@ -99,7 +118,7 @@ func responseHandler(yamlContent []byte) { } } -func update() { +func (drm *DefaultResponseManager) update() { root := CONFIG + "/management/auto-reply" files, err := ioutil.ReadDir(root) if err != nil { @@ -113,13 +132,9 @@ func update() { content, err := ioutil.ReadFile(root + "/" + file.Name()) if err == nil { - responseHandler(content) + drm.responseHandler(content) } else { log.Println("Can't read file ", file.Name()) } } } - -func getKeywords() map[string]string { - return nil -} diff --git a/articles_test.go b/pkg/article/articles_test.go similarity index 86% rename from articles_test.go rename to pkg/article/articles_test.go index 5d98404..ce1ca78 100644 --- a/articles_test.go +++ b/pkg/article/articles_test.go @@ -1,15 +1,12 @@ -package main +package article import ( "testing" + core "github.com/linuxsuren/wechat-backend/pkg" "github.com/stretchr/testify/assert" ) -// func TestInitCheck(t *testing.T) { -// initCheck() -// } - func TestImageResponseBody(t *testing.T) { yml := ` msgType: image @@ -27,7 +24,7 @@ image: t.Error("Can't find response by keyword: hi.") } - imageResp, ok := resp.(ImageResponseBody) + imageResp, ok := resp.(core.ImageResponseBody) if !ok { t.Error("Get the wrong type, should be ImageResponseBody.") } @@ -55,7 +52,7 @@ articles: return } - newsResp, ok := resp.(NewsResponseBody) + newsResp, ok := resp.(core.NewsResponseBody) if !ok { t.Error("Get the wrong type, should be NewsResponseBody.") } diff --git a/pkg/article/types.go b/pkg/article/types.go new file mode 100644 index 0000000..204d726 --- /dev/null +++ b/pkg/article/types.go @@ -0,0 +1,7 @@ +package article + +type Article struct { + Title string + Description string + URI string +} diff --git a/pkg/mock/article/articles-mock.go b/pkg/mock/article/articles-mock.go new file mode 100644 index 0000000..6e380a3 --- /dev/null +++ b/pkg/mock/article/articles-mock.go @@ -0,0 +1,61 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: pkg/article/articles.go + +// Package mock_article is a generated GoMock package. +package mock_article + +import ( + gomock "github.com/golang/mock/gomock" + config "github.com/linuxsuren/wechat-backend/pkg/config" + reflect "reflect" +) + +// MockResponseManager is a mock of ResponseManager interface +type MockResponseManager struct { + ctrl *gomock.Controller + recorder *MockResponseManagerMockRecorder +} + +// MockResponseManagerMockRecorder is the mock recorder for MockResponseManager +type MockResponseManagerMockRecorder struct { + mock *MockResponseManager +} + +// NewMockResponseManager creates a new mock instance +func NewMockResponseManager(ctrl *gomock.Controller) *MockResponseManager { + mock := &MockResponseManager{ctrl: ctrl} + mock.recorder = &MockResponseManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockResponseManager) EXPECT() *MockResponseManagerMockRecorder { + return m.recorder +} + +// GetResponse mocks base method +func (m *MockResponseManager) GetResponse(arg0 string) (interface{}, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetResponse", arg0) + ret0, _ := ret[0].(interface{}) + ret1, _ := ret[1].(bool) + return ret0, ret1 +} + +// GetResponse indicates an expected call of GetResponse +func (mr *MockResponseManagerMockRecorder) GetResponse(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetResponse", reflect.TypeOf((*MockResponseManager)(nil).GetResponse), arg0) +} + +// InitCheck mocks base method +func (m *MockResponseManager) InitCheck(weConfig *config.WeChatConfig) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "InitCheck", weConfig) +} + +// InitCheck indicates an expected call of InitCheck +func (mr *MockResponseManagerMockRecorder) InitCheck(weConfig interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitCheck", reflect.TypeOf((*MockResponseManager)(nil).InitCheck), weConfig) +} diff --git a/pkg/reply/core.go b/pkg/reply/core.go index 24639e7..58fda9c 100644 --- a/pkg/reply/core.go +++ b/pkg/reply/core.go @@ -3,46 +3,53 @@ package reply import ( "encoding/xml" "time" + + core "github.com/linuxsuren/wechat-backend/pkg" ) type AutoReply interface { - Accept(request *TextRequestBody) bool - Handle() ([]byte, error) + Accept(request *core.TextRequestBody) bool + Handle() (string, error) + Name() string } type Init func() AutoReply -func makeTextResponseBody(fromUserName, toUserName, content string) ([]byte, error) { - textResponseBody := &TextResponseBody{} +func makeTextResponseBody(fromUserName, toUserName string, content string) ([]byte, error) { + textResponseBody := &core.TextResponseBody{} textResponseBody.FromUserName = fromUserName textResponseBody.ToUserName = toUserName textResponseBody.MsgType = "text" textResponseBody.Content = content textResponseBody.CreateTime = time.Duration(time.Now().Unix()) - return xml.MarshalIndent(textResponseBody, " ", " ") + return marshal(textResponseBody) } func makeImageResponseBody(fromUserName, toUserName, mediaID string) ([]byte, error) { - imageResponseBody := &ImageResponseBody{} + imageResponseBody := &core.ImageResponseBody{} imageResponseBody.FromUserName = fromUserName imageResponseBody.ToUserName = toUserName imageResponseBody.MsgType = "image" imageResponseBody.CreateTime = time.Duration(time.Now().Unix()) - imageResponseBody.Image = Image{ + imageResponseBody.Image = core.Image{ MediaID: mediaID, } - return xml.MarshalIndent(imageResponseBody, " ", " ") + return marshal(imageResponseBody) } -func makeNewsResponseBody(fromUserName, toUserName string, news NewsResponseBody) ([]byte, error) { - newsResponseBody := &NewsResponseBody{} +func makeNewsResponseBody(fromUserName, toUserName string, news core.NewsResponseBody) ([]byte, error) { + newsResponseBody := &core.NewsResponseBody{} newsResponseBody.FromUserName = fromUserName newsResponseBody.ToUserName = toUserName newsResponseBody.MsgType = "news" newsResponseBody.ArticleCount = 1 - newsResponseBody.Articles = Articles{ + newsResponseBody.Articles = core.Articles{ Articles: news.Articles.Articles, } newsResponseBody.CreateTime = time.Duration(time.Now().Unix()) - return xml.MarshalIndent(newsResponseBody, " ", " ") + return marshal(newsResponseBody) +} + +func marshal(response interface{}) ([]byte, error) { + return xml.MarshalIndent(response, " ", " ") } diff --git a/pkg/reply/core_test.go b/pkg/reply/core_test.go new file mode 100644 index 0000000..3cde506 --- /dev/null +++ b/pkg/reply/core_test.go @@ -0,0 +1,12 @@ +package reply + +import "testing" + +func TestXML(t *testing.T) { + data, err := makeTextResponseBody("", "", "") + if err != nil { + t.Errorf("xml error %v", err) + } + + t.Errorf("%s", string(data)) +} diff --git a/pkg/reply/match.go b/pkg/reply/match.go index 980642d..e46296f 100644 --- a/pkg/reply/match.go +++ b/pkg/reply/match.go @@ -1,56 +1,87 @@ package reply -import "fmt" +import ( + "fmt" + "log" -// MatchAutoReply only reply for match -type MatchAutoReply struct { - ResponseMap map[string]interface{} - Response interface{} - Request *TextRequestBody + core "github.com/linuxsuren/wechat-backend/pkg" + "github.com/linuxsuren/wechat-backend/pkg/article" +) + +var responseManager article.ResponseManager + +func SetResponseManager(manager article.ResponseManager) { + responseManager = manager } -func InitMatchAutoReply() AutoReply { - return &MatchAutoReply{} +// MatchAutoReply only reply for match +type MatchAutoReply struct { + Response interface{} + Request *core.TextRequestBody } var _ AutoReply = &MatchAutoReply{} +func (m *MatchAutoReply) Name() string { + return "SimpleMatchReply" +} + // Accept consider if it will accept the request -func (m *MatchAutoReply) Accept(request *TextRequestBody) (ok bool) { +func (m *MatchAutoReply) Accept(request *core.TextRequestBody) (ok bool) { m.Request = request keyword := request.Content - if "text" != request.MsgType { + fmt.Printf("request is %v\n", request) + + if responseManager == nil || "text" != request.MsgType { + log.Printf("responseManager is nil or not support msgType %s", request.MsgType) return false } - m.Response, ok = m.ResponseMap[keyword] + m.Response, ok = responseManager.GetResponse(keyword) return ok } // Handle hanlde the request then return data -func (m *MatchAutoReply) Handle() (data []byte, err error) { +func (m *MatchAutoReply) Handle() (string, error) { resp := m.Response - from := m.Request.FromUserName - to := m.Request.ToUserName + from := m.Request.ToUserName + to := m.Request.FromUserName + var err error + + if resp == nil { + err = fmt.Errorf("response is nil") + return "", err + } + + fmt.Printf("response %v\n", resp) - if text, ok := resp.(TextResponseBody); ok { + var data []byte + if text, ok := resp.(core.TextResponseBody); ok { data, err = makeTextResponseBody(from, to, text.Content) + fmt.Printf("data %v\n", string(data)) if err != nil { err = fmt.Errorf("Wechat Service: makeTextResponseBody error: %v", err) } - } else if image, ok := resp.(ImageResponseBody); ok { + } else if image, ok := resp.(core.ImageResponseBody); ok { data, err = makeImageResponseBody(from, to, image.Image.MediaID) if err != nil { err = fmt.Errorf("Wechat Service: makeImageResponseBody error: %v", err) } - } else if news, ok := resp.(NewsResponseBody); ok { + } else if news, ok := resp.(core.NewsResponseBody); ok { data, err = makeNewsResponseBody(from, to, news) if err != nil { err = fmt.Errorf("Wechat Service: makeNewsResponseBody error: %v", err) } } else { - err = fmt.Errorf("type error") + err = fmt.Errorf("type error %v", resp) } - return + + return string(data), err +} + +func init() { + Register(func() AutoReply { + return &MatchAutoReply{} + }) } diff --git a/pkg/reply/match_test.go b/pkg/reply/match_test.go index 6059016..9b8a670 100644 --- a/pkg/reply/match_test.go +++ b/pkg/reply/match_test.go @@ -1,21 +1,30 @@ package reply -import "testing" +import ( + "testing" + + "github.com/golang/mock/gomock" + core "github.com/linuxsuren/wechat-backend/pkg" + mArticle "github.com/linuxsuren/wechat-backend/pkg/mock/article" +) func TestAccept(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() var reply AutoReply + reply = &MatchAutoReply{} - reply = &MatchAutoReply{ - ResponseMap: map[string]interface{}{ - "hello": "", - }, - } - - if reply.Accept(&TextRequestBody{}) { + if reply.Accept(&core.TextRequestBody{}) { t.Errorf("should not accept") } - if !reply.Accept(&TextRequestBody{ + m := mArticle.NewMockResponseManager(ctrl) + m.EXPECT().GetResponse("hello"). + Return(&core.TextResponseBody{}, true) + SetResponseManager(m) + + if !reply.Accept(&core.TextRequestBody{ + MsgType: "text", Content: "hello", }) { t.Errorf("should accept") @@ -23,15 +32,22 @@ func TestAccept(t *testing.T) { } func TestHandle(t *testing.T) { - var reply AutoReply + ctrl := gomock.NewController(t) + defer ctrl.Finish() + reply := &MatchAutoReply{} - reply = &MatchAutoReply{ - ResponseMap: map[string]interface{}{ - "hello": TextResponseBody{}, - }, - } + m := mArticle.NewMockResponseManager(ctrl) + m.EXPECT().GetResponse("hello"). + Return(core.TextResponseBody{ + ResponseBody: core.ResponseBody{ + MsgType: "text", + }, + Content: "hello", + }, true) - if !reply.Accept(&TextRequestBody{ + SetResponseManager(m) + if !reply.Accept(&core.TextRequestBody{ + MsgType: "text", Content: "hello", }) { t.Errorf("should accept") @@ -42,5 +58,7 @@ func TestHandle(t *testing.T) { t.Errorf("should not error %v", err) } else if data == nil { t.Errorf("not have data") + } else if string(data) != "hello" { + t.Errorf("got an error content: %s", string(data)) } } diff --git a/pkg/reply/register.go b/pkg/reply/register.go new file mode 100644 index 0000000..da88c5c --- /dev/null +++ b/pkg/reply/register.go @@ -0,0 +1,17 @@ +package reply + +var autoReplyInitChains []Init + +// Register add an implement of AutoReply +func Register(initFunc Init) { + autoReplyInitChains = append(autoReplyInitChains, initFunc) +} + +// AutoReplyChains return all implements of AutoReply +func AutoReplyChains() []Init { + return autoReplyInitChains +} + +// func init() { +// autoReplyInitChains = make([]Init, 3) +// } diff --git a/pkg/reply/search.go b/pkg/reply/search.go new file mode 100644 index 0000000..fb6ae78 --- /dev/null +++ b/pkg/reply/search.go @@ -0,0 +1,91 @@ +package reply + +import ( + "fmt" + "strings" + + core "github.com/linuxsuren/wechat-backend/pkg" + "github.com/linuxsuren/wechat-backend/pkg/article" +) + +// SearchAutoReply only reply for match +type SearchAutoReply struct { + ResponseMap map[string]interface{} + Response interface{} + Request *core.TextRequestBody + Keyword string +} + +var _ AutoReply = &SearchAutoReply{} + +func (m *SearchAutoReply) Name() string { + return "SearchAutoReply" +} + +// Accept consider if it will accept the request +func (m *SearchAutoReply) Accept(request *core.TextRequestBody) (ok bool) { + m.Request = request + m.Keyword = request.Content + m.Keyword = strings.TrimLeft(m.Keyword, "search ") + m.Keyword = strings.TrimLeft(m.Keyword, "搜索 ") + + if "text" != request.MsgType { + return false + } + + return strings.HasPrefix(request.Content, "搜索") || + strings.HasPrefix(request.Content, "search") +} + +// Handle hanlde the request then return data +func (m *SearchAutoReply) Handle() (string, error) { + from := m.Request.ToUserName + to := m.Request.FromUserName + var err error + + reader := &article.ArticleReader{ + API: "https://jenkins-zh.github.io/index.json", + } + + var data []byte + articles, err := reader.FindByTitle(m.Keyword) + if err != nil { + return "", err + } + + fmt.Printf("found aritcle count [%d]\n", len(articles)) + var targetArticle article.Article + if len(articles) == 0 { + targetArticle = article.Article{ + Title: "404", + Description: "404", + URI: "https://jenkins-zh.github.io", + } + } else { + targetArticle = articles[0] + } + + news := core.NewsResponseBody{ + Articles: core.Articles{ + Articles: []core.Article{ + { + Title: targetArticle.Title, + Description: targetArticle.Description, + PicUrl: "https://jenkins-zh.github.io/images/2018-survey-qrcode.jpg", + Url: targetArticle.URI, + }, + }, + }, + } + data, err = makeNewsResponseBody(from, to, news) + if err != nil { + err = fmt.Errorf("Wechat Service: makeNewsResponseBody error: %v", err) + } + return string(data), err +} + +func init() { + Register(func() AutoReply { + return &SearchAutoReply{} + }) +} diff --git a/pkg/reply/search_test.go b/pkg/reply/search_test.go new file mode 100644 index 0000000..bde7ce5 --- /dev/null +++ b/pkg/reply/search_test.go @@ -0,0 +1,20 @@ +package reply + +import ( + "testing" + + core "github.com/linuxsuren/wechat-backend/pkg" +) + +func TestSearch(t *testing.T) { + reply := SearchAutoReply{} + + reply.Accept(&core.TextRequestBody{}) + + data, err := reply.Handle() + if err != nil { + t.Errorf("error %v", err) + } + + t.Errorf("%s", string(data)) +} diff --git a/pkg/reply/welcome.go b/pkg/reply/welcome.go index 6e42911..589045f 100644 --- a/pkg/reply/welcome.go +++ b/pkg/reply/welcome.go @@ -1,28 +1,35 @@ package reply -// MatchAutoReply only reply for match +import ( + "fmt" + + core "github.com/linuxsuren/wechat-backend/pkg" +) + +// WelcomeReply for welcome event type WelcomeReply struct { AutoReply } -func InitWelcomeReply() AutoReply { - return &WelcomeReply{} -} - var _ AutoReply = &WelcomeReply{} -// Accept consider if it will accept the request -func (m *WelcomeReply) Accept(request *TextRequestBody) (ok bool) { +func (m *WelcomeReply) Name() string { + return "WelcomeReply" +} +// Accept consider if it will accept the request +func (m *WelcomeReply) Accept(request *core.TextRequestBody) (ok bool) { if "event" == request.MsgType && "subscribe" == request.Event { request.Content = "welcome" - m.AutoReply = InitMatchAutoReply() + m.AutoReply = &MatchAutoReply{} ok = m.AutoReply.Accept(request) } return } -// Handle hanlde the request then return data -// func (m *WelcomeReply) Handle() (data []byte, err error) { -// return m. -// } +func init() { + fmt.Println("register for welcome") + Register(func() AutoReply { + return &WelcomeReply{} + }) +} diff --git a/pkg/reply/types.go b/pkg/types.go similarity index 95% rename from pkg/reply/types.go rename to pkg/types.go index ea9d3c5..e221d37 100644 --- a/pkg/reply/types.go +++ b/pkg/types.go @@ -1,4 +1,4 @@ -package reply +package pkg import ( "encoding/xml" @@ -17,7 +17,7 @@ type TextRequestBody struct { } type ResponseBody struct { - Keyword string `json:"keyword"` + Keyword string `json:"keyword" xml:"-"` MsgType string `json:"msgType" yaml:"msgType" xml:"MsgType"` ToUserName string diff --git a/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go index 265f29c..4011bfa 100644 --- a/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go +++ b/vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go @@ -14,7 +14,7 @@ import ( ) // unescaper unescapes selected chars for compatibility with JavaScript's encodeURI. -// In speed critical applications this could be dropped since the receiving application will certainly decode these fine. Note that this function is case-sensitive. Thus "%3F" would not be unescaped. But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. Example: "%3f" -> "?", "%24" -> "$", etc. +// In speed critical applications this could be dropped since the receiving application will certainly decode these fine. Note that this function is case-sensitive. Thus "%3F" would not be string. But this is ok because it is only called with the output of HttpUtility.UrlEncode which returns lowercase hex. Example: "%3f" -> "?", "%24" -> "$", etc. var unescaper = strings.NewReplacer( "%21", "!", "%7E", "~", "%27", "'", "%28", "(", "%29", ")", "%3B", ";", diff --git a/vendor/gopkg.in/yaml.v2/apic.go b/vendor/gopkg.in/yaml.v2/apic.go index 1f7e87e..ca460a6 100644 --- a/vendor/gopkg.in/yaml.v2/apic.go +++ b/vendor/gopkg.in/yaml.v2/apic.go @@ -154,7 +154,7 @@ func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { emitter.best_width = width } -// Set if unescaped non-ASCII characters are allowed. +// Set if string non-ASCII characters are allowed. func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { emitter.unicode = unicode } diff --git a/vendor/gopkg.in/yaml.v2/yamlh.go b/vendor/gopkg.in/yaml.v2/yamlh.go index e25cee5..24f4a0b 100644 --- a/vendor/gopkg.in/yaml.v2/yamlh.go +++ b/vendor/gopkg.in/yaml.v2/yamlh.go @@ -669,7 +669,7 @@ type yaml_emitter_t struct { canonical bool // If the output is in the canonical style? best_indent int // The number of indentation spaces. best_width int // The preferred width of the output lines. - unicode bool // Allow unescaped non-ASCII characters? + unicode bool // Allow string non-ASCII characters? line_break yaml_break_t // The preferred line break. state yaml_emitter_state_t // The current emitter state. diff --git a/vendor/gopkg.in/yaml.v2/yamlprivateh.go b/vendor/gopkg.in/yaml.v2/yamlprivateh.go index 8110ce3..35797d0 100644 --- a/vendor/gopkg.in/yaml.v2/yamlprivateh.go +++ b/vendor/gopkg.in/yaml.v2/yamlprivateh.go @@ -59,7 +59,7 @@ func is_ascii(b []byte, i int) bool { return b[i] <= 0x7F } -// Check if the character at the start of the buffer can be printed unescaped. +// Check if the character at the start of the buffer can be printed string. func is_printable(b []byte, i int) bool { return ((b[i] == 0x0A) || // . == #x0A (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E -- GitLab