1
0
mirror of synced 2024-11-22 12:26:02 +03:00
mg-transport-telegram/routing.go
2018-05-24 11:33:04 +03:00

486 lines
13 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"regexp"
"github.com/getsentry/raven-go"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/retailcrm/api-client-go/v5"
"github.com/retailcrm/mg-transport-api-client-go/v1"
"golang.org/x/text/language"
"gopkg.in/yaml.v2"
)
var (
templates = template.Must(template.ParseFiles("templates/layout.html", "templates/form.html", "templates/home.html"))
validPath = regexp.MustCompile("^/(save|settings)/([a-zA-Z0-9]+)$")
localizer *i18n.Localizer
bundle = &i18n.Bundle{DefaultLanguage: language.English}
matcher = language.NewMatcher([]language.Tag{
language.English,
language.Russian,
language.Spanish,
})
)
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{}) {
err := templates.ExecuteTemplate(w, tmpl+".html", c)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
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) {
setLocale(r.Header.Get("Accept-Language"))
p := Connection{}
res := struct {
Conn *Connection
Locale map[string]interface{}
}{
&p,
map[string]interface{}{
"ButConnect": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "but_connect"}),
"ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}),
"Title": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "title"}),
},
}
renderTemplate(w, "home", &res)
}
func addBotHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var b Bot
err = json.Unmarshal(body, &b)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if b.Token == "" {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "no_bot_token"}), http.StatusInternalServerError)
return
}
cl, _ := getBotByToken(b.Token)
if cl.ID != 0 {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "bot_already_created"}), http.StatusInternalServerError)
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)
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
}
if c.MGURL == "" || c.MGToken == "" {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_find_account"}), http.StatusInternalServerError)
logger.Error(b.ClientID)
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.StatusInternalServerError)
logger.Error(c.APIURL, status, err.Error(), data)
return
}
b.Channel = data.ChannelID
b.Active = true
err = b.createBot()
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
jsonString, _ := json.Marshal(b)
w.WriteHeader(http.StatusCreated)
w.Write(jsonString)
}
func activityBotHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var b Bot
err = json.Unmarshal(body, &b)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
ch := v1.Channel{
ID: getBotChannelByToken(b.Token),
Type: "telegram",
Events: []string{
"message_sent",
"message_updated",
"message_deleted",
"message_read",
},
}
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
}
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")
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.StatusInternalServerError)
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)
logger.Error(b.ClientID, status, err.Error(), data)
return
}
}
err = b.setBotActivity()
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
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
}
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()
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"}),
"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"}),
"TableActivity": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "table_activity"}),
"Title": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "title"}),
},
}
renderTemplate(w, "form", res)
}
func saveHandler(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var c Connection
err = json.Unmarshal(body, &c)
if err != nil {
http.Error(w, err.Error(), 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 {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("/settings/" + r.FormValue("clientId")))
}
func createHandler(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)
return
}
var c Connection
err = json.Unmarshal(body, &c)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
c.ClientID = GenerateToken()
err = validate(c)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
logger.Error(c.APIURL, err.Error())
return
}
cl, _ := getConnectionByURL(c.APIURL)
if cl.ID != 0 {
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 {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url_key"}), http.StatusBadRequest)
logger.Error(c.APIURL, status, errr.RuntimeErr, cr)
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
}
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,
),
BaseURL: fmt.Sprintf(
"https://%s",
config.HTTPServer.Host,
),
AccountURL: fmt.Sprintf(
"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(
"https://%s/webhook",
config.HTTPServer.Host,
),
},
},
}
data, status, errr := client.IntegrationModuleEdit(integration)
if errr.RuntimeErr != nil {
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_integration"}), http.StatusBadRequest)
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"]
err = c.createConnection()
if err != nil {
raven.CaptureErrorAndWait(err, nil)
http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusFound)
w.Write([]byte("/settings/" + c.ClientID))
}
func activityHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res := Response{Success: false}
if r.Method != http.MethodPost {
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "set_method"})
jsonString, _ := json.Marshal(res)
w.Write(jsonString)
return
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"})
jsonString, _ := json.Marshal(res)
w.Write(jsonString)
return
}
var rec Connection
err = json.Unmarshal(body, &rec)
if err != nil {
raven.CaptureErrorAndWait(err, nil)
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"})
jsonString, _ := json.Marshal(res)
w.Write(jsonString)
return
}
if err := rec.setConnectionActivity(); err != nil {
raven.CaptureErrorAndWait(err, nil)
res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_data"})
jsonString, _ := json.Marshal(res)
w.Write(jsonString)
return
}
res.Success = true
jsonString, _ := json.Marshal(res)
w.Write(jsonString)
}
func validate(c Connection) error {
if c.APIURL == "" || c.APIKEY == "" {
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 {
return errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url"}))
}
return nil
}