1
0
mirror of synced 2024-11-25 21:26:02 +03:00
mg-transport-telegram/routing.go

653 lines
17 KiB
Go
Raw Normal View History

package main
import (
"encoding/json"
"errors"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"time"
"github.com/getsentry/raven-go"
"github.com/go-telegram-bot-api/telegram-bot-api"
2018-05-18 18:11:09 +03:00
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/retailcrm/api-client-go/v5"
"github.com/retailcrm/mg-transport-api-client-go/v1"
2018-05-18 18:11:09 +03:00
"golang.org/x/text/language"
"gopkg.in/yaml.v2"
)
var (
2018-05-22 10:34:39 +03:00
templates = template.Must(template.ParseFiles("templates/layout.html", "templates/form.html", "templates/home.html"))
validPath = regexp.MustCompile(`^/(save|settings|telegram)/([a-zA-Z0-9-:_+]+)$`)
2018-05-18 18:11:09 +03:00
localizer *i18n.Localizer
bundle = &i18n.Bundle{DefaultLanguage: language.English}
matcher = language.NewMatcher([]language.Tag{
language.English,
language.Russian,
language.Spanish,
})
)
2018-05-18 18:11:09 +03:00
func init() {
bundle.RegisterUnmarshalFunc("yml", yaml.Unmarshal)
files, err := ioutil.ReadDir("translate")
if err != nil {
logger.Error(err)
}
for _, f := range files {
if !f.IsDir() {
bundle.MustLoadMessageFile("translate/" + f.Name())
}
}
}
func setLocale(al string) {
tag, _ := language.MatchStrings(matcher, al)
localizer = i18n.NewLocalizer(bundle, tag.String())
}
// Response struct
type Response struct {
Success bool `json:"success"`
Error string `json:"error"`
}
func setWrapperRoutes() {
http.HandleFunc("/", connectHandler)
http.HandleFunc("/settings/", makeHandler(settingsHandler))
http.HandleFunc("/save/", saveHandler)
http.HandleFunc("/create/", createHandler)
http.HandleFunc("/actions/activity", activityHandler)
}
func renderTemplate(w http.ResponseWriter, tmpl string, c interface{}) {
tm, err := template.ParseFiles("templates/layout.html", "templates/"+tmpl+".html")
if err != nil {
raven.CaptureErrorAndWait(err, nil)
2018-05-24 17:16:21 +03:00
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = tm.Execute(w, &c)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
2018-05-24 17:16:21 +03:00
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return
}
fn(w, r, m[2])
}
}
func connectHandler(w http.ResponseWriter, r *http.Request) {
2018-05-18 18:11:09 +03:00
setLocale(r.Header.Get("Accept-Language"))
p := Connection{}
2018-05-18 18:11:09 +03:00
res := struct {
Conn *Connection
Locale map[string]interface{}
}{
&p,
map[string]interface{}{
"ButConnect": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "but_connect"}),
2018-05-22 10:34:39 +03:00
"ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}),
2018-05-24 11:33:04 +03:00
"Title": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "title"}),
2018-05-18 18:11:09 +03:00
},
}
renderTemplate(w, "home", &res)
}
func addBotHandler(w http.ResponseWriter, r *http.Request) {
setLocale(r.Header.Get("Accept-Language"))
body, err := ioutil.ReadAll(r.Body)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError)
return
}
var b Bot
err = json.Unmarshal(body, &b)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
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.StatusBadRequest)
return
}
2018-05-24 18:08:48 +03:00
cl := getBotByToken(b.Token)
if cl.ID != 0 {
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.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)
2018-05-24 18:08:48 +03:00
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{
"message_sent",
"message_updated",
"message_deleted",
"message_read",
},
}
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.StatusBadRequest)
logger.Error(c.APIURL, status, err.Error(), data)
return
}
b.Channel = data.ChannelID
b.Active = true
err = b.createBot()
if err != nil {
2018-05-24 18:08:48 +03:00
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError)
2018-05-24 17:16:21 +03:00
logger.Error(c.APIURL, err.Error())
return
}
jsonString, err := json.Marshal(b)
if err != nil {
2018-05-24 18:08:48 +03:00
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError)
2018-05-24 17:16:21 +03:00
logger.Error(c.APIURL, err.Error())
return
}
w.WriteHeader(http.StatusCreated)
2018-05-18 18:11:09 +03:00
w.Write(jsonString)
}
func activityBotHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
return
}
var b Bot
err = json.Unmarshal(body, &b)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
return
}
ch := v1.Channel{
ID: getBotChannelByToken(b.Token),
Type: "telegram",
Events: []string{
"message_sent",
"message_updated",
"message_deleted",
"message_read",
},
}
2018-05-24 18:08:48 +03:00
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 {
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)
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)
return
}
}
err = b.setBotActivity()
if err != nil {
2018-05-24 17:16:21 +03:00
logger.Error(b.ClientID, err.Error())
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) {
2018-05-18 18:11:09 +03:00
setLocale(r.Header.Get("Accept-Language"))
2018-05-24 18:08:48 +03:00
p := getConnection(uid)
if p.ID == 0 {
http.Redirect(w, r, "/", http.StatusFound)
}
bots := Bots{}
2018-05-24 18:08:48 +03:00
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 {
2018-05-18 18:11:09 +03:00
Conn *Connection
Bots Bots
Locale map[string]interface{}
}{
p,
bots,
2018-05-18 18:11:09 +03:00
map[string]interface{}{
"ButConnect": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "but_connect"}),
2018-05-22 10:34:39 +03:00
"ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}),
2018-05-18 18:11:09 +03:00
"TabSettings": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "tab_settings"}),
"TabBots": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "tab_bots"}),
"TableName": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "table_name"}),
"TableToken": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "table_token"}),
"AddBot": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "add_bot"}),
2018-05-18 18:11:09 +03:00
"TableActivity": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "table_activity"}),
2018-05-24 11:33:04 +03:00
"Title": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "title"}),
2018-05-18 18:11:09 +03:00
},
}
renderTemplate(w, "form", res)
}
func saveHandler(w http.ResponseWriter, r *http.Request) {
setLocale(r.Header.Get("Accept-Language"))
body, err := ioutil.ReadAll(r.Body)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
return
}
var c Connection
err = json.Unmarshal(body, &c)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
return
}
err = validate(c)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
logger.Error(c.APIURL, err.Error())
return
}
err = c.saveConnection()
if err != nil {
2018-05-24 18:08:48 +03:00
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
2018-05-24 17:16:21 +03:00
logger.Error(c.APIURL, err.Error())
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "successful"})))
}
func createHandler(w http.ResponseWriter, r *http.Request) {
2018-05-18 18:11:09 +03:00
setLocale(r.Header.Get("Accept-Language"))
2018-05-22 10:34:39 +03:00
body, err := ioutil.ReadAll(r.Body)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
2018-05-22 10:34:39 +03:00
return
}
var c Connection
err = json.Unmarshal(body, &c)
if err != nil {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError)
2018-05-22 10:34:39 +03:00
return
}
2018-05-22 10:34:39 +03:00
c.ClientID = GenerateToken()
err = validate(c)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
logger.Error(c.APIURL, err.Error())
return
}
2018-05-24 18:08:48 +03:00
cl := getConnectionByURL(c.APIURL)
if cl.ID != 0 {
2018-05-18 18:11:09 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "connection_already_created"}), http.StatusBadRequest)
return
}
client := v5.New(c.APIURL, c.APIKEY)
cr, status, errr := client.APICredentials()
if errr.RuntimeErr != nil {
raven.CaptureErrorAndWait(errr.RuntimeErr, nil)
2018-05-24 17:16:21 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusInternalServerError)
logger.Error(c.APIURL, status, errr.RuntimeErr, cr)
return
}
if !cr.Success {
2018-05-18 18:11:09 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url_key"}), http.StatusBadRequest)
logger.Error(c.APIURL, status, errr.ApiErr, cr)
return
}
integration := v5.IntegrationModule{
Code: transport,
IntegrationCode: transport,
Active: true,
Name: "Telegram",
ClientID: c.ClientID,
Logo: fmt.Sprintf(
"https://%s/web/telegram_logo.svg",
config.HTTPServer.Host,
),
2018-05-22 10:34:39 +03:00
BaseURL: fmt.Sprintf(
"https://%s",
config.HTTPServer.Host,
),
AccountURL: fmt.Sprintf(
2018-05-22 10:34:39 +03:00
"https://%s/settings/%s",
config.HTTPServer.Host,
c.ClientID,
),
Actions: map[string]string{"activity": "/actions/activity"},
Integrations: &v5.Integrations{
MgTransport: &v5.MgTransport{
WebhookUrl: fmt.Sprintf(
2018-05-22 10:34:39 +03:00
"https://%s/webhook",
config.HTTPServer.Host,
),
},
},
}
data, status, errr := client.IntegrationModuleEdit(integration)
if errr.RuntimeErr != nil {
raven.CaptureErrorAndWait(errr.RuntimeErr, nil)
2018-05-24 17:16:21 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_integration"}), http.StatusInternalServerError)
logger.Error(c.APIURL, status, errr.RuntimeErr, data)
return
}
if status >= http.StatusBadRequest {
http.Error(w, errr.ApiErr, http.StatusBadRequest)
logger.Error(c.APIURL, status, errr.ApiErr, data)
return
}
c.MGURL = data.Info["baseUrl"]
c.MGToken = data.Info["token"]
c.Active = true
err = c.createConnection()
if err != nil {
raven.CaptureErrorAndWait(err, nil)
2018-05-24 18:08:48 +03:00
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError)
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 {
2018-05-24 17:16:21 +03:00
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusFound)
w.Write(jss)
}
func activityHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res := Response{Success: false}
if r.Method != http.MethodPost {
2018-05-18 18:11:09 +03:00
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "set_method"})
2018-05-24 17:16:21 +03:00
jsonString, err := json.Marshal(res)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
return
}
w.Write(jsonString)
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
2018-05-18 18:11:09 +03:00
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"})
2018-05-24 17:16:21 +03:00
jsonString, err := json.Marshal(res)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
return
}
w.Write(jsonString)
return
}
var rec Connection
err = json.Unmarshal(body, &rec)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
2018-05-18 18:11:09 +03:00
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"})
2018-05-24 17:16:21 +03:00
jsonString, err := json.Marshal(res)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
return
}
w.Write(jsonString)
return
}
if err := rec.setConnectionActivity(); err != nil {
raven.CaptureErrorAndWait(err, nil)
2018-05-18 18:11:09 +03:00
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"})
2018-05-24 17:16:21 +03:00
jsonString, err := json.Marshal(res)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
return
}
w.Write(jsonString)
return
}
res.Success = true
2018-05-24 17:16:21 +03:00
jsonString, err := json.Marshal(res)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(err)
return
}
w.Write(jsonString)
}
func validate(c Connection) error {
if c.APIURL == "" || c.APIKEY == "" {
2018-05-18 18:11:09 +03:00
return errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "missing_url_key"}))
}
if res, _ := regexp.MatchString(`https://?[\da-z\.-]+\.(retailcrm\.(ru|pro)|ecomlogic\.com)`, c.APIURL); !res {
2018-05-18 18:11:09 +03:00
return errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url"}))
}
return nil
}
func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string) {
ok := make(chan bool)
2018-05-24 17:16:21 +03:00
bytes, err := ioutil.ReadAll(r.Body)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
logger.Error(token, err)
2018-05-24 17:16:21 +03:00
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)
}
}