2021-12-01 15:40:23 +03:00
|
|
|
|
package util
|
2019-09-04 15:22:27 +03:00
|
|
|
|
|
|
|
|
|
import (
|
2023-10-18 14:41:37 +03:00
|
|
|
|
"bytes"
|
2019-12-12 09:35:05 +03:00
|
|
|
|
// nolint:gosec
|
2019-09-04 15:22:27 +03:00
|
|
|
|
"crypto/sha1"
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
2023-10-18 14:41:37 +03:00
|
|
|
|
"io"
|
2019-09-04 15:22:27 +03:00
|
|
|
|
"net/http"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync/atomic"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
|
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
|
|
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
2023-10-18 14:41:37 +03:00
|
|
|
|
"github.com/gin-gonic/gin"
|
2021-11-03 17:27:56 +03:00
|
|
|
|
retailcrm "github.com/retailcrm/api-client-go/v2"
|
2021-12-01 15:40:23 +03:00
|
|
|
|
"github.com/retailcrm/mg-transport-core/v2/core/config"
|
|
|
|
|
|
2024-06-14 18:10:48 +03:00
|
|
|
|
v1 "github.com/retailcrm/mg-transport-api-client-go/v1"
|
|
|
|
|
|
2021-12-01 15:40:23 +03:00
|
|
|
|
"github.com/retailcrm/mg-transport-core/v2/core/logger"
|
|
|
|
|
|
|
|
|
|
"github.com/retailcrm/mg-transport-core/v2/core/util/errorutil"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
markdownSymbols = []string{"*", "_", "`", "["}
|
|
|
|
|
slashRegex = regexp.MustCompile(`/+$`)
|
2019-09-04 15:22:27 +03:00
|
|
|
|
)
|
|
|
|
|
|
2021-12-13 16:49:42 +03:00
|
|
|
|
var (
|
|
|
|
|
DefaultScopes = []string{
|
|
|
|
|
"integration_read",
|
|
|
|
|
"integration_write",
|
|
|
|
|
}
|
|
|
|
|
DefaultCredentials = []string{
|
|
|
|
|
"/api/integration-modules/{code}",
|
|
|
|
|
"/api/integration-modules/{code}/edit",
|
|
|
|
|
}
|
|
|
|
|
)
|
2021-11-03 17:27:56 +03:00
|
|
|
|
|
2020-10-20 15:19:36 +03:00
|
|
|
|
var defaultCurrencies = map[string]string{
|
|
|
|
|
"rub": "₽",
|
|
|
|
|
"uah": "₴",
|
|
|
|
|
"byn": "Br",
|
|
|
|
|
"kzt": "₸",
|
|
|
|
|
"usd": "$",
|
|
|
|
|
"eur": "€",
|
|
|
|
|
"prb": "PRB",
|
|
|
|
|
"mdl": "L",
|
|
|
|
|
"kgs": "с",
|
|
|
|
|
"pln": "zł",
|
|
|
|
|
"azn": "₼",
|
|
|
|
|
"amd": "֏",
|
|
|
|
|
"thb": "฿",
|
|
|
|
|
"aed": "AED",
|
|
|
|
|
"nok": "kr",
|
|
|
|
|
"cad": "C$",
|
|
|
|
|
"czk": "Kč",
|
|
|
|
|
"sek": "kr",
|
|
|
|
|
"dkk": "kr",
|
|
|
|
|
"ron": "lei",
|
|
|
|
|
"uzs": "So'm",
|
|
|
|
|
"aud": "$",
|
|
|
|
|
"chf": "₣",
|
|
|
|
|
"inr": "₹",
|
|
|
|
|
"bgn": "лв",
|
|
|
|
|
"ngn": "₦",
|
|
|
|
|
"huf": "ƒ",
|
|
|
|
|
"ils": "₪",
|
|
|
|
|
"try": "₺",
|
|
|
|
|
"stn": "₡",
|
|
|
|
|
"ars": "$",
|
|
|
|
|
"bob": "Bs",
|
|
|
|
|
"ves": "Bs",
|
|
|
|
|
"gtq": "Q",
|
|
|
|
|
"hnl": "L",
|
|
|
|
|
"dop": "RD$",
|
|
|
|
|
"cop": "COL$",
|
|
|
|
|
"crc": "₡",
|
|
|
|
|
"cup": "$MN",
|
|
|
|
|
"nio": "C$",
|
|
|
|
|
"pab": "B/",
|
|
|
|
|
"pyg": "₲",
|
|
|
|
|
"pen": "S/",
|
|
|
|
|
"svc": "₡",
|
|
|
|
|
"uyu": "$U",
|
|
|
|
|
"gel": "₾",
|
|
|
|
|
"gbp": "£",
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// Utils service object.
|
2019-09-04 15:22:27 +03:00
|
|
|
|
type Utils struct {
|
2021-12-01 15:40:23 +03:00
|
|
|
|
Logger logger.Logger
|
2019-09-04 15:22:27 +03:00
|
|
|
|
slashRegex *regexp.Regexp
|
2021-12-01 15:40:23 +03:00
|
|
|
|
AWS config.AWS
|
|
|
|
|
TokenCounter uint32
|
|
|
|
|
IsDebug bool
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// NewUtils will create new Utils instance.
|
2021-12-01 15:40:23 +03:00
|
|
|
|
func NewUtils(awsConfig config.AWS, logger logger.Logger, debug bool) *Utils {
|
2019-09-04 15:22:27 +03:00
|
|
|
|
return &Utils{
|
|
|
|
|
IsDebug: debug,
|
2021-12-01 15:40:23 +03:00
|
|
|
|
AWS: awsConfig,
|
2019-09-04 15:22:27 +03:00
|
|
|
|
Logger: logger,
|
|
|
|
|
TokenCounter: 0,
|
2019-09-04 15:52:41 +03:00
|
|
|
|
slashRegex: slashRegex,
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-01 15:40:23 +03:00
|
|
|
|
// ResetUtils resets the utils inner state.
|
|
|
|
|
func (u *Utils) ResetUtils(awsConfig config.AWS, debug bool, tokenCounter uint32) {
|
2019-09-04 15:22:27 +03:00
|
|
|
|
u.TokenCounter = tokenCounter
|
2021-12-01 15:40:23 +03:00
|
|
|
|
u.AWS = awsConfig
|
2019-09-04 15:22:27 +03:00
|
|
|
|
u.IsDebug = debug
|
2019-09-04 15:52:41 +03:00
|
|
|
|
u.slashRegex = slashRegex
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GenerateToken will generate long pseudo-random string.
|
|
|
|
|
func (u *Utils) GenerateToken() string {
|
|
|
|
|
c := atomic.AddUint32(&u.TokenCounter, 1)
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%d%d", time.Now().UnixNano(), c))))
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// GetAPIClient will initialize RetailCRM api client from url and key.
|
2021-12-13 16:49:42 +03:00
|
|
|
|
// Scopes will be used to determine if client is valid. If there are no scopes - credentials will be used instead.
|
|
|
|
|
func (u *Utils) GetAPIClient(
|
|
|
|
|
url, key string, scopes []string, credentials ...[]string) (*retailcrm.Client, int, error) {
|
2021-11-03 17:27:56 +03:00
|
|
|
|
client := retailcrm.New(url, key).
|
2023-10-18 14:27:23 +03:00
|
|
|
|
WithLogger(logger.APIClientAdapter(u.Logger))
|
2019-09-04 15:22:27 +03:00
|
|
|
|
client.Debug = u.IsDebug
|
|
|
|
|
|
2021-11-03 17:27:56 +03:00
|
|
|
|
cr, status, err := client.APICredentials()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, status, err
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-03 17:27:56 +03:00
|
|
|
|
if res := u.checkScopes(cr.Scopes, scopes); len(res) != 0 {
|
2021-12-13 16:49:42 +03:00
|
|
|
|
if len(credentials) == 0 || len(cr.Scopes) > 0 {
|
2023-11-24 15:23:07 +03:00
|
|
|
|
u.Logger.Error(url, logger.HTTPStatusCode(status), logger.Body(res))
|
2021-12-13 16:49:42 +03:00
|
|
|
|
return nil, http.StatusBadRequest, errorutil.NewInsufficientScopesErr(res)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if res := u.checkScopes(cr.Credentials, credentials[0]); len(res) != 0 {
|
2023-11-24 15:23:07 +03:00
|
|
|
|
u.Logger.Error(url, logger.HTTPStatusCode(status), logger.Body(res))
|
2021-12-13 16:49:42 +03:00
|
|
|
|
return nil, http.StatusBadRequest, errorutil.NewInsufficientScopesErr(res)
|
|
|
|
|
}
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return client, 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-03 17:27:56 +03:00
|
|
|
|
func (u *Utils) checkScopes(scopes []string, scopesRequired []string) []string {
|
|
|
|
|
rs := make([]string, len(scopesRequired))
|
|
|
|
|
copy(rs, scopesRequired)
|
2019-09-04 15:22:27 +03:00
|
|
|
|
|
2021-11-03 17:27:56 +03:00
|
|
|
|
for _, vs := range scopes {
|
|
|
|
|
for kn, vn := range rs {
|
|
|
|
|
if vn == vs {
|
|
|
|
|
if len(rs) == 1 {
|
|
|
|
|
rs = rs[:0]
|
2019-09-04 15:22:27 +03:00
|
|
|
|
break
|
|
|
|
|
}
|
2021-11-03 17:27:56 +03:00
|
|
|
|
rs = append(rs[:kn], rs[kn+1:]...)
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-03 17:27:56 +03:00
|
|
|
|
return rs
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// UploadUserAvatar will upload avatar for user.
|
2019-09-04 15:22:27 +03:00
|
|
|
|
func (u *Utils) UploadUserAvatar(url string) (picURLs3 string, err error) {
|
|
|
|
|
s3Config := &aws.Config{
|
|
|
|
|
Credentials: credentials.NewStaticCredentials(
|
2021-12-01 15:40:23 +03:00
|
|
|
|
u.AWS.AccessKeyID,
|
|
|
|
|
u.AWS.SecretAccessKey,
|
2019-09-04 15:22:27 +03:00
|
|
|
|
""),
|
2021-12-01 15:40:23 +03:00
|
|
|
|
Region: aws.String(u.AWS.Region),
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
2022-04-12 17:11:52 +03:00
|
|
|
|
if u.AWS.Endpoint != "" {
|
|
|
|
|
s3Config.Endpoint = aws.String(u.AWS.Endpoint)
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 15:22:27 +03:00
|
|
|
|
s := session.Must(session.NewSession(s3Config))
|
|
|
|
|
uploader := s3manager.NewUploader(s)
|
|
|
|
|
|
2019-12-12 09:35:05 +03:00
|
|
|
|
// nolint:gosec
|
2019-09-04 15:22:27 +03:00
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
|
|
if resp.StatusCode >= http.StatusBadRequest {
|
|
|
|
|
return "", fmt.Errorf("get: %v code: %v", url, resp.StatusCode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result, err := uploader.Upload(&s3manager.UploadInput{
|
2021-12-01 15:40:23 +03:00
|
|
|
|
Bucket: aws.String(u.AWS.Bucket),
|
|
|
|
|
Key: aws.String(fmt.Sprintf("%v/%v.jpg", u.AWS.FolderName, u.GenerateToken())),
|
2019-09-04 15:22:27 +03:00
|
|
|
|
Body: resp.Body,
|
2021-12-01 15:40:23 +03:00
|
|
|
|
ContentType: aws.String(u.AWS.ContentType),
|
2019-09-04 15:22:27 +03:00
|
|
|
|
ACL: aws.String("public-read"),
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
picURLs3 = result.Location
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// RemoveTrailingSlash will remove slash at the end of any string.
|
2019-09-18 18:02:40 +03:00
|
|
|
|
func (u *Utils) RemoveTrailingSlash(crmURL string) string {
|
|
|
|
|
return u.slashRegex.ReplaceAllString(crmURL, ``)
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// GetMGItemData will upload file to MG by URL and return information about attachable item.
|
2019-09-04 15:22:27 +03:00
|
|
|
|
func GetMGItemData(client *v1.MgClient, url string, caption string) (v1.Item, int, error) {
|
|
|
|
|
item := v1.Item{}
|
|
|
|
|
|
|
|
|
|
data, st, err := client.UploadFileByURL(
|
|
|
|
|
v1.UploadFileByUrlRequest{
|
|
|
|
|
Url: url,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return item, st, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
item.ID = data.ID
|
|
|
|
|
item.Caption = caption
|
|
|
|
|
|
|
|
|
|
return item, st, err
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// GetEntitySHA1 will serialize any value to JSON and return SHA1 hash of this JSON.
|
2019-09-04 15:22:27 +03:00
|
|
|
|
func GetEntitySHA1(v interface{}) (hash string, err error) {
|
|
|
|
|
res, _ := json.Marshal(v)
|
|
|
|
|
|
2019-12-12 09:35:05 +03:00
|
|
|
|
// nolint:gosec
|
2019-09-04 15:22:27 +03:00
|
|
|
|
h := sha1.New()
|
|
|
|
|
_, err = h.Write(res)
|
|
|
|
|
hash = fmt.Sprintf("%x", h.Sum(nil))
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// ReplaceMarkdownSymbols will remove markdown symbols from text.
|
2019-09-04 15:22:27 +03:00
|
|
|
|
func ReplaceMarkdownSymbols(s string) string {
|
2019-09-04 15:52:41 +03:00
|
|
|
|
for _, v := range markdownSymbols {
|
2021-12-01 15:40:23 +03:00
|
|
|
|
s = strings.ReplaceAll(s, v, "\\"+v)
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// DefaultCurrencies will return default currencies list for all bots.
|
2019-09-04 15:22:27 +03:00
|
|
|
|
func DefaultCurrencies() map[string]string {
|
2020-10-20 15:19:36 +03:00
|
|
|
|
return defaultCurrencies
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetCurrencySymbol returns currency symbol by it's ISO 4127 code.
|
2021-02-09 14:57:14 +03:00
|
|
|
|
// It returns provided currency code in uppercase if currency symbol cannot be found.
|
2020-10-20 15:19:36 +03:00
|
|
|
|
func GetCurrencySymbol(code string) string {
|
|
|
|
|
if i, ok := DefaultCurrencies()[strings.ToLower(code)]; ok {
|
|
|
|
|
return i
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
2020-10-20 15:19:36 +03:00
|
|
|
|
|
|
|
|
|
return strings.ToUpper(code)
|
2019-09-04 15:22:27 +03:00
|
|
|
|
}
|
2021-09-29 10:27:36 +03:00
|
|
|
|
|
|
|
|
|
func FormatCurrencyValue(value float32) string {
|
|
|
|
|
return fmt.Sprintf("%.2f", value)
|
|
|
|
|
}
|
2023-10-18 14:41:37 +03:00
|
|
|
|
|
|
|
|
|
// BindJSONWithRaw will perform usual ShouldBindJSON and will return the original body data.
|
|
|
|
|
func BindJSONWithRaw(c *gin.Context, obj any) ([]byte, error) {
|
|
|
|
|
closer := c.Request.Body
|
|
|
|
|
defer func() { _ = closer.Close() }()
|
|
|
|
|
data, err := io.ReadAll(closer)
|
|
|
|
|
if err != nil {
|
2023-10-18 15:40:46 +03:00
|
|
|
|
return []byte{}, err
|
2023-10-18 14:41:37 +03:00
|
|
|
|
}
|
|
|
|
|
c.Request.Body = io.NopCloser(bytes.NewReader(data))
|
|
|
|
|
return data, c.ShouldBindJSON(obj)
|
|
|
|
|
}
|