package bitrixClient import ( "encoding/json" "fmt" "github.com/gofiber/fiber/v2" "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/backend/quiz/bitrix/internal/models" "penahub.gitlab.yandexcloud.net/backend/quiz/bitrix/internal/workers/limiter" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "sync" "time" ) type Bitrix struct { fiberClient *fiber.Client logger *zap.Logger redirectionURL string integrationID string integrationSecret string rateLimiter *limiter.RateLimiter fileMutex sync.Mutex } type BitrixDeps struct { FiberClient *fiber.Client Logger *zap.Logger RedirectionURL string IntegrationID string IntegrationSecret string RateLimiter *limiter.RateLimiter } func NewBitrixClient(deps BitrixDeps) *Bitrix { if deps.FiberClient == nil { deps.FiberClient = fiber.AcquireClient() } return &Bitrix{ fiberClient: deps.FiberClient, logger: deps.Logger, redirectionURL: deps.RedirectionURL, integrationSecret: deps.IntegrationSecret, integrationID: deps.IntegrationID, rateLimiter: deps.RateLimiter, } } // https://dev.1c-bitrix.ru/rest_help/users/user_search.php func (b *Bitrix) GetUserList(accessToken string, domain string) (*models.ResponseGetListUsers, error) { for { if b.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/rest/user.search", domain) agent := b.fiberClient.Post(uri) agent.Set("Authorization", "Bearer "+accessToken) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.logger.Error("error sending request in GetUserList", zap.Error(err)) } return nil, fmt.Errorf("request GetUserList failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("error GetUserList statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var userListResponse models.ResponseGetListUsers err := json.Unmarshal(resBody, &userListResponse) if err != nil { b.logger.Error("error unmarshal ResponseGetListUsers:", zap.Error(err)) return nil, err } return &userListResponse, nil } time.Sleep(b.rateLimiter.Interval) } } // https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=99&LESSON_ID=2486 func (b *Bitrix) CreateWebHook(req models.WebHookRequest, domain string) (*models.CreateWebHookResp, error) { for { if b.rateLimiter.Check() { req.SetClientID(b.integrationID) req.SetClientSecret(b.integrationSecret) bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in CreateWebHook:", zap.Error(err)) return nil, err } agent := b.fiberClient.Get(fmt.Sprintf("https://%s/oauth/token/", domain)) agent.Set("Content-Type", "application/json").Body(bodyBytes) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err = range errs { b.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)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var tokens models.CreateWebHookResp err = json.Unmarshal(resBody, &tokens) if err != nil { b.logger.Error("error unmarshal CreateWebHookResp:", zap.Error(err)) return nil, err } return &tokens, nil } time.Sleep(b.rateLimiter.Interval) } } func (b *Bitrix) GetListSteps(accessToken string, domain string) (*models.StepsResponse, error) { for { if b.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/rest/crm.status.list", domain) agent := b.fiberClient.Post(uri) agent.Set("Authorization", "Bearer "+accessToken) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.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("error GetListSteps statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var result models.StepsResponse err := json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal StepsResponse", zap.Error(err)) return nil, err } return &result, nil } time.Sleep(b.rateLimiter.Interval) } } func (b *Bitrix) GetListPipelines(entityTypeID model.IntegerEntityType, accessToken string, domain string) (*models.CategoryResponse, error) { for { if b.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/rest/crm.category.list", domain) agent := b.fiberClient.Post(uri) agent.Set("Authorization", "Bearer "+accessToken) agent.Set("Content-Type", "application/json") requestBody := map[string]interface{}{ "entityTypeId": entityTypeID, } agent.JSON(requestBody) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.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("error GetListPipelines statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var result models.CategoryResponse err := json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal CategoryResponse", zap.Error(err)) return nil, err } return &result, nil } time.Sleep(b.rateLimiter.Interval) } } func (b *Bitrix) GetListFields(fieldType model.FieldsType, accessToken string, domain string) (*models.FieldsResponse, error) { for { if b.rateLimiter.Check() { var listFields models.FieldsResponse switch fieldType { case model.FieldTypeCompany: fullURL := fmt.Sprintf("https://%s/rest/crm.company.userfield.list", domain) agent := b.fiberClient.Post(fullURL) agent.Set("Authorization", "Bearer "+accessToken) requestBody := map[string]interface{}{ "order": map[string]string{"SORT": "ASC"}, "filter": map[string]string{"LANG": "ru"}, } agent.JSON(requestBody) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.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)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } err := json.Unmarshal(resBody, &listFields) if err != nil { b.logger.Error("error unmarshal models.Company:", zap.Error(err)) return nil, err } return &listFields, nil case model.FieldTypeLead: fullURL := fmt.Sprintf("https://%s/rest/crm.lead.userfield.list", domain) agent := b.fiberClient.Post(fullURL) agent.Set("Authorization", "Bearer "+accessToken) requestBody := map[string]interface{}{ "order": map[string]string{"SORT": "ASC"}, "filter": map[string]string{"LANG": "ru"}, } agent.JSON(requestBody) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.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)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } err := json.Unmarshal(resBody, &listFields) if err != nil { b.logger.Error("error unmarshal models.Lead:", zap.Error(err)) return nil, err } return &listFields, nil case model.FieldTypeContact: fullURL := fmt.Sprintf("https://%s/rest/crm.contact.userfield.list", domain) agent := b.fiberClient.Post(fullURL) agent.Set("Authorization", "Bearer "+accessToken) requestBody := map[string]interface{}{ "order": map[string]string{"SORT": "ASC"}, "filter": map[string]string{"LANG": "ru"}, } agent.JSON(requestBody) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.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)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } err := json.Unmarshal(resBody, &listFields) if err != nil { b.logger.Error("error unmarshal models.Contact:", zap.Error(err)) return nil, err } return &listFields, nil case model.FieldTypeDeal: fullURL := fmt.Sprintf("https://%s/rest/crm.deal.userfield.list", domain) agent := b.fiberClient.Post(fullURL) agent.Set("Authorization", "Bearer "+accessToken) requestBody := map[string]interface{}{ "order": map[string]string{"SORT": "ASC"}, "filter": map[string]string{"LANG": "ru"}, } agent.JSON(requestBody) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.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)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } err := json.Unmarshal(resBody, &listFields) if err != nil { b.logger.Error("error unmarshal models.Company:", zap.Error(err)) return nil, err } return &listFields, nil } } time.Sleep(b.rateLimiter.Interval) } } func (b *Bitrix) GetListTags() { } func (b *Bitrix) GetCurrentUser(accessToken string, domain string) (*models.ResponseGetCurrentUser, error) { for { if b.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/rest/user.current", domain) agent := b.fiberClient.Post(uri) agent.Set("Authorization", "Bearer "+accessToken) statusCode, resBody, errs := agent.Bytes() if len(errs) > 0 { for _, err := range errs { b.logger.Error("error sending request in GetCurrentUser", zap.Error(err)) } return nil, fmt.Errorf("request GetCurrentUser failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("error GetCurrentUser statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return nil, fmt.Errorf(errorMessage) } var result models.ResponseGetCurrentUser err := json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal CurrentUser", zap.Error(err)) return nil, err } return &result, nil } time.Sleep(b.rateLimiter.Interval) } } // before neeed call req.GenFieldName() func (b *Bitrix) AddFields(req models.AddFields, entity model.FieldsType, accessToken string, domain string) (int32, error) { for { if b.rateLimiter.Check() { var result models.MultiResp switch entity { case model.FieldTypeContact: bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in Add Fields:", zap.Error(err)) return 0, err } uri := fmt.Sprintf("https://%s/rest/crm.contact.userfield.add", domain) agent := b.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 { b.logger.Error("error sending request in AddFields", zap.Error(err)) } return 0, fmt.Errorf("request AddFields failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("error AddFields contact statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return 0, fmt.Errorf(errorMessage) } err = json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal AddFields", zap.Error(err)) return 0, err } return result.ID, nil case model.FieldTypeCompany: bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in Add Fields:", zap.Error(err)) return 0, err } uri := fmt.Sprintf("https://%s/rest/crm.company.userfield.add", domain) agent := b.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 { b.logger.Error("error sending request in AddFields", zap.Error(err)) } return 0, fmt.Errorf("request AddFields failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("error AddFields company statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return 0, fmt.Errorf(errorMessage) } err = json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal AddFields", zap.Error(err)) return 0, err } return result.ID, nil case model.FieldTypeDeal: bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in Add Fields:", zap.Error(err)) return 0, err } uri := fmt.Sprintf("https://%s/rest/crm.deal.userfield.add", domain) agent := b.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 { b.logger.Error("error sending request in AddFields", zap.Error(err)) } return 0, fmt.Errorf("request AddFields failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("error AddFields deal statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return 0, fmt.Errorf(errorMessage) } err = json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal AddFields", zap.Error(err)) return 0, err } return result.ID, nil case model.FieldTypeLead: bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in Add Fields:", zap.Error(err)) return 0, err } uri := fmt.Sprintf("https://%s/rest/crm.lead.userfield.add", domain) agent := b.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 { b.logger.Error("error sending request in AddFields", zap.Error(err)) } return 0, fmt.Errorf("request AddFields failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("error AddFields lead statusCode - %d, respBody - %s", statusCode, string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return 0, fmt.Errorf(errorMessage) } err = json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal AddFields", zap.Error(err)) return 0, err } return result.ID, nil } } time.Sleep(b.rateLimiter.Interval) } } func (b *Bitrix) CreatingDeal(req map[string]map[string]interface{}, accessToken string, domain string) (int32, error) { for { if b.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/rest/crm.deal.add", domain) bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in Creating Deal:", zap.Error(err)) return 0, err } agent := b.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 { b.logger.Error("error sending request in Creating Deal", zap.Error(err)) } return 0, fmt.Errorf("request failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from Creating Deal: %s", string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return 0, fmt.Errorf(errorMessage) } var resp models.MultiResp err = json.Unmarshal(resBody, &resp) if err != nil { b.logger.Error("error unmarshal response body in Creating Deal:", zap.Error(err)) return 0, err } return resp.ID, nil } time.Sleep(b.rateLimiter.Interval) } } func (b *Bitrix) CreateCompany(req map[string]map[string]interface{}, accessToken string, domain string) (int32, error) { for { if b.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/rest/crm.company.add", domain) bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in Creating Company:", zap.Error(err)) return 0, err } agent := b.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 { b.logger.Error("error sending request in Creating Company", zap.Error(err)) } return 0, fmt.Errorf("request failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from Creating Company: %s", string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return 0, fmt.Errorf(errorMessage) } var result models.MultiResp err = json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal response body in Creating Company:", zap.Error(err)) return 0, err } return result.ID, nil } time.Sleep(b.rateLimiter.Interval) } } // todo как в амо предусмотреть не дублирование контактов func (b *Bitrix) CreateContact(req map[string]map[string]interface{}, accessToken string, domain string) (int32, error) { for { if b.rateLimiter.Check() { uri := fmt.Sprintf("https://%s/rest/crm.contact.add", domain) bodyBytes, err := json.Marshal(req) if err != nil { b.logger.Error("error marshal req in Creating Contact:", zap.Error(err)) return 0, err } agent := b.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 { b.logger.Error("error sending request in Creating Contact", zap.Error(err)) } return 0, fmt.Errorf("request failed: %v", errs[0]) } if statusCode != fiber.StatusOK { errorMessage := fmt.Sprintf("received an incorrect response from Creating Contact: %s", string(resBody)) b.logger.Error(errorMessage, zap.Int("status", statusCode)) return 0, fmt.Errorf(errorMessage) } var result models.MultiResp err = json.Unmarshal(resBody, &result) if err != nil { b.logger.Error("error unmarshal response body in Creating Contact:", zap.Error(err)) return 0, err } return result.ID, nil } time.Sleep(b.rateLimiter.Interval) } }