package amoClient import ( amo2 "amocrm/internal/models/amo" "encoding/json" "fmt" "net/url" "time" "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 rateLimiter *RateLimiter } type AmoDeps struct { BaseApiURL string UserInfoURL string FiberClient *fiber.Client Logger *zap.Logger RedirectionURL string IntegrationID string IntegrationSecret string RateLimiter *RateLimiter } 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, rateLimiter: deps.RateLimiter, } } // todo тут наверное ко всем этим методам токен авторизации нужно прикладывать прочитать подробнее про это // https://www.amocrm.ru/developers/content/crm_platform/users-api#users-list func (a *Amo) GetUserList(req amo2.RequestGetListUsers) (*amo2.ResponseGetListUsers, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("%s/api/v4/users?page=%d&limit=%d&with=%s", a.baseApiURL, req.Page, req.Limit, req.With) agent := a.fiberClient.Get(uri) 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) } } var userListResponse amo2.ResponseGetListUsers err := json.Unmarshal(resBody, &userListResponse) if err != nil { a.logger.Error("error unmarshal ResponseGetListUsers:", zap.Error(err)) return nil, err } return &userListResponse, nil } time.Sleep(a.rateLimiter.interval) } } // https://www.amocrm.ru/developers/content/oauth/step-by-step // POST /oauth2/access_token // тут и создание по коду и обновление по рефрешу в этом клиенте func (a *Amo) CreateWebHook(req amo2.WebHookRequest) (*amo2.CreateWebHookResp, error) { 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 { errorMessage := fmt.Sprintf("received an incorrect response from CreateWebHook: %d", statusCode) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var tokens amo2.CreateWebHookResp err = json.Unmarshal(resBody, &tokens) if err != nil { a.logger.Error("error unmarshal CreateWebHookResp:", zap.Error(err)) return nil, err } return &tokens, nil } time.Sleep(a.rateLimiter.interval) } } // 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() { for { if a.rateLimiter.Check() { return } time.Sleep(a.rateLimiter.interval) } } // 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 func (a *Amo) GetListSteps(pipelineID int) (*amo2.ResponseGetListSteps, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("%s/api/v4/leads/pipelines/%d/statuses", a.baseApiURL, pipelineID) agent := a.fiberClient.Get(uri) 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 { errorMessage := fmt.Sprintf("received an incorrect response from GetListSteps: %d", statusCode) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var listSteps amo2.ResponseGetListSteps err := json.Unmarshal(resBody, &listSteps) if err != nil { a.logger.Error("error unmarshal ResponseGetListSteps:", zap.Error(err)) return nil, err } return &listSteps, nil } time.Sleep(a.rateLimiter.interval) } } // 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 // GET /api/v4/leads/custom_fields // GET /api/v4/contacts/custom_fields // GET /api/v4/companies/custom_fields // GET /api/v4/customers/custom_fields // GET /api/v4/customers/segments/custom_fields // GET /api/v4/catalogs/{catalog_id}/custom_fields // эти методы все относятся к одному и тому же, поэтому на вход будет урл и рек стуктура, выход у них один и тот же func (a *Amo) GetListFields(req amo2.GetListFieldsReq, url string) (*amo2.ResponseGetListFields, error) { for { if a.rateLimiter.Check() { fullURL := fmt.Sprintf("%s?limit=%d&page=%d", url, req.Limit, req.Page) agent := a.fiberClient.Get(fullURL) 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 { errorMessage := fmt.Sprintf("received an incorrect response from GetListFields: %d", statusCode) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var listFields amo2.ResponseGetListFields err := json.Unmarshal(resBody, &listFields) if err != nil { a.logger.Error("error unmarshal ResponseGetListFields:", zap.Error(err)) return nil, err } return &listFields, nil } time.Sleep(a.rateLimiter.interval) } } // 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 // GET /api/v4/{entity_type:leads|contacts|companies|customers}/tags func (a *Amo) GetListTags(req amo2.GetListTagsReq) (*amo2.ResponseGetListTags, error) { 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) 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 { errorMessage := fmt.Sprintf("received an incorrect response from GetListTags: %d", statusCode) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var listTags amo2.ResponseGetListTags err := json.Unmarshal(resBody, &listTags) if err != nil { a.logger.Error("error unmarshal ResponseGetListTags:", zap.Error(err)) return nil, err } return &listTags, nil } time.Sleep(a.rateLimiter.interval) } } func (a *Amo) GetUserInfo(accessToken string) (*amo2.AmocrmUserInformation, error) { 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)) } return nil, fmt.Errorf("request GetListTags failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from GetUserInfo: %d", statusCode) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var userInfo amo2.AmocrmUserInformation err := json.Unmarshal(resBody, &userInfo) if err != nil { a.logger.Error("error unmarshal AmocrmUserInformation:", zap.Error(err)) return nil, err } return &userInfo, nil } time.Sleep(a.rateLimiter.interval) } }