package amoClient import ( "bytes" "encoding/json" "fmt" "gitea.pena/SQuiz/amocrm/internal/models" "gitea.pena/SQuiz/amocrm/internal/workers/limiter" "gitea.pena/SQuiz/common/model" "io" "net/url" "os" "strings" "sync" "time" "github.com/gofiber/fiber/v2" "go.uber.org/zap" ) type Amo struct { fiberClient *fiber.Client logger *zap.Logger redirectionURL string integrationID string integrationSecret string rateLimiter *limiter.RateLimiter fileMutex sync.Mutex } type AmoDeps struct { FiberClient *fiber.Client Logger *zap.Logger RedirectionURL string IntegrationID string IntegrationSecret string RateLimiter *limiter.RateLimiter } func NewAmoClient(deps AmoDeps) *Amo { if deps.FiberClient == nil { deps.FiberClient = fiber.AcquireClient() } return &Amo{ fiberClient: deps.FiberClient, logger: deps.Logger, redirectionURL: deps.RedirectionURL, integrationSecret: deps.IntegrationSecret, integrationID: deps.IntegrationID, rateLimiter: deps.RateLimiter, } } // токен должен быть с правами администратора // https://www.amocrm.ru/developers/content/crm_platform/users-api#users-list func (a *Amo) GetUserList(req models.RequestGetListUsers, accesToken string, domain string) (*models.ResponseGetListUsers, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/ajax/v3/users?with=rights&page=%d&limit=%d", domain, req.Page, req.Limit) agent := a.fiberClient.Get(uri) agent.Set("Authorization", "Bearer "+accesToken) 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 models.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 models.WebHookRequest, domain string) (*models.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("https://" + domain + "/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: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var tokens models.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/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, accessToken string, domain string) (*models.ResponseGetListSteps, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/leads/pipelines/%d/statuses", domain, pipelineID) agent := a.fiberClient.Get(uri) 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 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: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var listSteps models.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 models.GetListFieldsReq, accessToken string, domain string) (*models.ResponseGetListFields, error) { for { if a.rateLimiter.Check() { fullURL := fmt.Sprintf("https://%s/api/v4/%s/custom_fields?limit=%d&page=%d", domain, req.EntityType, req.Limit, req.Page) agent := a.fiberClient.Get(fullURL) 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 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: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var listFields models.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 models.GetListTagsReq, accessToken string, domain string) (*models.ResponseGetListTags, error) { for { if a.rateLimiter.Check() { fullURL := fmt.Sprintf("https://%s/api/v4/%s/tags?", domain, 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) 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]) } a.logger.Info("Succesfull get tags:", zap.String("respBody", string(resBody)), zap.String("fullUrl", fullURL)) if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from GetListTags(%s): %s", fullURL, string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var listTags models.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) } } // https://www.amocrm.ru/developers/content/crm_platform/account-info // GET /api/v4/account func (a *Amo) GetUserInfo(accessToken string, domain string) (*models.AmocrmUserInformation, error) { for { if a.rateLimiter.Check() { url := fmt.Sprintf("https://%s/api/v4/account?with=drive_url", domain) agent := a.fiberClient.Get(url) 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 GetUserInfo failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from GetUserInfo: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var userInfo models.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) } } // 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, domain string) (*models.PipelineResponse, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/leads/pipelines", domain) agent := a.fiberClient.Get(uri) 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 GetListPipelines", zap.Error(err)) } return nil, fmt.Errorf("request GetListPipelines failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from GetListPipelines: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var userInfo models.PipelineResponse err := json.Unmarshal(resBody, &userInfo) if err != nil { a.logger.Error("error unmarshal PipelineResponse:", zap.Error(err)) return nil, err } return &userInfo, nil } time.Sleep(a.rateLimiter.Interval) } } // токен должен быть с правами администратора // 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, domain string) (*models.OneUserInfo, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/users/%d?with=role,uuid", domain, id) agent := a.fiberClient.Get(uri) 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 GetUserByID", zap.Error(err)) } return nil, fmt.Errorf("request GetUserByID failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from Get User By ID:%s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var userInfo models.OneUserInfo err := json.Unmarshal(resBody, &userInfo) if err != nil { a.logger.Error("error unmarshal response body in Get User By ID:", zap.Error(err)) return nil, err } return &userInfo, nil } time.Sleep(a.rateLimiter.Interval) } } func (a *Amo) AddFields(req []models.AddLeadsFields, entity model.EntityType, accessToken string, domain string) (*models.ResponseGetListFields, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/%s/custom_fields", domain, entity) bodyBytes, err := json.Marshal(req) if err != nil { 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 { 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 { 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 { a.logger.Error("error unmarshal response body in Add Fields:", zap.Error(err)) return nil, err } return &fields, nil } time.Sleep(a.rateLimiter.Interval) } } func (a *Amo) CreatingDeal(req []models.DealReq, accessToken string, domain string) ([]models.DealResp, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/leads/complex", domain) 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) } } func (a *Amo) UpdatingDeal(req []models.UpdateDealReq, accessToken string, domain string) (*models.UpdateDealResp, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/leads", domain) bodyBytes, err := json.Marshal(req) if err != nil { a.logger.Error("error marshal req in Updating Deal:", zap.Error(err)) return nil, err } agent := a.fiberClient.Patch(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 Updating Deal for updating 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 Updating Deal: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode), zap.String("domain", domain), zap.String("token", accessToken)) return nil, fmt.Errorf(errorMessage) } var resp models.UpdateDealResp err = json.Unmarshal(resBody, &resp) if err != nil { a.logger.Error("error unmarshal response body in Updating Deal:", zap.Error(err)) return nil, err } return &resp, nil } time.Sleep(a.rateLimiter.Interval) } } func (a *Amo) CreatingCustomer(req []models.Customer, accessToken string, domain string) (*models.CustomerResp, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/customers", domain) bodyBytes, err := json.Marshal(req) if err != nil { a.logger.Error("error marshal req in Creating Customer:", 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 Customer for creating customers", 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 Customer: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var resp models.CustomerResp err = json.Unmarshal(resBody, &resp) if err != nil { a.logger.Error("error unmarshal response body in Creating Customer:", zap.Error(err)) return nil, err } return &resp, nil } time.Sleep(a.rateLimiter.Interval) } } // todo подумать на счет хранилища в амо func (a *Amo) downloadFile(urlFile string) (*os.File, error) { var err error agent := a.fiberClient.Get(urlFile) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err = range errs { a.logger.Error("error sending request for getting file by url", zap.Error(err)) } return nil, fmt.Errorf("request failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from getting file by url: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } fileName := strings.Split(urlFile, "/") tmpFile, err := os.Create(fileName[len(fileName)-1]) if err != nil { return nil, err } _, err = io.Copy(tmpFile, bytes.NewReader(resBody)) if err != nil { return nil, err } return tmpFile, nil } func (a *Amo) UploadFileToAmo(urlFile string, accessToken string, driveURL string) (*models.ValuesFile, error) { a.fileMutex.Lock() defer a.fileMutex.Unlock() localFile, err := a.downloadFile(urlFile) if err != nil { return nil, err } defer os.Remove(localFile.Name()) fileInfo, err := os.Stat(localFile.Name()) if err != nil { return nil, err } fileSize := fileInfo.Size() createSessionData := &models.CreateSession{ FileName: localFile.Name(), FileSize: fileSize, } uri := fmt.Sprintf("%s/v1.0/sessions", driveURL) bodyBytes, err := json.Marshal(createSessionData) if err != nil { a.logger.Error("error marshal create session data:", 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 to create session for upload file in amo", 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 upload file session: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var resp models.UploadSession err = json.Unmarshal(resBody, &resp) if err != nil { a.logger.Error("error unmarshal response body in creating upload file session:", zap.Error(err)) return nil, err } response, err := a.createPart(resp, localFile) if err != nil { a.logger.Error("error create part file sending to amo:", zap.Error(err)) return nil, err } return &models.ValuesFile{ Value: models.ValueFile{ FileUUID: response.UUID, VersionUUID: response.VersionUUID, FileName: response.Name, FileSize: response.Size, }, }, nil } func (a *Amo) createPart(uploadData models.UploadSession, file *os.File) (*models.UploadedFile, error) { defer file.Close() fileInfo, err := file.Stat() if err != nil { return nil, err } fileSize := fileInfo.Size() var uploadedFile models.UploadedFile maxSize := uploadData.MaxPartSize var remainingSize = fileSize var start int64 = 0 for remainingSize > 0 { end := start + maxSize if end > fileSize { end = fileSize } partSize := end - start partFile, err := os.OpenFile(file.Name(), os.O_RDONLY, 0644) if err != nil { return nil, err } defer partFile.Close() _, err = partFile.Seek(start, io.SeekStart) if err != nil { return nil, err } buffer := make([]byte, partSize) _, err = partFile.Read(buffer) if err != nil { return nil, err } agent := a.fiberClient.Post(uploadData.UploadURL).Body(buffer) if err != nil { return nil, err } statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err = range errs { a.logger.Error("error sending request to upload part file to amo", zap.Error(err)) } return nil, fmt.Errorf("request failed: %v", errs[0]) } if statusCode != fiber.StatusOK && statusCode != fiber.StatusAccepted { return nil, fmt.Errorf("failed to upload part file to amo, status: %d, respBody: %s", statusCode, string(resBody)) } start = end remainingSize -= partSize if statusCode == fiber.StatusAccepted { var next struct { NextUrl string `json:"next_url"` SessionID int `json:"session_id"` } if err := json.Unmarshal(resBody, &next); err != nil { return nil, err } uploadData.UploadURL = next.NextUrl continue } if err := json.Unmarshal(resBody, &uploadedFile); err != nil { return nil, err } return &uploadedFile, nil } return nil, nil } func (a *Amo) CreateContact(req []models.CreateContactReq, domain, accessToken string) (*models.ContactResponse, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/contacts", domain) bodyBytes, err := json.Marshal(req) if err != nil { a.logger.Error("error marshal req in Creating Contact:", 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 Contact", 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 Contact: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var resp models.ContactResponse err = json.Unmarshal(resBody, &resp) if err != nil { a.logger.Error("error unmarshal response body in Creating Contact:", zap.Error(err)) return nil, err } return &resp, nil } time.Sleep(a.rateLimiter.Interval) } } func (a *Amo) UpdateContact(req models.CreateContactReq, domain, accessToken string, idContact int32) (*models.ContactResponse, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/contacts/%d", domain, idContact) bodyBytes, err := json.Marshal(req) if err != nil { a.logger.Error("error marshal req in Update Contact:", zap.Error(err)) return nil, err } agent := a.fiberClient.Patch(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 Update Contact", zap.Error(err)) } return nil, fmt.Errorf("request failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from Update Contact: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var resp models.ContactResponse err = json.Unmarshal(resBody, &resp) if err != nil { a.logger.Error("error unmarshal response body in Update Contact:", zap.Error(err)) return nil, err } return &resp, nil } time.Sleep(a.rateLimiter.Interval) } } func (a *Amo) LinkedContactToContact(req []models.LinkedContactReq, domain, accessToken string, id int32) (*models.LinkedContactResponse, error) { for { if a.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/api/v4/contacts/%d/link", domain, id) bodyBytes, err := json.Marshal(req) if err != nil { a.logger.Error("error marshal req in Linked Contact To Contact:", 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 Linked Contact To Contact", zap.Error(err)) } return nil, fmt.Errorf("request failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from Linked Contact To Contact: %s", string(resBody)) a.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var resp models.LinkedContactResponse err = json.Unmarshal(resBody, &resp) if err != nil { a.logger.Error("error unmarshal response body in Linked Contact To Contact:", zap.Error(err)) return nil, err } return &resp, nil } time.Sleep(a.rateLimiter.Interval) } }