diff --git a/repository.go b/repository.go index 5ac999c..609ada7 100644 --- a/repository.go +++ b/repository.go @@ -1,17 +1,17 @@ package main -func getConnection(uid string) (*Connection, error) { +func getConnection(uid string) *Connection { var connection Connection orm.DB.First(&connection, "client_id = ?", uid) - return &connection, nil + return &connection } -func getConnectionByURL(urlCrm string) (*Connection, error) { +func getConnectionByURL(urlCrm string) *Connection { var connection Connection orm.DB.First(&connection, "api_url = ?", urlCrm) - return &connection, nil + return &connection } func (c *Connection) setConnectionActivity() error { @@ -26,11 +26,11 @@ func (c *Connection) saveConnection() error { return orm.DB.Model(c).Where("client_id = ?", c.ClientID).Update(c).Error } -func getBotByToken(token string) (*Bot, error) { +func getBotByToken(token string) *Bot { var bot Bot orm.DB.First(&bot, "token = ?", token) - return &bot, nil + return &bot } func (b *Bot) createBot() error { diff --git a/routing.go b/routing.go index 421939b..bacaacd 100644 --- a/routing.go +++ b/routing.go @@ -8,8 +8,11 @@ import ( "io/ioutil" "net/http" "regexp" + "strconv" + "time" "github.com/getsentry/raven-go" + "github.com/go-telegram-bot-api/telegram-bot-api" "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/retailcrm/api-client-go/v5" "github.com/retailcrm/mg-transport-api-client-go/v1" @@ -19,7 +22,7 @@ import ( var ( templates = template.Must(template.ParseFiles("templates/layout.html", "templates/form.html", "templates/home.html")) - validPath = regexp.MustCompile("^/(save|settings)/([a-zA-Z0-9]+)$") + validPath = regexp.MustCompile(`^/(save|settings|telegram)/([a-zA-Z0-9-:_+]+)$`) localizer *i18n.Localizer bundle = &i18n.Bundle{DefaultLanguage: language.English} matcher = language.NewMatcher([]language.Tag{ @@ -62,7 +65,14 @@ func setWrapperRoutes() { } func renderTemplate(w http.ResponseWriter, tmpl string, c interface{}) { - err := templates.ExecuteTemplate(w, tmpl+".html", c) + tm, err := template.ParseFiles("templates/layout.html", "templates/"+tmpl+".html") + if err != nil { + raven.CaptureErrorAndWait(err, nil) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + err = tm.Execute(w, &c) if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -99,9 +109,11 @@ func connectHandler(w http.ResponseWriter, r *http.Request) { } func addBotHandler(w http.ResponseWriter, r *http.Request) { + setLocale(r.Header.Get("Accept-Language")) body, err := ioutil.ReadAll(r.Body) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) return } @@ -109,40 +121,51 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { err = json.Unmarshal(body, &b) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) return } if b.Token == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "no_bot_token"}), http.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "no_bot_token"}), http.StatusBadRequest) return } - cl, _ := getBotByToken(b.Token) + cl := getBotByToken(b.Token) if cl.ID != 0 { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "bot_already_created"}), http.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "bot_already_created"}), http.StatusBadRequest) return } bot, err := GetBotInfo(b.Token) if err != nil { logger.Error(b.Token, err.Error()) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_token"}), http.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_token"}), http.StatusBadRequest) + return + } + + bot.Debug = false + + _, 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 + } + + _, 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) - c, err := getConnection(b.ClientID) - if err != nil { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_find_account"}), http.StatusInternalServerError) - logger.Error(b.ClientID, err.Error()) - return - } - + c := getConnection(b.ClientID) if c.MGURL == "" || c.MGToken == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_find_account"}), http.StatusInternalServerError) - logger.Error(b.ClientID) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) + logger.Error(b.ClientID, "MGURL or MGToken is empty") return } @@ -159,7 +182,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { var client = v1.New(c.MGURL, c.MGToken) data, status, err := client.ActivateTransportChannel(ch) if status != http.StatusCreated { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activating_channel"}), http.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activating_channel"}), http.StatusBadRequest) logger.Error(c.APIURL, status, err.Error(), data) return } @@ -170,11 +193,19 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { err = b.createBot() if err != nil { raven.CaptureErrorAndWait(err, nil) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) + logger.Error(c.APIURL, err.Error()) + return + } + + jsonString, err := json.Marshal(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()) return } - jsonString, _ := json.Marshal(b) w.WriteHeader(http.StatusCreated) w.Write(jsonString) } @@ -182,7 +213,8 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { func activityBotHandler(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -190,7 +222,8 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { err = json.Unmarshal(body, &b) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -205,16 +238,10 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { }, } - c, err := getConnection(b.ClientID) - if err != nil { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_find_account"}), http.StatusInternalServerError) - logger.Error(b.ClientID, err.Error()) - return - } - + c := getConnection(b.ClientID) if c.MGURL == "" || c.MGToken == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_find_account"}), http.StatusInternalServerError) - logger.Error(b.ClientID, "not find account") + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) + logger.Error(b.ClientID, "MGURL or MGToken is empty") return } @@ -223,14 +250,14 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { if b.Active { data, status, err := client.DeactivateTransportChannel(ch.ID) if status > http.StatusOK { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_deactivating_channel"}), http.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_deactivating_channel"}), http.StatusBadRequest) logger.Error(b.ClientID, 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.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activating_channel"}), http.StatusBadRequest) logger.Error(b.ClientID, status, err.Error(), data) return } @@ -238,8 +265,8 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { err = b.setBotActivity() if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, err.Error(), http.StatusInternalServerError) + logger.Error(b.ClientID, err.Error()) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -249,32 +276,26 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) { setLocale(r.Header.Get("Accept-Language")) - p, err := getConnection(uid) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - + p := getConnection(uid) if p.ID == 0 { http.Redirect(w, r, "/", http.StatusFound) } bots := Bots{} - bots.getBotsByClientID(uid) - - client := v5.New(p.APIURL, p.APIKEY) - sites, _, _ := client.Sites() + err := bots.getBotsByClientID(uid) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + return + } res := struct { Conn *Connection Bots Bots - Sites map[string]v5.Site Locale map[string]interface{} }{ p, bots, - sites.Sites, map[string]interface{}{ "ButConnect": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "but_connect"}), "ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}), @@ -292,9 +313,12 @@ func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) { } func saveHandler(w http.ResponseWriter, r *http.Request) { + setLocale(r.Header.Get("Accept-Language")) + body, err := ioutil.ReadAll(r.Body) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -302,7 +326,8 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { err = json.Unmarshal(body, &c) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -316,12 +341,13 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { err = c.saveConnection() if err != nil { raven.CaptureErrorAndWait(err, nil) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + logger.Error(c.APIURL, err.Error()) return } w.WriteHeader(http.StatusOK) - w.Write([]byte("/settings/" + r.FormValue("clientId"))) + w.Write([]byte(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "successful"}))) } func createHandler(w http.ResponseWriter, r *http.Request) { @@ -329,7 +355,8 @@ func createHandler(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -337,7 +364,8 @@ func createHandler(w http.ResponseWriter, r *http.Request) { err = json.Unmarshal(body, &c) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + raven.CaptureErrorAndWait(err, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -350,7 +378,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { return } - cl, _ := getConnectionByURL(c.APIURL) + cl := getConnectionByURL(c.APIURL) if cl.ID != 0 { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "connection_already_created"}), http.StatusBadRequest) return @@ -360,7 +388,8 @@ func createHandler(w http.ResponseWriter, r *http.Request) { cr, status, errr := client.APICredentials() if errr.RuntimeErr != nil { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url_key"}), http.StatusBadRequest) + 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) return } @@ -403,7 +432,8 @@ func createHandler(w http.ResponseWriter, r *http.Request) { data, status, errr := client.IntegrationModuleEdit(integration) if errr.RuntimeErr != nil { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_integration"}), http.StatusBadRequest) + raven.CaptureErrorAndWait(errr.RuntimeErr, nil) + http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_integration"}), http.StatusInternalServerError) logger.Error(c.APIURL, status, errr.RuntimeErr, data) return } @@ -416,6 +446,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { c.MGURL = data.Info["baseUrl"] c.MGToken = data.Info["token"] + c.Active = true err = c.createConnection() if err != nil { @@ -424,8 +455,23 @@ func createHandler(w http.ResponseWriter, r *http.Request) { return } + res := struct { + Url string + Message string + }{ + Url: "/settings/" + c.ClientID, + Message: localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "successful"}), + } + + 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) + return + } + w.WriteHeader(http.StatusFound) - w.Write([]byte("/settings/" + c.ClientID)) + w.Write(jss) } func activityHandler(w http.ResponseWriter, r *http.Request) { @@ -434,7 +480,12 @@ func activityHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "set_method"}) - jsonString, _ := json.Marshal(res) + jsonString, err := json.Marshal(res) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } w.Write(jsonString) return } @@ -443,7 +494,12 @@ func activityHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"}) - jsonString, _ := json.Marshal(res) + jsonString, err := json.Marshal(res) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } w.Write(jsonString) return } @@ -454,7 +510,12 @@ func activityHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"}) - jsonString, _ := json.Marshal(res) + jsonString, err := json.Marshal(res) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } w.Write(jsonString) return } @@ -462,13 +523,24 @@ func activityHandler(w http.ResponseWriter, r *http.Request) { if err := rec.setConnectionActivity(); err != nil { raven.CaptureErrorAndWait(err, nil) res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"}) - jsonString, _ := json.Marshal(res) + jsonString, err := json.Marshal(res) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } w.Write(jsonString) return } res.Success = true - jsonString, _ := json.Marshal(res) + jsonString, err := json.Marshal(res) + if err != nil { + raven.CaptureErrorAndWait(err, nil) + logger.Error(err) + return + } + w.Write(jsonString) } @@ -483,3 +555,98 @@ 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 ed2e7ef..bbff5ec 100644 --- a/routing_test.go +++ b/routing_test.go @@ -3,16 +3,24 @@ package main import ( "net/http" "net/http/httptest" + "strings" "testing" - "strings" - - "encoding/json" - "github.com/h2non/gock" - "github.com/retailcrm/mg-transport-api-client-go/v1" ) +func init() { + c := Connection{ + ClientID: "123123", + APIKEY: "test", + APIURL: "https://test.retailcrm.ru", + MGURL: "https://test.retailcrm.pro", + MGToken: "test-token", + Active: true, + } + + c.createConnection() +} func TestRouting_connectHandler(t *testing.T) { req, err := http.NewRequest("GET", "/", nil) if err != nil { @@ -34,40 +42,123 @@ func TestRouting_addBotHandler(t *testing.T) { defer gock.Off() gock.New("https://api.telegram.org"). - Post("/botbot123:test/getMe"). + Post("/bot123123:Qwerty/getMe"). Reply(200). BodyString(`{"ok":true,"result":{"id":123,"is_bot":true,"first_name":"Test","username":"TestBot"}}`) - ch := v1.Channel{ - Type: "telegram", - Events: []string{ - "message_sent", - "message_updated", - "message_deleted", - "message_read", - }, - } - str, _ := json.Marshal(ch) + gock.New("https://api.telegram.org"). + Post("/bot123123:Qwerty/setWebhook"). + MatchType("url"). + BodyString("url=https%3A%2F%2Ftest.com%2Ftelegram%2F123123%3AQwerty"). + Reply(201). + BodyString(`{"ok":true}`) - gock.New("https://mg-test.com"). + gock.New("https://api.telegram.org"). + Post("/bot123123:Qwerty/getWebhookInfo"). + Reply(200). + BodyString(`{"ok":true,"result":{"url":"https://test.com/telegram/123123:Qwerty","has_custom_certificate":false,"pending_update_count":0}}`) + + gock.New("https://test.retailcrm.pro"). Post("/api/v1/transport/channels"). - JSON(str). + BodyString(`{"ID":0,"Type":"telegram","Events":["message_sent","message_updated","message_deleted","message_read"]}`). MatchHeader("Content-Type", "application/json"). MatchHeader("X-Transport-Token", "test-token"). - Reply(200). + Reply(201). BodyString(`{"id": 1}`) - req, err := http.NewRequest("POST", "/add-bot/", strings.NewReader(`{"token": "bot123:test", "clientId": "test"}`)) + req, err := http.NewRequest("POST", "/add-bot/", strings.NewReader(`{"token": "123123:Qwerty", "clientId": "123123"}`)) if err != nil { t.Fatal(err) } rr := httptest.NewRecorder() handler := http.HandlerFunc(addBotHandler) - handler.ServeHTTP(rr, req) + if rr.Code != http.StatusCreated { t.Errorf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusCreated) } } + +func TestRouting_activityBotHandler(t *testing.T) { + defer gock.Off() + + gock.New("https://test.retailcrm.pro"). + Post("/api/v1/transport/channels"). + BodyString(`{"ID":1,"Type":"telegram","Events":["message_sent","message_updated","message_deleted","message_read"]}`). + MatchHeader("Content-Type", "application/json"). + MatchHeader("X-Transport-Token", "123123"). + Reply(200). + BodyString(`{"id": 1}`) + + req, err := http.NewRequest("POST", "/activity-bot/", strings.NewReader(`{"token": "123123:Qwerty", "active": false, "clientId": "123123"}`)) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + 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) + } +} + +func TestRouting_settingsHandler(t *testing.T) { + req, err := http.NewRequest("GET", "/settings/123123", nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + 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) + } +} + +func TestRouting_saveHandler(t *testing.T) { + req, err := http.NewRequest("POST", "/save/", + strings.NewReader( + `{"clientId": "123123", + "api_url": "https://test.retailcrm.ru", + "api_key": "test"}`, + )) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + 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) + } +} + +func TestRouting_activityHandler(t *testing.T) { + req, err := http.NewRequest("POST", "/actions/activity", + strings.NewReader( + `{"clientId": "123123","activity": {"active": true}}`, + )) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + 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) + } +} diff --git a/telegram.go b/telegram.go index 2a79e55..2901a35 100644 --- a/telegram.go +++ b/telegram.go @@ -9,6 +9,7 @@ import ( func setTransportRoutes() { http.HandleFunc("/add-bot/", addBotHandler) http.HandleFunc("/activity-bot/", activityBotHandler) + http.HandleFunc("/telegram/", makeHandler(telegramWebhookHandler)) } // GetBotInfo function diff --git a/templates/form.html b/templates/form.html index 02d5190..f7f8f3d 100644 --- a/templates/form.html +++ b/templates/form.html @@ -1,17 +1,14 @@ -{{template "header"}} +{{define "body"}}
-
+ -
-
-
-
+
@@ -36,7 +33,7 @@
- +
@@ -51,7 +48,7 @@
- +
@@ -77,4 +74,4 @@ -{{template "footer"}} +{{end}} diff --git a/templates/home.html b/templates/home.html index 92f3fb5..2e4dd6e 100644 --- a/templates/home.html +++ b/templates/home.html @@ -1,9 +1,6 @@ -{{template "header"}} -
-
-
-
-
+{{define "body"}} +
+
@@ -25,4 +22,4 @@
-{{template "footer"}} +{{end}} diff --git a/templates/layout.html b/templates/layout.html index 297f802..4089a39 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -1,9 +1,8 @@ -{{ define "header" }} - + {{.Locale.Title}} @@ -13,13 +12,10 @@
-{{ end }} - -{{ define "footer" }} + {{template "body" .}}
-{{ end }} diff --git a/translate/translate.en.yml b/translate/translate.en.yml index 3902433..b7521a9 100644 --- a/translate/translate.en.yml +++ b/translate/translate.en.yml @@ -5,12 +5,15 @@ table_name: Bot name table_token: Bot token table_activity: Activity api_key: API Key +add_bot: Add bot +title: Telegram transport for retailCRM +successful: Data successfully updated no_bot_token: Enter the bot token wrong_data: Incorrect data set_method: Set POST method bot_already_created: Bot already created -not_find_account: Could not find account, please contact technical support +not_found_account: The account could not be found, contact technical support error_activating_channel: Error while activating the channel error_deactivating_channel: Error while deactivating the channel incorrect_url_key: Enter the correct CRM url or apiKey @@ -20,5 +23,6 @@ connection_already_created: Connection already created missing_url_key: Missing crm url or apiKey incorrect_url: Enter the correct CRM url incorrect_token: Set correct bot token -add_bot: Add bot -title: Telegram transport for retailCRM +error_creating_webhook: Error while creating webhook +error_adding_bot: Error while adding bot +error_save: An error occurred while saving, contact technical support diff --git a/translate/translate.ru.yml b/translate/translate.ru.yml index 0aae946..5d1b65f 100644 --- a/translate/translate.ru.yml +++ b/translate/translate.ru.yml @@ -5,12 +5,15 @@ table_name: Имя table_token: Токен table_activity: Активность api_key: API Ключ +add_bot: Добавить бота +title: Модуль подключения Telegram к retailCRM +successful: Данные успешно обновлены no_bot_token: Введите токен wrong_data: Неверные данные set_method: Установить метод POST bot_already_created: Бот уже создан -not_find_account: Не удалось найти учетную запись, обратитесь в службу технической поддержки +not_found_account: Не удалось найти учетную запись, обратитесь в службу технической поддержки error_activating_channel: Ошибка при активации канала error_deactivating_channel: Ошибка при отключении канала incorrect_url_key: Введите корректный URL или apiKey @@ -20,5 +23,6 @@ connection_already_created: Соединение уже создано missing_url_key: Отсутствует URL или apiKey incorrect_url: Введите корректный URL CRM incorrect_token: Установите корректный токен -add_bot: Добавить бота -title: Модуль подключения Telegram к retailCRM +error_creating_webhook: Ошибка при создании webhook +error_adding_bot: Ошибка при добавлении бота +error_save: Ошибка при сохранении, обратитесь в службу технической поддержки diff --git a/web/script.js b/web/script.js index 0a9a99f..e78d8bf 100644 --- a/web/script.js +++ b/web/script.js @@ -14,8 +14,8 @@ $("#save").on("submit", function(e) { send( $(this).attr('action'), formDataToObj($(this).serializeArray()), - function () { - return 0; + function (data) { + M.toast({html: data}); } ) }); @@ -31,6 +31,7 @@ $("#add-bot").on("submit", function(e) { bots.removeClass("hide") } $("#bots tbody").append(getBotTemplate(data)); + $("#token").val(""); } ) }); @@ -56,7 +57,6 @@ $(document).on("click", ".activity-bot", function(e) { }); function send(url, data, callback) { - $('#msg').empty(); $.ajax({ url: url, data: JSON.stringify(data), @@ -65,12 +65,14 @@ function send(url, data, callback) { error: function (res){ if (res.status < 400) { if (res.responseText) { + let resObj = JSON.parse(res.responseText); + sessionStorage.setItem("createdMsg", resObj.Message); + document.location.replace( - location.protocol.concat("//").concat(window.location.host) + res.responseText + location.protocol.concat("//").concat(window.location.host) + resObj.Url ); } } else { - //$('#msg').html(`

${res.responseText}

`); M.toast({html: res.responseText}) } } @@ -106,4 +108,12 @@ $( document ).ready(function() { if ($("table tbody").children().length === 0) { $("#bots").addClass("hide"); } + + let createdMsg = sessionStorage.getItem("createdMsg"); + if (createdMsg) { + setTimeout(function() { + M.toast({html: createdMsg}); + sessionStorage.removeItem("createdMsg"); + }, 1000); + } }); diff --git a/web/style.css b/web/style.css index cbda80d..69bda59 100644 --- a/web/style.css +++ b/web/style.css @@ -6,6 +6,16 @@ text-align: right; } +#tab{ + width: 50%; + margin: 0 auto 23px; +} + +.tab-el-center{ + width: 67%; + margin: 0 auto; +} + #bots .activity-bot{ float: right; }
{{.Locale.TableName}}