n8n-nodes-pragma-bitrix24 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +51 -0
  2. package/README.md +340 -0
  3. package/dist/credentials/Bitrix24Api.credentials.js +55 -0
  4. package/dist/nodes/Bitrix24/Bitrix24.node.js +1410 -0
  5. package/dist/nodes/Bitrix24/Company.js +17 -0
  6. package/dist/nodes/Bitrix24/Contact.js +31 -0
  7. package/dist/nodes/Bitrix24/Deal.js +17 -0
  8. package/dist/nodes/Bitrix24/Helpers.js +300 -0
  9. package/dist/nodes/Bitrix24/Lead.js +17 -0
  10. package/dist/nodes/Bitrix24/SmartProcess.js +1 -0
  11. package/dist/nodes/Bitrix24/bitrix24.svg +7 -0
  12. package/dist/nodes/Bitrix24/types.js +58 -0
  13. package/dist/nodes/Bitrix24AI/Bitrix24AI.node.js +205 -0
  14. package/dist/nodes/Bitrix24App/Bitrix24App.node.js +179 -0
  15. package/dist/nodes/Bitrix24Auxiliary/Bitrix24Auxiliary.node.js +566 -0
  16. package/dist/nodes/Bitrix24Booking/Bitrix24Booking.node.js +871 -0
  17. package/dist/nodes/Bitrix24Calendar/Bitrix24Calendar.node.js +471 -0
  18. package/dist/nodes/Bitrix24ChatBot/Bitrix24ChatBot.node.js +522 -0
  19. package/dist/nodes/Bitrix24Commerce/Bitrix24Commerce.node.js +431 -0
  20. package/dist/nodes/Bitrix24Department/Bitrix24Department.node.js +317 -0
  21. package/dist/nodes/Bitrix24Disk/Bitrix24Disk.node.js +334 -0
  22. package/dist/nodes/Bitrix24Document/Bitrix24Document.node.js +280 -0
  23. package/dist/nodes/Bitrix24Entity/Bitrix24Entity.node.js +263 -0
  24. package/dist/nodes/Bitrix24Group/Bitrix24Group.node.js +327 -0
  25. package/dist/nodes/Bitrix24Lists/Bitrix24Lists.node.js +406 -0
  26. package/dist/nodes/Bitrix24Log/Bitrix24Log.node.js +309 -0
  27. package/dist/nodes/Bitrix24Mail/Bitrix24Mail.node.js +109 -0
  28. package/dist/nodes/Bitrix24MessageService/Bitrix24MessageService.node.js +218 -0
  29. package/dist/nodes/Bitrix24OpenChannels/Bitrix24OpenChannels.node.js +379 -0
  30. package/dist/nodes/Bitrix24PaySystem/Bitrix24PaySystem.node.js +241 -0
  31. package/dist/nodes/Bitrix24Pipeline/Bitrix24Pipeline.node.js +553 -0
  32. package/dist/nodes/Bitrix24Pipeline/bitrix24.svg +7 -0
  33. package/dist/nodes/Bitrix24Sale/Bitrix24Sale.node.js +391 -0
  34. package/dist/nodes/Bitrix24Scrum/Bitrix24Scrum.node.js +555 -0
  35. package/dist/nodes/Bitrix24Scrum/bitrix24.svg +7 -0
  36. package/dist/nodes/Bitrix24Sign/Bitrix24Sign.node.js +132 -0
  37. package/dist/nodes/Bitrix24Social/Bitrix24Social.node.js +224 -0
  38. package/dist/nodes/Bitrix24Task/Bitrix24Task.node.js +444 -0
  39. package/dist/nodes/Bitrix24Telephony/Bitrix24Telephony.node.js +511 -0
  40. package/dist/nodes/Bitrix24Timeman/Bitrix24Timeman.node.js +196 -0
  41. package/dist/nodes/Bitrix24Tool/Bitrix24Tool.node.js +1035 -0
  42. package/dist/nodes/Bitrix24Tool/bitrix24.svg +7 -0
  43. package/dist/nodes/Bitrix24Trigger/Bitrix24Trigger.node.js +184 -0
  44. package/dist/nodes/Bitrix24User/Bitrix24User.node.js +351 -0
  45. package/dist/nodes/Bitrix24UserField/Bitrix24UserField.node.js +386 -0
  46. package/dist/nodes/Bitrix24UserField/bitrix24.svg +7 -0
  47. package/dist/nodes/shared/bitrix24.svg +7 -0
  48. package/dist/nodes/shared/localization.js +189 -0
  49. package/dist/nodes/shared/types.js +22 -0
  50. package/index.js +10 -0
  51. package/package.json +108 -0
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Company = void 0;
4
+ class Company {
5
+ static getDescription() {
6
+ return {
7
+ name: 'Company',
8
+ value: 'company',
9
+ };
10
+ }
11
+ // Методы специфичные для Company
12
+ static getDefaultFields() {
13
+ return ['TITLE', 'COMPANY_TYPE', 'INDUSTRY', 'PHONE', 'EMAIL', 'WEB', 'OPENED', 'ASSIGNED_BY_ID'];
14
+ }
15
+ }
16
+ exports.Company = Company;
17
+ Company.resource = 'company';
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Contact = void 0;
4
+ const types_1 = require("./types");
5
+ class Contact {
6
+ static getDescription() {
7
+ return {
8
+ name: 'Contact',
9
+ value: 'contact',
10
+ };
11
+ }
12
+ // Методы специфичные для Contact
13
+ static getDefaultFields() {
14
+ return ['NAME', 'SECOND_NAME', 'LAST_NAME', 'PHONE', 'EMAIL', 'COMPANY_ID', 'OPENED', 'ASSIGNED_BY_ID', 'TYPE_ID'];
15
+ }
16
+ }
17
+ exports.Contact = Contact;
18
+ Contact.resource = 'contact';
19
+ // Типы значений для полей контакта
20
+ Contact.communicationTypes = [
21
+ { name: 'Work', value: types_1.CommunicationType.WORK },
22
+ { name: 'Mobile', value: types_1.CommunicationType.MOBILE },
23
+ { name: 'Home', value: types_1.CommunicationType.HOME },
24
+ { name: 'Fax', value: types_1.CommunicationType.FAX },
25
+ { name: 'Telegram', value: types_1.CommunicationType.TELEGRAM },
26
+ { name: 'WhatsApp', value: types_1.CommunicationType.WHATSAPP },
27
+ { name: 'Viber', value: types_1.CommunicationType.VIBER },
28
+ { name: 'Facebook', value: types_1.CommunicationType.FACEBOOK },
29
+ { name: 'Skype', value: types_1.CommunicationType.SKYPE },
30
+ { name: 'Other', value: types_1.CommunicationType.OTHER },
31
+ ];
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Deal = void 0;
4
+ class Deal {
5
+ static getDescription() {
6
+ return {
7
+ name: 'Deal',
8
+ value: 'deal',
9
+ };
10
+ }
11
+ // Методы специфичные для Deal
12
+ static getDefaultFields() {
13
+ return ['TITLE', 'TYPE_ID', 'STAGE_ID', 'OPPORTUNITY', 'CURRENCY_ID', 'COMPANY_ID', 'CONTACT_ID', 'OPENED', 'ASSIGNED_BY_ID'];
14
+ }
15
+ }
16
+ exports.Deal = Deal;
17
+ Deal.resource = 'deal';
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BitrixSuccessSchema = exports.BitrixErrorSchema = void 0;
4
+ exports.sleep = sleep;
5
+ exports.safeJsonParse = safeJsonParse;
6
+ exports.bitrixRequestWithRetry = bitrixRequestWithRetry;
7
+ exports.bitrixBatchRequest = bitrixBatchRequest;
8
+ exports.prepareBatchCommands = prepareBatchCommands;
9
+ exports.bitrixFetchAll = bitrixFetchAll;
10
+ const n8n_workflow_1 = require("n8n-workflow");
11
+ const zod_1 = require("zod");
12
+ const DEFAULT_CONFIG = {
13
+ minDelayMs: 100, // 10 req/sec for paid plans (use 500 for free)
14
+ maxDelayMs: 10000, // 10 seconds max
15
+ maxRetries: 5,
16
+ circuitBreakerThreshold: 5,
17
+ circuitBreakerTimeoutMs: 30000,
18
+ };
19
+ // ============================================
20
+ // Zod Schemas for Runtime Validation
21
+ // ============================================
22
+ exports.BitrixErrorSchema = zod_1.z.object({
23
+ error: zod_1.z.string().optional(),
24
+ error_description: zod_1.z.string().optional(),
25
+ result: zod_1.z.unknown().optional(),
26
+ });
27
+ exports.BitrixSuccessSchema = zod_1.z.object({
28
+ result: zod_1.z.unknown(),
29
+ time: zod_1.z.record(zod_1.z.unknown()).optional(),
30
+ total: zod_1.z.number().optional(),
31
+ next: zod_1.z.number().optional(),
32
+ });
33
+ // ============================================
34
+ // Utility Functions
35
+ // ============================================
36
+ /**
37
+ * Sleep helper for delays
38
+ */
39
+ async function sleep(ms) {
40
+ return new Promise((resolve) => setTimeout(resolve, ms));
41
+ }
42
+ /**
43
+ * Safely parse JSON with error handling
44
+ * @param jsonString - Raw JSON string
45
+ * @param fieldName - Field name for error messages
46
+ */
47
+ function safeJsonParse(jsonString, fieldName = 'JSON') {
48
+ try {
49
+ return JSON.parse(jsonString);
50
+ }
51
+ catch (error) {
52
+ throw new Error(`Invalid ${fieldName}: ${error.message}. Please check the JSON syntax.`);
53
+ }
54
+ }
55
+ const circuitBreakerMap = new Map();
56
+ function getCircuitBreaker(workflowId) {
57
+ if (!circuitBreakerMap.has(workflowId)) {
58
+ circuitBreakerMap.set(workflowId, { failures: 0, lastFailure: 0, isOpen: false });
59
+ }
60
+ return circuitBreakerMap.get(workflowId);
61
+ }
62
+ function resetCircuitBreaker(workflowId) {
63
+ const breaker = getCircuitBreaker(workflowId);
64
+ breaker.failures = 0;
65
+ breaker.isOpen = false;
66
+ }
67
+ function recordFailure(workflowId, config) {
68
+ const breaker = getCircuitBreaker(workflowId);
69
+ breaker.failures++;
70
+ breaker.lastFailure = Date.now();
71
+ if (breaker.failures >= config.circuitBreakerThreshold) {
72
+ breaker.isOpen = true;
73
+ }
74
+ }
75
+ const rateLimiterMap = new Map();
76
+ function getRateLimiter(domain, config) {
77
+ if (!rateLimiterMap.has(domain)) {
78
+ rateLimiterMap.set(domain, { lastRequestTime: 0, currentDelay: config.minDelayMs });
79
+ }
80
+ return rateLimiterMap.get(domain);
81
+ }
82
+ async function waitForRateLimit(domain, config) {
83
+ const limiter = getRateLimiter(domain, config);
84
+ const now = Date.now();
85
+ const timeSinceLastRequest = now - limiter.lastRequestTime;
86
+ if (timeSinceLastRequest < limiter.currentDelay) {
87
+ await sleep(limiter.currentDelay - timeSinceLastRequest);
88
+ }
89
+ limiter.lastRequestTime = Date.now();
90
+ }
91
+ function updateRateLimitFromHeader(domain, retryAfter, config) {
92
+ const limiter = getRateLimiter(domain, config);
93
+ if (retryAfter) {
94
+ // Server told us to wait, increase delay
95
+ limiter.currentDelay = Math.min(retryAfter * 1000, config.maxDelayMs);
96
+ }
97
+ else {
98
+ // Success, gradually reduce delay
99
+ limiter.currentDelay = Math.max(limiter.currentDelay * 0.9, config.minDelayMs);
100
+ }
101
+ }
102
+ // ============================================
103
+ // Main Request Function
104
+ // ============================================
105
+ /**
106
+ * Enhanced Bitrix24 API request with:
107
+ * - Adaptive rate limiting
108
+ * - Per-workflow circuit breaker
109
+ * - Exponential backoff
110
+ * - Rich error messages
111
+ */
112
+ async function bitrixRequestWithRetry(method, endpoint, body = {}, qs = {}, overrideAuth, config = {}) {
113
+ const finalConfig = { ...DEFAULT_CONFIG, ...config };
114
+ const workflowId = this.getWorkflow().id?.toString() || 'default';
115
+ // Get domain for rate limiting
116
+ let domain = '';
117
+ let url = '';
118
+ if (overrideAuth) {
119
+ domain = overrideAuth.domain;
120
+ url = `https://${overrideAuth.domain}/rest/${endpoint}`;
121
+ qs.auth = overrideAuth.accessToken;
122
+ }
123
+ else {
124
+ const credentials = await this.getCredentials('bitrix24Api');
125
+ if (!credentials) {
126
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No credentials returned!');
127
+ }
128
+ const webhookUrl = credentials.webhookUrl;
129
+ // Extract domain from webhook URL
130
+ const domainMatch = webhookUrl.match(/https?:\/\/([^/]+)/);
131
+ domain = domainMatch ? domainMatch[1] : 'unknown';
132
+ url = `${webhookUrl}${endpoint}`;
133
+ }
134
+ // Circuit Breaker Check
135
+ const breaker = getCircuitBreaker(workflowId);
136
+ if (breaker.isOpen) {
137
+ const timeSinceLastFailure = Date.now() - breaker.lastFailure;
138
+ if (timeSinceLastFailure < finalConfig.circuitBreakerTimeoutMs) {
139
+ const waitTime = Math.ceil((finalConfig.circuitBreakerTimeoutMs - timeSinceLastFailure) / 1000);
140
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Circuit Breaker Active: Bitrix24 API has too many failures. Wait ${waitTime}s or check API status.`, { description: 'The circuit breaker opens after 5 consecutive failures to prevent cascading errors.' });
141
+ }
142
+ // Reset if timeout passed
143
+ resetCircuitBreaker(workflowId);
144
+ }
145
+ let attempt = 0;
146
+ let delay = finalConfig.minDelayMs;
147
+ while (attempt < finalConfig.maxRetries) {
148
+ try {
149
+ // Wait for rate limit
150
+ await waitForRateLimit(domain, finalConfig);
151
+ const options = {
152
+ method,
153
+ url,
154
+ body,
155
+ qs,
156
+ json: true,
157
+ returnFullResponse: true, // Get headers for Retry-After
158
+ };
159
+ const response = await this.helpers.httpRequest(options);
160
+ const responseBody = response.body;
161
+ const headers = response.headers;
162
+ // Check for Retry-After header
163
+ const retryAfter = headers['retry-after'] ? parseInt(headers['retry-after'], 10) : undefined;
164
+ updateRateLimitFromHeader(domain, retryAfter, finalConfig);
165
+ // Runtime Validation
166
+ const parsed = exports.BitrixSuccessSchema.safeParse(responseBody);
167
+ if (!parsed.success) {
168
+ const errorParsed = exports.BitrixErrorSchema.safeParse(responseBody);
169
+ if (errorParsed.success && errorParsed.data.error) {
170
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Bitrix24 API Error: ${errorParsed.data.error_description || errorParsed.data.error}`, { description: getErrorSuggestion(errorParsed.data.error_description || errorParsed.data.error) });
171
+ }
172
+ }
173
+ // Success! Reset breaker
174
+ resetCircuitBreaker(workflowId);
175
+ return responseBody;
176
+ }
177
+ catch (error) {
178
+ const err = error;
179
+ // Handle Rate Limiting (429) and Server Errors (5xx)
180
+ const status = err.response?.status;
181
+ if (status === 429 || (status && status >= 500 && status < 600)) {
182
+ attempt++;
183
+ // Check for Retry-After header on 429
184
+ const retryAfter = err.response?.headers?.['retry-after'];
185
+ if (retryAfter) {
186
+ delay = parseInt(retryAfter, 10) * 1000;
187
+ updateRateLimitFromHeader(domain, parseInt(retryAfter, 10), finalConfig);
188
+ }
189
+ if (attempt >= finalConfig.maxRetries) {
190
+ recordFailure(workflowId, finalConfig);
191
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Bitrix24 API Request Failed after ${finalConfig.maxRetries} attempts: ${err.message}`, { description: 'Consider reducing request frequency or upgrading your Bitrix24 plan.' });
192
+ }
193
+ await sleep(delay);
194
+ delay = Math.min(delay * 2, finalConfig.maxDelayMs); // Exponential backoff
195
+ continue;
196
+ }
197
+ // For other errors, throw immediately with helpful message
198
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), formatErrorMessage(err), { description: getErrorSuggestion(err.response?.data?.error_description || err.message) });
199
+ }
200
+ }
201
+ // Should never reach here, but TypeScript needs it
202
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Unexpected error in Bitrix24 request handler');
203
+ }
204
+ // ============================================
205
+ // Batch API Support
206
+ // ============================================
207
+ /**
208
+ * Execute multiple API calls in a single batch request
209
+ * More efficient than multiple individual requests
210
+ */
211
+ async function bitrixBatchRequest(commands, haltOnError = false) {
212
+ return bitrixRequestWithRetry.call(this, 'POST', 'batch.json', {
213
+ halt: haltOnError ? 1 : 0,
214
+ cmd: commands,
215
+ });
216
+ }
217
+ /**
218
+ * Prepare batch commands for CRM operations
219
+ */
220
+ function prepareBatchCommands(resource, method, items, prefix = 'cmd') {
221
+ const commands = {};
222
+ items.forEach((item, index) => {
223
+ const params = new URLSearchParams();
224
+ Object.entries(item).forEach(([key, value]) => {
225
+ if (typeof value === 'object') {
226
+ params.append(key, JSON.stringify(value));
227
+ }
228
+ else {
229
+ params.append(key, String(value));
230
+ }
231
+ });
232
+ commands[`${prefix}_${index}`] = `${resource}.${method}?${params.toString()}`;
233
+ });
234
+ return commands;
235
+ }
236
+ // ============================================
237
+ // Error Handling Helpers
238
+ // ============================================
239
+ function formatErrorMessage(err) {
240
+ const apiError = err.response?.data;
241
+ if (apiError?.error_description) {
242
+ return `Bitrix24 Error: ${apiError.error_description}`;
243
+ }
244
+ if (apiError?.error) {
245
+ return `Bitrix24 Error: ${apiError.error}`;
246
+ }
247
+ return err.message;
248
+ }
249
+ function getErrorSuggestion(errorText) {
250
+ const suggestions = {
251
+ 'ACCESS_DENIED': 'Your webhook or user lacks permissions for this action. Check scope settings in Bitrix24.',
252
+ 'limit': 'You hit the API rate limit. Consider upgrading your Bitrix24 plan or reducing request frequency.',
253
+ 'NOT_FOUND': 'The requested entity does not exist. Verify the ID is correct.',
254
+ 'INVALID_CREDENTIALS': 'Your webhook URL is invalid or expired. Generate a new one in Bitrix24.',
255
+ 'INVALID_REQUEST': 'The request parameters are incorrect. Check required fields.',
256
+ 'METHOD_NOT_FOUND': 'This API method does not exist or is not available for your plan.',
257
+ 'NO_AUTH_FOUND': 'Authentication failed. Verify your webhook URL includes a valid token.',
258
+ 'QUERY_LIMIT_EXCEEDED': 'Too many items in query. Use pagination or reduce filter scope.',
259
+ 'EXPIRED_TOKEN': 'OAuth token has expired. Reconnect your Bitrix24 integration.',
260
+ };
261
+ for (const [key, suggestion] of Object.entries(suggestions)) {
262
+ if (errorText.toLowerCase().includes(key.toLowerCase())) {
263
+ return suggestion;
264
+ }
265
+ }
266
+ return 'Check Bitrix24 API documentation for more details on this error.';
267
+ }
268
+ // ============================================
269
+ // Pagination Helpers
270
+ // ============================================
271
+ /**
272
+ * Fetch all pages of a paginated response
273
+ * Includes safety limit to prevent memory issues
274
+ */
275
+ async function bitrixFetchAll(method, endpoint, params = {}, maxPages = 100) {
276
+ const allResults = [];
277
+ let start = 0;
278
+ let page = 0;
279
+ while (page < maxPages) {
280
+ const response = await bitrixRequestWithRetry.call(this, method, endpoint, {
281
+ ...params,
282
+ start,
283
+ });
284
+ const results = response.result;
285
+ if (results && Array.isArray(results)) {
286
+ allResults.push(...results);
287
+ }
288
+ // Check if there are more pages
289
+ if (response.next === undefined || response.next === null) {
290
+ break;
291
+ }
292
+ start = response.next;
293
+ page++;
294
+ }
295
+ if (page >= maxPages) {
296
+ // Warn about pagination limit
297
+ this.logger?.warn?.(`Bitrix24: Stopped after ${maxPages} pages to prevent memory issues. Total fetched: ${allResults.length}`);
298
+ }
299
+ return allResults;
300
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Lead = void 0;
4
+ class Lead {
5
+ static getDescription() {
6
+ return {
7
+ name: 'Lead',
8
+ value: 'lead',
9
+ };
10
+ }
11
+ // Методы специфичные для Lead
12
+ static getDefaultFields() {
13
+ return ['TITLE', 'NAME', 'SECOND_NAME', 'LAST_NAME', 'STATUS_ID', 'OPENED', 'ASSIGNED_BY_ID', 'PHONE', 'EMAIL'];
14
+ }
15
+ }
16
+ exports.Lead = Lead;
17
+ Lead.resource = 'lead';
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="32px" height="32px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
3
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4
+ <path d="M16,0 C24.8365,0 32,7.1635 32,16 C32,24.8365 24.8365,32 16,32 C7.1635,32 0,24.8365 0,16 C0,7.1635 7.1635,0 16,0" fill="#2FC7F7"/>
5
+ <path d="M13.3333,8 L18.6667,8 C21.6122,8 24,10.3878 24,13.3333 L24,18.6667 C24,21.6122 21.6122,24 18.6667,24 L13.3333,24 C10.3878,24 8,21.6122 8,18.6667 L8,13.3333 C8,10.3878 10.3878,8 13.3333,8" fill="#FFFFFF"/>
6
+ </g>
7
+ </svg>
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sleep = exports.processFormFields = exports.formatCommunicationField = exports.formatContactCommunicationField = exports.CommunicationType = void 0;
4
+ var CommunicationType;
5
+ (function (CommunicationType) {
6
+ CommunicationType["WORK"] = "WORK";
7
+ CommunicationType["MOBILE"] = "MOBILE";
8
+ CommunicationType["HOME"] = "HOME";
9
+ CommunicationType["FAX"] = "FAX";
10
+ CommunicationType["OTHER"] = "OTHER";
11
+ CommunicationType["TELEGRAM"] = "TELEGRAM";
12
+ CommunicationType["WHATSAPP"] = "WHATSAPP";
13
+ CommunicationType["VIBER"] = "VIBER";
14
+ CommunicationType["FACEBOOK"] = "FACEBOOK";
15
+ CommunicationType["SKYPE"] = "SKYPE";
16
+ })(CommunicationType || (exports.CommunicationType = CommunicationType = {}));
17
+ // Формат для коммуникационных полей в сущности Contact
18
+ const formatContactCommunicationField = (value, type = CommunicationType.WORK) => {
19
+ return [{ VALUE: value, VALUE_TYPE: type }];
20
+ };
21
+ exports.formatContactCommunicationField = formatContactCommunicationField;
22
+ // Формат для коммуникационных полей для всех других сущностей
23
+ const formatCommunicationField = (value, type = CommunicationType.WORK) => {
24
+ return [{ VALUE: value, TYPE: type }];
25
+ };
26
+ exports.formatCommunicationField = formatCommunicationField;
27
+ const processFormFields = (fieldsCollection) => {
28
+ const fields = {};
29
+ for (const field of fieldsCollection) {
30
+ // Проверяем, является ли это полем связи (PHONE, EMAIL, IM, WEB)
31
+ if (['PHONE', 'EMAIL', 'IM', 'WEB'].some(type => field.fieldName.includes(type))) {
32
+ const valueType = field.fieldValueType || CommunicationType.WORK;
33
+ // Для полей контакта нужна специальная обработка
34
+ if (field.resource === 'contact') {
35
+ // Используем специальный формат для контактов
36
+ fields[field.fieldName] = (0, exports.formatContactCommunicationField)(field.fieldValue, valueType);
37
+ }
38
+ else {
39
+ // Для других сущностей используем стандартный формат
40
+ fields[field.fieldName] = (0, exports.formatCommunicationField)(field.fieldValue, valueType);
41
+ }
42
+ }
43
+ else if (field.field?.type === 'enumeration' && field.field.items && Array.isArray(field.field.items)) {
44
+ const selectedOption = field.field.items.find(item => item.VALUE === field.fieldValue);
45
+ if (selectedOption) {
46
+ fields[field.fieldName] = selectedOption.ID;
47
+ }
48
+ }
49
+ else {
50
+ fields[field.fieldName] = field.fieldValue;
51
+ }
52
+ }
53
+ return fields;
54
+ };
55
+ exports.processFormFields = processFormFields;
56
+ // Helper function for rate limiting
57
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
58
+ exports.sleep = sleep;
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ /**
3
+ * Bitrix24 AI — Нода управления AI-движками
4
+ *
5
+ * Bitrix24 PRAGMA.by nodes for n8n
6
+ * Профессиональная интеграция с Битрикс24.
7
+ *
8
+ * @author PRAGMA & Азбука Решений
9
+ * @copyright 2026 PRAGMA (https://pragma.by/) & Азбука Решений (https://abc-solution.ru/)
10
+ *
11
+ * Контакты:
12
+ * 🇧🇾 PRAGMA: +375 (44) 702-70-90 | https://pragma.by/
13
+ * 🇷🇺 Азбука Решений: +7 (939) 555-19-60 | https://abc-solution.ru/
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.Bitrix24AI = void 0;
17
+ const Helpers_1 = require("../Bitrix24/Helpers");
18
+ class Bitrix24AI {
19
+ constructor() {
20
+ this.description = {
21
+ displayName: 'Bitrix24 AI Копилот',
22
+ name: 'bitrix24Ai',
23
+ icon: 'file:bitrix24.svg',
24
+ group: ['transform'],
25
+ version: 1,
26
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
27
+ description: 'Управление AI-движками и промптами в Битрикс24 CoPilot',
28
+ defaults: {
29
+ name: 'Bitrix24 AI Копилот',
30
+ },
31
+ inputs: ['main'],
32
+ outputs: ['main'],
33
+ credentials: [
34
+ {
35
+ name: 'bitrix24Api',
36
+ required: true,
37
+ },
38
+ ],
39
+ properties: [
40
+ {
41
+ displayName: 'Resource',
42
+ name: 'resource',
43
+ type: 'options',
44
+ noDataExpression: true,
45
+ options: [
46
+ {
47
+ name: 'Engine',
48
+ value: 'engine',
49
+ description: 'Manage AI Engines',
50
+ },
51
+ {
52
+ name: 'Prompt',
53
+ value: 'prompt',
54
+ description: 'Manage AI Prompts',
55
+ },
56
+ ],
57
+ default: 'engine',
58
+ required: true,
59
+ },
60
+ {
61
+ displayName: 'Operation',
62
+ name: 'operation',
63
+ type: 'options',
64
+ noDataExpression: true,
65
+ options: [
66
+ // Engine
67
+ {
68
+ name: 'Register',
69
+ value: 'register',
70
+ action: 'Register new AI Engine',
71
+ displayOptions: { show: { resource: ['engine'] } },
72
+ },
73
+ {
74
+ name: 'Unregister',
75
+ value: 'unregister',
76
+ action: 'Delete AI Engine',
77
+ displayOptions: { show: { resource: ['engine'] } },
78
+ },
79
+ // Prompt
80
+ {
81
+ name: 'Register',
82
+ value: 'register',
83
+ action: 'Register new Prompt',
84
+ displayOptions: { show: { resource: ['prompt'] } },
85
+ },
86
+ {
87
+ name: 'Unregister',
88
+ value: 'unregister',
89
+ action: 'Delete Prompt',
90
+ displayOptions: { show: { resource: ['prompt'] } },
91
+ },
92
+ ],
93
+ default: 'register',
94
+ required: true,
95
+ },
96
+ // Engine Fields
97
+ {
98
+ displayName: 'Code',
99
+ name: 'code',
100
+ type: 'string',
101
+ default: 'n8n_ai',
102
+ displayOptions: { show: { resource: ['engine'], operation: ['register', 'unregister'] } },
103
+ description: 'Unique code for the engine',
104
+ required: true,
105
+ },
106
+ {
107
+ displayName: 'Category',
108
+ name: 'category',
109
+ type: 'options',
110
+ options: [
111
+ { name: 'Text', value: 'text' },
112
+ { name: 'Image', value: 'image' },
113
+ { name: 'Audio', value: 'audio' },
114
+ ],
115
+ default: 'text',
116
+ displayOptions: { show: { resource: ['engine'], operation: ['register'] } },
117
+ required: true,
118
+ },
119
+ {
120
+ displayName: 'Handler URL',
121
+ name: 'handlerUrl',
122
+ type: 'string',
123
+ default: '',
124
+ displayOptions: { show: { resource: ['engine'], operation: ['register'] } },
125
+ description: 'Webhook URL of n8n workflow to handle completions',
126
+ required: true,
127
+ },
128
+ {
129
+ displayName: 'Name',
130
+ name: 'name',
131
+ type: 'string',
132
+ default: 'n8n AI Model',
133
+ displayOptions: { show: { resource: ['engine'], operation: ['register'] } },
134
+ },
135
+ // Prompt Fields
136
+ {
137
+ displayName: 'Prompt Code',
138
+ name: 'promptCode',
139
+ type: 'string',
140
+ default: '',
141
+ displayOptions: { show: { resource: ['prompt'], operation: ['register', 'unregister'] } },
142
+ required: true,
143
+ },
144
+ // Note: Prompt registration is complex, usually requires localizations etc.
145
+ // Simplified for now assuming basic usage.
146
+ ],
147
+ };
148
+ }
149
+ async execute() {
150
+ const items = this.getInputData();
151
+ const returnData = [];
152
+ const resource = this.getNodeParameter('resource', 0);
153
+ const operation = this.getNodeParameter('operation', 0);
154
+ for (let i = 0; i < items.length; i++) {
155
+ try {
156
+ let endpoint = '';
157
+ let body = {};
158
+ if (resource === 'engine') {
159
+ if (operation === 'register') {
160
+ endpoint = 'ai.engine.register';
161
+ body.CODE = this.getNodeParameter('code', i);
162
+ body.CATEGORY = this.getNodeParameter('category', i);
163
+ body.COM_HANDLER = this.getNodeParameter('handlerUrl', i);
164
+ // Name implies Localization settings (more complex)
165
+ // For now we assume user will manually config or simple pass
166
+ // Actually AI engine registration is tricky.
167
+ }
168
+ else if (operation === 'unregister') {
169
+ endpoint = 'ai.engine.unregister';
170
+ body.CODE = this.getNodeParameter('code', i);
171
+ }
172
+ }
173
+ else if (resource === 'prompt') {
174
+ if (operation === 'register') {
175
+ endpoint = 'ai.prompt.register';
176
+ body.CODE = this.getNodeParameter('promptCode', i);
177
+ }
178
+ else if (operation === 'unregister') {
179
+ endpoint = 'ai.prompt.unregister';
180
+ body.CODE = this.getNodeParameter('promptCode', i);
181
+ }
182
+ }
183
+ const response = await Helpers_1.bitrixRequestWithRetry.call(this, 'POST', endpoint, body);
184
+ let result = response.result;
185
+ if (Array.isArray(result)) {
186
+ for (const item of result) {
187
+ returnData.push({ json: item, pairedItem: { item: i } });
188
+ }
189
+ }
190
+ else {
191
+ returnData.push({ json: result, pairedItem: { item: i } });
192
+ }
193
+ }
194
+ catch (error) {
195
+ if (this.continueOnFail()) {
196
+ returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
197
+ continue;
198
+ }
199
+ throw error;
200
+ }
201
+ }
202
+ return [returnData];
203
+ }
204
+ }
205
+ exports.Bitrix24AI = Bitrix24AI;