@alexsab-ru/scripts 0.14.0 → 0.15.1
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.
- package/docs/campaign-guide.md +234 -0
- package/examples/campaign-examples.js +126 -0
- package/examples/nobounce-examples.js +280 -0
- package/index.js +2 -0
- package/lib/campaign/index.js +5 -0
- package/lib/campaign/persist-campaign-data.js +162 -0
- package/lib/campaign/types.d.ts +290 -0
- package/lib/cookie.js +9 -4
- package/lib/form.js +3 -1
- package/lib/nobounce/index.js +5 -0
- package/lib/nobounce/nobounce.js +336 -0
- package/lib/nobounce/types.d.ts +255 -0
- package/package.json +20 -1
- package/lib/calltouch/fixed_calltouch_module.js +0 -312
- package/lib/calltouch/fixed_create_request.js +0 -176
- package/lib/fixed_analytics.js +0 -240
- package/lib/fixed_form.js +0 -443
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Модуль для отправки данных в CallTouch API
|
|
3
|
-
* @module calltouch-integration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Проверяет и очищает значение от потенциально опасного содержимого
|
|
8
|
-
* @param {*} value - Проверяемое значение
|
|
9
|
-
* @return {string} Проверенное и очищенное значение или пустая строка
|
|
10
|
-
*/
|
|
11
|
-
const getValueOrEmpty = (value) => {
|
|
12
|
-
if (value === undefined || value === null || value === 'undefined') {
|
|
13
|
-
return '';
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const stringValue = String(value);
|
|
17
|
-
// Базовая очистка от потенциально опасного содержимого
|
|
18
|
-
return stringValue
|
|
19
|
-
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
|
20
|
-
.replace(/[<>]/g, '')
|
|
21
|
-
.trim();
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Валидация телефонного номера
|
|
26
|
-
* @param {string} phone - Номер телефона для валидации
|
|
27
|
-
* @return {boolean} true если номер валиден
|
|
28
|
-
*/
|
|
29
|
-
const validatePhone = (phone) => {
|
|
30
|
-
if (!phone || typeof phone !== 'string') return false;
|
|
31
|
-
|
|
32
|
-
// Удаляем все символы кроме цифр
|
|
33
|
-
const cleanPhone = phone.replace(/\D/g, '');
|
|
34
|
-
|
|
35
|
-
// Проверяем длину (российские номера)
|
|
36
|
-
return cleanPhone.length >= 10 && cleanPhone.length <= 11;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Валидация email адреса
|
|
41
|
-
* @param {string} email - Email для валидации
|
|
42
|
-
* @return {boolean} true если email валиден
|
|
43
|
-
*/
|
|
44
|
-
const validateEmail = (email) => {
|
|
45
|
-
if (!email || typeof email !== 'string') return false;
|
|
46
|
-
|
|
47
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
48
|
-
return emailRegex.test(email) && email.length <= 254;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Обрабатывает данные события и формирует объект для отправки в CallTouch
|
|
53
|
-
* @param {Object} eventData - Данные о событии
|
|
54
|
-
* @param {string} sessionId - ID сессии
|
|
55
|
-
* @return {Object} Объект с данными и комментарием для отправки
|
|
56
|
-
*/
|
|
57
|
-
function processEventData(eventData, sessionId) {
|
|
58
|
-
if (!eventData || typeof eventData !== 'object') {
|
|
59
|
-
throw new Error('Event data must be an object');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Основные поля для передачи в API
|
|
63
|
-
const basicFields = {
|
|
64
|
-
name: { key: 'fio', label: null },
|
|
65
|
-
phone: { key: 'phoneNumber', label: null },
|
|
66
|
-
email: { key: 'email', label: null },
|
|
67
|
-
form: { key: 'subject', label: null }
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// Поля для включения в комментарий
|
|
71
|
-
const commentFields = {
|
|
72
|
-
dealer: { label: 'Дилер' },
|
|
73
|
-
dealershipName: { label: 'ДЦ' },
|
|
74
|
-
salon: { label: 'Салон' },
|
|
75
|
-
vehicleNameplate: { label: 'Авто' },
|
|
76
|
-
priceDealership: { label: 'Цена' },
|
|
77
|
-
vehicleModel: { label: 'Модель' },
|
|
78
|
-
vehicleBrand: { label: 'Марка' },
|
|
79
|
-
service: { label: 'Услуга' },
|
|
80
|
-
source: { label: 'Источник' },
|
|
81
|
-
medium: { label: 'Канал' },
|
|
82
|
-
campaign: { label: 'Кампания' }
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Собираем основные данные
|
|
86
|
-
const ct_data = {
|
|
87
|
-
requestUrl: location.href,
|
|
88
|
-
sessionId: sessionId || ''
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// Обрабатываем основные поля с валидацией
|
|
92
|
-
Object.entries(basicFields).forEach(([fieldName, config]) => {
|
|
93
|
-
const value = getValueOrEmpty(eventData[fieldName]);
|
|
94
|
-
if (value) {
|
|
95
|
-
// Дополнительная валидация для специфичных полей
|
|
96
|
-
if (fieldName === 'phone' && !validatePhone(value)) {
|
|
97
|
-
console.warn(`Invalid phone number: ${value}`);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (fieldName === 'email' && !validateEmail(value)) {
|
|
101
|
-
console.warn(`Invalid email: ${value}`);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
ct_data[config.key] = value;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Формируем комментарий из всех доступных полей
|
|
109
|
-
const ct_comment = [];
|
|
110
|
-
|
|
111
|
-
Object.entries(commentFields).forEach(([fieldName, config]) => {
|
|
112
|
-
const value = getValueOrEmpty(eventData[fieldName]);
|
|
113
|
-
if (value && value.length <= 100) { // Ограничиваем длину значений
|
|
114
|
-
ct_comment.push(`${config.label}: ${value}`);
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// Добавляем комментарий в объект данных, если он не пустой
|
|
119
|
-
if (ct_comment.length > 0) {
|
|
120
|
-
ct_data.comment = ct_comment.join(', ').substring(0, 500); // Ограничиваем общую длину
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return ct_data;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Создает XMLHttpRequest с таймаутом
|
|
128
|
-
* @param {string} url - URL для запроса
|
|
129
|
-
* @param {string} postData - Данные для отправки
|
|
130
|
-
* @param {number} timeout - Таймаут в миллисекундах
|
|
131
|
-
* @return {Promise} Promise с результатом запроса
|
|
132
|
-
*/
|
|
133
|
-
function createTimeoutRequest(url, postData, timeout = 10000) {
|
|
134
|
-
return new Promise((resolve, reject) => {
|
|
135
|
-
const request = new XMLHttpRequest();
|
|
136
|
-
let timeoutId;
|
|
137
|
-
|
|
138
|
-
// Устанавливаем таймаут
|
|
139
|
-
timeoutId = setTimeout(() => {
|
|
140
|
-
request.abort();
|
|
141
|
-
reject(new Error('Request timeout'));
|
|
142
|
-
}, timeout);
|
|
143
|
-
|
|
144
|
-
request.open("POST", url, true);
|
|
145
|
-
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
146
|
-
|
|
147
|
-
request.onload = function() {
|
|
148
|
-
clearTimeout(timeoutId);
|
|
149
|
-
if (this.status >= 200 && this.status < 400) {
|
|
150
|
-
try {
|
|
151
|
-
const response = JSON.parse(this.responseText);
|
|
152
|
-
resolve({
|
|
153
|
-
status: 'success',
|
|
154
|
-
response: response,
|
|
155
|
-
rawResponse: this.responseText
|
|
156
|
-
});
|
|
157
|
-
} catch (parseError) {
|
|
158
|
-
resolve({
|
|
159
|
-
status: 'success',
|
|
160
|
-
response: this.responseText,
|
|
161
|
-
rawResponse: this.responseText
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
reject(new Error(`API returned status: ${this.status}`));
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
request.onerror = function() {
|
|
170
|
-
clearTimeout(timeoutId);
|
|
171
|
-
reject(new Error('Connection error'));
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
request.onabort = function() {
|
|
175
|
-
clearTimeout(timeoutId);
|
|
176
|
-
reject(new Error('Request aborted'));
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
request.send(postData);
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Отправляет данные лида в CallTouch API
|
|
185
|
-
* @param {Object} options - Параметры для отправки
|
|
186
|
-
* @param {string} options.siteId - ID сайта в CallTouch
|
|
187
|
-
* @param {string} options.eventCategory - Категория события (например, 'Lead')
|
|
188
|
-
* @param {Object} options.eventProperties - Данные о событии
|
|
189
|
-
* @param {string} [options.eventProperties.name] - Имя клиента
|
|
190
|
-
* @param {string} options.eventProperties.phone - Телефон клиента
|
|
191
|
-
* @param {string} [options.eventProperties.email] - Email клиента
|
|
192
|
-
* @param {string} [options.eventProperties.form] - Название формы
|
|
193
|
-
* @param {string} [options.eventProperties.dealershipName] - Название дилерского центра
|
|
194
|
-
* @param {string} [options.eventProperties.salon] - Название салона
|
|
195
|
-
* @param {string} [options.eventProperties.vehicleNameplate] - Название автомобиля
|
|
196
|
-
* @param {string} [options.eventProperties.priceDealership] - Цена автомобиля
|
|
197
|
-
* @param {string} [options.sessionId] - ID сессии (опционально, по умолчанию window.call_value)
|
|
198
|
-
* @param {number} [options.timeout] - Таймаут запроса в миллисекундах (по умолчанию 10000)
|
|
199
|
-
* @return {Promise} Promise с результатом запроса
|
|
200
|
-
*/
|
|
201
|
-
function sendToCallTouch(options) {
|
|
202
|
-
return new Promise((resolve, reject) => {
|
|
203
|
-
try {
|
|
204
|
-
// Валидация входных параметров
|
|
205
|
-
if (!options || typeof options !== 'object') {
|
|
206
|
-
return reject(new Error('Options object is required'));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const {
|
|
210
|
-
siteId,
|
|
211
|
-
eventCategory,
|
|
212
|
-
eventProperties,
|
|
213
|
-
sessionId = window.call_value,
|
|
214
|
-
timeout = 10000
|
|
215
|
-
} = options;
|
|
216
|
-
|
|
217
|
-
// Проверяем, что категория события - Lead
|
|
218
|
-
if (eventCategory !== 'Lead') {
|
|
219
|
-
return resolve({ status: 'skipped', message: 'Not a Lead event' });
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Проверяем обязательные параметры
|
|
223
|
-
if (!siteId || typeof siteId !== 'string') {
|
|
224
|
-
return reject(new Error('Valid siteId is required'));
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (!eventProperties || typeof eventProperties !== 'object') {
|
|
228
|
-
return reject(new Error('Event properties object is required'));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Обрабатываем данные с помощью оптимизированной функции
|
|
232
|
-
let ct_data;
|
|
233
|
-
try {
|
|
234
|
-
ct_data = processEventData(eventProperties, sessionId);
|
|
235
|
-
} catch (processError) {
|
|
236
|
-
return reject(new Error(`Data processing error: ${processError.message}`));
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Проверяем наличие телефона
|
|
240
|
-
if (!ct_data.phoneNumber) {
|
|
241
|
-
return resolve({ status: 'error', message: 'Phone number is required' });
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Формируем строку параметров для POST-запроса
|
|
245
|
-
const post_data = Object.keys(ct_data)
|
|
246
|
-
.filter(key => ct_data[key] && ct_data[key] !== '') // Исключаем пустые поля
|
|
247
|
-
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(ct_data[key])}`)
|
|
248
|
-
.join('&');
|
|
249
|
-
|
|
250
|
-
if (!post_data) {
|
|
251
|
-
return reject(new Error('No valid data to send'));
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// URL для запроса к API CallTouch
|
|
255
|
-
const CT_URL = `https://api.calltouch.ru/calls-service/RestAPI/requests/${encodeURIComponent(siteId)}/register/`;
|
|
256
|
-
|
|
257
|
-
// Проверяем, не отправляется ли уже запрос (защита от дублирования)
|
|
258
|
-
if (window.ct_snd_flag) {
|
|
259
|
-
return resolve({ status: 'skipped', message: 'Another request is in progress' });
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Устанавливаем флаг для предотвращения дублирования запросов
|
|
263
|
-
window.ct_snd_flag = 1;
|
|
264
|
-
const flagTimeout = setTimeout(() => {
|
|
265
|
-
window.ct_snd_flag = 0;
|
|
266
|
-
}, Math.max(timeout + 5000, 25000)); // Сбрасываем флаг через таймаут + буфер
|
|
267
|
-
|
|
268
|
-
// Отправляем запрос с таймаутом
|
|
269
|
-
createTimeoutRequest(CT_URL, post_data, timeout)
|
|
270
|
-
.then(result => {
|
|
271
|
-
clearTimeout(flagTimeout);
|
|
272
|
-
window.ct_snd_flag = 0;
|
|
273
|
-
resolve({
|
|
274
|
-
...result,
|
|
275
|
-
data: ct_data
|
|
276
|
-
});
|
|
277
|
-
})
|
|
278
|
-
.catch(error => {
|
|
279
|
-
clearTimeout(flagTimeout);
|
|
280
|
-
window.ct_snd_flag = 0;
|
|
281
|
-
reject(error);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
} catch (error) {
|
|
285
|
-
console.error('CallTouch error:', error);
|
|
286
|
-
reject(error);
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Экспорт функций для использования в других модулях
|
|
292
|
-
export { sendToCallTouch, getValueOrEmpty, validatePhone, validateEmail };
|
|
293
|
-
|
|
294
|
-
// Для совместимости с CommonJS
|
|
295
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
296
|
-
module.exports = {
|
|
297
|
-
sendToCallTouch,
|
|
298
|
-
getValueOrEmpty,
|
|
299
|
-
validatePhone,
|
|
300
|
-
validateEmail
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Для использования через тег <script>
|
|
305
|
-
if (typeof window !== 'undefined') {
|
|
306
|
-
window.CallTouchAPI = {
|
|
307
|
-
sendToCallTouch,
|
|
308
|
-
getValueOrEmpty,
|
|
309
|
-
validatePhone,
|
|
310
|
-
validateEmail
|
|
311
|
-
};
|
|
312
|
-
}
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Создает запрос на обратный звонок через CallTouch Widget API
|
|
3
|
-
* @param {string} routeKey - Ключ маршрута виджета
|
|
4
|
-
* @param {string} phoneValue - Номер телефона клиента
|
|
5
|
-
* @param {string} nameValue - Имя клиента (опционально)
|
|
6
|
-
* @param {boolean} verbose - Включить подробное логирование
|
|
7
|
-
* @param {number} timeout - Таймаут для операций (по умолчанию 15000ms)
|
|
8
|
-
* @return {Promise} Promise с результатом создания запроса
|
|
9
|
-
*/
|
|
10
|
-
export const createRequest = (routeKey, phoneValue, nameValue = '', verbose = false, timeout = 15000) => {
|
|
11
|
-
return new Promise((resolve, reject) => {
|
|
12
|
-
// Валидация входных параметров
|
|
13
|
-
if (!routeKey || typeof routeKey !== 'string') {
|
|
14
|
-
return reject(new Error('Route key is required and must be a string'));
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
if (!phoneValue || typeof phoneValue !== 'string') {
|
|
18
|
-
return reject(new Error('Phone value is required and must be a string'));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Проверяем доступность CallTouch Widget API
|
|
22
|
-
if (typeof window === 'undefined' || !window.ctw) {
|
|
23
|
-
return reject(new Error('CallTouch Widget API is not available (window.ctw is not defined)'));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
let timeoutId;
|
|
27
|
-
let isResolved = false;
|
|
28
|
-
|
|
29
|
-
// Устанавливаем общий таймаут для всей операции
|
|
30
|
-
timeoutId = setTimeout(() => {
|
|
31
|
-
if (!isResolved) {
|
|
32
|
-
isResolved = true;
|
|
33
|
-
reject(new Error(`Operation timeout after ${timeout}ms`));
|
|
34
|
-
}
|
|
35
|
-
}, timeout);
|
|
36
|
-
|
|
37
|
-
const cleanup = () => {
|
|
38
|
-
if (timeoutId) {
|
|
39
|
-
clearTimeout(timeoutId);
|
|
40
|
-
timeoutId = null;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
// Получаем данные маршрута
|
|
46
|
-
window.ctw.getRouteKeyData(routeKey, function(success, data) {
|
|
47
|
-
if (isResolved) return;
|
|
48
|
-
|
|
49
|
-
if (verbose) {
|
|
50
|
-
console.log('getRouteKeyData result:', { success, data });
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (!success) {
|
|
54
|
-
cleanup();
|
|
55
|
-
isResolved = true;
|
|
56
|
-
const errorMsg = data && data.message ? data.message : 'Failed to get route key data';
|
|
57
|
-
reject(new Error(`Route key data error: ${errorMsg}`));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Проверяем наличие виджета
|
|
62
|
-
if (!data || !data.widgetFound) {
|
|
63
|
-
cleanup();
|
|
64
|
-
isResolved = true;
|
|
65
|
-
reject(new Error(`Widget not found for route key: ${routeKey}, or callback service is not active`));
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Проверяем настройки виджета
|
|
70
|
-
const widgetData = data.widgetData || {};
|
|
71
|
-
|
|
72
|
-
if (verbose) {
|
|
73
|
-
if (widgetData.callCenterWorkingMode === 'working_hours') {
|
|
74
|
-
console.log('Call center is working, displaying widget');
|
|
75
|
-
} else {
|
|
76
|
-
if (widgetData.collectNonWorkingRequests) {
|
|
77
|
-
console.log('Call center is not working, but can display non-working hours form');
|
|
78
|
-
} else {
|
|
79
|
-
console.log('Call center is not working, not collecting non-working hours requests');
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Нормализуем номер телефона
|
|
85
|
-
let phone_ct = phoneValue.replace(/[^0-9]/g, '');
|
|
86
|
-
|
|
87
|
-
// Убираем префиксы и добавляем российский код
|
|
88
|
-
if (phone_ct.startsWith('8')) {
|
|
89
|
-
phone_ct = phone_ct.substring(1);
|
|
90
|
-
}
|
|
91
|
-
if (phone_ct.startsWith('7')) {
|
|
92
|
-
phone_ct = phone_ct.substring(1);
|
|
93
|
-
}
|
|
94
|
-
phone_ct = '7' + phone_ct;
|
|
95
|
-
|
|
96
|
-
// Валидируем нормализованный номер
|
|
97
|
-
if (phone_ct.length !== 11 || !phone_ct.startsWith('7')) {
|
|
98
|
-
cleanup();
|
|
99
|
-
isResolved = true;
|
|
100
|
-
reject(new Error('Invalid phone number format after normalization'));
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Подготавливаем дополнительные поля
|
|
105
|
-
const additionalFields = [];
|
|
106
|
-
if (nameValue && nameValue.trim().length > 0) {
|
|
107
|
-
additionalFields.push({
|
|
108
|
-
"name": "Name",
|
|
109
|
-
"value": nameValue.trim().substring(0, 100) // Ограничиваем длину
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Создаем запрос на обратный звонок
|
|
114
|
-
window.ctw.createRequest(
|
|
115
|
-
routeKey,
|
|
116
|
-
phone_ct,
|
|
117
|
-
additionalFields,
|
|
118
|
-
function (success, requestData) {
|
|
119
|
-
if (isResolved) return;
|
|
120
|
-
|
|
121
|
-
cleanup();
|
|
122
|
-
isResolved = true;
|
|
123
|
-
|
|
124
|
-
if (verbose) {
|
|
125
|
-
console.log('createRequest result:', { success, requestData });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (success) {
|
|
129
|
-
if (verbose && requestData && requestData.callbackRequestId) {
|
|
130
|
-
console.log('Callback request created with ID:', requestData.callbackRequestId);
|
|
131
|
-
}
|
|
132
|
-
resolve(requestData || { success: true });
|
|
133
|
-
} else {
|
|
134
|
-
let errorText = 'Error in createRequest';
|
|
135
|
-
|
|
136
|
-
if (requestData && requestData.type) {
|
|
137
|
-
switch (requestData.type) {
|
|
138
|
-
case "request_throttle_timeout":
|
|
139
|
-
case "request_throttle_count":
|
|
140
|
-
errorText = 'Request limit reached, please try again later';
|
|
141
|
-
break;
|
|
142
|
-
case "request_phone_blacklisted":
|
|
143
|
-
errorText = 'Phone number is blacklisted';
|
|
144
|
-
break;
|
|
145
|
-
case "validation_error":
|
|
146
|
-
errorText = 'Invalid data provided';
|
|
147
|
-
break;
|
|
148
|
-
case "widget_not_found":
|
|
149
|
-
errorText = 'Widget configuration not found';
|
|
150
|
-
break;
|
|
151
|
-
case "service_unavailable":
|
|
152
|
-
errorText = 'Callback service is temporarily unavailable';
|
|
153
|
-
break;
|
|
154
|
-
default:
|
|
155
|
-
errorText = `Request failed: ${requestData.type}`;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (verbose) {
|
|
159
|
-
console.log('CallTouch error:', errorText);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
reject(new Error(errorText));
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
);
|
|
167
|
-
});
|
|
168
|
-
} catch (error) {
|
|
169
|
-
cleanup();
|
|
170
|
-
if (!isResolved) {
|
|
171
|
-
isResolved = true;
|
|
172
|
-
reject(new Error(`Unexpected error: ${error.message}`));
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
};
|