amocrm/pkg/amoClient/amo.go

471 lines
16 KiB
Go
Raw Normal View History

2024-04-08 15:48:24 +00:00
package amoClient
2024-04-04 18:02:10 +00:00
import (
2024-04-17 12:21:06 +00:00
"amocrm/internal/models"
2024-04-22 07:49:46 +00:00
"amocrm/internal/workers/limiter"
2024-04-04 18:32:56 +00:00
"encoding/json"
"fmt"
2024-04-08 08:20:10 +00:00
"net/url"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
2024-04-10 10:53:19 +00:00
"time"
2024-04-08 08:20:10 +00:00
2024-04-04 18:02:10 +00:00
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
type Amo struct {
baseApiURL string
userInfoURL string
fiberClient *fiber.Client
logger *zap.Logger
redirectionURL string
integrationID string
integrationSecret string
2024-04-22 07:49:46 +00:00
rateLimiter *limiter.RateLimiter
2024-04-04 18:02:10 +00:00
}
type AmoDeps struct {
BaseApiURL string
UserInfoURL string
FiberClient *fiber.Client
Logger *zap.Logger
RedirectionURL string
IntegrationID string
IntegrationSecret string
2024-04-22 07:49:46 +00:00
RateLimiter *limiter.RateLimiter
2024-04-04 18:02:10 +00:00
}
func NewAmoClient(deps AmoDeps) *Amo {
if deps.FiberClient == nil {
deps.FiberClient = fiber.AcquireClient()
}
return &Amo{
baseApiURL: deps.BaseApiURL,
userInfoURL: deps.UserInfoURL,
fiberClient: deps.FiberClient,
logger: deps.Logger,
redirectionURL: deps.RedirectionURL,
integrationSecret: deps.IntegrationSecret,
integrationID: deps.IntegrationID,
2024-04-10 10:53:19 +00:00
rateLimiter: deps.RateLimiter,
2024-04-04 18:02:10 +00:00
}
}
2024-04-12 14:51:26 +00:00
// токен должен быть с правами администратора
2024-04-04 18:02:10 +00:00
// https://www.amocrm.ru/developers/content/crm_platform/users-api#users-list
2024-04-19 16:05:42 +00:00
func (a *Amo) GetUserList(req models.RequestGetListUsers, accesToken string) (*models.ResponseGetListUsers, error) {
2024-04-10 10:53:19 +00:00
for {
if a.rateLimiter.Check() {
uri := fmt.Sprintf("https://penadigitaltech.amocrm.ru/ajax/v3/users?with=rights&page=%d&limit=%d", req.Page, req.Limit)
2024-04-10 10:53:19 +00:00
agent := a.fiberClient.Get(uri)
2024-04-19 16:05:42 +00:00
agent.Set("Authorization", "Bearer "+accesToken)
2024-04-10 10:53:19 +00:00
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err := range errs {
a.logger.Error("error sending request in GetUserList", zap.Error(err))
}
return nil, fmt.Errorf("request GetUserList failed: %v", errs[0])
}
if statusCode != fiber.StatusOK {
switch statusCode {
case fiber.StatusForbidden:
errorMessage := fmt.Sprintf("error GetUserList StatusForbidden - %d", statusCode)
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
case fiber.StatusUnauthorized:
errorMessage := fmt.Sprintf("error GetUserList StatusUnauthorized - %d", statusCode)
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
default:
errorMessage := fmt.Sprintf("error GetUserList statusCode - %d", statusCode)
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
}
2024-04-17 12:21:06 +00:00
var userListResponse models.ResponseGetListUsers
2024-04-10 10:53:19 +00:00
err := json.Unmarshal(resBody, &userListResponse)
if err != nil {
a.logger.Error("error unmarshal ResponseGetListUsers:", zap.Error(err))
return nil, err
}
return &userListResponse, nil
2024-04-04 18:32:56 +00:00
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
2024-04-04 18:32:56 +00:00
}
2024-04-04 18:02:10 +00:00
}
// https://www.amocrm.ru/developers/content/oauth/step-by-step
// POST /oauth2/access_token
// тут и создание по коду и обновление по рефрешу в этом клиенте
2024-04-17 12:21:06 +00:00
func (a *Amo) CreateWebHook(req models.WebHookRequest) (*models.CreateWebHookResp, error) {
2024-04-10 10:53:19 +00:00
for {
if a.rateLimiter.Check() {
req.SetClientID(a.integrationID)
req.SetClientSecret(a.integrationSecret)
req.SetRedirectURL(a.redirectionURL)
bodyBytes, err := json.Marshal(req)
if err != nil {
a.logger.Error("error marshal req in CreateWebHook:", zap.Error(err))
return nil, err
}
agent := a.fiberClient.Post(a.baseApiURL + "/oauth2/access_token")
agent.Set("Content-Type", "application/json").Body(bodyBytes)
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err = range errs {
a.logger.Error("error sending request in CreateWebHook for create or update tokens", zap.Error(err))
}
return nil, fmt.Errorf("request failed: %v", errs[0])
}
if statusCode != fiber.StatusOK {
2024-04-11 15:50:27 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from CreateWebHook: %s", string(resBody))
2024-04-10 10:53:19 +00:00
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
2024-04-17 12:21:06 +00:00
var tokens models.CreateWebHookResp
2024-04-10 10:53:19 +00:00
err = json.Unmarshal(resBody, &tokens)
if err != nil {
a.logger.Error("error unmarshal CreateWebHookResp:", zap.Error(err))
return nil, err
}
return &tokens, nil
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
}
2024-04-04 18:02:10 +00:00
}
// https://www.amocrm.ru/developers/content/oauth/step-by-step#%D0%A5%D1%83%D0%BA-%D0%BE%D0%B1-%D0%BE%D1%82%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B8-%D0%B8%D0%BD%D1%82%D0%B5%D0%B3%D1%80%D0%B0%D1%86%D0%B8%D0%B8
func (a *Amo) DeleteWebHook() {
2024-04-10 10:53:19 +00:00
for {
if a.rateLimiter.Check() {
return
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
2024-04-10 10:53:19 +00:00
}
2024-04-04 18:02:10 +00:00
}
// https://www.amocrm.ru/developers/content/crm_platform/leads_pipelines#%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D1%81%D1%82%D0%B0%D1%82%D1%83%D1%81%D0%BE%D0%B2-%D0%B2%D0%BE%D1%80%D0%BE%D0%BD%D0%BA%D0%B8-%D1%81%D0%B4%D0%B5%D0%BB%D0%BE%D0%BA
// GET /api/v4/leads/pipelines/{pipeline_id}/statuses
2024-04-17 12:21:06 +00:00
func (a *Amo) GetListSteps(pipelineID int, accessToken string) (*models.ResponseGetListSteps, error) {
2024-04-10 10:53:19 +00:00
for {
if a.rateLimiter.Check() {
uri := fmt.Sprintf("%s/api/v4/leads/pipelines/%d/statuses", a.baseApiURL, pipelineID)
agent := a.fiberClient.Get(uri)
2024-04-11 15:50:27 +00:00
agent.Set("Authorization", "Bearer "+accessToken)
2024-04-10 10:53:19 +00:00
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err := range errs {
a.logger.Error("error sending request in GetListSteps", zap.Error(err))
}
return nil, fmt.Errorf("request GetListSteps failed: %v", errs[0])
}
if statusCode != fiber.StatusOK {
2024-04-11 15:50:27 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from GetListSteps: %s", string(resBody))
2024-04-10 10:53:19 +00:00
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
2024-04-17 12:21:06 +00:00
var listSteps models.ResponseGetListSteps
2024-04-10 10:53:19 +00:00
err := json.Unmarshal(resBody, &listSteps)
if err != nil {
a.logger.Error("error unmarshal ResponseGetListSteps:", zap.Error(err))
return nil, err
}
return &listSteps, nil
2024-04-05 12:16:20 +00:00
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
2024-04-05 12:16:20 +00:00
}
2024-04-04 18:02:10 +00:00
}
// https://www.amocrm.ru/developers/content/crm_platform/custom-fields#%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%BF%D0%BE%D0%BB%D0%B5%D0%B9-%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8
2024-04-05 12:16:20 +00:00
// GET /api/v4/leads/custom_fields
// GET /api/v4/contacts/custom_fields
// GET /api/v4/companies/custom_fields
// GET /api/v4/customers/custom_fields
2024-04-12 11:52:38 +00:00
// пока без этих двух
2024-04-05 12:16:20 +00:00
// GET /api/v4/customers/segments/custom_fields
// GET /api/v4/catalogs/{catalog_id}/custom_fields
// эти методы все относятся к одному и тому же, поэтому на вход будет урл и рек стуктура, выход у них один и тот же
2024-04-17 12:21:06 +00:00
func (a *Amo) GetListFields(req models.GetListFieldsReq, accessToken string) (*models.ResponseGetListFields, error) {
2024-04-10 10:53:19 +00:00
for {
if a.rateLimiter.Check() {
2024-04-12 11:52:38 +00:00
fullURL := fmt.Sprintf("%s/api/v4/%s/custom_fields?limit=%d&page=%d", a.baseApiURL, req.EntityType, req.Limit, req.Page)
2024-04-10 10:53:19 +00:00
agent := a.fiberClient.Get(fullURL)
2024-04-12 11:52:38 +00:00
agent.Set("Authorization", "Bearer "+accessToken)
2024-04-10 10:53:19 +00:00
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err := range errs {
a.logger.Error("error sending request in GetListFields", zap.Error(err))
}
return nil, fmt.Errorf("request GetListFields failed: %v", errs[0])
}
if statusCode != fiber.StatusOK {
2024-04-11 15:50:27 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from GetListFields: %s", string(resBody))
2024-04-10 10:53:19 +00:00
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
2024-04-17 12:21:06 +00:00
var listFields models.ResponseGetListFields
2024-04-10 10:53:19 +00:00
err := json.Unmarshal(resBody, &listFields)
if err != nil {
a.logger.Error("error unmarshal ResponseGetListFields:", zap.Error(err))
return nil, err
}
return &listFields, nil
2024-04-05 12:16:20 +00:00
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
2024-04-05 12:16:20 +00:00
}
2024-04-04 18:02:10 +00:00
}
// https://www.amocrm.ru/developers/content/crm_platform/tags-api#%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D1%82%D0%B5%D0%B3%D0%BE%D0%B2-%D0%B4%D0%BB%D1%8F-%D1%81%D1%83%D1%89%D0%BD%D0%BE%D1%81%D1%82%D0%B8
2024-04-05 12:16:20 +00:00
// GET /api/v4/{entity_type:leads|contacts|companies|customers}/tags
2024-04-17 12:21:06 +00:00
func (a *Amo) GetListTags(req models.GetListTagsReq, accessToken string) (*models.ResponseGetListTags, error) {
2024-04-10 10:53:19 +00:00
for {
if a.rateLimiter.Check() {
fullURL := fmt.Sprintf("%s/api/v4/%s/tags?", a.baseApiURL, req.EntityType)
if req.Filter.Name != "" {
fullURL += "&filter[name]=" + url.QueryEscape(req.Filter.Name)
}
if len(req.Filter.ID) > 0 {
for _, id := range req.Filter.ID {
fullURL += fmt.Sprintf("&filter[id][]=%d", id)
}
}
if req.Filter.Query != "" {
fullURL += "&filter[query]=" + url.QueryEscape(req.Filter.Query)
}
fullURL += fmt.Sprintf("&page=%d&limit=%d", req.Page, req.Limit)
agent := a.fiberClient.Get(fullURL)
agent.Set("Authorization", "Bearer "+accessToken)
2024-04-10 10:53:19 +00:00
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err := range errs {
a.logger.Error("error sending request in GetListTags", zap.Error(err))
}
return nil, fmt.Errorf("request GetListTags failed: %v", errs[0])
}
if statusCode != fiber.StatusOK {
2024-04-11 15:50:27 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from GetListTags: %s", string(resBody))
2024-04-10 10:53:19 +00:00
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
2024-04-17 12:21:06 +00:00
var listTags models.ResponseGetListTags
2024-04-10 10:53:19 +00:00
err := json.Unmarshal(resBody, &listTags)
if err != nil {
a.logger.Error("error unmarshal ResponseGetListTags:", zap.Error(err))
return nil, err
}
return &listTags, nil
2024-04-05 12:16:20 +00:00
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
2024-04-05 12:16:20 +00:00
}
2024-04-04 18:02:10 +00:00
}
// https://www.amocrm.ru/developers/content/crm_platform/account-info
// GET /api/v4/account
2024-04-17 12:21:06 +00:00
func (a *Amo) GetUserInfo(accessToken string) (*models.AmocrmUserInformation, error) {
2024-04-10 10:53:19 +00:00
for {
if a.rateLimiter.Check() {
agent := a.fiberClient.Get(a.userInfoURL)
agent.Set("Authorization", "Bearer "+accessToken)
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err := range errs {
a.logger.Error("error sending request in GetUserInfo", zap.Error(err))
}
2024-04-11 13:58:31 +00:00
return nil, fmt.Errorf("request GetUserInfo failed: %v", errs[0])
2024-04-10 10:53:19 +00:00
}
if statusCode != fiber.StatusOK {
2024-04-11 15:50:27 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from GetUserInfo: %s", string(resBody))
2024-04-10 10:53:19 +00:00
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
2024-04-17 12:21:06 +00:00
var userInfo models.AmocrmUserInformation
2024-04-10 10:53:19 +00:00
err := json.Unmarshal(resBody, &userInfo)
if err != nil {
a.logger.Error("error unmarshal AmocrmUserInformation:", zap.Error(err))
return nil, err
}
return &userInfo, nil
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
}
}
2024-04-11 13:58:31 +00:00
2024-04-19 16:05:42 +00:00
// https://www.amocrm.ru/developers/content/crm_platform/leads_pipelines#%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%B2%D0%BE%D1%80%D0%BE%D0%BD%D0%BE%D0%BA-%D1%81%D0%B4%D0%B5%D0%BB%D0%BE%D0%BA
// GET /api/v4/leads/pipelines
func (a *Amo) GetListPipelines(accessToken string) (*models.PipelineResponse, error) {
2024-04-11 13:58:31 +00:00
for {
if a.rateLimiter.Check() {
2024-04-19 16:05:42 +00:00
uri := fmt.Sprintf("%s/api/v4/leads/pipelines", a.baseApiURL)
2024-04-11 15:08:54 +00:00
agent := a.fiberClient.Get(uri)
2024-04-19 16:05:42 +00:00
agent.Set("Authorization", "Bearer "+accessToken)
2024-04-11 13:58:31 +00:00
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err := range errs {
2024-04-19 16:05:42 +00:00
a.logger.Error("error sending request in GetListPipelines", zap.Error(err))
2024-04-11 13:58:31 +00:00
}
2024-04-19 16:05:42 +00:00
return nil, fmt.Errorf("request GetListPipelines failed: %v", errs[0])
2024-04-11 13:58:31 +00:00
}
if statusCode != fiber.StatusOK {
2024-04-19 16:05:42 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from GetListPipelines: %s", string(resBody))
2024-04-11 13:58:31 +00:00
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
2024-04-19 16:05:42 +00:00
var userInfo models.PipelineResponse
2024-04-11 13:58:31 +00:00
err := json.Unmarshal(resBody, &userInfo)
if err != nil {
2024-04-19 16:05:42 +00:00
a.logger.Error("error unmarshal PipelineResponse:", zap.Error(err))
2024-04-11 13:58:31 +00:00
return nil, err
}
return &userInfo, nil
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
2024-04-11 13:58:31 +00:00
}
}
2024-04-11 15:08:54 +00:00
2024-04-19 16:05:42 +00:00
// токен должен быть с правами администратора
// https://www.amocrm.ru/developers/content/crm_platform/users-api#user-detail
// GET /api/v4/users/{id
func (a *Amo) GetUserByID(id int32, accessToken string) (*models.OneUserInfo, error) {
2024-04-11 15:08:54 +00:00
for {
if a.rateLimiter.Check() {
2024-04-19 16:05:42 +00:00
uri := fmt.Sprintf("%s/api/v4/users/%d?with=role,uuid", a.baseApiURL, id)
2024-04-11 15:08:54 +00:00
agent := a.fiberClient.Get(uri)
agent.Set("Authorization", "Bearer "+accessToken)
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err := range errs {
2024-04-19 16:05:42 +00:00
a.logger.Error("error sending request in GetUserByID", zap.Error(err))
2024-04-11 15:08:54 +00:00
}
2024-04-19 16:05:42 +00:00
return nil, fmt.Errorf("request GetUserByID failed: %v", errs[0])
2024-04-11 15:08:54 +00:00
}
if statusCode != fiber.StatusOK {
2024-05-04 18:38:36 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from Get User By ID:%s", string(resBody))
2024-04-11 15:08:54 +00:00
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
2024-04-19 16:05:42 +00:00
var userInfo models.OneUserInfo
2024-04-11 15:08:54 +00:00
err := json.Unmarshal(resBody, &userInfo)
if err != nil {
2024-05-04 18:38:36 +00:00
a.logger.Error("error unmarshal response body in Get User By ID:", zap.Error(err))
2024-04-11 15:08:54 +00:00
return nil, err
}
return &userInfo, nil
}
2024-04-22 07:49:46 +00:00
time.Sleep(a.rateLimiter.Interval)
2024-04-11 15:08:54 +00:00
}
}
func (a *Amo) AddFields(req []models.AddLeadsFields, entity model.EntityType, accessToken string) (*models.ResponseGetListFields, error) {
for {
if a.rateLimiter.Check() {
uri := fmt.Sprintf("%s/api/v4/%s/custom_fields", a.baseApiURL, entity)
bodyBytes, err := json.Marshal(req)
if err != nil {
2024-05-04 18:38:36 +00:00
a.logger.Error("error marshal req in Add Fields:", zap.Error(err))
return nil, err
}
agent := a.fiberClient.Post(uri)
agent.Set("Content-Type", "application/json").Body(bodyBytes)
agent.Set("Authorization", "Bearer "+accessToken)
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err = range errs {
2024-05-04 18:38:36 +00:00
a.logger.Error("error sending request in Add Fields for add fields", zap.Error(err))
}
return nil, fmt.Errorf("request failed: %v", errs[0])
}
if statusCode != fiber.StatusOK {
2024-05-04 18:38:36 +00:00
errorMessage := fmt.Sprintf("received an incorrect response from Add Fields: %s", string(resBody))
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
var fields models.ResponseGetListFields
err = json.Unmarshal(resBody, &fields)
if err != nil {
2024-05-04 18:38:36 +00:00
a.logger.Error("error unmarshal response body in Add Fields:", zap.Error(err))
return nil, err
}
return &fields, nil
}
time.Sleep(a.rateLimiter.Interval)
}
}
2024-05-04 18:38:36 +00:00
func (a *Amo) CreatingDeal(req []models.DealReq, accessToken string) (*models.DealResp, error) {
for {
if a.rateLimiter.Check() {
2024-05-04 21:06:17 +00:00
uri := fmt.Sprintf("%s/api/v4/leads/complex", a.baseApiURL)
2024-05-04 18:38:36 +00:00
bodyBytes, err := json.Marshal(req)
if err != nil {
a.logger.Error("error marshal req in Creating Deal:", zap.Error(err))
return nil, err
}
agent := a.fiberClient.Post(uri)
agent.Set("Content-Type", "application/json").Body(bodyBytes)
agent.Set("Authorization", "Bearer "+accessToken)
statusCode, resBody, errs := agent.Bytes()
if len(errs) > 0 {
for _, err = range errs {
a.logger.Error("error sending request in Creating Deal for creating deals", zap.Error(err))
}
return nil, fmt.Errorf("request failed: %v", errs[0])
}
if statusCode != fiber.StatusOK {
errorMessage := fmt.Sprintf("received an incorrect response from Creating Deal: %s", string(resBody))
a.logger.Error(errorMessage, zap.Int("status", statusCode))
return nil, fmt.Errorf(errorMessage)
}
var resp models.DealResp
err = json.Unmarshal(resBody, &resp)
if err != nil {
a.logger.Error("error unmarshal response body in Creating Deal:", zap.Error(err))
return nil, err
}
return &resp, nil
}
time.Sleep(a.rateLimiter.Interval)
}
}