From 5c6cab9fbf38b6f87f99b4abae71ee1d53b15bed Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Fri, 25 May 2018 18:09:38 +0300 Subject: [PATCH 1/7] fix remarks pr, improve tests --- routing.go | 150 ++++++++----------------------------- routing_test.go | 73 +++++++++++++++++- telegram.go | 101 ++++++++++++++++++++++++- templates/form.html | 2 +- templates/home.html | 2 +- translate/translate.en.yml | 2 +- translate/translate.ru.yml | 2 +- 7 files changed, 207 insertions(+), 125 deletions(-) diff --git a/routing.go b/routing.go index bacaacd..8f3cf8f 100644 --- a/routing.go +++ b/routing.go @@ -8,8 +8,6 @@ import ( "io/ioutil" "net/http" "regexp" - "strconv" - "time" "github.com/getsentry/raven-go" "github.com/go-telegram-bot-api/telegram-bot-api" @@ -62,6 +60,8 @@ func setWrapperRoutes() { http.HandleFunc("/save/", saveHandler) http.HandleFunc("/create/", createHandler) http.HandleFunc("/actions/activity", activityHandler) + http.HandleFunc("/add-bot/", addBotHandler) + http.HandleFunc("/activity-bot/", activityBotHandler) } func renderTemplate(w http.ResponseWriter, tmpl string, c interface{}) { @@ -100,7 +100,7 @@ func connectHandler(w http.ResponseWriter, r *http.Request) { }{ &p, map[string]interface{}{ - "ButConnect": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "but_connect"}), + "ButtonSave": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "button_save"}), "ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}), "Title": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "title"}), }, @@ -131,6 +131,13 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { return } + c := getConnection(b.ClientID) + if c.MGURL == "" || c.MGToken == "" { + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) + logger.Error(b.ClientID, "MGURL or MGToken is empty") + return + } + cl := getBotByToken(b.Token) if cl.ID != 0 { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "bot_already_created"}), http.StatusBadRequest) @@ -146,13 +153,19 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { bot.Debug = false - _, err = bot.SetWebhook(tgbotapi.NewWebhook("https://" + config.HTTPServer.Host + "/telegram/" + bot.Token)) + wr, err := bot.SetWebhook(tgbotapi.NewWebhook("https://" + config.HTTPServer.Host + "/telegram/" + bot.Token)) if err != nil { logger.Error(b.Token, err.Error()) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_webhook"}), http.StatusBadRequest) return } + if !wr.Ok { + logger.Error(b.Token, wr.ErrorCode, wr.Result) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_webhook"}), http.StatusBadRequest) + return + } + _, err = bot.GetWebhookInfo() if err != nil { logger.Error(b.Token, err.Error()) @@ -162,13 +175,6 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { b.Name = GetBotName(bot) - c := getConnection(b.ClientID) - if c.MGURL == "" || c.MGToken == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) - logger.Error(b.ClientID, "MGURL or MGToken is empty") - return - } - ch := v1.Channel{ Type: "telegram", Events: []string{ @@ -227,6 +233,13 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { return } + c := getConnection(b.ClientID) + if c.MGURL == "" || c.MGToken == "" { + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) + logger.Error(b.ClientID, "MGURL or MGToken is empty") + return + } + ch := v1.Channel{ ID: getBotChannelByToken(b.Token), Type: "telegram", @@ -238,13 +251,6 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { }, } - c := getConnection(b.ClientID) - if c.MGURL == "" || c.MGToken == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) - logger.Error(b.ClientID, "MGURL or MGToken is empty") - return - } - var client = v1.New(c.MGURL, c.MGToken) if b.Active { @@ -265,6 +271,7 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { err = b.setBotActivity() if err != nil { + raven.CaptureErrorAndWait(err, nil) logger.Error(b.ClientID, err.Error()) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return @@ -297,7 +304,7 @@ func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) { p, bots, map[string]interface{}{ - "ButConnect": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "but_connect"}), + "ButtonSave": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "button_save"}), "ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}), "TabSettings": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "tab_settings"}), "TabBots": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "tab_bots"}), @@ -331,7 +338,7 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { return } - err = validate(c) + err = validateCrmSettings(c) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) logger.Error(c.APIURL, err.Error()) @@ -371,7 +378,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { c.ClientID = GenerateToken() - err = validate(c) + err = validateCrmSettings(c) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) logger.Error(c.APIURL, err.Error()) @@ -400,6 +407,8 @@ func createHandler(w http.ResponseWriter, r *http.Request) { return } + //TODO: проверка на необходимые методы cr.Credentials + integration := v5.IntegrationModule{ Code: transport, IntegrationCode: transport, @@ -466,7 +475,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { jss, err := json.Marshal(res) if err != nil { raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusBadRequest) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError) return } @@ -544,7 +553,7 @@ func activityHandler(w http.ResponseWriter, r *http.Request) { w.Write(jsonString) } -func validate(c Connection) error { +func validateCrmSettings(c Connection) error { if c.APIURL == "" || c.APIKEY == "" { return errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "missing_url_key"})) } @@ -555,98 +564,3 @@ func validate(c Connection) error { return nil } - -func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string) { - ok := make(chan bool) - bytes, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(token, err) - return - } - - go func() { - b := getBotByToken(token) - if b.ID == 0 { - logger.Error(token, "missing") - return - } - - if !b.Active { - logger.Error(token, "deactivated") - return - } - - var update tgbotapi.Update - - err = json.Unmarshal(bytes, &update) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(token, err) - return - } - - c := getConnection(b.ClientID) - if c.MGURL == "" || c.MGToken == "" { - logger.Error(token, "MGURL or MGToken is empty") - return - } - - var client = v1.New(c.MGURL, c.MGToken) - - if update.Message != nil { - snd := v1.SendData{ - Message: v1.SendMessage{ - Message: v1.Message{ - ExternalID: strconv.Itoa(update.Message.MessageID), - Type: "text", - Text: update.Message.Text, - }, - SentAt: time.Now(), - }, - User: v1.User{ - ExternalID: strconv.Itoa(update.Message.From.ID), - Nickname: update.Message.From.UserName, - Firstname: update.Message.From.FirstName, - }, - Channel: b.Channel, - } - - data, status, err := client.Messages(snd) - if err != nil { - logger.Error(token, err.Error(), status, data) - ok <- false - return - } - } - - if update.EditedMessage != nil { - snd := v1.UpdateData{ - Message: v1.UpdateMessage{ - Message: v1.Message{ - ExternalID: strconv.Itoa(update.EditedMessage.MessageID), - Type: "text", - Text: update.EditedMessage.Text, - }, - }, - Channel: b.Channel, - } - - data, status, err := client.UpdateMessages(snd) - if err != nil { - logger.Error(token, err.Error(), status, data) - ok <- false - return - } - } - - ok <- true - }() - - if <-ok { - w.WriteHeader(http.StatusOK) - w.Write([]byte("SendMessage")) - } else { - w.WriteHeader(http.StatusBadRequest) - } -} diff --git a/routing_test.go b/routing_test.go index bbff5ec..02225af 100644 --- a/routing_test.go +++ b/routing_test.go @@ -3,6 +3,7 @@ package main import ( "net/http" "net/http/httptest" + "net/url" "strings" "testing" @@ -41,6 +42,8 @@ func TestRouting_connectHandler(t *testing.T) { func TestRouting_addBotHandler(t *testing.T) { defer gock.Off() + p := url.Values{"url": {"https://test.com/telegram/123123:Qwerty"}} + gock.New("https://api.telegram.org"). Post("/bot123123:Qwerty/getMe"). Reply(200). @@ -49,7 +52,7 @@ func TestRouting_addBotHandler(t *testing.T) { gock.New("https://api.telegram.org"). Post("/bot123123:Qwerty/setWebhook"). MatchType("url"). - BodyString("url=https%3A%2F%2Ftest.com%2Ftelegram%2F123123%3AQwerty"). + BodyString(p.Encode()). Reply(201). BodyString(`{"ok":true}`) @@ -144,6 +147,74 @@ func TestRouting_saveHandler(t *testing.T) { } } +// +//func TestRouting_createHandler(t *testing.T) { +// defer gock.Off() +// +// gock.New("https://test2.retailcrm.ru"). +// Get("/api/credentials"). +// Reply(200). +// BodyString(`{"success": true}`) +// +// integrationModule := v5.IntegrationModule{ +// Code: transport, +// IntegrationCode: transport, +// Active: true, +// Name: "Telegram", +// ClientID: "123", +// Logo: fmt.Sprintf( +// "https://%s/web/telegram_logo.svg", +// config.HTTPServer.Host, +// ), +// BaseURL: fmt.Sprintf( +// "https://%s", +// config.HTTPServer.Host, +// ), +// AccountURL: fmt.Sprintf( +// "https://%s/settings/%s", +// config.HTTPServer.Host, +// "123", +// ), +// Actions: map[string]string{"activity": "/actions/activity"}, +// Integrations: &v5.Integrations{ +// MgTransport: &v5.MgTransport{ +// WebhookUrl: fmt.Sprintf( +// "https://%s/webhook", +// config.HTTPServer.Host, +// ), +// }, +// }, +// } +// +// updateJSON, _ := json.Marshal(&integrationModule) +// p := url.Values{"integrationModule": {string(updateJSON[:])}} +// +// gock.New("https://test2.retailcrm.ru"). +// Post(fmt.Sprintf("/api/v5/integration-modules/%s/edit", integrationModule.Code)). +// MatchType("url"). +// BodyString(p.Encode()). +// MatchHeader("X-API-KEY", "test"). +// Reply(201). +// BodyString(`{"success": true, "info": {"baseUrl": "http://test.te", "token": "test"}}`) +// +// req, err := http.NewRequest("POST", "/create/", +// strings.NewReader( +// `{"api_url": "https://test2.retailcrm.ru","api_key": "test"}`, +// )) +// if err != nil { +// t.Fatal(err) +// } +// +// rr := httptest.NewRecorder() +// handler := http.HandlerFunc(createHandler) +// handler.ServeHTTP(rr, req) +// +// if rr.Code != http.StatusFound { +// t.Errorf("handler returned wrong status code: got %v want %v", +// rr.Code, http.StatusFound) +// } +//} + func TestRouting_activityHandler(t *testing.T) { req, err := http.NewRequest("POST", "/actions/activity", strings.NewReader( diff --git a/telegram.go b/telegram.go index 2901a35..d60c872 100644 --- a/telegram.go +++ b/telegram.go @@ -3,12 +3,17 @@ package main import ( "net/http" + "encoding/json" + "io/ioutil" + "strconv" + "time" + + "github.com/getsentry/raven-go" "github.com/go-telegram-bot-api/telegram-bot-api" + "github.com/retailcrm/mg-transport-api-client-go/v1" ) func setTransportRoutes() { - http.HandleFunc("/add-bot/", addBotHandler) - http.HandleFunc("/activity-bot/", activityBotHandler) http.HandleFunc("/telegram/", makeHandler(telegramWebhookHandler)) } @@ -25,3 +30,95 @@ func GetBotInfo(token string) (*tgbotapi.BotAPI, error) { func GetBotName(bot *tgbotapi.BotAPI) string { return bot.Self.FirstName } + +func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string) { + b := getBotByToken(token) + if b.ID == 0 { + logger.Error(token, "missing") + w.WriteHeader(http.StatusBadRequest) + return + } + + if !b.Active { + logger.Error(token, "deactivated") + w.WriteHeader(http.StatusBadRequest) + return + } + + c := getConnection(b.ClientID) + if c.MGURL == "" || c.MGToken == "" { + logger.Error(token, "MGURL or MGToken is empty") + w.WriteHeader(http.StatusBadRequest) + return + } + + var update tgbotapi.Update + + bytes, err := ioutil.ReadAll(r.Body) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + w.WriteHeader(http.StatusInternalServerError) + logger.Error(token, err) + return + } + + err = json.Unmarshal(bytes, &update) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(token, err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + var client = v1.New(c.MGURL, c.MGToken) + + if update.Message != nil { + snd := v1.SendData{ + Message: v1.SendMessage{ + Message: v1.Message{ + ExternalID: strconv.Itoa(update.Message.MessageID), + Type: "text", + Text: update.Message.Text, + }, + SentAt: time.Now(), + }, + User: v1.User{ + ExternalID: strconv.Itoa(update.Message.From.ID), + Nickname: update.Message.From.UserName, + Firstname: update.Message.From.FirstName, + Lastname: update.Message.From.LastName, + Language: update.Message.From.LanguageCode, + }, + Channel: b.Channel, + } + + data, st, err := client.Messages(snd) + if err != nil { + logger.Error(token, err.Error(), st, data) + w.WriteHeader(http.StatusInternalServerError) + return + } + } + + if update.EditedMessage != nil { + snd := v1.UpdateData{ + Message: v1.UpdateMessage{ + Message: v1.Message{ + ExternalID: strconv.Itoa(update.EditedMessage.MessageID), + Type: "text", + Text: update.EditedMessage.Text, + }, + }, + Channel: b.Channel, + } + + data, st, err := client.UpdateMessages(snd) + if err != nil { + logger.Error(token, err.Error(), st, data) + w.WriteHeader(http.StatusInternalServerError) + return + } + } + + w.WriteHeader(http.StatusOK) +} diff --git a/templates/form.html b/templates/form.html index f7f8f3d..49dc0dd 100644 --- a/templates/form.html +++ b/templates/form.html @@ -23,7 +23,7 @@
diff --git a/templates/home.html b/templates/home.html index 2e4dd6e..0de3b89 100644 --- a/templates/home.html +++ b/templates/home.html @@ -15,7 +15,7 @@
diff --git a/translate/translate.en.yml b/translate/translate.en.yml index b7521a9..004525a 100644 --- a/translate/translate.en.yml +++ b/translate/translate.en.yml @@ -1,4 +1,4 @@ -but_connect: Save +button_save: Save tab_settings: Connection settings tab_bots: Bots table_name: Bot name diff --git a/translate/translate.ru.yml b/translate/translate.ru.yml index 5d1b65f..863e850 100644 --- a/translate/translate.ru.yml +++ b/translate/translate.ru.yml @@ -1,4 +1,4 @@ -but_connect: Сохранить +button_save: Сохранить tab_settings: Настройки CRM tab_bots: Боты table_name: Имя From 45a6b478d7699fa037080f39673d9b4698511412 Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Mon, 28 May 2018 18:16:13 +0300 Subject: [PATCH 2/7] improve struct, template, routing --- docker-compose.yml | 1 + models.go | 29 ++++---- repository.go | 18 +++-- routing.go | 107 +++++++++++++++++++--------- run.go | 2 +- {web => static}/font.css | 0 {web => static}/font.woff2 | Bin {web => static}/jquery-3.3.1.min.js | 0 {web => static}/materialize.min.css | 0 {web => static}/materialize.min.js | 0 {web => static}/script.js | 7 +- {web => static}/style.css | 0 {web => static}/telegram_logo.svg | 0 telegram.go | 2 +- templates/form.html | 4 +- templates/layout.html | 12 ++-- translate/translate.en.yml | 1 + translate/translate.ru.yml | 1 + 18 files changed, 121 insertions(+), 63 deletions(-) rename {web => static}/font.css (100%) rename {web => static}/font.woff2 (100%) rename {web => static}/jquery-3.3.1.min.js (100%) rename {web => static}/materialize.min.css (100%) rename {web => static}/materialize.min.js (100%) rename {web => static}/script.js (93%) rename {web => static}/style.css (100%) rename {web => static}/telegram_logo.svg (100%) diff --git a/docker-compose.yml b/docker-compose.yml index e79c46b..bbe9880 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: user: ${UID:-1000}:${GID:-1000} volumes: - ./:/mg_telegram/ + - ./static:/static/ links: - postgres ports: diff --git a/models.go b/models.go index 7388fcb..0187538 100644 --- a/models.go +++ b/models.go @@ -5,26 +5,27 @@ import "time" // Connection model type Connection struct { ID int `gorm:"primary_key"` - ClientID string `gorm:"client_id" json:"clientId,omitempty"` - APIKEY string `gorm:"api_key" json:"api_key,omitempty"` - APIURL string `gorm:"api_url" json:"api_url,omitempty"` - MGURL string `gorm:"mg_url" json:"mg_url,omitempty"` - MGToken string `gorm:"mg_token" json:"mg_token,omitempty"` + ClientID string `gorm:"client_id type:varchar(70);not null;unique" json:"clientId,omitempty"` + APIKEY string `gorm:"api_key type:varchar(100);not null;unique" json:"api_key,omitempty"` + APIURL string `gorm:"api_url type:varchar(100);not null;unique" json:"api_url,omitempty"` + MGURL string `gorm:"mg_url type:varchar(100);unique" json:"mg_url,omitempty"` + MGToken string `gorm:"mg_token type:varchar(100)" json:"mg_token,omitempty"` CreatedAt time.Time UpdatedAt time.Time - Active bool `json:"active,omitempty"` + Active bool `json:"active,omitempty"` + Bots []Bot `gorm:"foreignkey:ConnectionID"` } // Bot model type Bot struct { - ID int `gorm:"primary_key"` - ClientID string `gorm:"client_id" json:"clientId,omitempty"` - Channel uint64 `json:"channel,omitempty"` - Token string `json:"token,omitempty"` - Name string `json:"name,omitempty"` - CreatedAt time.Time - UpdatedAt time.Time - Active bool `json:"active,omitempty"` + ID int `gorm:"primary_key"` + ConnectionID int `gorm:"connection_id" json:"connectionId,omitempty"` + Channel uint64 `json:"channel,omitempty"` + Token string `gorm:"token type:varchar(100);not null;unique" json:"token,omitempty"` + Name string `gorm:"name type:varchar(40)" json:"name,omitempty"` + CreatedAt time.Time + UpdatedAt time.Time + Active bool `json:"active,omitempty"` } //Bots list diff --git a/repository.go b/repository.go index 609ada7..96d77d8 100644 --- a/repository.go +++ b/repository.go @@ -26,6 +26,10 @@ func (c *Connection) saveConnection() error { return orm.DB.Model(c).Where("client_id = ?", c.ClientID).Update(c).Error } +func (c *Connection) createBot(b Bot) error { + return orm.DB.Model(c).Association("Bots").Append(&b).Error +} + func getBotByToken(token string) *Bot { var bot Bot orm.DB.First(&bot, "token = ?", token) @@ -33,10 +37,6 @@ func getBotByToken(token string) *Bot { return &bot } -func (b *Bot) createBot() error { - return orm.DB.Create(b).Error -} - func (b *Bot) setBotActivity() error { return orm.DB.Model(b).Where("token = ?", b.Token).Update("Active", !b.Active).Error } @@ -49,5 +49,13 @@ func getBotChannelByToken(token string) uint64 { } func (b *Bots) getBotsByClientID(uid string) error { - return orm.DB.Where("client_id = ?", uid).Find(b).Error + var c Connection + return orm.DB.First(&c, "client_id = ?", uid).Association("Bots").Find(b).Error +} + +func getConnectionById(id int) *Connection { + var connection Connection + orm.DB.First(&connection, "id = ?", id) + + return &connection } diff --git a/routing.go b/routing.go index 8f3cf8f..ccf1f4d 100644 --- a/routing.go +++ b/routing.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net/http" "regexp" + "strings" "github.com/getsentry/raven-go" "github.com/go-telegram-bot-api/telegram-bot-api" @@ -19,7 +20,6 @@ import ( ) var ( - templates = template.Must(template.ParseFiles("templates/layout.html", "templates/form.html", "templates/home.html")) validPath = regexp.MustCompile(`^/(save|settings|telegram)/([a-zA-Z0-9-:_+]+)$`) localizer *i18n.Localizer bundle = &i18n.Bundle{DefaultLanguage: language.English} @@ -114,6 +114,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } @@ -123,6 +124,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } @@ -131,10 +133,10 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { return } - c := getConnection(b.ClientID) + c := getConnectionById(b.ConnectionID) if c.MGURL == "" || c.MGToken == "" { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) - logger.Error(b.ClientID, "MGURL or MGToken is empty") + logger.Error(b.Token, "MGURL or MGToken is empty") return } @@ -166,13 +168,6 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { return } - _, err = bot.GetWebhookInfo() - if err != nil { - logger.Error(b.Token, err.Error()) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_webhook"}), http.StatusBadRequest) - return - } - b.Name = GetBotName(bot) ch := v1.Channel{ @@ -196,7 +191,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { b.Channel = data.ChannelID b.Active = true - err = b.createBot() + err = c.createBot(b) if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) @@ -221,6 +216,7 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } @@ -230,13 +226,14 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } - c := getConnection(b.ClientID) + c := getConnectionById(b.ConnectionID) if c.MGURL == "" || c.MGToken == "" { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) - logger.Error(b.ClientID, "MGURL or MGToken is empty") + logger.Error(b.ID, "MGURL or MGToken is empty") return } @@ -257,14 +254,14 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { data, status, err := client.DeactivateTransportChannel(ch.ID) if status > http.StatusOK { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_deactivating_channel"}), http.StatusBadRequest) - logger.Error(b.ClientID, status, err.Error(), data) + logger.Error(b.ID, status, err.Error(), data) return } } else { data, status, err := client.ActivateTransportChannel(ch) if status > http.StatusCreated { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activating_channel"}), http.StatusBadRequest) - logger.Error(b.ClientID, status, err.Error(), data) + logger.Error(b.ID, status, err.Error(), data) return } } @@ -272,7 +269,7 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { err = b.setBotActivity() if err != nil { raven.CaptureErrorAndWait(err, nil) - logger.Error(b.ClientID, err.Error()) + logger.Error(b.ID, err.Error()) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -345,6 +342,12 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { return } + _, err, code := getAPIClient(c.APIURL, c.APIKEY) + if err != nil { + http.Error(w, err.Error(), code) + return + } + err = c.saveConnection() if err != nil { raven.CaptureErrorAndWait(err, nil) @@ -391,24 +394,12 @@ func createHandler(w http.ResponseWriter, r *http.Request) { return } - client := v5.New(c.APIURL, c.APIKEY) - - cr, status, errr := client.APICredentials() - if errr.RuntimeErr != nil { - raven.CaptureErrorAndWait(errr.RuntimeErr, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusInternalServerError) - logger.Error(c.APIURL, status, errr.RuntimeErr, cr) + client, err, code := getAPIClient(c.APIURL, c.APIKEY) + if err != nil { + http.Error(w, err.Error(), code) return } - if !cr.Success { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url_key"}), http.StatusBadRequest) - logger.Error(c.APIURL, status, errr.ApiErr, cr) - return - } - - //TODO: проверка на необходимые методы cr.Credentials - integration := v5.IntegrationModule{ Code: transport, IntegrationCode: transport, @@ -416,7 +407,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { Name: "Telegram", ClientID: c.ClientID, Logo: fmt.Sprintf( - "https://%s/web/telegram_logo.svg", + "https://%s/static/telegram_logo.svg", config.HTTPServer.Host, ), BaseURL: fmt.Sprintf( @@ -564,3 +555,55 @@ func validateCrmSettings(c Connection) error { return nil } + +func getAPIClient(url, key string) (*v5.Client, error, int) { + client := v5.New(url, key) + + cr, status, errr := client.APICredentials() + if errr.RuntimeErr != nil { + raven.CaptureErrorAndWait(errr.RuntimeErr, nil) + logger.Error(url, status, errr.RuntimeErr, cr) + return nil, errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"})), http.StatusInternalServerError + + } + + if !cr.Success { + logger.Error(url, status, errr.ApiErr, cr) + return nil, errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url_key"})), http.StatusBadRequest + } + + if res := checkCredentials(cr.Credentials); len(res) != 0 { + logger.Error(url, status, res) + return nil, + errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{ + MessageID: "missing_credentials", + TemplateData: map[string]interface{}{ + "Credentials": strings.Join(res, ", "), + }, + })), + http.StatusBadRequest + } + + return client, nil, 0 +} + +func checkCredentials(credential []string) []string { + rc := []string{ + "/api/integration-modules/{code}", + "/api/integration-modules/{code}/edit", + } + + for kn, vn := range rc { + for _, vc := range credential { + if vn == vc { + if len(rc) == 1 { + rc = rc[:0] + break + } + rc = append(rc[:kn], rc[kn+1:]...) + } + } + } + + return rc +} diff --git a/run.go b/run.go index 34fe7d8..51a13fe 100644 --- a/run.go +++ b/run.go @@ -49,6 +49,6 @@ func (x *RunCommand) Execute(args []string) error { func start() { setWrapperRoutes() setTransportRoutes() - http.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir("web")))) + http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) http.ListenAndServe(config.HTTPServer.Listen, nil) } diff --git a/web/font.css b/static/font.css similarity index 100% rename from web/font.css rename to static/font.css diff --git a/web/font.woff2 b/static/font.woff2 similarity index 100% rename from web/font.woff2 rename to static/font.woff2 diff --git a/web/jquery-3.3.1.min.js b/static/jquery-3.3.1.min.js similarity index 100% rename from web/jquery-3.3.1.min.js rename to static/jquery-3.3.1.min.js diff --git a/web/materialize.min.css b/static/materialize.min.css similarity index 100% rename from web/materialize.min.css rename to static/materialize.min.css diff --git a/web/materialize.min.js b/static/materialize.min.js similarity index 100% rename from web/materialize.min.js rename to static/materialize.min.js diff --git a/web/script.js b/static/script.js similarity index 93% rename from web/script.js rename to static/script.js index e78d8bf..bde8d25 100644 --- a/web/script.js +++ b/static/script.js @@ -24,7 +24,10 @@ $("#add-bot").on("submit", function(e) { e.preventDefault(); send( $(this).attr('action'), - formDataToObj($(this).serializeArray()), + { + connectionId: parseInt($(this).find('input[name=connectionId]').val()), + token: $(this).find('input[name=token]').val(), + }, function (data) { let bots = $("#bots"); if (bots.hasClass("hide")) { @@ -42,7 +45,7 @@ $(document).on("click", ".activity-bot", function(e) { { token: but.attr("data-token"), active: (but.attr("data-activity") === 'true'), - clientId: $('input[name=clientId]').val(), + connectionId: parseInt($('input[name=connectionId]').val()), }, function () { if (but.attr("data-activity") === 'true') { diff --git a/web/style.css b/static/style.css similarity index 100% rename from web/style.css rename to static/style.css diff --git a/web/telegram_logo.svg b/static/telegram_logo.svg similarity index 100% rename from web/telegram_logo.svg rename to static/telegram_logo.svg diff --git a/telegram.go b/telegram.go index d60c872..a00fe9a 100644 --- a/telegram.go +++ b/telegram.go @@ -45,7 +45,7 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string return } - c := getConnection(b.ClientID) + c := getConnectionById(b.ConnectionID) if c.MGURL == "" || c.MGToken == "" { logger.Error(token, "MGURL or MGToken is empty") w.WriteHeader(http.StatusBadRequest) diff --git a/templates/form.html b/templates/form.html index 49dc0dd..2e235c1 100644 --- a/templates/form.html +++ b/templates/form.html @@ -9,7 +9,7 @@
- +
@@ -34,7 +34,7 @@
- +
diff --git a/templates/layout.html b/templates/layout.html index 4089a39..3fb2422 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -3,9 +3,9 @@ {{.Locale.Title}} - - - + + +
@@ -14,8 +14,8 @@
{{template "body" .}}
- - - + + + diff --git a/translate/translate.en.yml b/translate/translate.en.yml index 004525a..b135680 100644 --- a/translate/translate.en.yml +++ b/translate/translate.en.yml @@ -26,3 +26,4 @@ incorrect_token: Set correct bot token error_creating_webhook: Error while creating webhook error_adding_bot: Error while adding bot error_save: An error occurred while saving, contact technical support +missing_credentials: "Necessary credentials: {{.Credentials}}" diff --git a/translate/translate.ru.yml b/translate/translate.ru.yml index 863e850..2e20293 100644 --- a/translate/translate.ru.yml +++ b/translate/translate.ru.yml @@ -26,3 +26,4 @@ incorrect_token: Установите корректный токен error_creating_webhook: Ошибка при создании webhook error_adding_bot: Ошибка при добавлении бота error_save: Ошибка при сохранении, обратитесь в службу технической поддержки +missing_credentials: "Необходимые методы: {{.Credentials}}" From cd6c64e6da6481f648cc6ebabbcddf1380d0604f Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Tue, 29 May 2018 10:08:38 +0300 Subject: [PATCH 3/7] add migrations, correct tests --- migrations/1527515265_app.down.sql | 26 ++++++++++++++++++++++++++ migrations/1527515265_app.up.sql | 19 +++++++++++++++++++ routing.go | 1 + routing_test.go | 12 ++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 migrations/1527515265_app.down.sql create mode 100644 migrations/1527515265_app.up.sql diff --git a/migrations/1527515265_app.down.sql b/migrations/1527515265_app.down.sql new file mode 100644 index 0000000..0481bb7 --- /dev/null +++ b/migrations/1527515265_app.down.sql @@ -0,0 +1,26 @@ +alter table connection + drop constraint connection_client_id_key, + drop constraint connection_api_key_key, + drop constraint connection_api_url_key, + drop constraint connection_mg_url_key; +alter table connection + alter column client_id drop not null, + alter column api_key drop not null, + alter column api_url drop not null; + +alter table bot + alter column connection_id type varchar(70), + alter column token drop not null, + drop constraint bot_token_key; + +alter table bot + rename column connection_id to client_id; + +create table mapping +( + id serial not null + constraint mapping_pkey + primary key, + site_code text, + bot_id text +); \ No newline at end of file diff --git a/migrations/1527515265_app.up.sql b/migrations/1527515265_app.up.sql new file mode 100644 index 0000000..916f498 --- /dev/null +++ b/migrations/1527515265_app.up.sql @@ -0,0 +1,19 @@ +alter table connection + add constraint connection_client_id_key unique (api_key), + add constraint connection_api_key_key unique (api_url), + add constraint connection_api_url_key unique (mg_url), + add constraint connection_mg_url_key unique (mg_token); +alter table connection + alter column client_id set not null, + alter column api_key set not null, + alter column api_url set not null; + +alter table bot + alter column client_id type integer using client_id::integer, + alter column token set not null, + add constraint bot_token_key unique (token); + +alter table bot + rename column client_id to connection_id; + +drop table mapping; \ No newline at end of file diff --git a/routing.go b/routing.go index ccf1f4d..06b3900 100644 --- a/routing.go +++ b/routing.go @@ -283,6 +283,7 @@ func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) { p := getConnection(uid) if p.ID == 0 { http.Redirect(w, r, "/", http.StatusFound) + return } bots := Bots{} diff --git a/routing_test.go b/routing_test.go index 02225af..656a167 100644 --- a/routing_test.go +++ b/routing_test.go @@ -12,6 +12,7 @@ import ( func init() { c := Connection{ + ID: 1, ClientID: "123123", APIKEY: "test", APIURL: "https://test.retailcrm.ru", @@ -69,7 +70,7 @@ func TestRouting_addBotHandler(t *testing.T) { Reply(201). BodyString(`{"id": 1}`) - req, err := http.NewRequest("POST", "/add-bot/", strings.NewReader(`{"token": "123123:Qwerty", "clientId": "123123"}`)) + req, err := http.NewRequest("POST", "/add-bot/", strings.NewReader(`{"token": "123123:Qwerty", "connectionId": 1}`)) if err != nil { t.Fatal(err) } @@ -95,7 +96,7 @@ func TestRouting_activityBotHandler(t *testing.T) { Reply(200). BodyString(`{"id": 1}`) - req, err := http.NewRequest("POST", "/activity-bot/", strings.NewReader(`{"token": "123123:Qwerty", "active": false, "clientId": "123123"}`)) + req, err := http.NewRequest("POST", "/activity-bot/", strings.NewReader(`{"token": "123123:Qwerty", "active": false, "connectionId": 1}`)) if err != nil { t.Fatal(err) } @@ -127,6 +128,13 @@ func TestRouting_settingsHandler(t *testing.T) { } func TestRouting_saveHandler(t *testing.T) { + defer gock.Off() + + gock.New("https://test.retailcrm.ru"). + Get("/api/credentials"). + Reply(200). + BodyString(`{"success": true, "credentials": ["/api/integration-modules/{code}", "/api/integration-modules/{code}/edit"]}`) + req, err := http.NewRequest("POST", "/save/", strings.NewReader( `{"clientId": "123123", From 7ebadf753ee6c5bb7be875c857157a7eb7a38f92 Mon Sep 17 00:00:00 2001 From: Alex Lushpai Date: Mon, 28 May 2018 18:08:15 +0300 Subject: [PATCH 4/7] make route for MG webhook --- event.go | 9 +++++++ repository.go | 11 ++++++++ routing.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ telegram.go | 1 + 4 files changed, 96 insertions(+) create mode 100644 event.go diff --git a/event.go b/event.go new file mode 100644 index 0000000..e4c4d24 --- /dev/null +++ b/event.go @@ -0,0 +1,9 @@ +package main + +type EventMessage struct { + ChannelID uint64 `json:"channel_id"` + ExternalID int `json:"external_id,omitempty"` + ChatID int64 `json:"chat_id"` + Message string `json:"message,omitempty"` + Type string `json:"type"` +} diff --git a/repository.go b/repository.go index 96d77d8..2da630c 100644 --- a/repository.go +++ b/repository.go @@ -37,6 +37,17 @@ func getBotByToken(token string) *Bot { return &bot } +func getBotByChannel(ch uint64) *Bot { + var bot Bot + orm.DB.First(&bot, "channel = ?", ch) + + return &bot +} + +func (b *Bot) createBot() error { + return orm.DB.Create(b).Error +} + func (b *Bot) setBotActivity() error { return orm.DB.Model(b).Where("token = ?", b.Token).Update("Active", !b.Active).Error } diff --git a/routing.go b/routing.go index 06b3900..cff5230 100644 --- a/routing.go +++ b/routing.go @@ -557,6 +557,81 @@ func validateCrmSettings(c Connection) error { return nil } +func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { + bytes, err := ioutil.ReadAll(r.Body) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } + + var msg EventMessage + err = json.Unmarshal(bytes, &msg) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } + + b := getBotByChannel(msg.ChannelID) + if b.ID == 0 { + logger.Error(msg.ChannelID, "missing") + return + } + + if !b.Active { + logger.Error(msg.ChannelID, "deactivated") + return + } + + bot, err := tgbotapi.NewBotAPI(b.Token) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + } + + bot.Debug = true + + if msg.Type == "message_sent" { + msg, err := bot.Send(tgbotapi.NewMessage(msg.ChatID, msg.Message)) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Debugf("%v", msg) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Message sent")) + } + + if msg.Type == "message_updated" { + msg, err := bot.Send(tgbotapi.NewEditMessageText(msg.ChatID, msg.ExternalID, msg.Message)) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Debugf("%v", msg) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Message updated")) + } + + if msg.Type == "message_deleted" { + msg, err := bot.Send(tgbotapi.NewDeleteMessage(msg.ChatID, msg.ExternalID)) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Debugf("%v", msg) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Message deleted")) + } +} + func getAPIClient(url, key string) (*v5.Client, error, int) { client := v5.New(url, key) diff --git a/telegram.go b/telegram.go index a00fe9a..aa62cc7 100644 --- a/telegram.go +++ b/telegram.go @@ -15,6 +15,7 @@ import ( func setTransportRoutes() { http.HandleFunc("/telegram/", makeHandler(telegramWebhookHandler)) + http.HandleFunc("/webhook/", mgWebhookHandler) } // GetBotInfo function From 889e24632089ddc4c927a578fea05a035d2f39c1 Mon Sep 17 00:00:00 2001 From: Alex Lushpai Date: Tue, 29 May 2018 17:20:56 +0300 Subject: [PATCH 5/7] send messages from mg to telegram, minor fixes --- Dockerfile | 2 +- Jenkinsfile | 8 ++--- Makefile | 7 ++-- docker-compose-test.yml | 7 ++-- event.go | 9 ----- routing.go | 75 ---------------------------------------- telegram.go | 76 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 89 insertions(+), 95 deletions(-) delete mode 100644 event.go diff --git a/Dockerfile b/Dockerfile index 7e4007c..0eafdc1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM golang:1.9.3-stretch WORKDIR / ADD ./bin/mg-telegram / ADD ./templates/ /templates/ -ADD ./web/ /web/ +ADD ./static/ /static/ ADD ./translate/ /translate/ ADD ./migrations/ /migrations/ diff --git a/Jenkinsfile b/Jenkinsfile index ab87e76..6540574 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,15 +15,15 @@ pipeline { stages { stage('Prepare') { steps { - sh 'cp config_test.yml.dist config_test.yml' - compose 'up -d --build postgres' - compose 'run --rm mg_telegram make migrate_test' + sh 'cp config_test.yml.dist config.yml' + compose 'up -d --build postgres_test' + compose 'run --rm mg_telegram_test make migrate_test' } } stage('Tests') { steps { - compose 'run --rm --no-deps mg_telegram make jenkins_test' + compose 'run --rm --no-deps mg_telegram_test make jenkins_test' } post { diff --git a/Makefile b/Makefile index d79f368..e166e68 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ BIN=$(ROOT_DIR)/bin/mg-telegram REVISION=$(shell git describe --tags 2>/dev/null || git log --format="v0.0-%h" -n 1 || echo "v0.0-unknown") ifndef GOPATH - $(error GOPATH must be defined) + $(error GOPATH must be defined) endif export GOPATH := $(GOPATH):$(ROOT_DIR) @@ -22,8 +22,9 @@ run: migrate @${BIN} --config $(CONFIG_FILE) run test: deps fmt - @echo "==> Running tests" - @cd $(SRC_DIR) && go test ./... -v -cpu 2 -cover -race + @echo "==> Running tests (result in test-report.xml)" + @go get -v -u github.com/jstemmer/go-junit-report + @cd $(SRC_DIR) && go test ./... -v -cpu 2 -cover -race | go-junit-report -set-exit-code > $(SRC_DIR)/test-report.xml jenkins_test: deps @echo "==> Running tests (result in test-report.xml)" diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 6d4babf..cba0c50 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -1,7 +1,7 @@ version: '2.1' services: - postgres: + postgres_test: image: postgres:9.6 environment: POSTGRES_USER: mg_telegram_test @@ -10,13 +10,14 @@ services: ports: - ${POSTGRES_ADDRESS:-127.0.0.1:5450}:${POSTGRES_PORT:-5450} - mg_telegram: + mg_telegram_test: image: golang:1.9.3-stretch working_dir: /mg_telegram user: ${UID:-1000}:${GID:-1000} volumes: - ./:/mg_telegram/ links: - - postgres + - postgres_test ports: - ${MG_TELEGRAM_ADDRESS:-3002}:3002 + command: make migrate_test diff --git a/event.go b/event.go deleted file mode 100644 index e4c4d24..0000000 --- a/event.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -type EventMessage struct { - ChannelID uint64 `json:"channel_id"` - ExternalID int `json:"external_id,omitempty"` - ChatID int64 `json:"chat_id"` - Message string `json:"message,omitempty"` - Type string `json:"type"` -} diff --git a/routing.go b/routing.go index cff5230..06b3900 100644 --- a/routing.go +++ b/routing.go @@ -557,81 +557,6 @@ func validateCrmSettings(c Connection) error { return nil } -func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { - bytes, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - return - } - - var msg EventMessage - err = json.Unmarshal(bytes, &msg) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - return - } - - b := getBotByChannel(msg.ChannelID) - if b.ID == 0 { - logger.Error(msg.ChannelID, "missing") - return - } - - if !b.Active { - logger.Error(msg.ChannelID, "deactivated") - return - } - - bot, err := tgbotapi.NewBotAPI(b.Token) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - } - - bot.Debug = true - - if msg.Type == "message_sent" { - msg, err := bot.Send(tgbotapi.NewMessage(msg.ChatID, msg.Message)) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - logger.Debugf("%v", msg) - w.WriteHeader(http.StatusOK) - w.Write([]byte("Message sent")) - } - - if msg.Type == "message_updated" { - msg, err := bot.Send(tgbotapi.NewEditMessageText(msg.ChatID, msg.ExternalID, msg.Message)) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - logger.Debugf("%v", msg) - w.WriteHeader(http.StatusOK) - w.Write([]byte("Message updated")) - } - - if msg.Type == "message_deleted" { - msg, err := bot.Send(tgbotapi.NewDeleteMessage(msg.ChatID, msg.ExternalID)) - if err != nil { - logger.Error(err) - w.WriteHeader(http.StatusBadRequest) - return - } - - logger.Debugf("%v", msg) - w.WriteHeader(http.StatusOK) - w.Write([]byte("Message deleted")) - } -} - func getAPIClient(url, key string) (*v5.Client, error, int) { client := v5.New(url, key) diff --git a/telegram.go b/telegram.go index aa62cc7..0a9939d 100644 --- a/telegram.go +++ b/telegram.go @@ -123,3 +123,79 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string w.WriteHeader(http.StatusOK) } + +func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { + bytes, err := ioutil.ReadAll(r.Body) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } + + var msg v1.WebhookRequest + err = json.Unmarshal(bytes, &msg) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } + + uid, _ := strconv.Atoi(msg.Data.ExternalMessageID) + + b := getBotByChannel(msg.Data.ChannelID) + if b.ID == 0 { + logger.Error(msg.Data.ChannelID, "missing") + return + } + + if !b.Active { + logger.Error(msg.Data.ChannelID, "deactivated") + return + } + + bot, err := tgbotapi.NewBotAPI(b.Token) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } + + if msg.Type == "message_sent" { + msg, err := bot.Send(tgbotapi.NewMessage(msg.Data.ExternalChatID, msg.Data.Content)) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Debugf("%v", msg) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Message sent")) + } + + if msg.Type == "message_updated" { + msg, err := bot.Send(tgbotapi.NewEditMessageText(msg.Data.ExternalChatID, uid, msg.Data.Content)) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Debugf("%v", msg) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Message updated")) + } + + if msg.Type == "message_deleted" { + msg, err := bot.Send(tgbotapi.NewDeleteMessage(msg.Data.ExternalChatID, uid)) + if err != nil { + logger.Error(err) + w.WriteHeader(http.StatusBadRequest) + return + } + + logger.Debugf("%v", msg) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Message deleted")) + } +} From 3933319b0e7bbce51b5f40e629f70e8601171869 Mon Sep 17 00:00:00 2001 From: Alex Lushpai Date: Thu, 31 May 2018 15:03:57 +0300 Subject: [PATCH 6/7] update docker & jenkinks configuration --- .gitignore | 4 ++-- Dockerfile | 4 ++-- Jenkinsfile | 4 ++-- Makefile | 11 +++++------ config_test.yml.dist | 2 +- docker-compose-test.yml | 9 +++++---- docker-compose.yml | 6 +++--- main.go | 16 +++++++++------- routing_test.go | 4 ++++ run.go | 12 ++++++------ 10 files changed, 39 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 7dbb826..e636d91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -gin-bin config.yml +config_test.yml .idea/ /bin/* -mg-telegram +*.xml \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 0eafdc1..4f24cb4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM golang:1.9.3-stretch WORKDIR / -ADD ./bin/mg-telegram / +ADD ./bin/transport / ADD ./templates/ /templates/ ADD ./static/ /static/ ADD ./translate/ /translate/ @@ -9,6 +9,6 @@ ADD ./migrations/ /migrations/ EXPOSE 3001 -ENTRYPOINT ["/mg-telegram"] +ENTRYPOINT ["/transport"] CMD ["run"] diff --git a/Jenkinsfile b/Jenkinsfile index 6540574..8d4862e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -15,7 +15,7 @@ pipeline { stages { stage('Prepare') { steps { - sh 'cp config_test.yml.dist config.yml' + sh 'cp config_test.yml.dist config_test.yml' compose 'up -d --build postgres_test' compose 'run --rm mg_telegram_test make migrate_test' } @@ -23,7 +23,7 @@ pipeline { stage('Tests') { steps { - compose 'run --rm --no-deps mg_telegram_test make jenkins_test' + compose 'run --rm mg_telegram_test make jenkins_test' } post { diff --git a/Makefile b/Makefile index e166e68..8f47b24 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ SRC_DIR=$(ROOT_DIR) MIGRATIONS_DIR=$(ROOT_DIR)/migrations CONFIG_FILE=$(ROOT_DIR)/config.yml CONFIG_TEST_FILE=$(ROOT_DIR)/config_test.yml -BIN=$(ROOT_DIR)/bin/mg-telegram +BIN=$(ROOT_DIR)/bin/transport REVISION=$(shell git describe --tags 2>/dev/null || git log --format="v0.0-%h" -n 1 || echo "v0.0-unknown") ifndef GOPATH @@ -22,9 +22,8 @@ run: migrate @${BIN} --config $(CONFIG_FILE) run test: deps fmt - @echo "==> Running tests (result in test-report.xml)" - @go get -v -u github.com/jstemmer/go-junit-report - @cd $(SRC_DIR) && go test ./... -v -cpu 2 -cover -race | go-junit-report -set-exit-code > $(SRC_DIR)/test-report.xml + @echo "==> Running tests" + @cd $(SRC_DIR) && go test ./... -v -cpu 2 jenkins_test: deps @echo "==> Running tests (result in test-report.xml)" @@ -43,10 +42,10 @@ deps: @go get -d -v $(DEPS) migrate: build - @${BIN} --config $(CONFIG_FILE) migrate -p ./migrations/ + ${BIN} --config $(CONFIG_FILE) migrate -p $(MIGRATIONS_DIR) migrate_test: build - @${BIN} --config $(CONFIG_TEST_FILE) migrate ./migrations/ + @${BIN} --config $(CONFIG_TEST_FILE) migrate -p $(MIGRATIONS_DIR) migrate_down: build @${BIN} --config $(CONFIG_FILE) migrate -v down diff --git a/config_test.yml.dist b/config_test.yml.dist index db022a0..1132bce 100644 --- a/config_test.yml.dist +++ b/config_test.yml.dist @@ -1,5 +1,5 @@ database: - connection: postgres://mg_telegram_test:mg_telegram_test@postgres_test:5450/mg_telegram_test?sslmode=disable + connection: postgres://mg_telegram_test:mg_telegram_test@postgres_test:5432/mg_telegram_test?sslmode=disable http_server: host: ~ diff --git a/docker-compose-test.yml b/docker-compose-test.yml index cba0c50..7233b5c 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -8,16 +8,17 @@ services: POSTGRES_PASSWORD: mg_telegram_test POSTGRES_DATABASE: mg_telegram_test ports: - - ${POSTGRES_ADDRESS:-127.0.0.1:5450}:${POSTGRES_PORT:-5450} + - ${POSTGRES_ADDRESS:-127.0.0.1:5434}:${POSTGRES_PORT:-5432} mg_telegram_test: image: golang:1.9.3-stretch - working_dir: /mg_telegram + working_dir: /mgtg user: ${UID:-1000}:${GID:-1000} volumes: - - ./:/mg_telegram/ + - ./:/mgtg/ + - ./static:/static/ links: - postgres_test ports: - ${MG_TELEGRAM_ADDRESS:-3002}:3002 - command: make migrate_test +# command: make migrate_test diff --git a/docker-compose.yml b/docker-compose.yml index bbe9880..2ebc45e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,14 +8,14 @@ services: POSTGRES_PASSWORD: mg_telegram POSTGRES_DATABASE: mg_telegram ports: - - ${POSTGRES_ADDRESS:-127.0.0.1:5434}:5432 + - ${POSTGRES_ADDRESS:-127.0.0.1:5434}:${POSTGRES_PORT:-5432} mg_telegram: image: golang:1.9.3-stretch - working_dir: /mg_telegram + working_dir: /mgtg user: ${UID:-1000}:${GID:-1000} volumes: - - ./:/mg_telegram/ + - ./:/mgtg - ./static:/static/ links: - postgres diff --git a/main.go b/main.go index f90cbc8..48e6de6 100644 --- a/main.go +++ b/main.go @@ -3,8 +3,9 @@ package main import ( "os" - "github.com/getsentry/raven-go" "github.com/jessevdk/go-flags" + + "github.com/op/go-logging" ) // Options struct @@ -14,12 +15,13 @@ type Options struct { const transport = "mg-telegram" -var options Options -var parser = flags.NewParser(&options, flags.Default) - -func init() { - raven.SetDSN(config.SentryDSN) -} +var ( + config *TransportConfig + orm *Orm + logger *logging.Logger + options Options + parser = flags.NewParser(&options, flags.Default) +) func main() { if _, err := parser.Parse(); err != nil { diff --git a/routing_test.go b/routing_test.go index 656a167..d1fc9df 100644 --- a/routing_test.go +++ b/routing_test.go @@ -11,6 +11,10 @@ import ( ) func init() { + config = LoadConfig("config_test.yml") + orm = NewDb(config) + logger = newLogger() + c := Connection{ ID: 1, ClientID: "123123", diff --git a/run.go b/run.go index 51a13fe..c553730 100644 --- a/run.go +++ b/run.go @@ -7,16 +7,11 @@ import ( "os/signal" "syscall" + "github.com/getsentry/raven-go" _ "github.com/golang-migrate/migrate/database/postgres" _ "github.com/golang-migrate/migrate/source/file" ) -var ( - config = LoadConfig("config.yml") - orm = NewDb(config) - logger = newLogger() -) - func init() { parser.AddCommand("run", "Run mg-telegram", @@ -30,6 +25,11 @@ type RunCommand struct{} // Execute command func (x *RunCommand) Execute(args []string) error { + config = LoadConfig(options.Config) + orm = NewDb(config) + logger = newLogger() + raven.SetDSN(config.SentryDSN) + go start() c := make(chan os.Signal, 1) From 5a7dce7e14ab88c866fcce9dd909e94812b5c494 Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Thu, 31 May 2018 16:18:52 +0300 Subject: [PATCH 7/7] improve migrations, models, minor fixes --- migrations/1527515265_app.down.sql | 32 +++--- migrations/1527515265_app.up.sql | 30 ++++-- models.go | 10 +- repository.go | 32 ++++-- routing.go | 34 +++--- routing_test.go | 161 ++++++++++++----------------- telegram.go | 32 +++--- 7 files changed, 166 insertions(+), 165 deletions(-) diff --git a/migrations/1527515265_app.down.sql b/migrations/1527515265_app.down.sql index 0481bb7..b83d4fb 100644 --- a/migrations/1527515265_app.down.sql +++ b/migrations/1527515265_app.down.sql @@ -1,26 +1,32 @@ alter table connection - drop constraint connection_client_id_key, - drop constraint connection_api_key_key, - drop constraint connection_api_url_key, - drop constraint connection_mg_url_key; -alter table connection + drop constraint connection_key, alter column client_id drop not null, alter column api_key drop not null, - alter column api_url drop not null; + alter column api_url drop not null, + alter column mg_url drop not null, + alter column mg_token drop not null, + alter column api_url type varchar(100), + alter column mg_url type varchar(100); alter table bot - alter column connection_id type varchar(70), + add column client_id varchar(70); + +update bot b + set client_id = c.client_id + from connection c + where b.connection_id = c.id; + +alter table bot + drop column connection_id, + alter column channel drop not null, alter column token drop not null, - drop constraint bot_token_key; - -alter table bot - rename column connection_id to client_id; + drop constraint bot_key; create table mapping ( - id serial not null + id serial not null constraint mapping_pkey primary key, site_code text, bot_id text -); \ No newline at end of file +); diff --git a/migrations/1527515265_app.up.sql b/migrations/1527515265_app.up.sql index 916f498..d0a94f7 100644 --- a/migrations/1527515265_app.up.sql +++ b/migrations/1527515265_app.up.sql @@ -1,19 +1,27 @@ alter table connection - add constraint connection_client_id_key unique (api_key), - add constraint connection_api_key_key unique (api_url), - add constraint connection_api_url_key unique (mg_url), - add constraint connection_mg_url_key unique (mg_token); -alter table connection + add constraint connection_key unique (client_id, mg_token), alter column client_id set not null, alter column api_key set not null, - alter column api_url set not null; + alter column api_url set not null, + alter column mg_url set not null, + alter column mg_token set not null, + alter column api_url type varchar(255), + alter column mg_url type varchar(255); alter table bot - alter column client_id type integer using client_id::integer, + add column connection_id integer; + +update bot b + set connection_id = c.id + from connection c + where b.client_id = c.client_id; + +alter table bot + drop column client_id, + alter column channel set not null, alter column token set not null, - add constraint bot_token_key unique (token); + add constraint bot_key unique (channel, token); -alter table bot - rename column client_id to connection_id; +alter table bot add foreign key (connection_id) references connection on delete cascade; -drop table mapping; \ No newline at end of file +drop table mapping; diff --git a/models.go b/models.go index 0187538..aaa5236 100644 --- a/models.go +++ b/models.go @@ -6,10 +6,10 @@ import "time" type Connection struct { ID int `gorm:"primary_key"` ClientID string `gorm:"client_id type:varchar(70);not null;unique" json:"clientId,omitempty"` - APIKEY string `gorm:"api_key type:varchar(100);not null;unique" json:"api_key,omitempty"` - APIURL string `gorm:"api_url type:varchar(100);not null;unique" json:"api_url,omitempty"` - MGURL string `gorm:"mg_url type:varchar(100);unique" json:"mg_url,omitempty"` - MGToken string `gorm:"mg_token type:varchar(100)" json:"mg_token,omitempty"` + APIKEY string `gorm:"api_key type:varchar(100);not null" json:"api_key,omitempty"` + APIURL string `gorm:"api_url type:varchar(255);not null" json:"api_url,omitempty"` + MGURL string `gorm:"mg_url type:varchar(255);not null;" json:"mg_url,omitempty"` + MGToken string `gorm:"mg_token type:varchar(100);not null;unique" json:"mg_token,omitempty"` CreatedAt time.Time UpdatedAt time.Time Active bool `json:"active,omitempty"` @@ -20,7 +20,7 @@ type Connection struct { type Bot struct { ID int `gorm:"primary_key"` ConnectionID int `gorm:"connection_id" json:"connectionId,omitempty"` - Channel uint64 `json:"channel,omitempty"` + Channel uint64 `gorm:"channel;not null;unique" json:"channel,omitempty"` Token string `gorm:"token type:varchar(100);not null;unique" json:"token,omitempty"` Name string `gorm:"name type:varchar(40)" json:"name,omitempty"` CreatedAt time.Time diff --git a/repository.go b/repository.go index 2da630c..1492e24 100644 --- a/repository.go +++ b/repository.go @@ -1,5 +1,7 @@ package main +import "github.com/jinzhu/gorm" + func getConnection(uid string) *Connection { var connection Connection orm.DB.First(&connection, "client_id = ?", uid) @@ -30,11 +32,18 @@ func (c *Connection) createBot(b Bot) error { return orm.DB.Model(c).Association("Bots").Append(&b).Error } -func getBotByToken(token string) *Bot { - var bot Bot - orm.DB.First(&bot, "token = ?", token) +func getConnectionByBotToken(token string) (*Connection, error) { + var c Connection + err := orm.DB.Where("active = ?", true). + Preload("Bots", "token = ?", token). + First(&c).Error + if gorm.IsRecordNotFoundError(err) { + return &c, nil + } else { + return &c, err + } - return &bot + return &c, nil } func getBotByChannel(ch uint64) *Bot { @@ -44,10 +53,6 @@ func getBotByChannel(ch uint64) *Bot { return &bot } -func (b *Bot) createBot() error { - return orm.DB.Create(b).Error -} - func (b *Bot) setBotActivity() error { return orm.DB.Model(b).Where("token = ?", b.Token).Update("Active", !b.Active).Error } @@ -59,9 +64,14 @@ func getBotChannelByToken(token string) uint64 { return b.Channel } -func (b *Bots) getBotsByClientID(uid string) error { - var c Connection - return orm.DB.First(&c, "client_id = ?", uid).Association("Bots").Find(b).Error +func (c Connection) getBotsByClientID() Bots { + var b Bots + err := orm.DB.Model(c).Association("Bots").Find(&b).Error + if err != nil { + logger.Error(err) + } + + return b } func getConnectionById(id int) *Connection { diff --git a/routing.go b/routing.go index 06b3900..edc4a8d 100644 --- a/routing.go +++ b/routing.go @@ -133,15 +133,15 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { return } - c := getConnectionById(b.ConnectionID) - if c.MGURL == "" || c.MGToken == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) - logger.Error(b.Token, "MGURL or MGToken is empty") + cb, err := getConnectionByBotToken(b.Token) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } - cl := getBotByToken(b.Token) - if cl.ID != 0 { + if len(cb.Bots) != 0 { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "bot_already_created"}), http.StatusBadRequest) return } @@ -180,22 +180,22 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { }, } - var client = v1.New(c.MGURL, c.MGToken) + var client = v1.New(cb.MGURL, cb.MGToken) data, status, err := client.ActivateTransportChannel(ch) if status != http.StatusCreated { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activating_channel"}), http.StatusBadRequest) - logger.Error(c.APIURL, status, err.Error(), data) + logger.Error(cb.APIURL, status, err.Error(), data) return } b.Channel = data.ChannelID b.Active = true - err = c.createBot(b) + err = cb.createBot(b) if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) + logger.Error(cb.APIURL, err.Error()) return } @@ -203,7 +203,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) + logger.Error(cb.APIURL, err.Error()) return } @@ -286,13 +286,7 @@ func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) { return } - bots := Bots{} - err := bots.getBotsByClientID(uid) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) - return - } + bots := p.getBotsByClientID() res := struct { Conn *Connection @@ -368,6 +362,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } @@ -377,6 +372,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + logger.Error(c.APIURL, err.Error()) return } @@ -453,6 +449,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError) + logger.Error(c.APIURL, err.Error()) return } @@ -468,6 +465,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError) + logger.Error(c.APIURL, err.Error()) return } diff --git a/routing_test.go b/routing_test.go index d1fc9df..ef1bb12 100644 --- a/routing_test.go +++ b/routing_test.go @@ -1,13 +1,20 @@ package main import ( + "encoding/json" + "fmt" + "io/ioutil" "net/http" "net/http/httptest" "net/url" + "sort" "strings" "testing" "github.com/h2non/gock" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" ) func init() { @@ -26,7 +33,9 @@ func init() { } c.createConnection() + orm.DB.Where("token = 123123:Qwerty").Delete(Bot{}) } + func TestRouting_connectHandler(t *testing.T) { req, err := http.NewRequest("GET", "/", nil) if err != nil { @@ -37,11 +46,8 @@ func TestRouting_connectHandler(t *testing.T) { handler := http.HandlerFunc(connectHandler) handler.ServeHTTP(rr, req) - - if rr.Code != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", - rr.Code, http.StatusOK) - } + assert.Equal(t, http.StatusOK, rr.Code, + fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) } func TestRouting_addBotHandler(t *testing.T) { @@ -78,15 +84,25 @@ func TestRouting_addBotHandler(t *testing.T) { if err != nil { t.Fatal(err) } - rr := httptest.NewRecorder() handler := http.HandlerFunc(addBotHandler) handler.ServeHTTP(rr, req) + require.Equal(t, http.StatusCreated, rr.Code, + fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusCreated)) - if rr.Code != http.StatusCreated { - t.Errorf("handler returned wrong status code: got %v want %v", - rr.Code, http.StatusCreated) + bytes, err := ioutil.ReadAll(rr.Body) + if err != nil { + t.Fatal(err) } + + var res map[string]interface{} + + err = json.Unmarshal(bytes, &res) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, "123123:Qwerty", res["token"]) } func TestRouting_activityBotHandler(t *testing.T) { @@ -109,10 +125,8 @@ func TestRouting_activityBotHandler(t *testing.T) { handler := http.HandlerFunc(activityBotHandler) handler.ServeHTTP(rr, req) - if rr.Code != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", - rr.Code, http.StatusOK) - } + assert.Equal(t, http.StatusOK, rr.Code, + fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) } func TestRouting_settingsHandler(t *testing.T) { @@ -125,10 +139,8 @@ func TestRouting_settingsHandler(t *testing.T) { handler := http.HandlerFunc(makeHandler(settingsHandler)) handler.ServeHTTP(rr, req) - if rr.Code != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", - rr.Code, http.StatusOK) - } + assert.Equal(t, http.StatusOK, rr.Code, + fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) } func TestRouting_saveHandler(t *testing.T) { @@ -153,80 +165,10 @@ func TestRouting_saveHandler(t *testing.T) { handler := http.HandlerFunc(saveHandler) handler.ServeHTTP(rr, req) - if rr.Code != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", - rr.Code, http.StatusOK) - } + assert.Equal(t, http.StatusOK, rr.Code, + fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) } -// -//func TestRouting_createHandler(t *testing.T) { -// defer gock.Off() -// -// gock.New("https://test2.retailcrm.ru"). -// Get("/api/credentials"). -// Reply(200). -// BodyString(`{"success": true}`) -// -// integrationModule := v5.IntegrationModule{ -// Code: transport, -// IntegrationCode: transport, -// Active: true, -// Name: "Telegram", -// ClientID: "123", -// Logo: fmt.Sprintf( -// "https://%s/web/telegram_logo.svg", -// config.HTTPServer.Host, -// ), -// BaseURL: fmt.Sprintf( -// "https://%s", -// config.HTTPServer.Host, -// ), -// AccountURL: fmt.Sprintf( -// "https://%s/settings/%s", -// config.HTTPServer.Host, -// "123", -// ), -// Actions: map[string]string{"activity": "/actions/activity"}, -// Integrations: &v5.Integrations{ -// MgTransport: &v5.MgTransport{ -// WebhookUrl: fmt.Sprintf( -// "https://%s/webhook", -// config.HTTPServer.Host, -// ), -// }, -// }, -// } -// -// updateJSON, _ := json.Marshal(&integrationModule) -// p := url.Values{"integrationModule": {string(updateJSON[:])}} -// -// gock.New("https://test2.retailcrm.ru"). -// Post(fmt.Sprintf("/api/v5/integration-modules/%s/edit", integrationModule.Code)). -// MatchType("url"). -// BodyString(p.Encode()). -// MatchHeader("X-API-KEY", "test"). -// Reply(201). -// BodyString(`{"success": true, "info": {"baseUrl": "http://test.te", "token": "test"}}`) -// -// req, err := http.NewRequest("POST", "/create/", -// strings.NewReader( -// `{"api_url": "https://test2.retailcrm.ru","api_key": "test"}`, -// )) -// if err != nil { -// t.Fatal(err) -// } -// -// rr := httptest.NewRecorder() -// handler := http.HandlerFunc(createHandler) -// handler.ServeHTTP(rr, req) -// -// if rr.Code != http.StatusFound { -// t.Errorf("handler returned wrong status code: got %v want %v", -// rr.Code, http.StatusFound) -// } -//} - func TestRouting_activityHandler(t *testing.T) { req, err := http.NewRequest("POST", "/actions/activity", strings.NewReader( @@ -240,8 +182,41 @@ func TestRouting_activityHandler(t *testing.T) { handler := http.HandlerFunc(activityHandler) handler.ServeHTTP(rr, req) - if rr.Code != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", - rr.Code, http.StatusOK) - } + assert.Equal(t, http.StatusOK, rr.Code, + fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) +} + +func TestRouting_TranslateLoader(t *testing.T) { + type m map[string]string + te := [][]string{} + + dt := "translate" + + files, err := ioutil.ReadDir(dt) + if err != nil { + t.Fatal(err) + } + + for _, f := range files { + ms := m{} + if !f.IsDir() { + res, err := ioutil.ReadFile(fmt.Sprintf("%s/%s", dt, f.Name())) + if err != nil { + t.Fatal(err) + } + + err = yaml.Unmarshal(res, &ms) + if err != nil { + t.Fatal(err) + } + + keys := []string{} + for kms := range ms { + keys = append(keys, kms) + } + sort.Strings(keys) + te = append(te, keys) + } + } + } diff --git a/telegram.go b/telegram.go index 0a9939d..11916a7 100644 --- a/telegram.go +++ b/telegram.go @@ -1,10 +1,9 @@ package main import ( - "net/http" - "encoding/json" "io/ioutil" + "net/http" "strconv" "time" @@ -33,26 +32,26 @@ func GetBotName(bot *tgbotapi.BotAPI) string { } func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string) { - b := getBotByToken(token) - if b.ID == 0 { + c, err := getConnectionByBotToken(token) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(token, err.Error()) + w.WriteHeader(http.StatusInternalServerError) + return + } + + if len(c.Bots) == 0 { logger.Error(token, "missing") w.WriteHeader(http.StatusBadRequest) return } - if !b.Active { + if !c.Bots[0].Active { logger.Error(token, "deactivated") w.WriteHeader(http.StatusBadRequest) return } - c := getConnectionById(b.ConnectionID) - if c.MGURL == "" || c.MGToken == "" { - logger.Error(token, "MGURL or MGToken is empty") - w.WriteHeader(http.StatusBadRequest) - return - } - var update tgbotapi.Update bytes, err := ioutil.ReadAll(r.Body) @@ -90,11 +89,12 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string Lastname: update.Message.From.LastName, Language: update.Message.From.LanguageCode, }, - Channel: b.Channel, + Channel: c.Bots[0].Channel, } data, st, err := client.Messages(snd) if err != nil { + raven.CaptureErrorAndWait(err, nil) logger.Error(token, err.Error(), st, data) w.WriteHeader(http.StatusInternalServerError) return @@ -110,11 +110,12 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string Text: update.EditedMessage.Text, }, }, - Channel: b.Channel, + Channel: c.Bots[0].Channel, } data, st, err := client.UpdateMessages(snd) if err != nil { + raven.CaptureErrorAndWait(err, nil) logger.Error(token, err.Error(), st, data) w.WriteHeader(http.StatusInternalServerError) return @@ -163,6 +164,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { if msg.Type == "message_sent" { msg, err := bot.Send(tgbotapi.NewMessage(msg.Data.ExternalChatID, msg.Data.Content)) if err != nil { + raven.CaptureErrorAndWait(err, nil) logger.Error(err) w.WriteHeader(http.StatusBadRequest) return @@ -176,6 +178,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { if msg.Type == "message_updated" { msg, err := bot.Send(tgbotapi.NewEditMessageText(msg.Data.ExternalChatID, uid, msg.Data.Content)) if err != nil { + raven.CaptureErrorAndWait(err, nil) logger.Error(err) w.WriteHeader(http.StatusBadRequest) return @@ -189,6 +192,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { if msg.Type == "message_deleted" { msg, err := bot.Send(tgbotapi.NewDeleteMessage(msg.Data.ExternalChatID, uid)) if err != nil { + raven.CaptureErrorAndWait(err, nil) logger.Error(err) w.WriteHeader(http.StatusBadRequest) return